worker.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // Copyright (C) 2022, 2024 Artifex Software, Inc.
  2. //
  3. // This file is part of MuPDF.
  4. //
  5. // MuPDF is free software: you can redistribute it and/or modify it under the
  6. // terms of the GNU Affero General Public License as published by the Free
  7. // Software Foundation, either version 3 of the License, or (at your option)
  8. // any later version.
  9. //
  10. // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
  11. // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  12. // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  13. // details.
  14. //
  15. // You should have received a copy of the GNU Affero General Public License
  16. // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
  17. //
  18. // Alternative licensing terms are available from the licensor.
  19. // For commercial licensing, see <https://www.artifex.com/> or contact
  20. // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
  21. // CA 94129, USA, for further information.
  22. "use strict"
  23. import * as mupdf from "../../dist/mupdf.js"
  24. const methods = {}
  25. onmessage = async function (event) {
  26. let [ func, id, args ] = event.data
  27. try {
  28. let result = methods[func](...args)
  29. postMessage([ "RESULT", id, result ])
  30. } catch (error) {
  31. postMessage([ "ERROR", id, { name: error.name, message: error.message, stack: error.stack } ])
  32. }
  33. }
  34. var document_next_id = 1
  35. var document_map = {} // open mupdf.Document handles
  36. class WorkerBlobStream {
  37. constructor(blob) {
  38. this.reader = new FileReaderSync()
  39. this.blob = blob
  40. }
  41. fileSize() {
  42. return this.blob.size
  43. }
  44. read(memory, offset, size, position) {
  45. let data = this.reader.readAsArrayBuffer(this.blob.slice(position, position + size))
  46. memory.set(new Uint8Array(data), offset)
  47. return data.byteLength
  48. }
  49. close() {
  50. this.reader = null
  51. this.blob = null
  52. }
  53. }
  54. methods.openDocumentFromBlob = function (blob, magic) {
  55. let stm = new mupdf.Stream(new WorkerBlobStream(blob))
  56. let doc_id = document_next_id++
  57. document_map[doc_id] = mupdf.Document.openDocument(stm, magic)
  58. return doc_id
  59. }
  60. methods.openDocumentFromBuffer = function (buffer, magic) {
  61. let doc_id = document_next_id++
  62. document_map[doc_id] = mupdf.Document.openDocument(buffer, magic)
  63. return doc_id
  64. }
  65. methods.closeDocument = function (doc_id) {
  66. let doc = document_map[doc_id]
  67. doc.destroy()
  68. delete document_map[doc_id]
  69. }
  70. methods.documentTitle = function (doc_id) {
  71. let doc = document_map[doc_id]
  72. return doc.getMetaData(mupdf.Document.META_INFO_TITLE)
  73. }
  74. methods.documentOutline = function (doc_id) {
  75. let doc = document_map[doc_id]
  76. return doc.loadOutline()
  77. }
  78. methods.countPages = function (doc_id) {
  79. let doc = document_map[doc_id]
  80. return doc.countPages()
  81. }
  82. methods.getPageSize = function (doc_id, page_number) {
  83. let doc = document_map[doc_id]
  84. let page = doc.loadPage(page_number)
  85. let bounds = page.getBounds()
  86. return { width: bounds[2] - bounds[0], height: bounds[3] - bounds[1] }
  87. }
  88. methods.getPageLinks = function (doc_id, page_number) {
  89. let doc = document_map[doc_id]
  90. let page = doc.loadPage(page_number)
  91. let links = page.getLinks()
  92. return links.map((link) => {
  93. const [ x0, y0, x1, y1 ] = link.getBounds()
  94. let href
  95. if (link.isExternal())
  96. href = link.getURI()
  97. else
  98. href = `#page${doc.resolveLink(link) + 1}`
  99. return {
  100. x: x0,
  101. y: y0,
  102. w: x1 - x0,
  103. h: y1 - y0,
  104. href,
  105. }
  106. })
  107. }
  108. methods.getPageText = function (doc_id, page_number) {
  109. let doc = document_map[doc_id]
  110. let page = doc.loadPage(page_number)
  111. let text = page.toStructuredText().asJSON()
  112. return JSON.parse(text)
  113. }
  114. methods.search = function (doc_id, page_number, needle) {
  115. let doc = document_map[doc_id]
  116. let page = doc.loadPage(page_number)
  117. const hits = page.search(needle)
  118. let result = []
  119. for (let hit of hits) {
  120. for (let quad of hit) {
  121. const [ ulx, uly, urx, ury, llx, lly, lrx, lry ] = quad
  122. result.push({
  123. x: ulx,
  124. y: uly,
  125. w: urx - ulx,
  126. h: lly - uly,
  127. })
  128. }
  129. }
  130. return result
  131. }
  132. methods.getPageAnnotations = function (doc_id, page_number, dpi) {
  133. let doc = document_map[doc_id]
  134. let page = doc.loadPage(page_number)
  135. if (page == null) {
  136. return []
  137. }
  138. const annotations = page.getAnnotations()
  139. const doc_to_screen = [ dpi = 72, 0, 0, dpi / 72, 0, 0 ]
  140. return annotations.map((annotation) => {
  141. const [ x0, y0, x1, y1 ] = mupdf.Matrix.transformRect(annotation.getBounds())
  142. return {
  143. x: x0,
  144. y: y0,
  145. w: x1 - x0,
  146. h: y1 - y0,
  147. type: annotation.getType(),
  148. ref: annotation.pointer,
  149. }
  150. })
  151. }
  152. methods.drawPageAsPixmap = function (doc_id, page_number, dpi) {
  153. const doc_to_screen = mupdf.Matrix.scale(dpi / 72, dpi / 72)
  154. let doc = document_map[doc_id]
  155. let page = doc.loadPage(page_number)
  156. let bbox = mupdf.Rect.transform(page.getBounds(), doc_to_screen)
  157. let pixmap = new mupdf.Pixmap(mupdf.ColorSpace.DeviceRGB, bbox, true)
  158. pixmap.clear(255)
  159. let device = new mupdf.DrawDevice(doc_to_screen, pixmap)
  160. page.run(device, mupdf.Matrix.identity)
  161. device.close()
  162. // TODO: do we need to make a copy with slice() ?
  163. let imageData = new ImageData(pixmap.getPixels().slice(), pixmap.getWidth(), pixmap.getHeight())
  164. pixmap.destroy()
  165. // TODO: do we need to pass image data as transferable to avoid copying?
  166. return imageData
  167. }
  168. postMessage([ "INIT", 0, Object.keys(methods) ])