1 #include "emscripten.h"
2 #include "mupdf/fitz.h"
3
4 static fz_context *ctx;
5
wasm_rethrow(fz_context * ctx)6 void wasm_rethrow(fz_context *ctx)
7 {
8 EM_ASM({ throw new Error(UTF8ToString($0)); }, fz_caught_message(ctx));
9 }
10
11 EMSCRIPTEN_KEEPALIVE
initContext(void)12 void initContext(void)
13 {
14 ctx = fz_new_context(NULL, NULL, 100<<20);
15 if (!ctx)
16 {
17 EM_ASM({ throw new Error("Cannot create MuPDF context!"); });
18 }
19 fz_register_document_handlers(ctx);
20 }
21
22 EMSCRIPTEN_KEEPALIVE
openDocumentFromBuffer(char * magic,unsigned char * data,size_t len)23 fz_document *openDocumentFromBuffer(char *magic, unsigned char *data, size_t len)
24 {
25 fz_document *document = NULL;
26 fz_buffer *buf = NULL;
27 fz_stream *stm = NULL;
28
29 fz_var(buf);
30 fz_var(stm);
31
32 /* NOTE: We take ownership of input data! */
33
34 fz_try(ctx)
35 {
36 buf = fz_new_buffer_from_data(ctx, data, len);
37 stm = fz_open_buffer(ctx, buf);
38 document = fz_open_document_with_stream(ctx, magic, stm);
39 }
40 fz_always(ctx)
41 {
42 fz_drop_stream(ctx, stm);
43 fz_drop_buffer(ctx, buf);
44 }
45 fz_catch(ctx)
46 {
47 fz_free(ctx, data);
48 wasm_rethrow(ctx);
49 }
50 return document;
51 }
52
53 EMSCRIPTEN_KEEPALIVE
freeDocument(fz_document * doc)54 void freeDocument(fz_document *doc)
55 {
56 fz_drop_document(ctx, doc);
57 }
58
59 EMSCRIPTEN_KEEPALIVE
countPages(fz_document * doc)60 int countPages(fz_document *doc)
61 {
62 int n = 1;
63 fz_try(ctx)
64 n = fz_count_pages(ctx, doc);
65 fz_catch(ctx)
66 wasm_rethrow(ctx);
67 return n;
68 }
69
70 static fz_page *lastPage = NULL;
71
loadPage(fz_document * doc,int number)72 static void loadPage(fz_document *doc, int number)
73 {
74 static fz_document *lastPageDoc = NULL;
75 static int lastPageNumber = -1;
76 if (lastPageNumber != number || lastPageDoc != doc)
77 {
78 if (lastPage)
79 {
80 fz_drop_page(ctx, lastPage);
81 lastPage = NULL;
82 lastPageDoc = NULL;
83 lastPageNumber = -1;
84 }
85 lastPage = fz_load_page(ctx, doc, number-1);
86 lastPageDoc = doc;
87 lastPageNumber = number;
88 }
89 }
90
91 EMSCRIPTEN_KEEPALIVE
pageText(fz_document * doc,int number,float dpi)92 char *pageText(fz_document *doc, int number, float dpi)
93 {
94 static unsigned char *data = NULL;
95 fz_stext_page *text = NULL;
96 fz_buffer *buf = NULL;
97 fz_output *out = NULL;
98
99 fz_var(buf);
100 fz_var(out);
101 fz_var(text);
102
103 fz_stext_options opts = { FZ_STEXT_PRESERVE_SPANS };
104
105 fz_free(ctx, data);
106 data = NULL;
107
108 fz_try(ctx)
109 {
110 loadPage(doc, number);
111
112 buf = fz_new_buffer(ctx, 0);
113 out = fz_new_output_with_buffer(ctx, buf);
114 text = fz_new_stext_page_from_page(ctx, lastPage, &opts);
115
116 fz_print_stext_page_as_json(ctx, out, text, dpi / 72);
117 fz_close_output(ctx, out);
118 fz_terminate_buffer(ctx, buf);
119
120 fz_buffer_extract(ctx, buf, &data);
121 }
122 fz_always(ctx)
123 {
124 fz_drop_stext_page(ctx, text);
125 fz_drop_output(ctx, out);
126 fz_drop_buffer(ctx, buf);
127 }
128 fz_catch(ctx)
129 {
130 wasm_rethrow(ctx);
131 }
132
133 return (char*)data;
134 }
135
136 static fz_buffer *lastDrawBuffer = NULL;
137
138 EMSCRIPTEN_KEEPALIVE
doDrawPageAsPNG(fz_document * doc,int number,float dpi)139 void doDrawPageAsPNG(fz_document *doc, int number, float dpi)
140 {
141 float zoom = dpi / 72;
142 fz_pixmap *pix = NULL;
143
144 fz_var(pix);
145
146 if (lastDrawBuffer)
147 fz_drop_buffer(ctx, lastDrawBuffer);
148 lastDrawBuffer = NULL;
149
150 fz_try(ctx)
151 {
152 loadPage(doc, number);
153 pix = fz_new_pixmap_from_page(ctx, lastPage, fz_scale(zoom, zoom), fz_device_rgb(ctx), 0);
154 lastDrawBuffer = fz_new_buffer_from_pixmap_as_png(ctx, pix, fz_default_color_params);
155 }
156 fz_always(ctx)
157 fz_drop_pixmap(ctx, pix);
158 fz_catch(ctx)
159 wasm_rethrow(ctx);
160 }
161
162 EMSCRIPTEN_KEEPALIVE
getLastDrawData(void)163 unsigned char *getLastDrawData(void)
164 {
165 return lastDrawBuffer ? lastDrawBuffer->data : 0;
166 }
167
168 EMSCRIPTEN_KEEPALIVE
getLastDrawSize(void)169 int getLastDrawSize(void)
170 {
171 return lastDrawBuffer ? lastDrawBuffer->len : 0;
172 }
173
pageBounds(fz_document * doc,int number,float dpi)174 static fz_irect pageBounds(fz_document *doc, int number, float dpi)
175 {
176 fz_irect bbox = fz_empty_irect;
177 fz_try(ctx)
178 {
179 loadPage(doc, number);
180 bbox = fz_round_rect(fz_transform_rect(fz_bound_page(ctx, lastPage), fz_scale(dpi/72, dpi/72)));
181 }
182 fz_catch(ctx)
183 wasm_rethrow(ctx);
184 return bbox;
185 }
186
187 EMSCRIPTEN_KEEPALIVE
pageWidth(fz_document * doc,int number,float dpi)188 int pageWidth(fz_document *doc, int number, float dpi)
189 {
190 fz_irect bbox = fz_empty_irect;
191 fz_try(ctx)
192 {
193 loadPage(doc, number);
194 bbox = pageBounds(doc, number, dpi);
195 }
196 fz_catch(ctx)
197 wasm_rethrow(ctx);
198 return bbox.x1 - bbox.x0;
199 }
200
201 EMSCRIPTEN_KEEPALIVE
pageHeight(fz_document * doc,int number,float dpi)202 int pageHeight(fz_document *doc, int number, float dpi)
203 {
204 fz_irect bbox = fz_empty_irect;
205 fz_try(ctx)
206 {
207 loadPage(doc, number);
208 bbox = pageBounds(doc, number, dpi);
209 }
210 fz_catch(ctx)
211 wasm_rethrow(ctx);
212 return bbox.y1 - bbox.y0;
213 }
214
215 EMSCRIPTEN_KEEPALIVE
pageLinks(fz_document * doc,int number,float dpi)216 char *pageLinks(fz_document *doc, int number, float dpi)
217 {
218 static unsigned char *data = NULL;
219 fz_buffer *buf = NULL;
220 fz_link *links = NULL;
221 fz_link *link;
222
223 fz_var(buf);
224 fz_var(links);
225
226 fz_free(ctx, data);
227 data = NULL;
228
229 fz_try(ctx)
230 {
231 loadPage(doc, number);
232
233 links = fz_load_links(ctx, lastPage);
234
235 buf = fz_new_buffer(ctx, 0);
236
237 fz_append_string(ctx, buf, "[");
238 for (link = links; link; link = link->next)
239 {
240 fz_irect bbox = fz_round_rect(fz_transform_rect(link->rect, fz_scale(dpi/72, dpi/72)));
241 fz_append_string(ctx, buf, "{");
242 fz_append_printf(ctx, buf, "%q:%d,", "x", bbox.x0);
243 fz_append_printf(ctx, buf, "%q:%d,", "y", bbox.y0);
244 fz_append_printf(ctx, buf, "%q:%d,", "w", bbox.x1 - bbox.x0);
245 fz_append_printf(ctx, buf, "%q:%d,", "h", bbox.y1 - bbox.y0);
246 if (fz_is_external_link(ctx, link->uri))
247 {
248 fz_append_printf(ctx, buf, "%q:%q", "href", link->uri);
249 }
250 else
251 {
252 fz_location link_loc = fz_resolve_link(ctx, doc, link->uri, NULL, NULL);
253 int link_page = fz_page_number_from_location(ctx, doc, link_loc);
254 fz_append_printf(ctx, buf, "%q:\"#page%d\"", "href", link_page+1);
255 }
256 fz_append_string(ctx, buf, "}");
257 if (link->next)
258 fz_append_string(ctx, buf, ",");
259 }
260 fz_append_string(ctx, buf, "]");
261 fz_terminate_buffer(ctx, buf);
262
263 fz_buffer_extract(ctx, buf, &data);
264 }
265 fz_always(ctx)
266 {
267 fz_drop_buffer(ctx, buf);
268 fz_drop_link(ctx, links);
269 }
270 fz_catch(ctx)
271 {
272 wasm_rethrow(ctx);
273 }
274
275 return (char*)data;
276 }
277
278 EMSCRIPTEN_KEEPALIVE
search(fz_document * doc,int number,float dpi,const char * needle)279 char *search(fz_document *doc, int number, float dpi, const char *needle)
280 {
281 static unsigned char *data = NULL;
282 fz_buffer *buf = NULL;
283 fz_quad hits[500];
284 int i, n;
285
286 fz_var(buf);
287
288 fz_free(ctx, data);
289 data = NULL;
290
291 fz_try(ctx)
292 {
293 loadPage(doc, number);
294
295 n = fz_search_page(ctx, lastPage, needle, hits, nelem(hits));
296
297 buf = fz_new_buffer(ctx, 0);
298
299 fz_append_string(ctx, buf, "[");
300 for (i = 0; i < n; ++i)
301 {
302 fz_rect rect = fz_rect_from_quad(hits[i]);
303 fz_irect bbox = fz_round_rect(fz_transform_rect(rect, fz_scale(dpi/72, dpi/72)));
304 if (i > 0) fz_append_string(ctx, buf, ",");
305 fz_append_printf(ctx, buf, "{%q:%d,", "x", bbox.x0);
306 fz_append_printf(ctx, buf, "%q:%d,", "y", bbox.y0);
307 fz_append_printf(ctx, buf, "%q:%d,", "w", bbox.x1 - bbox.x0);
308 fz_append_printf(ctx, buf, "%q:%d}", "h", bbox.y1 - bbox.y0);
309 }
310 fz_append_string(ctx, buf, "]");
311 fz_terminate_buffer(ctx, buf);
312
313 fz_buffer_extract(ctx, buf, &data);
314 }
315 fz_always(ctx)
316 {
317 fz_drop_buffer(ctx, buf);
318 }
319 fz_catch(ctx)
320 {
321 wasm_rethrow(ctx);
322 }
323
324 return (char*)data;
325 }
326
327 EMSCRIPTEN_KEEPALIVE
documentTitle(fz_document * doc)328 char *documentTitle(fz_document *doc)
329 {
330 static char buf[100], *result = NULL;
331 fz_try(ctx)
332 {
333 if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_TITLE, buf, sizeof buf) > 0)
334 result = buf;
335 }
336 fz_catch(ctx)
337 wasm_rethrow(ctx);
338 return result;
339 }
340
341 EMSCRIPTEN_KEEPALIVE
loadOutline(fz_document * doc)342 fz_outline *loadOutline(fz_document *doc)
343 {
344 fz_outline *outline = NULL;
345 fz_var(outline);
346 fz_try(ctx)
347 {
348 outline = fz_load_outline(ctx, doc);
349 }
350 fz_catch(ctx)
351 {
352 fz_drop_outline(ctx, outline);
353 wasm_rethrow(ctx);
354 }
355 return outline;
356 }
357
358 EMSCRIPTEN_KEEPALIVE
freeOutline(fz_outline * outline)359 void freeOutline(fz_outline *outline)
360 {
361 fz_drop_outline(ctx, outline);
362 }
363
364 EMSCRIPTEN_KEEPALIVE
outlineTitle(fz_outline * node)365 char *outlineTitle(fz_outline *node)
366 {
367 return node->title;
368 }
369
370 EMSCRIPTEN_KEEPALIVE
outlinePage(fz_outline * node)371 int outlinePage(fz_outline *node)
372 {
373 return node->page + 1;
374 }
375
376 EMSCRIPTEN_KEEPALIVE
outlineDown(fz_outline * node)377 fz_outline *outlineDown(fz_outline *node)
378 {
379 return node->down;
380 }
381
382 EMSCRIPTEN_KEEPALIVE
outlineNext(fz_outline * node)383 fz_outline *outlineNext(fz_outline *node)
384 {
385 return node->next;
386 }
387