1 #include "mupdf/fitz.h"
2 
3 #include <assert.h>
4 #include <string.h>
5 
6 #undef DEBUG_OCR
7 
8 #if !defined(HAVE_LEPTONICA) || !defined(HAVE_TESSERACT)
9 #ifndef OCR_DISABLED
10 #define OCR_DISABLED
11 #endif
12 #endif
13 
14 #ifndef OCR_DISABLED
15 #include "tessocr.h"
16 
17 /*
18 
19 This device can be used in 2 modes, with or without a list.
20 
21 In both modes the OCR device is created with a target device. The
22 caller runs the page to the device, and the device processes the calls
23 and (eventually) calls through to the target.
24 
25 In both modes, all incoming calls are forwarded to an internal draw
26 device to render the page, so the page rendering is always complete.
27 The incoming calls are also forwarded (mostly, eventually) to the
28 target. Where the 2 modes differ is in the timing/content of those
29 forwarded calls.
30 
31 In the first mode (without a list), the device instantly forwards all
32 non-text calls to the target. When the OCR device is closed, an OCR pass
33 is performed, and the recovered text is forwarded to the target. All
34 recovered text is listed as Courier, and ends up on top of the content.
35 
36 This is fine for text extraction and probably for most cases of document
37 conversion. It's no good for correcting the unicode values within a
38 document though.
39 
40 So, we have concocted a second way of working, using a display list. In
41 this mode, as well as rendering every device call that comes in, it
42 forwards them to a display list (and not the target). When the device
43 is closed we OCR the text image, and store the results. We then play
44 the list back through a 'rewrite' device to the target. The rewrite
45 device rewrites the text objects with the correct unicode values. Any
46 characters given by the OCR pass that aren't used by the rewrite step
47 are then sent through as invisible text.
48 
49 This means that all the target device sees is the exact same graphical
50 objects in the exact same order, but with corrected unicode values.
51 Also, any text that appears in the document as a result of images or
52 line art is sent through as 'invisible' text at the end, so it will work
53 for cut/paste or search.
54 
55 Or, at least, that was the plan. Unfortunately, it turns out that
56 Tesseract (with the LSTM engine (the most modern one)) is really bad at
57 giving bounding boxes for characters. It seems that the neural network
58 can say "hey, there is an 'X'", but it can't actually say where the X
59 occurred within the word. So tesseract knows where the words are, and
60 knows the order of the letters within the word, but basically guesses
61 at bboxes for the letters.
62 
63 Because of this, we can't rely on character bboxes from tesseract to be
64 correct. We have to work off the word bboxes alone, together with the
65 order in which characters are passed to us.
66 
67 So, as Tesseract gives us data, we store the word bbox, together with
68 the list of chars within that word.
69 
70 When we play the list back through the display device, we then have to
71 rewrite text objects based on which word they are in. For the first
72 version, we'll make the extremely dodgy assumption that characters
73 come in the same order within the word.
74 
75 For future versions we may want to collect bboxes for each text char
76 on our initial list building pass, collate those into matching 'words'
77 and sort them accordingly.
78 */
79 
80 
81 typedef struct word_record_s {
82 	int len;
83 	fz_rect bbox;
84 	int n;
85 	int unicode[1];
86 } word_record;
87 
88 typedef struct fz_ocr_device_s
89 {
90 	fz_device super;
91 
92 	fz_device *target;
93 	fz_display_list *list;
94 	fz_device *list_dev;
95 	fz_device *draw_dev;
96 	fz_pixmap *pixmap;
97 
98 	fz_rect mediabox;
99 	fz_matrix ctm;
100 
101 	fz_rect word_bbox;
102 	fz_font *font;
103 
104 	/* Current word */
105 	int char_max;
106 	int char_len;
107 	int *chars;
108 
109 	/* Entire page */
110 	int words_max;
111 	int words_len;
112 	word_record **words;
113 
114 	char *language;
115 } fz_ocr_device;
116 
117 static void
fz_ocr_fill_path(fz_context * ctx,fz_device * dev,const fz_path * path,int even_odd,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)118 fz_ocr_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm,
119 	fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
120 {
121 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
122 
123 	fz_fill_path(ctx, ocr->list_dev, path, even_odd, ctm, colorspace, color, alpha, color_params);
124 	fz_fill_path(ctx, ocr->draw_dev, path, even_odd, ctm, colorspace, color, alpha, color_params);
125 }
126 
127 static void
fz_ocr_stroke_path(fz_context * ctx,fz_device * dev,const fz_path * path,const fz_stroke_state * stroke,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)128 fz_ocr_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke,
129 	fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
130 {
131 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
132 
133 	fz_stroke_path(ctx, ocr->list_dev, path, stroke, ctm, colorspace, color, alpha, color_params);
134 	fz_stroke_path(ctx, ocr->draw_dev, path, stroke, ctm, colorspace, color, alpha, color_params);
135 }
136 
137 static void
fz_ocr_fill_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)138 fz_ocr_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm,
139 	fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
140 {
141 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
142 
143 	if (ocr->list_dev != ocr->target)
144 		fz_fill_text(ctx, ocr->list_dev, text, ctm, colorspace, color, alpha, color_params);
145 	fz_fill_text(ctx, ocr->draw_dev, text, ctm, colorspace, color, alpha, color_params);
146 }
147 
148 static void
fz_ocr_stroke_text(fz_context * ctx,fz_device * dev,const fz_text * text,const fz_stroke_state * stroke,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)149 fz_ocr_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke,
150 	fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
151 {
152 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
153 
154 	if (ocr->list_dev != ocr->target)
155 		fz_stroke_text(ctx, ocr->list_dev, text, stroke, ctm, colorspace, color, alpha, color_params);
156 	fz_stroke_text(ctx, ocr->draw_dev, text, stroke, ctm, colorspace, color, alpha, color_params);
157 }
158 
159 static void
fz_ocr_fill_shade(fz_context * ctx,fz_device * dev,fz_shade * shade,fz_matrix ctm,float alpha,fz_color_params color_params)160 fz_ocr_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha, fz_color_params color_params)
161 {
162 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
163 
164 	fz_fill_shade(ctx, ocr->list_dev, shade, ctm, alpha, color_params);
165 	fz_fill_shade(ctx, ocr->draw_dev, shade, ctm, alpha, color_params);
166 }
167 
168 static void
fz_ocr_fill_image(fz_context * ctx,fz_device * dev,fz_image * image,fz_matrix ctm,float alpha,fz_color_params color_params)169 fz_ocr_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm, float alpha, fz_color_params color_params)
170 {
171 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
172 
173 	fz_fill_image(ctx, ocr->list_dev, image, ctm, alpha, color_params);
174 	fz_fill_image(ctx, ocr->draw_dev, image, ctm, alpha, color_params);
175 }
176 
177 static void
fz_ocr_fill_image_mask(fz_context * ctx,fz_device * dev,fz_image * image,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)178 fz_ocr_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm,
179 	fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
180 {
181 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
182 
183 	fz_fill_image_mask(ctx, ocr->list_dev, image, ctm, colorspace, color, alpha, color_params);
184 	fz_fill_image_mask(ctx, ocr->draw_dev, image, ctm, colorspace, color, alpha, color_params);
185 }
186 
187 static void
fz_ocr_clip_path(fz_context * ctx,fz_device * dev,const fz_path * path,int even_odd,fz_matrix ctm,fz_rect scissor)188 fz_ocr_clip_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm, fz_rect scissor)
189 {
190 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
191 
192 	fz_clip_path(ctx, ocr->list_dev, path, even_odd, ctm, scissor);
193 	fz_clip_path(ctx, ocr->draw_dev, path, even_odd, ctm, scissor);
194 }
195 
196 static void
fz_ocr_clip_stroke_path(fz_context * ctx,fz_device * dev,const fz_path * path,const fz_stroke_state * stroke,fz_matrix ctm,fz_rect scissor)197 fz_ocr_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor)
198 {
199 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
200 
201 	fz_clip_stroke_path(ctx, ocr->list_dev, path, stroke, ctm, scissor);
202 	fz_clip_stroke_path(ctx, ocr->draw_dev, path, stroke, ctm, scissor);
203 }
204 
205 static void
fz_ocr_clip_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm,fz_rect scissor)206 fz_ocr_clip_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm, fz_rect scissor)
207 {
208 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
209 
210 	if (ocr->list_dev != ocr->target)
211 		fz_clip_text(ctx, ocr->list_dev, text, ctm, scissor);
212 	fz_clip_text(ctx, ocr->draw_dev, text, ctm, scissor);
213 }
214 
215 static void
fz_ocr_clip_stroke_text(fz_context * ctx,fz_device * dev,const fz_text * text,const fz_stroke_state * stroke,fz_matrix ctm,fz_rect scissor)216 fz_ocr_clip_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor)
217 {
218 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
219 
220 	if (ocr->list_dev != ocr->target)
221 		fz_clip_stroke_text(ctx, ocr->list_dev, text, stroke, ctm, scissor);
222 	fz_clip_stroke_text(ctx, ocr->draw_dev, text, stroke, ctm, scissor);
223 }
224 
225 static void
fz_ocr_ignore_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm)226 fz_ocr_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm)
227 {
228 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
229 
230 	/* Ignore text is generally used when text has been sent as
231 	 * part of other graphics - such as line art or images. As such
232 	 * we'll pick up the 'true' unicode values of such text in the
233 	 * OCR phase. We therefore send text to the list device (so
234 	 * it can be rewritten), but not direct to the target. */
235 	if (ocr->list_dev != ocr->target)
236 		fz_ignore_text(ctx, ocr->list_dev, text, ctm);
237 	fz_ignore_text(ctx, ocr->draw_dev, text, ctm);
238 }
239 
240 static void
fz_ocr_clip_image_mask(fz_context * ctx,fz_device * dev,fz_image * image,fz_matrix ctm,fz_rect scissor)241 fz_ocr_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm, fz_rect scissor)
242 {
243 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
244 
245 	fz_clip_image_mask(ctx, ocr->list_dev, image, ctm, scissor);
246 	fz_clip_image_mask(ctx, ocr->draw_dev, image, ctm, scissor);
247 }
248 
249 static void
fz_ocr_pop_clip(fz_context * ctx,fz_device * dev)250 fz_ocr_pop_clip(fz_context *ctx, fz_device *dev)
251 {
252 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
253 
254 	fz_pop_clip(ctx, ocr->list_dev);
255 	fz_pop_clip(ctx, ocr->draw_dev);
256 }
257 
258 static void
fz_ocr_begin_mask(fz_context * ctx,fz_device * dev,fz_rect rect,int luminosity,fz_colorspace * colorspace,const float * color,fz_color_params color_params)259 fz_ocr_begin_mask(fz_context *ctx, fz_device *dev, fz_rect rect, int luminosity, fz_colorspace *colorspace, const float *color, fz_color_params color_params)
260 {
261 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
262 
263 	fz_begin_mask(ctx, ocr->list_dev, rect, luminosity, colorspace, color, color_params);
264 	fz_begin_mask(ctx, ocr->draw_dev, rect, luminosity, colorspace, color, color_params);
265 }
266 
267 static void
fz_ocr_end_mask(fz_context * ctx,fz_device * dev)268 fz_ocr_end_mask(fz_context *ctx, fz_device *dev)
269 {
270 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
271 
272 	fz_end_mask(ctx, ocr->list_dev);
273 	fz_end_mask(ctx, ocr->draw_dev);
274 }
275 
276 static void
fz_ocr_begin_group(fz_context * ctx,fz_device * dev,fz_rect rect,fz_colorspace * cs,int isolated,int knockout,int blendmode,float alpha)277 fz_ocr_begin_group(fz_context *ctx, fz_device *dev, fz_rect rect, fz_colorspace *cs, int isolated, int knockout, int blendmode, float alpha)
278 {
279 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
280 
281 	fz_begin_group(ctx, ocr->list_dev, rect, cs, isolated, knockout, blendmode, alpha);
282 	fz_begin_group(ctx, ocr->draw_dev, rect, cs, isolated, knockout, blendmode, alpha);
283 }
284 
285 static void
fz_ocr_end_group(fz_context * ctx,fz_device * dev)286 fz_ocr_end_group(fz_context *ctx, fz_device *dev)
287 {
288 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
289 
290 	fz_pop_clip(ctx, ocr->list_dev);
291 	fz_pop_clip(ctx, ocr->draw_dev);
292 }
293 
294 static int
fz_ocr_begin_tile(fz_context * ctx,fz_device * dev,fz_rect area,fz_rect view,float xstep,float ystep,fz_matrix ctm,int id)295 fz_ocr_begin_tile(fz_context *ctx, fz_device *dev, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm, int id)
296 {
297 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
298 
299 	/* Always pass 0 as tile id here so that neither device can
300 	 * disagree about whether the contents need to be sent. */
301 	(void)fz_begin_tile_id(ctx, ocr->list_dev, area, view, xstep, ystep, ctm, 0);
302 	(void)fz_begin_tile_id(ctx, ocr->draw_dev, area, view, xstep, ystep, ctm, 0);
303 
304 	return 0;
305 }
306 
307 static void
fz_ocr_end_tile(fz_context * ctx,fz_device * dev)308 fz_ocr_end_tile(fz_context *ctx, fz_device *dev)
309 {
310 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
311 
312 	fz_end_tile(ctx, ocr->list_dev);
313 	fz_end_tile(ctx, ocr->draw_dev);
314 }
315 
316 static void
fz_ocr_render_flags(fz_context * ctx,fz_device * dev,int set,int clear)317 fz_ocr_render_flags(fz_context *ctx, fz_device *dev, int set, int clear)
318 {
319 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
320 
321 	fz_render_flags(ctx, ocr->list_dev, set, clear);
322 	fz_render_flags(ctx, ocr->draw_dev, set, clear);
323 }
324 
325 static void
fz_ocr_set_default_colorspaces(fz_context * ctx,fz_device * dev,fz_default_colorspaces * cs)326 fz_ocr_set_default_colorspaces(fz_context *ctx, fz_device *dev, fz_default_colorspaces *cs)
327 {
328 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
329 
330 	fz_set_default_colorspaces(ctx, ocr->list_dev, cs);
331 	fz_set_default_colorspaces(ctx, ocr->draw_dev, cs);
332 }
333 
334 static void
fz_ocr_begin_layer(fz_context * ctx,fz_device * dev,const char * layer_name)335 fz_ocr_begin_layer(fz_context *ctx, fz_device *dev, const char *layer_name)
336 {
337 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
338 
339 	fz_begin_layer(ctx, ocr->list_dev, layer_name);
340 	fz_begin_layer(ctx, ocr->draw_dev, layer_name);
341 }
342 
343 static void
fz_ocr_end_layer(fz_context * ctx,fz_device * dev)344 fz_ocr_end_layer(fz_context *ctx, fz_device *dev)
345 {
346 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
347 
348 	fz_end_layer(ctx, ocr->list_dev);
349 	fz_end_layer(ctx, ocr->draw_dev);
350 }
351 
352 static void
drop_ocr_device(fz_context * ctx,fz_ocr_device * ocr)353 drop_ocr_device(fz_context *ctx, fz_ocr_device *ocr)
354 {
355 	int i;
356 
357 	if (ocr == NULL)
358 		return;
359 
360 	if (ocr->list_dev != ocr->target)
361 		fz_drop_device(ctx, ocr->list_dev);
362 	fz_drop_display_list(ctx, ocr->list);
363 	fz_drop_device(ctx, ocr->draw_dev);
364 	fz_drop_pixmap(ctx, ocr->pixmap);
365 	for (i = 0; i < ocr->words_len; i++)
366 		fz_free(ctx, ocr->words[i]);
367 	fz_free(ctx, ocr->words);
368 	fz_free(ctx, ocr->chars);
369 	fz_free(ctx, ocr->language);
370 }
371 
372 static void
flush_word(fz_context * ctx,fz_ocr_device * ocr)373 flush_word(fz_context *ctx, fz_ocr_device *ocr)
374 {
375 	float color = 1;
376 	fz_color_params params = { 0 };
377 	int i;
378 	fz_text *text = NULL;
379 	fz_matrix trm;
380 	float step;
381 	fz_rect char_bbox;
382 
383 	if (ocr->char_len == 0)
384 		return;
385 
386 	/* If we're not sending direct to the target device, then insert
387 	 * all the chars we've found into a table so we can rewrite
388 	 * the text objects that come from the list device on the fly.
389 	 */
390 	if (ocr->list_dev != ocr->target)
391 	{
392 		word_record *word;
393 
394 		if (ocr->words_len == ocr->words_max)
395 		{
396 			int new_max = ocr->words_max * 2;
397 			if (new_max == 0)
398 				new_max = 32;
399 			ocr->words = fz_realloc_array(ctx, ocr->words, new_max, word_record *);
400 			ocr->words_max = new_max;
401 		}
402 		word = (word_record *)Memento_label(fz_malloc(ctx, sizeof(word_record) + sizeof(int) * (ocr->char_len-1)), "word_record");
403 		word->len = ocr->char_len;
404 		word->bbox = ocr->word_bbox;
405 		word->n = 0;
406 		memcpy(word->unicode, ocr->chars, ocr->char_len * sizeof(int));
407 		ocr->words[ocr->words_len++] = word;
408 		ocr->char_len = 0;
409 		return;
410 	}
411 	/* FIXME: Look at font-name. */
412 	/* All this is a bit horrid, because the detection of sizes for
413 	 * the glyphs depends on the width of the glyphs. Use Courier
414 	 * because it's monospaced. */
415 	if (ocr->font == NULL)
416 		ocr->font = fz_new_base14_font(ctx, "Courier");
417 
418 	fz_var(text);
419 
420 	fz_try(ctx)
421 	{
422 		text = fz_new_text(ctx);
423 
424 		/* Divide the word box into equal lengths. */
425 		/* This falls down when we have words with chars of
426 		 * different widths in, but it's acceptable for these
427 		 * purposes. */
428 		/* FIXME: This assumes L2R motion of text. */
429 		step = (ocr->word_bbox.x1 - ocr->word_bbox.x0) / ocr->char_len;
430 		char_bbox.x1 = ocr->word_bbox.x0;
431 		char_bbox.y0 = ocr->word_bbox.y0;
432 		char_bbox.y1 = ocr->word_bbox.y1;
433 		for (i = 0; i < ocr->char_len; i++)
434 		{
435 			char_bbox.x0 = char_bbox.x1;
436 			char_bbox.x1 += step;
437 			/* Horrid constants that happen to work with Courier. */
438 			trm.a = 10.0f/6 * (char_bbox.x1 - char_bbox.x0);
439 			trm.b = 0;
440 			trm.c = 0;
441 			trm.d = (char_bbox.y1 - char_bbox.y0);
442 			trm.e = char_bbox.x0;
443 			trm.f = char_bbox.y0;
444 			fz_show_glyph(ctx, text, ocr->font, trm,
445 				ocr->chars[i], ocr->chars[i],
446 					0, 0, FZ_BIDI_LTR, 0);
447 		}
448 
449 		fz_fill_text(ctx, ocr->target, text, fz_identity,
450 				fz_device_gray(ctx), &color, 1, params);
451 	}
452 	fz_always(ctx)
453 	{
454 		fz_drop_text(ctx, text);
455 	}
456 	fz_catch(ctx)
457 		fz_rethrow(ctx);
458 
459 	ocr->char_len = 0;
460 }
461 
462 static void
char_callback(fz_context * ctx,void * arg,int unicode,const char * font_name,const int * line_bbox,const int * word_bbox,const int * char_bbox,int pointsize)463 char_callback(fz_context *ctx, void *arg, int unicode,
464 		const char *font_name,
465 		const int *line_bbox, const int *word_bbox,
466 		const int *char_bbox, int pointsize)
467 {
468 	fz_ocr_device *ocr = (fz_ocr_device *)arg;
469 	fz_rect bbox = { word_bbox[0]-1, word_bbox[1]-1, word_bbox[2]+1, word_bbox[3]+1 };
470 
471 	if (unicode == 'b')
472 	{
473 		fz_device *device = fz_new_draw_device(ctx, fz_identity, ocr->pixmap);
474 		fz_path *path = fz_new_path(ctx);
475 		fz_color_params params = {0};
476 		fz_stroke_state stroke = { 0 };
477 		float color = 0;
478 		stroke.linewidth = 1;
479 		fz_rectto(ctx, path, char_bbox[0], char_bbox[1], char_bbox[2], char_bbox[3]);
480 		fz_stroke_path(ctx, device, path, &stroke, fz_identity, fz_device_gray(ctx), &color, 1, params);
481 		fz_drop_path(ctx, path);
482 		fz_close_device(ctx, device);
483 		fz_drop_device(ctx, device);
484 	}
485 
486 	if (bbox.x0 != ocr->word_bbox.x0 ||
487 		bbox.y0 != ocr->word_bbox.y0 ||
488 		bbox.x1 != ocr->word_bbox.x1 ||
489 		bbox.y1 != ocr->word_bbox.y1)
490 	{
491 		flush_word(ctx, ocr);
492 		ocr->word_bbox = bbox;
493 	}
494 
495 	if (ocr->char_max == ocr->char_len)
496 	{
497 		int new_max = ocr->char_max * 2;
498 		if (new_max == 0)
499 			new_max = 32;
500 		ocr->chars = fz_realloc_array(ctx, ocr->chars, new_max, int);
501 		ocr->char_max = new_max;
502 	}
503 
504 	ocr->chars[ocr->char_len++] = unicode;
505 }
506 
507 
508 typedef struct
509 {
510 	fz_device super;
511 
512 	fz_device *target;
513 	int words_len;
514 	word_record **words;
515 	int current;
516 } fz_rewrite_device;
517 
518 static fz_text_span *
fz_clone_text_span(fz_context * ctx,const fz_text_span * span)519 fz_clone_text_span(fz_context *ctx, const fz_text_span *span)
520 {
521 	fz_text_span *cspan;
522 
523 	if (span == NULL)
524 		return NULL;
525 
526 	cspan = fz_malloc_struct(ctx, fz_text_span);
527 	*cspan = *span;
528 	cspan->cap = cspan->len;
529 	cspan->items = fz_malloc_no_throw(ctx, sizeof(*cspan->items) * cspan->len);
530 	if (cspan->items == NULL)
531 	{
532 		fz_free(ctx, cspan);
533 		fz_throw(ctx, FZ_ERROR_MEMORY, "Failed to malloc while cloning text span");
534 	}
535 	memcpy(cspan->items, span->items, sizeof(*cspan->items) * cspan->len);
536 	fz_keep_font(ctx, cspan->font);
537 
538 	return cspan;
539 }
540 
541 #ifdef DEBUG_OCR
542 static void
debug_word(fz_context * ctx,word_record * word)543 debug_word(fz_context *ctx, word_record *word)
544 {
545 	int i;
546 
547 	fz_write_printf(ctx, fz_stdout(ctx), "   %g %g %g %g:",
548 			word->bbox.x0,
549 			word->bbox.y0,
550 			word->bbox.x1,
551 			word->bbox.y1);
552 
553 	for (i = 0; i < word->n; i++)
554 	{
555 		int unicode = word->unicode[i];
556 		if (unicode >= 32 && unicode < 127)
557 			fz_write_printf(ctx, fz_stdout(ctx), "%c", unicode);
558 		else
559 			fz_write_printf(ctx, fz_stdout(ctx), "<%04x>", unicode);
560 	}
561 	if (word->n < word->len)
562 	{
563 		int unicode = word->unicode[i++];
564 		if (unicode >= 32 && unicode < 127)
565 			fz_write_printf(ctx, fz_stdout(ctx), "{%c}", unicode);
566 		else
567 			fz_write_printf(ctx, fz_stdout(ctx), "{<%04x>}", unicode);
568 		for (; i < word->len; i++)
569 		{
570 			int unicode = word->unicode[i];
571 			if (unicode >= 32 && unicode < 127)
572 				fz_write_printf(ctx, fz_stdout(ctx), "%c", unicode);
573 			else
574 				fz_write_printf(ctx, fz_stdout(ctx), "<%04x>", unicode);
575 		}
576 	}
577 	fz_write_printf(ctx, fz_stdout(ctx), "\n");
578 }
579 #endif
580 
581 static void
rewrite_char(fz_context * ctx,fz_rewrite_device * dev,fz_matrix ctm,fz_text_item * item,fz_point vadv)582 rewrite_char(fz_context *ctx, fz_rewrite_device *dev, fz_matrix ctm, fz_text_item *item, fz_point vadv)
583 {
584 	int i, start;
585 	fz_point p = { item->x, item->y };
586 
587 	/* No point in trying to rewrite spaces! */
588 	if (item->ucs == 32)
589 		return;
590 
591 	p = fz_transform_point(p, ctm);
592 	p.x += vadv.x/2;
593 	p.y += vadv.y/2;
594 
595 #ifdef DEBUG_OCR
596 	fz_write_printf(ctx, fz_stdout(ctx), "Looking for '%c' at %g %g\n", item->ucs, p.x, p.y);
597 #endif
598 
599 	start = dev->current;
600 	for (i = start; i < dev->words_len; i++)
601 	{
602 #ifdef DEBUG_OCR
603 		debug_word(ctx, dev->words[i]);
604 #endif
605 		if (dev->words[i]->n >= dev->words[i]->len)
606 			continue;
607 		if (dev->words[i]->bbox.x0 <= p.x &&
608 			dev->words[i]->bbox.x1 >= p.x &&
609 			dev->words[i]->bbox.y0 <= p.y &&
610 			dev->words[i]->bbox.y1 >= p.y)
611 		{
612 			item->ucs = dev->words[i]->unicode[dev->words[i]->n++];
613 			dev->current = i;
614 			return;
615 		}
616 	}
617 	for (i = 0; i < start; i++)
618 	{
619 #ifdef DEBUG_OCR
620 		debug_word(ctx, dev->words[i]);
621 #endif
622 		if (dev->words[i]->n >= dev->words[i]->len)
623 			continue;
624 		if (dev->words[i]->bbox.x0 <= p.x &&
625 			dev->words[i]->bbox.x1 >= p.x &&
626 			dev->words[i]->bbox.y0 <= p.y &&
627 			dev->words[i]->bbox.y1 >= p.y)
628 		{
629 			item->ucs = dev->words[i]->unicode[dev->words[i]->n++];
630 			dev->current = i;
631 			return;
632 		}
633 	}
634 }
635 
636 static fz_text_span *
rewrite_span(fz_context * ctx,fz_rewrite_device * dev,fz_matrix ctm,const fz_text_span * span)637 rewrite_span(fz_context *ctx, fz_rewrite_device *dev, fz_matrix ctm, const fz_text_span *span)
638 {
639 	fz_text_span *rspan = fz_clone_text_span(ctx, span);
640 	int wmode = span->wmode;
641 	int i;
642 	fz_point dir;
643 	fz_matrix trm = span->trm;
644 
645 	trm.e = 0;
646 	trm.f = 0;
647 	trm = fz_concat(trm, ctm);
648 
649 	if (wmode == 0)
650 	{
651 		dir.x = 1;
652 		dir.y = 0;
653 	}
654 	else
655 	{
656 		dir.x = 0;
657 		dir.y = -1;
658 	}
659 	dir = fz_transform_vector(dir, trm);
660 
661 	/* And do the actual rewriting */
662 	for (i = 0; i < rspan->len; i++) {
663 		float advance = fz_advance_glyph(ctx, span->font, rspan->items[i].gid, wmode);
664 		fz_point vadv = { dir.x * advance, dir.y * advance };
665 		rewrite_char(ctx, dev, ctm, &rspan->items[i], vadv);
666 	}
667 
668 	return rspan;
669 }
670 
671 static fz_text *
rewrite_text(fz_context * ctx,fz_rewrite_device * dev,fz_matrix ctm,const fz_text * text)672 rewrite_text(fz_context *ctx, fz_rewrite_device *dev, fz_matrix ctm, const fz_text *text)
673 {
674 	fz_text *rtext = fz_new_text(ctx);
675 	fz_text_span *span = text->head;
676 	fz_text_span **dspan = &rtext->head;
677 
678 	fz_try(ctx)
679 	{
680 		while (span)
681 		{
682 			*dspan = rewrite_span(ctx, dev, ctm, span);
683 			rtext->tail = *dspan;
684 			dspan = &(*dspan)->next;
685 			span = span->next;
686 		}
687 	}
688 	fz_catch(ctx)
689 	{
690 		fz_drop_text(ctx, rtext);
691 		fz_rethrow(ctx);
692 	}
693 
694 	return rtext;
695 }
696 
697 static void
rewrite_fill_path(fz_context * ctx,fz_device * dev,const fz_path * path,int even_odd,fz_matrix ctm,fz_colorspace * cs,const float * color,float alpha,fz_color_params params)698 rewrite_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm, fz_colorspace *cs, const float *color, float alpha, fz_color_params params)
699 {
700 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
701 
702 	fz_fill_path(ctx, rewrite->target, path, even_odd, ctm, cs, color, alpha, params);
703 }
704 
705 static void
rewrite_stroke_path(fz_context * ctx,fz_device * dev,const fz_path * path,const fz_stroke_state * stroke,fz_matrix ctm,fz_colorspace * cs,const float * color,float alpha,fz_color_params params)706 rewrite_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, fz_colorspace *cs, const float *color, float alpha, fz_color_params params)
707 {
708 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
709 
710 	fz_stroke_path(ctx, rewrite->target, path, stroke, ctm, cs, color, alpha, params);
711 }
712 
713 static void
rewrite_clip_path(fz_context * ctx,fz_device * dev,const fz_path * path,int even_odd,fz_matrix ctm,fz_rect scissor)714 rewrite_clip_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm, fz_rect scissor)
715 {
716 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
717 
718 	fz_clip_path(ctx, rewrite->target, path, even_odd, ctm, scissor);
719 }
720 
721 static void
rewrite_clip_stroke_path(fz_context * ctx,fz_device * dev,const fz_path * path,const fz_stroke_state * stroke,fz_matrix ctm,fz_rect scissor)722 rewrite_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor)
723 {
724 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
725 
726 	fz_clip_stroke_path(ctx, rewrite->target, path, stroke, ctm, scissor);
727 }
728 
729 static void
rewrite_fill_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm,fz_colorspace * cs,const float * color,float alpha,fz_color_params params)730 rewrite_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm, fz_colorspace *cs, const float *color, float alpha, fz_color_params params)
731 {
732 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
733 	fz_text *rtext = rewrite_text(ctx, rewrite, ctm, text);
734 
735 	fz_try(ctx)
736 		fz_fill_text(ctx, rewrite->target, rtext, ctm, cs, color, alpha, params);
737 	fz_always(ctx)
738 		fz_drop_text(ctx, rtext);
739 	fz_catch(ctx)
740 		fz_rethrow(ctx);
741 }
742 
743 static void
rewrite_stroke_text(fz_context * ctx,fz_device * dev,const fz_text * text,const fz_stroke_state * stroke,fz_matrix ctm,fz_colorspace * cs,const float * color,float alpha,fz_color_params params)744 rewrite_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, fz_colorspace *cs, const float *color, float alpha, fz_color_params params)
745 {
746 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
747 	fz_text *rtext = rewrite_text(ctx, rewrite, ctm, text);
748 
749 	fz_try(ctx)
750 		fz_stroke_text(ctx, rewrite->target, rtext, stroke, ctm, cs, color, alpha, params);
751 	fz_always(ctx)
752 		fz_drop_text(ctx, rtext);
753 	fz_catch(ctx)
754 		fz_rethrow(ctx);
755 }
756 
757 static void
rewrite_clip_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm,fz_rect scissor)758 rewrite_clip_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm, fz_rect scissor)
759 {
760 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
761 	fz_text *rtext = rewrite_text(ctx, rewrite, ctm, text);
762 
763 	fz_try(ctx)
764 		fz_clip_text(ctx, rewrite->target, rtext, ctm, scissor);
765 	fz_always(ctx)
766 		fz_drop_text(ctx, rtext);
767 	fz_catch(ctx)
768 		fz_rethrow(ctx);
769 }
770 
771 static void
rewrite_clip_stroke_text(fz_context * ctx,fz_device * dev,const fz_text * text,const fz_stroke_state * stroke,fz_matrix ctm,fz_rect scissor)772 rewrite_clip_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor)
773 {
774 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
775 	fz_text *rtext = rewrite_text(ctx, rewrite, ctm, text);
776 
777 	fz_try(ctx)
778 		fz_clip_stroke_text(ctx, rewrite->target, rtext, stroke, ctm, scissor);
779 	fz_always(ctx)
780 		fz_drop_text(ctx, rtext);
781 	fz_catch(ctx)
782 		fz_rethrow(ctx);
783 }
784 
785 static void
rewrite_ignore_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm)786 rewrite_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm)
787 {
788 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
789 	fz_text *rtext = rewrite_text(ctx, rewrite, ctm, text);
790 
791 	fz_try(ctx)
792 		fz_ignore_text(ctx, rewrite->target, rtext, ctm);
793 	fz_always(ctx)
794 		fz_drop_text(ctx, rtext);
795 	fz_catch(ctx)
796 		fz_rethrow(ctx);
797 }
798 
799 static void
rewrite_fill_shade(fz_context * ctx,fz_device * dev,fz_shade * shd,fz_matrix ctm,float alpha,fz_color_params color_params)800 rewrite_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shd, fz_matrix ctm, float alpha, fz_color_params color_params)
801 {
802 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
803 
804 	fz_fill_shade(ctx, rewrite->target, shd, ctm, alpha, color_params);
805 }
806 
807 static void
rewrite_fill_image(fz_context * ctx,fz_device * dev,fz_image * img,fz_matrix ctm,float alpha,fz_color_params color_params)808 rewrite_fill_image(fz_context *ctx, fz_device *dev, fz_image *img, fz_matrix ctm, float alpha, fz_color_params color_params)
809 {
810 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
811 
812 	fz_fill_image(ctx, rewrite->target, img, ctm, alpha, color_params);
813 }
814 
815 static void
rewrite_fill_image_mask(fz_context * ctx,fz_device * dev,fz_image * img,fz_matrix ctm,fz_colorspace * cs,const float * color,float alpha,fz_color_params color_params)816 rewrite_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *img, fz_matrix ctm, fz_colorspace *cs, const float *color, float alpha, fz_color_params color_params)
817 {
818 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
819 
820 	fz_fill_image_mask(ctx, rewrite->target, img, ctm, cs, color, alpha, color_params);
821 }
822 
823 static void
rewrite_clip_image_mask(fz_context * ctx,fz_device * dev,fz_image * img,fz_matrix ctm,fz_rect scissor)824 rewrite_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *img, fz_matrix ctm, fz_rect scissor)
825 {
826 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
827 
828 	fz_clip_image_mask(ctx, rewrite->target, img, ctm, scissor);
829 }
830 
831 static void
rewrite_pop_clip(fz_context * ctx,fz_device * dev)832 rewrite_pop_clip(fz_context *ctx, fz_device *dev)
833 {
834 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
835 
836 	fz_pop_clip(ctx, rewrite->target);
837 }
838 
839 static void
rewrite_begin_mask(fz_context * ctx,fz_device * dev,fz_rect area,int luminosity,fz_colorspace * cs,const float * bc,fz_color_params params)840 rewrite_begin_mask(fz_context *ctx, fz_device *dev, fz_rect area, int luminosity, fz_colorspace *cs, const float *bc, fz_color_params params)
841 {
842 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
843 
844 	fz_begin_mask(ctx, rewrite->target, area, luminosity, cs, bc, params);
845 }
846 
847 static void
rewrite_end_mask(fz_context * ctx,fz_device * dev)848 rewrite_end_mask(fz_context *ctx, fz_device *dev)
849 {
850 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
851 
852 	fz_end_mask(ctx, rewrite->target);
853 }
854 
855 static void
rewrite_begin_group(fz_context * ctx,fz_device * dev,fz_rect area,fz_colorspace * cs,int isolated,int knockout,int blendmode,float alpha)856 rewrite_begin_group(fz_context *ctx, fz_device *dev, fz_rect area, fz_colorspace *cs, int isolated, int knockout, int blendmode, float alpha)
857 {
858 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
859 
860 	fz_begin_group(ctx, rewrite->target, area, cs, isolated, knockout, blendmode, alpha);
861 }
862 
863 static void
rewrite_end_group(fz_context * ctx,fz_device * dev)864 rewrite_end_group(fz_context *ctx, fz_device *dev)
865 {
866 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
867 
868 	fz_end_group(ctx, rewrite->target);
869 }
870 
871 static int
rewrite_begin_tile(fz_context * ctx,fz_device * dev,fz_rect area,fz_rect view,float xstep,float ystep,fz_matrix ctm,int id)872 rewrite_begin_tile(fz_context *ctx, fz_device *dev, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm, int id)
873 {
874 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
875 
876 	return fz_begin_tile_id(ctx, rewrite->target, area, view, xstep, ystep, ctm, id);
877 }
878 
879 static void
rewrite_end_tile(fz_context * ctx,fz_device * dev)880 rewrite_end_tile(fz_context *ctx, fz_device *dev)
881 {
882 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
883 
884 	fz_end_tile(ctx, rewrite->target);
885 }
886 
887 static void
rewrite_render_flags(fz_context * ctx,fz_device * dev,int set,int clear)888 rewrite_render_flags(fz_context *ctx, fz_device *dev, int set, int clear)
889 {
890 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
891 
892 	fz_render_flags(ctx, rewrite->target, set, clear);
893 }
894 
895 static void
rewrite_set_default_colorspaces(fz_context * ctx,fz_device * dev,fz_default_colorspaces * cs)896 rewrite_set_default_colorspaces(fz_context *ctx, fz_device *dev, fz_default_colorspaces *cs)
897 {
898 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
899 
900 	fz_set_default_colorspaces(ctx, rewrite->target, cs);
901 }
902 
903 static void
rewrite_begin_layer(fz_context * ctx,fz_device * dev,const char * layer_name)904 rewrite_begin_layer(fz_context *ctx, fz_device *dev, const char *layer_name)
905 {
906 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
907 
908 	fz_begin_layer(ctx, rewrite->target, layer_name);
909 }
910 
911 static void
rewrite_end_layer(fz_context * ctx,fz_device * dev)912 rewrite_end_layer(fz_context *ctx, fz_device *dev)
913 {
914 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
915 
916 	fz_end_layer(ctx, rewrite->target);
917 }
918 
919 static void
rewrite_close(fz_context * ctx,fz_device * dev)920 rewrite_close(fz_context *ctx, fz_device *dev)
921 {
922 	fz_rewrite_device *rewrite = (fz_rewrite_device *)dev;
923 	fz_font *font;
924 	fz_text *text = NULL;
925 	fz_matrix trm;
926 	int i, j;
927 
928 	/* All this is a bit horrid, because the detection of sizes for
929 	 * the glyphs depends on the width of the glyphs. Use Courier
930 	 * because it's monospaced. */
931 	font = fz_new_base14_font(ctx, "Courier");
932 
933 	fz_var(text);
934 
935 	fz_try(ctx)
936 	{
937 		text = fz_new_text(ctx);
938 
939 		for (i = 0; i < rewrite->words_len; i++)
940 		{
941 			word_record *word = rewrite->words[i];
942 			fz_rect char_bbox;
943 			float step;
944 
945 			if (word->n >= word->len)
946 				continue;
947 			step = (word->bbox.x1 - word->bbox.x0) / word->len;
948 			char_bbox.x1 = word->bbox.x0;
949 			char_bbox.y0 = word->bbox.y0;
950 			char_bbox.y1 = word->bbox.y1;
951 			for (j = 0; j < word->len; j++)
952 			{
953 				char_bbox.x0 = char_bbox.x1;
954 				char_bbox.x1 += step;
955 				/* Horrid constants that happen to work with Courier. */
956 				trm.a = 10.0f/6 * (char_bbox.x1 - char_bbox.x0);
957 				trm.b = 0;
958 				trm.c = 0;
959 				trm.d = (char_bbox.y1 - char_bbox.y0);
960 				trm.e = char_bbox.x0;
961 				trm.f = char_bbox.y0;
962 				fz_show_glyph(ctx, text, font, trm,
963 					word->unicode[j], word->unicode[j],
964 					0, 0, FZ_BIDI_LTR, 0);
965 			}
966 		}
967 
968 		fz_ignore_text(ctx, rewrite->target, text, fz_identity);
969 	}
970 	fz_always(ctx)
971 	{
972 		fz_drop_text(ctx, text);
973 		fz_drop_font(ctx, font);
974 	}
975 	fz_catch(ctx)
976 		fz_rethrow(ctx);
977 }
978 
979 static fz_device *
new_rewrite_device(fz_context * ctx,fz_device * target,word_record ** words,int words_len)980 new_rewrite_device(fz_context *ctx, fz_device *target, word_record **words, int words_len)
981 {
982 	fz_rewrite_device *rewrite;
983 
984 	rewrite = fz_new_derived_device(ctx, fz_rewrite_device);
985 
986 	rewrite->super.close_device = rewrite_close;
987 
988 	rewrite->super.fill_path = rewrite_fill_path;
989 	rewrite->super.stroke_path = rewrite_stroke_path;
990 	rewrite->super.clip_path = rewrite_clip_path;
991 	rewrite->super.clip_stroke_path = rewrite_clip_stroke_path;
992 
993 	rewrite->super.fill_text = rewrite_fill_text;
994 	rewrite->super.stroke_text = rewrite_stroke_text;
995 	rewrite->super.clip_text = rewrite_clip_text;
996 	rewrite->super.clip_stroke_text = rewrite_clip_stroke_text;
997 	rewrite->super.ignore_text = rewrite_ignore_text;
998 
999 	rewrite->super.fill_shade = rewrite_fill_shade;
1000 	rewrite->super.fill_image = rewrite_fill_image;
1001 	rewrite->super.fill_image_mask = rewrite_fill_image_mask;
1002 	rewrite->super.clip_image_mask = rewrite_clip_image_mask;
1003 
1004 	rewrite->super.pop_clip = rewrite_pop_clip;
1005 
1006 	rewrite->super.begin_mask = rewrite_begin_mask;
1007 	rewrite->super.end_mask = rewrite_end_mask;
1008 	rewrite->super.begin_group = rewrite_begin_group;
1009 	rewrite->super.end_group = rewrite_end_group;
1010 
1011 	rewrite->super.begin_tile = rewrite_begin_tile;
1012 	rewrite->super.end_tile = rewrite_end_tile;
1013 
1014 	rewrite->super.render_flags = rewrite_render_flags;
1015 	rewrite->super.set_default_colorspaces = rewrite_set_default_colorspaces;
1016 
1017 	rewrite->super.begin_layer = rewrite_begin_layer;
1018 	rewrite->super.end_layer = rewrite_end_layer;
1019 
1020 	rewrite->target = target;
1021 	rewrite->words = words;
1022 	rewrite->words_len = words_len;
1023 	rewrite->current = 0;
1024 
1025 	return &rewrite->super;
1026 }
1027 
1028 static void
fz_ocr_close_device(fz_context * ctx,fz_device * dev)1029 fz_ocr_close_device(fz_context *ctx, fz_device *dev)
1030 {
1031 	fz_ocr_device *ocr = (fz_ocr_device *)dev;
1032 	void *tessapi;
1033 	fz_device *rewrite_device;
1034 	fz_rect bbox;
1035 
1036 	fz_close_device(ctx, ocr->draw_dev);
1037 
1038 	/* Now run the OCR */
1039 	tessapi = ocr_init(ctx, ocr->language);
1040 
1041 	fz_try(ctx)
1042 	{
1043 		ocr_recognise(ctx, tessapi, ocr->pixmap, char_callback, ocr);
1044 		flush_word(ctx, ocr);
1045 	}
1046 	fz_always(ctx)
1047 		ocr_fin(ctx, tessapi);
1048 	fz_catch(ctx)
1049 		fz_rethrow(ctx);
1050 
1051 	fz_save_pixmap_as_png(ctx, ocr->pixmap, "ass.png");
1052 
1053 	/* If we're not using a list, we're done! */
1054 	if (ocr->list_dev == ocr->target)
1055 		return;
1056 
1057 	fz_close_device(ctx, ocr->list_dev);
1058 
1059 	bbox = fz_transform_rect(ocr->mediabox, ocr->ctm);
1060 	rewrite_device = new_rewrite_device(ctx, ocr->target, ocr->words, ocr->words_len);
1061 	fz_try(ctx)
1062 	{
1063 		fz_run_display_list(ctx, ocr->list, rewrite_device,
1064 					fz_identity, bbox, NULL);
1065 	}
1066 	fz_always(ctx)
1067 	{
1068 		fz_close_device(ctx, rewrite_device);
1069 		fz_drop_device(ctx, rewrite_device);
1070 	}
1071 	fz_catch(ctx)
1072 		fz_rethrow(ctx);
1073 }
1074 
1075 static void
fz_ocr_drop_device(fz_context * ctx,fz_device * dev)1076 fz_ocr_drop_device(fz_context *ctx, fz_device *dev)
1077 {
1078 	drop_ocr_device(ctx, (fz_ocr_device *)dev);
1079 }
1080 #endif
1081 
1082 fz_device *
fz_new_ocr_device(fz_context * ctx,fz_device * target,fz_matrix ctm,fz_rect mediabox,int with_list,const char * language)1083 fz_new_ocr_device(fz_context *ctx, fz_device *target, fz_matrix ctm, fz_rect mediabox, int with_list, const char *language)
1084 {
1085 #ifdef OCR_DISABLED
1086 	fz_throw(ctx, FZ_ERROR_GENERIC, "OCR Disabled in this build");
1087 #else
1088 	fz_ocr_device *dev;
1089 
1090 	if (target == NULL)
1091 		fz_throw(ctx, FZ_ERROR_GENERIC, "OCR devices require a target");
1092 
1093 	dev = fz_new_derived_device(ctx, fz_ocr_device);
1094 
1095 	dev->super.close_device = fz_ocr_close_device;
1096 	dev->super.drop_device = fz_ocr_drop_device;
1097 
1098 	dev->super.fill_path = fz_ocr_fill_path;
1099 	dev->super.stroke_path = fz_ocr_stroke_path;
1100 	dev->super.clip_path = fz_ocr_clip_path;
1101 	dev->super.clip_stroke_path = fz_ocr_clip_stroke_path;
1102 
1103 	dev->super.fill_text = fz_ocr_fill_text;
1104 	dev->super.stroke_text = fz_ocr_stroke_text;
1105 	dev->super.clip_text = fz_ocr_clip_text;
1106 	dev->super.clip_stroke_text = fz_ocr_clip_stroke_text;
1107 	dev->super.ignore_text = fz_ocr_ignore_text;
1108 
1109 	dev->super.fill_shade = fz_ocr_fill_shade;
1110 	dev->super.fill_image = fz_ocr_fill_image;
1111 	dev->super.fill_image_mask = fz_ocr_fill_image_mask;
1112 	dev->super.clip_image_mask = fz_ocr_clip_image_mask;
1113 
1114 	dev->super.pop_clip = fz_ocr_pop_clip;
1115 
1116 	dev->super.begin_mask = fz_ocr_begin_mask;
1117 	dev->super.end_mask = fz_ocr_end_mask;
1118 	dev->super.begin_group = fz_ocr_begin_group;
1119 	dev->super.end_group = fz_ocr_end_group;
1120 
1121 	dev->super.begin_tile = fz_ocr_begin_tile;
1122 	dev->super.end_tile = fz_ocr_end_tile;
1123 
1124 	dev->super.render_flags = fz_ocr_render_flags;
1125 	dev->super.set_default_colorspaces = fz_ocr_set_default_colorspaces;
1126 	dev->super.begin_layer = fz_ocr_begin_layer;
1127 	dev->super.end_layer = fz_ocr_end_layer;
1128 
1129 	fz_try(ctx)
1130 	{
1131 		fz_rect bbox;
1132 		fz_irect ibox;
1133 		fz_point res;
1134 
1135 		dev->target = target;
1136 		dev->mediabox = mediabox;
1137 		dev->ctm = ctm;
1138 
1139 		bbox = fz_transform_rect(mediabox, ctm);
1140 		ibox = fz_round_rect(bbox);
1141 		/* Fudge the width to be a multiple of 4. */
1142 		ibox.x1 += (4-(ibox.x1-ibox.x0)) & 3;
1143 		dev->pixmap = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx),
1144 							ibox, NULL, 0);
1145 		fz_clear_pixmap(ctx, dev->pixmap);
1146 		res = fz_transform_point_xy(72, 72, ctm);
1147 		if (res.x < 0)
1148 			res.x = -res.x;
1149 		if (res.x < 1)
1150 			res.x = 1;
1151 		if (res.y < 0)
1152 			res.y = -res.y;
1153 		if (res.y < 1)
1154 			res.y = 1;
1155 		fz_set_pixmap_resolution(ctx, dev->pixmap, res.x, res.y);
1156 
1157 		dev->language = fz_strdup(ctx, language ? language : "eng");
1158 
1159 		dev->draw_dev = fz_new_draw_device(ctx, fz_identity, dev->pixmap);
1160 		if (with_list)
1161 		{
1162 			dev->list = fz_new_display_list(ctx, mediabox);
1163 			dev->list_dev = fz_new_list_device(ctx, dev->list);
1164 		} else
1165 			dev->list_dev = dev->target;
1166 	}
1167 	fz_catch(ctx)
1168 	{
1169 		drop_ocr_device(ctx, dev);
1170 		fz_rethrow(ctx);
1171 	}
1172 
1173 	return (fz_device*)dev;
1174 #endif
1175 }
1176