ocr-device.c 34 KB

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