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