output-pdfocr.c 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172
  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 <limits.h>
  26. #ifdef OCR_DISABLED
  27. /* In non-OCR builds, we need to define this otherwise SWIG Python gets SEGV
  28. when it attempts to import mupdf.py and _mupdf.py. */
  29. const char *fz_pdfocr_write_options_usage = "";
  30. #else
  31. #include "tessocr.h"
  32. const char *fz_pdfocr_write_options_usage =
  33. "PDFOCR output options:\n"
  34. "\tcompression=none: No compression (default)\n"
  35. "\tcompression=flate: Flate compression\n"
  36. "\tstrip-height=N: Strip height (default 0=fullpage)\n"
  37. "\tocr-language=<lang>: OCR language (default=eng)\n"
  38. "\tocr-datadir=<datadir>: OCR data path (default=rely on TESSDATA_PREFIX)\n"
  39. "\tskew=none,auto,<angle>: Whether to skew correct (default=none).\n"
  40. "\tskew-border=increase,maintain,decrease: Size change for border pixels (default=increase).\n"
  41. "\n";
  42. static const char funky_font[] =
  43. "3 0 obj\n<</BaseFont/GlyphLessFont/DescendantFonts[4 0 R]"
  44. "/Encoding/Identity-H/Subtype/Type0/ToUnicode 6 0 R/Type/Font"
  45. ">>\nendobj\n";
  46. static const char funky_font2[] =
  47. "4 0 obj\n"
  48. "<</BaseFont/GlyphLessFont/CIDToGIDMap 5 0 R"
  49. "/CIDSystemInfo<</Ordering (Identity)/Registry (Adobe)/Supplement 0>>"
  50. "/FontDescriptor 7 0 R/Subtype/CIDFontType2/Type/Font/DW 500>>"
  51. "\nendobj\n";
  52. static const char funky_font3[] =
  53. "5 0 obj\n<</Length 210/Filter/FlateDecode>>\nstream\n"
  54. "\x78\x9c\xec\xc2\x01\x09\x00\x00\x00\x02\xa0\xfa\x7f\xba\x21\x89"
  55. "\xa6\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  56. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  57. "\x80\x7b\x03\x00\x00\xff\xff\xec\xc2\x01\x0d\x00\x00\x00\xc2\x20"
  58. "\xdf\xbf\xb4\x45\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  59. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  60. "\x00\x00\x00\x00\x00\xeb\x00\x00\x00\xff\xff\xec\xc2\x01\x0d\x00"
  61. "\x00\x00\xc2\x20\xdf\xbf\xb4\x45\x18\x00\x00\x00\x00\x00\x00\x00"
  62. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  63. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xeb\x00\x00\x00\xff\xff\xed"
  64. "\xc2\x01\x0d\x00\x00\x00\xc2\x20\xdf\xbf\xb4\x45\x18\x00\x00\x00"
  65. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  66. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xeb\x00\xff"
  67. "\x00\x10"
  68. "\nendstream\nendobj\n";
  69. static const char funky_font4[] =
  70. "6 0 obj\n<</Length 353>>\nstream\n"
  71. "/CIDInit /ProcSet findresource begin\n"
  72. "12 dict begin\n"
  73. "begincmap\n"
  74. "/CIDSystemInfo\n"
  75. "<<\n"
  76. " /Registry (Adobe)\n"
  77. " /Ordering (UCS)\n"
  78. " /Supplement 0\n"
  79. ">> def\n"
  80. "/CMapName /Adobe-Identity-UCS def\n"
  81. "/CMapType 2 def\n"
  82. "1 begincodespacerange\n"
  83. "<0000> <FFFF>\n"
  84. "endcodespacerange\n"
  85. "1 beginbfrange\n"
  86. "<0000> <FFFF> <0000>\n"
  87. "endbfrange\n"
  88. "endcmap\n"
  89. "CMapName currentdict /CMap defineresource pop\n"
  90. "end\n"
  91. "end\n"
  92. "endstream\n"
  93. "endobj\n";
  94. static const char funky_font5[] =
  95. "7 0 obj\n"
  96. "<</Ascent 1000/CapHeight 1000/Descent -1/Flags 5"
  97. "/FontBBox[0 0 500 1000]/FontFile2 8 0 R/FontName/GlyphLessFont"
  98. "/ItalicAngle 0/StemV 80/Type/FontDescriptor>>\nendobj\n";
  99. static const char funky_font6[] =
  100. "8 0 obj\n<</Length 572/Length1 572>>\nstream\n"
  101. "\x00\x01\x00\x00\x00\x0a\x00\x80\x00\x03\x00\x20\x4f\x53\x2f\x32"
  102. "\x56\xde\xc8\x94\x00\x00\x01\x28\x00\x00\x00\x60\x63\x6d\x61\x70"
  103. "\x00\x0a\x00\x34\x00\x00\x01\x90\x00\x00\x00\x1e\x67\x6c\x79\x66"
  104. "\x15\x22\x41\x24\x00\x00\x01\xb8\x00\x00\x00\x18\x68\x65\x61\x64"
  105. "\x0b\x78\xf1\x65\x00\x00\x00\xac\x00\x00\x00\x36\x68\x68\x65\x61"
  106. "\x0c\x02\x04\x02\x00\x00\x00\xe4\x00\x00\x00\x24\x68\x6d\x74\x78"
  107. "\x04\x00\x00\x00\x00\x00\x01\x88\x00\x00\x00\x08\x6c\x6f\x63\x61"
  108. "\x00\x0c\x00\x00\x00\x00\x01\xb0\x00\x00\x00\x06\x6d\x61\x78\x70"
  109. "\x00\x04\x00\x05\x00\x00\x01\x08\x00\x00\x00\x20\x6e\x61\x6d\x65"
  110. "\xf2\xeb\x16\xda\x00\x00\x01\xd0\x00\x00\x00\x4b\x70\x6f\x73\x74"
  111. "\x00\x01\x00\x01\x00\x00\x02\x1c\x00\x00\x00\x20\x00\x01\x00\x00"
  112. "\x00\x01\x00\x00\xb0\x94\x71\x10\x5f\x0f\x3c\xf5\x04\x07\x08\x00"
  113. "\x00\x00\x00\x00\xcf\x9a\xfc\x6e\x00\x00\x00\x00\xd4\xc3\xa7\xf2"
  114. "\x00\x00\x00\x00\x04\x00\x08\x00\x00\x00\x00\x10\x00\x02\x00\x00"
  115. "\x00\x00\x00\x00\x00\x01\x00\x00\x08\x00\xff\xff\x00\x00\x04\x00"
  116. "\x00\x00\x00\x00\x04\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"
  117. "\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x00\x00\x02\x00\x04"
  118. "\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"
  119. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x01\x90\x00\x05"
  120. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  121. "\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x01\x00\x01\x00\x00\x00"
  122. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  123. "\x00\x00\x47\x4f\x4f\x47\x00\x40\x00\x00\x00\x00\x00\x01\xff\xff"
  124. "\x00\x00\x00\x01\x00\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  125. "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00"
  126. "\x00\x00\x00\x02\x00\x01\x00\x00\x00\x00\x00\x14\x00\x03\x00\x00"
  127. "\x00\x00\x00\x14\x00\x06\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00"
  128. "\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x04\x00"
  129. "\x08\x00\x00\x03\x00\x00\x31\x21\x11\x21\x04\x00\xfc\x00\x08\x00"
  130. "\x00\x00\x00\x03\x00\x2a\x00\x00\x00\x03\x00\x00\x00\x05\x00\x16"
  131. "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x05\x00\x0b\x00\x16\x00\x03"
  132. "\x00\x01\x04\x09\x00\x05\x00\x16\x00\x00\x00\x56\x00\x65\x00\x72"
  133. "\x00\x73\x00\x69\x00\x6f\x00\x6e\x00\x20\x00\x31\x00\x2e\x00\x30"
  134. "\x56\x65\x72\x73\x69\x6f\x6e\x20\x31\x2e\x30\x00\x00\x01\x00\x00"
  135. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
  136. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  137. "\nendstream\nendobj\n";
  138. #endif
  139. fz_pdfocr_options *
  140. fz_parse_pdfocr_options(fz_context *ctx, fz_pdfocr_options *opts, const char *args)
  141. {
  142. #ifdef OCR_DISABLED
  143. fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "No OCR support in this build");
  144. #else
  145. const char *val;
  146. memset(opts, 0, sizeof *opts);
  147. if (fz_has_option(ctx, args, "compression", &val))
  148. {
  149. if (fz_option_eq(val, "none"))
  150. opts->compress = 0;
  151. else if (fz_option_eq(val, "flate"))
  152. opts->compress = 1;
  153. else
  154. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unsupported PDFOCR compression %s (none, or flate only)", val);
  155. }
  156. if (fz_has_option(ctx, args, "strip-height", &val))
  157. {
  158. int i = fz_atoi(val);
  159. if (i <= 0)
  160. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unsupported PDFOCR strip height %d (suggest 0)", i);
  161. opts->strip_height = i;
  162. }
  163. if (fz_has_option(ctx, args, "ocr-language", &val))
  164. {
  165. fz_copy_option(ctx, val, opts->language, nelem(opts->language));
  166. }
  167. if (fz_has_option(ctx, args, "ocr-datadir", &val))
  168. {
  169. fz_copy_option(ctx, val, opts->datadir, nelem(opts->datadir));
  170. }
  171. if (fz_has_option(ctx, args, "skew", &val))
  172. {
  173. if (fz_option_eq(val, "auto"))
  174. opts->skew_correct = 1;
  175. else
  176. {
  177. opts->skew_correct = 2;
  178. opts->skew_angle = fz_atof(val);
  179. }
  180. }
  181. if (fz_has_option(ctx, args, "skew-border", &val))
  182. {
  183. if (fz_option_eq(val, "increase"))
  184. opts->skew_border = 0;
  185. else if (fz_option_eq(val, "maintain"))
  186. opts->skew_border = 1;
  187. else if (fz_option_eq(val, "decrease"))
  188. opts->skew_border = 2;
  189. else
  190. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unsupported skew-border option");
  191. }
  192. return opts;
  193. #endif
  194. }
  195. void
  196. fz_write_pixmap_as_pdfocr(fz_context *ctx, fz_output *out, const fz_pixmap *pixmap, const fz_pdfocr_options *pdfocr)
  197. {
  198. #ifdef OCR_DISABLED
  199. fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "No OCR support in this build");
  200. #else
  201. fz_band_writer *writer;
  202. if (!pixmap || !out)
  203. return;
  204. writer = fz_new_pdfocr_band_writer(ctx, out, pdfocr);
  205. fz_try(ctx)
  206. {
  207. fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace, pixmap->seps);
  208. fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
  209. fz_close_band_writer(ctx, writer);
  210. }
  211. fz_always(ctx)
  212. fz_drop_band_writer(ctx, writer);
  213. fz_catch(ctx)
  214. fz_rethrow(ctx);
  215. #endif
  216. }
  217. #ifndef OCR_DISABLED
  218. typedef struct pdfocr_band_writer_s
  219. {
  220. fz_band_writer super;
  221. fz_pdfocr_options options;
  222. /* The actual output size */
  223. int deskewed_w;
  224. int deskewed_h;
  225. int obj_num;
  226. int xref_max;
  227. int64_t *xref;
  228. int pages;
  229. int page_max;
  230. int *page_obj;
  231. unsigned char *stripbuf;
  232. unsigned char *compbuf;
  233. size_t complen;
  234. fz_pixmap *skew_bitmap;
  235. void *tessapi;
  236. fz_pixmap *ocrbitmap;
  237. fz_pdfocr_progress_fn *progress;
  238. void *progress_arg;
  239. } pdfocr_band_writer;
  240. static int
  241. new_obj(fz_context *ctx, pdfocr_band_writer *writer)
  242. {
  243. int64_t pos = fz_tell_output(ctx, writer->super.out);
  244. if (writer->obj_num >= writer->xref_max)
  245. {
  246. int new_max = writer->xref_max * 2;
  247. if (new_max < writer->obj_num + 8)
  248. new_max = writer->obj_num + 8;
  249. writer->xref = fz_realloc_array(ctx, writer->xref, new_max, int64_t);
  250. writer->xref_max = new_max;
  251. }
  252. writer->xref[writer->obj_num] = pos;
  253. return writer->obj_num++;
  254. }
  255. static void
  256. post_skew_write_header(fz_context *ctx, pdfocr_band_writer *writer, int w, int h)
  257. {
  258. fz_output *out = writer->super.out;
  259. int xres = writer->super.xres;
  260. int yres = writer->super.yres;
  261. int sh = writer->options.strip_height;
  262. int n = writer->super.n;
  263. int strips;
  264. int i;
  265. if (sh == 0)
  266. sh = h;
  267. assert(sh != 0 && "pdfocr_write_header() should not be given zero height input.");
  268. strips = (h + sh-1)/sh;
  269. writer->deskewed_w = w;
  270. writer->deskewed_h = h;
  271. writer->stripbuf = Memento_label(fz_malloc(ctx, (size_t)w * sh * n), "pdfocr_stripbuf");
  272. writer->complen = fz_deflate_bound(ctx, (size_t)w * sh * n);
  273. writer->compbuf = Memento_label(fz_malloc(ctx, writer->complen), "pdfocr_compbuf");
  274. /* Always round the width of ocrbitmap up to a multiple of 4. */
  275. writer->ocrbitmap = fz_new_pixmap(ctx, NULL, (w+3)&~3, h, NULL, 0);
  276. fz_set_pixmap_resolution(ctx, writer->ocrbitmap, xres, yres);
  277. /* Send the Page Object */
  278. fz_write_printf(ctx, out, "%d 0 obj\n<</Type/Page/Parent 2 0 R/Resources<</XObject<<", new_obj(ctx, writer));
  279. for (i = 0; i < strips; i++)
  280. fz_write_printf(ctx, out, "/I%d %d 0 R", i, writer->obj_num + i);
  281. fz_write_printf(ctx, out, ">>/Font<</F0 3 0 R>>>>/MediaBox[0 0 %g %g]/Contents %d 0 R>>\nendobj\n",
  282. w * 72.0f / xres, h * 72.0f / yres, writer->obj_num + strips);
  283. }
  284. static void
  285. pdfocr_write_header(fz_context *ctx, fz_band_writer *writer_, fz_colorspace *cs)
  286. {
  287. pdfocr_band_writer *writer = (pdfocr_band_writer *)writer_;
  288. fz_output *out = writer->super.out;
  289. int w = writer->super.w;
  290. int h = writer->super.h;
  291. int n = writer->super.n;
  292. int s = writer->super.s;
  293. int a = writer->super.alpha;
  294. int sh = writer->options.strip_height;
  295. if (sh == 0)
  296. sh = h;
  297. assert(sh != 0 && "pdfocr_write_header() should not be given zero height input.");
  298. if (a != 0)
  299. fz_throw(ctx, FZ_ERROR_ARGUMENT, "PDFOCR cannot write alpha channel");
  300. if (s != 0)
  301. fz_throw(ctx, FZ_ERROR_ARGUMENT, "PDFOCR cannot write spot colors");
  302. if (n != 3 && n != 1)
  303. fz_throw(ctx, FZ_ERROR_ARGUMENT, "PDFOCR expected to be Grayscale or RGB");
  304. fz_free(ctx, writer->stripbuf);
  305. writer->stripbuf = NULL;
  306. fz_free(ctx, writer->compbuf);
  307. writer->compbuf = NULL;
  308. fz_drop_pixmap(ctx, writer->ocrbitmap);
  309. writer->ocrbitmap = NULL;
  310. /* Send the file header on the first page */
  311. if (writer->pages == 0)
  312. {
  313. fz_write_string(ctx, out, "%PDF-1.4\n%PDFOCR-1.0\n");
  314. if (writer->xref_max < 9)
  315. {
  316. int new_max = 9;
  317. writer->xref = fz_realloc_array(ctx, writer->xref, new_max, int64_t);
  318. writer->xref_max = new_max;
  319. }
  320. writer->xref[3] = fz_tell_output(ctx, out);
  321. fz_write_data(ctx, out, funky_font, sizeof(funky_font)-1);
  322. writer->xref[4] = fz_tell_output(ctx, out);
  323. fz_write_data(ctx, out, funky_font2, sizeof(funky_font2)-1);
  324. writer->xref[5] = fz_tell_output(ctx, out);
  325. fz_write_data(ctx, out, funky_font3, sizeof(funky_font3)-1);
  326. writer->xref[6] = fz_tell_output(ctx, out);
  327. fz_write_data(ctx, out, funky_font4, sizeof(funky_font4)-1);
  328. writer->xref[7] = fz_tell_output(ctx, out);
  329. fz_write_data(ctx, out, funky_font5, sizeof(funky_font5)-1);
  330. writer->xref[8] = fz_tell_output(ctx, out);
  331. fz_write_data(ctx, out, funky_font6, sizeof(funky_font6)-1);
  332. }
  333. if (writer->page_max <= writer->pages)
  334. {
  335. int new_max = writer->page_max * 2;
  336. if (new_max == 0)
  337. new_max = writer->pages + 8;
  338. writer->page_obj = fz_realloc_array(ctx, writer->page_obj, new_max, int);
  339. writer->page_max = new_max;
  340. }
  341. writer->page_obj[writer->pages] = writer->obj_num;
  342. writer->pages++;
  343. if (writer->options.skew_correct)
  344. writer->skew_bitmap = fz_new_pixmap(ctx, n == 3 ? fz_device_rgb(ctx) : fz_device_gray(ctx), w, h, NULL, 0);
  345. else
  346. post_skew_write_header(ctx, writer, w, h);
  347. }
  348. static void
  349. flush_strip(fz_context *ctx, pdfocr_band_writer *writer, int fill)
  350. {
  351. unsigned char *data = writer->stripbuf;
  352. fz_output *out = writer->super.out;
  353. int w = writer->deskewed_w;
  354. int n = writer->super.n;
  355. size_t len = (size_t)w*n*fill;
  356. /* Buffer is full, compress it and write it. */
  357. if (writer->options.compress)
  358. {
  359. size_t destLen = writer->complen;
  360. fz_deflate(ctx, writer->compbuf, &destLen, data, len, FZ_DEFLATE_DEFAULT);
  361. len = destLen;
  362. data = writer->compbuf;
  363. }
  364. fz_write_printf(ctx, out, "%d 0 obj\n<</Width %d/ColorSpace/Device%s/Height %d%s/Subtype/Image",
  365. new_obj(ctx, writer), w, n == 1 ? "Gray" : "RGB", fill, writer->options.compress ? "/Filter/FlateDecode" : "");
  366. fz_write_printf(ctx, out, "/Length %zd/Type/XObject/BitsPerComponent 8>>\nstream\n", len);
  367. fz_write_data(ctx, out, data, len);
  368. fz_write_string(ctx, out, "\nendstream\nendobj\n");
  369. }
  370. static void
  371. post_skew_write_band(fz_context *ctx, pdfocr_band_writer *writer, int stride, int band_start, int band_height, const unsigned char *sp)
  372. {
  373. int w = writer->deskewed_w;
  374. int h = writer->deskewed_h;
  375. int n = writer->super.n;
  376. int x, y;
  377. int sh = writer->options.strip_height;
  378. int line;
  379. unsigned char *d;
  380. if (sh == 0)
  381. sh = h;
  382. for (line = 0; line < band_height; line++)
  383. {
  384. int dstline = (band_start+line) % sh;
  385. memcpy(writer->stripbuf + (size_t)w*n*dstline,
  386. sp + (size_t)line * w * n,
  387. (size_t)w * n);
  388. if (dstline+1 == sh)
  389. flush_strip(ctx, writer, dstline+1);
  390. }
  391. if (band_start + band_height == h && h % sh != 0)
  392. flush_strip(ctx, writer, h % sh);
  393. /* Copy strip to ocrbitmap, converting if required. */
  394. d = writer->ocrbitmap->samples;
  395. d += band_start*w;
  396. if (n == 1)
  397. {
  398. for (y = band_height; y > 0; y--)
  399. {
  400. memcpy(d, sp, w);
  401. if (writer->ocrbitmap->w - w)
  402. memset(d + w, 0, writer->ocrbitmap->w - w);
  403. d += writer->ocrbitmap->w;
  404. }
  405. }
  406. else
  407. {
  408. for (y = band_height; y > 0; y--)
  409. {
  410. for (x = w; x > 0; x--)
  411. {
  412. *d++ = (sp[0] + 2*sp[1] + sp[2] + 2)>>2;
  413. sp += 3;
  414. }
  415. for (x = writer->ocrbitmap->w - w; x > 0; x--)
  416. *d++ = 0;
  417. }
  418. }
  419. }
  420. static void
  421. pdfocr_write_band(fz_context *ctx, fz_band_writer *writer_, int stride, int band_start, int band_height, const unsigned char *sp)
  422. {
  423. pdfocr_band_writer *writer = (pdfocr_band_writer *)writer_;
  424. fz_output *out = writer->super.out;
  425. int w = writer->super.w;
  426. int n = writer->super.n;
  427. unsigned char *d;
  428. if (!out)
  429. return;
  430. if (writer->skew_bitmap)
  431. {
  432. d = writer->skew_bitmap->samples;
  433. d += band_start*w*n;
  434. memcpy(d, sp, w*n*band_height);
  435. }
  436. else
  437. post_skew_write_band(ctx, writer, stride, band_start, band_height, sp);
  438. }
  439. enum
  440. {
  441. WORD_CONTAINS_L2R = 1,
  442. WORD_CONTAINS_R2L = 2,
  443. WORD_CONTAINS_T2B = 4,
  444. WORD_CONTAINS_B2T = 8
  445. };
  446. typedef struct word_t
  447. {
  448. struct word_t *next;
  449. float bbox[4];
  450. int dirn;
  451. int len;
  452. int chars[FZ_FLEXIBLE_ARRAY];
  453. } word_t;
  454. typedef struct
  455. {
  456. fz_buffer *buf;
  457. pdfocr_band_writer *writer;
  458. /* We collate the current word into the following fields: */
  459. int word_max;
  460. int word_len;
  461. int *word_chars;
  462. float word_bbox[4];
  463. int word_dirn;
  464. int word_prev_char_bbox[4];
  465. /* When we finish a word, we try to add it to the line. If the
  466. * word fits onto the end of the existing line, great. If not,
  467. * we flush the entire line, and start a new one just with the
  468. * new word. This enables us to output a whole line at once,
  469. * which is beneficial to avoid jittering the font sizes
  470. * up/down, which looks bad when we try to select text in the
  471. * produced PDF. */
  472. word_t *line;
  473. word_t **line_tail;
  474. float line_bbox[4];
  475. int line_dirn;
  476. float cur_size;
  477. float cur_scale;
  478. float tx, ty;
  479. } char_callback_data_t;
  480. static void
  481. flush_words(fz_context *ctx, char_callback_data_t *cb)
  482. {
  483. float size;
  484. if (cb->line == NULL)
  485. return;
  486. if ((cb->line_dirn & (WORD_CONTAINS_T2B | WORD_CONTAINS_B2T)) != 0)
  487. {
  488. /* Vertical line */
  489. }
  490. else
  491. {
  492. /* Horizontal line */
  493. size = cb->line_bbox[3] - cb->line_bbox[1];
  494. if (size != 0 && size != cb->cur_size)
  495. {
  496. fz_append_printf(ctx, cb->buf, "/F0 %g Tf\n", size);
  497. cb->cur_size = size;
  498. }
  499. /* Guard against division by 0. This makes no difference to the
  500. * actual calculation as if size is 0, word->bbox[2] == word->bbox[0]
  501. * too. */
  502. if (size == 0)
  503. size = 1;
  504. }
  505. while (cb->line)
  506. {
  507. word_t *word = cb->line;
  508. float x, y;
  509. int i, len = word->len;
  510. float scale;
  511. if ((cb->line_dirn & (WORD_CONTAINS_T2B | WORD_CONTAINS_B2T)) != 0)
  512. {
  513. /* Contains vertical text. */
  514. size = (word->bbox[3] - word->bbox[1]) / len;
  515. if (size == 0)
  516. size = 1;
  517. if (size != cb->cur_size)
  518. {
  519. fz_append_printf(ctx, cb->buf, "/F0 %g Tf\n", size);
  520. cb->cur_size = size;
  521. }
  522. /* Set the scale so that our glyphs fill the line bbox. */
  523. scale = (cb->line_bbox[2] - cb->line_bbox[0]) / size * 200;
  524. if (scale != 0)
  525. {
  526. float letter_height = (word->bbox[3] - word->bbox[1]) / len;
  527. if (scale != cb->cur_scale)
  528. {
  529. fz_append_printf(ctx, cb->buf, "%d Tz\n", (int)scale);
  530. cb->cur_scale = scale;
  531. }
  532. for (i = 0; i < len; i++)
  533. {
  534. x = word->bbox[0];
  535. y = word->bbox[1] + letter_height * i;
  536. fz_append_printf(ctx, cb->buf, "%g %g Td\n", x-cb->tx, y-cb->ty);
  537. cb->tx = x;
  538. cb->ty = y;
  539. fz_append_printf(ctx, cb->buf, "<%04x>Tj\n", word->chars[i]);
  540. }
  541. }
  542. }
  543. else
  544. {
  545. scale = (word->bbox[2] - word->bbox[0]) / size / len * 200;
  546. if (scale != 0)
  547. {
  548. if (scale != cb->cur_scale)
  549. {
  550. fz_append_printf(ctx, cb->buf, "%d Tz\n", (int)scale);
  551. cb->cur_scale = scale;
  552. }
  553. if ((word->dirn & (WORD_CONTAINS_R2L | WORD_CONTAINS_L2R)) == WORD_CONTAINS_R2L)
  554. {
  555. /* Purely R2L text */
  556. x = word->bbox[0];
  557. y = cb->line_bbox[1];
  558. fz_append_printf(ctx, cb->buf, "%g %g Td\n", x-cb->tx, y-cb->ty);
  559. cb->tx = x;
  560. cb->ty = y;
  561. /* Tesseract has sent us R2L text in R2L order (i.e. in Logical order).
  562. * We want to output it in that same logical order, but PDF operators
  563. * all move the point as if outputting L2R. We can either reverse the
  564. * order of chars (bad, because of cut/paste) or we can perform
  565. * gymnastics with the position. We opt for the latter. */
  566. fz_append_printf(ctx, cb->buf, "[");
  567. for (i = 0; i < len; i++)
  568. {
  569. if (i == 0)
  570. {
  571. if (len > 1)
  572. fz_append_printf(ctx, cb->buf, "%d", -500*(len-1));
  573. }
  574. else
  575. fz_append_printf(ctx, cb->buf, "%d", 1000);
  576. fz_append_printf(ctx, cb->buf, "<%04x>", word->chars[i]);
  577. }
  578. fz_append_printf(ctx, cb->buf, "]TJ\n");
  579. }
  580. else
  581. {
  582. /* L2R (or mixed) text */
  583. x = word->bbox[0];
  584. y = cb->line_bbox[1];
  585. fz_append_printf(ctx, cb->buf, "%g %g Td\n", x-cb->tx, y-cb->ty);
  586. cb->tx = x;
  587. cb->ty = y;
  588. fz_append_printf(ctx, cb->buf, "<");
  589. for (i = 0; i < len; i++)
  590. fz_append_printf(ctx, cb->buf, "%04x", word->chars[i]);
  591. fz_append_printf(ctx, cb->buf, ">Tj\n");
  592. }
  593. }
  594. }
  595. cb->line = word->next;
  596. fz_free(ctx, word);
  597. }
  598. cb->line_tail = &cb->line;
  599. cb->line = NULL;
  600. cb->line_dirn = 0;
  601. }
  602. static void
  603. queue_word(fz_context *ctx, char_callback_data_t *cb)
  604. {
  605. word_t *word;
  606. int line_is_v, line_is_h, word_is_v, word_is_h;
  607. if (cb->word_len == 0)
  608. return;
  609. word = fz_malloc_flexible(ctx, word_t, chars, cb->word_len);
  610. word->next = NULL;
  611. word->len = cb->word_len;
  612. memcpy(word->bbox, cb->word_bbox, 4*sizeof(float));
  613. memcpy(word->chars, cb->word_chars, cb->word_len * sizeof(int));
  614. cb->word_len = 0;
  615. line_is_v = !!(cb->line_dirn & (WORD_CONTAINS_B2T | WORD_CONTAINS_T2B));
  616. word_is_v = !!(cb->word_dirn & (WORD_CONTAINS_B2T | WORD_CONTAINS_T2B));
  617. line_is_h = !!(cb->line_dirn & (WORD_CONTAINS_L2R | WORD_CONTAINS_R2L));
  618. word_is_h = !!(cb->word_dirn & (WORD_CONTAINS_L2R | WORD_CONTAINS_R2L));
  619. word->dirn = cb->word_dirn;
  620. cb->word_dirn = 0;
  621. /* Can we put the new word onto the end of the existing line? */
  622. if (cb->line != NULL &&
  623. !line_is_v && !word_is_v &&
  624. word->bbox[1] <= cb->line_bbox[3] &&
  625. word->bbox[3] >= cb->line_bbox[1] &&
  626. (word->bbox[0] >= cb->line_bbox[2] || word->bbox[2] <= cb->line_bbox[0]))
  627. {
  628. /* Can append (horizontal motion). */
  629. if (word->bbox[0] < cb->line_bbox[0])
  630. cb->line_bbox[0] = word->bbox[0];
  631. if (word->bbox[1] < cb->line_bbox[1])
  632. cb->line_bbox[1] = word->bbox[1];
  633. if (word->bbox[2] > cb->line_bbox[2])
  634. cb->line_bbox[2] = word->bbox[2];
  635. if (word->bbox[3] > cb->line_bbox[3])
  636. cb->line_bbox[3] = word->bbox[3];
  637. }
  638. else if (cb->line != NULL &&
  639. !line_is_h && !word_is_h &&
  640. word->bbox[0] <= cb->line_bbox[2] &&
  641. word->bbox[2] >= cb->line_bbox[0] &&
  642. (word->bbox[1] >= cb->line_bbox[3] || word->bbox[3] <= cb->line_bbox[1]))
  643. {
  644. /* Can append (vertical motion). */
  645. if (!word_is_v)
  646. word->dirn |= WORD_CONTAINS_T2B;
  647. if (word->bbox[0] < cb->line_bbox[0])
  648. cb->line_bbox[0] = word->bbox[0];
  649. if (word->bbox[1] < cb->line_bbox[1])
  650. cb->line_bbox[1] = word->bbox[1];
  651. if (word->bbox[2] > cb->line_bbox[2])
  652. cb->line_bbox[2] = word->bbox[2];
  653. if (word->bbox[3] > cb->line_bbox[3])
  654. cb->line_bbox[3] = word->bbox[3];
  655. }
  656. else
  657. {
  658. fz_try(ctx)
  659. flush_words(ctx, cb);
  660. fz_catch(ctx)
  661. {
  662. fz_free(ctx, word);
  663. fz_rethrow(ctx);
  664. }
  665. memcpy(cb->line_bbox, word->bbox, 4*sizeof(float));
  666. }
  667. *cb->line_tail = word;
  668. cb->line_tail = &word->next;
  669. cb->line_dirn |= word->dirn;
  670. }
  671. static void
  672. char_callback(fz_context *ctx, void *arg, int unicode,
  673. const char *font_name,
  674. const int *line_bbox, const int *word_bbox,
  675. const int *char_bbox, int pointsize)
  676. {
  677. char_callback_data_t *cb = (char_callback_data_t *)arg;
  678. pdfocr_band_writer *writer = cb->writer;
  679. float bbox[4];
  680. bbox[0] = word_bbox[0] * 72.0f / cb->writer->ocrbitmap->xres;
  681. bbox[3] = (writer->ocrbitmap->h - 1 - word_bbox[1]) * 72.0f / cb->writer->ocrbitmap->yres;
  682. bbox[2] = word_bbox[2] * 72.0f / cb->writer->ocrbitmap->yres;
  683. bbox[1] = (writer->ocrbitmap->h - 1 - word_bbox[3]) * 72.0f / cb->writer->ocrbitmap->yres;
  684. if (bbox[0] != cb->word_bbox[0] ||
  685. bbox[1] != cb->word_bbox[1] ||
  686. bbox[2] != cb->word_bbox[2] ||
  687. bbox[3] != cb->word_bbox[3])
  688. {
  689. queue_word(ctx, cb);
  690. memcpy(cb->word_bbox, bbox, 4 * sizeof(float));
  691. }
  692. if (cb->word_len == 0)
  693. {
  694. cb->word_dirn = 0;
  695. memcpy(cb->word_prev_char_bbox, char_bbox, 4 * sizeof(int));
  696. }
  697. else
  698. {
  699. int ox = cb->word_prev_char_bbox[0] + cb->word_prev_char_bbox[2];
  700. int oy = cb->word_prev_char_bbox[1] + cb->word_prev_char_bbox[3];
  701. int x = char_bbox[0] + char_bbox[2] - ox;
  702. int y = char_bbox[1] + char_bbox[3] - oy;
  703. int ax = x < 0 ? -x : x;
  704. int ay = y < 0 ? -y : y;
  705. if (ax > ay)
  706. {
  707. if (x > 0)
  708. cb->word_dirn |= WORD_CONTAINS_L2R;
  709. else if (x < 0)
  710. cb->word_dirn |= WORD_CONTAINS_R2L;
  711. }
  712. else if (ay < ax)
  713. {
  714. if (y > 0)
  715. cb->word_dirn |= WORD_CONTAINS_T2B;
  716. else if (y < 0)
  717. cb->word_dirn |= WORD_CONTAINS_B2T;
  718. }
  719. }
  720. if (cb->word_max == cb->word_len)
  721. {
  722. int newmax = cb->word_max * 2;
  723. if (newmax == 0)
  724. newmax = 16;
  725. cb->word_chars = fz_realloc_array(ctx, cb->word_chars, newmax, int);
  726. cb->word_max = newmax;
  727. }
  728. cb->word_chars[cb->word_len++] = unicode;
  729. }
  730. static int
  731. pdfocr_progress(fz_context *ctx, void *arg, int prog)
  732. {
  733. char_callback_data_t *cb = (char_callback_data_t *)arg;
  734. pdfocr_band_writer *writer = cb->writer;
  735. if (writer->progress == NULL)
  736. return 0;
  737. return writer->progress(ctx, writer->progress_arg, writer->pages - 1, prog);
  738. }
  739. static void
  740. do_skew_correct(fz_context *ctx, pdfocr_band_writer *writer)
  741. {
  742. fz_pixmap *deskewed;
  743. if (writer->options.skew_correct == 1)
  744. writer->options.skew_angle = fz_detect_skew(ctx, writer->skew_bitmap);
  745. deskewed = fz_deskew_pixmap(ctx, writer->skew_bitmap, writer->options.skew_angle, writer->options.skew_border);
  746. fz_try(ctx)
  747. {
  748. post_skew_write_header(ctx, writer, deskewed->w, deskewed->h);
  749. post_skew_write_band(ctx, writer, deskewed->stride, 0, deskewed->h, deskewed->samples);
  750. }
  751. fz_always(ctx)
  752. fz_drop_pixmap(ctx, deskewed);
  753. fz_catch(ctx)
  754. fz_rethrow(ctx);
  755. }
  756. static void
  757. pdfocr_write_trailer(fz_context *ctx, fz_band_writer *writer_)
  758. {
  759. pdfocr_band_writer *writer = (pdfocr_band_writer *)writer_;
  760. fz_output *out = writer->super.out;
  761. int xres = writer->super.xres;
  762. int yres = writer->super.yres;
  763. int sh = writer->options.strip_height;
  764. int strips;
  765. int w, h, i;
  766. size_t len;
  767. unsigned char *data;
  768. fz_buffer *buf = NULL;
  769. char_callback_data_t cb = { NULL };
  770. if (writer->options.skew_correct)
  771. do_skew_correct(ctx, writer);
  772. w = writer->deskewed_w;
  773. h = writer->deskewed_h;
  774. if (sh == 0)
  775. sh = h;
  776. strips = (h + sh-1)/sh;
  777. /* Send the Page contents */
  778. /* We need the length to this, so write to a buffer first */
  779. fz_var(buf);
  780. fz_var(cb);
  781. fz_try(ctx)
  782. {
  783. cb.writer = writer;
  784. cb.buf = buf = fz_new_buffer(ctx, 0);
  785. cb.line_tail = &cb.line;
  786. cb.word_dirn = 0;
  787. cb.line_dirn = 0;
  788. fz_append_printf(ctx, buf, "q\n%g 0 0 %g 0 0 cm\n", 72.0f/xres, 72.0f/yres);
  789. for (i = 0; i < strips; i++)
  790. {
  791. int at = h - (i+1)*sh;
  792. int this_sh = sh;
  793. if (at < 0)
  794. {
  795. this_sh += at;
  796. at = 0;
  797. }
  798. fz_append_printf(ctx, buf, "/P <</MCID 0>> BDC\nq\n%d 0 0 %d 0 %d cm\n/I%d Do\nQ\n",
  799. w, this_sh, at, i);
  800. }
  801. fz_append_printf(ctx, buf, "Q\nBT\n3 Tr\n");
  802. ocr_recognise(ctx, writer->tessapi, writer->ocrbitmap, char_callback, pdfocr_progress, &cb);
  803. queue_word(ctx, &cb);
  804. flush_words(ctx, &cb);
  805. fz_append_printf(ctx, buf, "ET\n");
  806. len = fz_buffer_storage(ctx, buf, &data);
  807. fz_write_printf(ctx, out, "%d 0 obj\n<</Length %zd>>\nstream\n", new_obj(ctx, writer), len);
  808. fz_write_data(ctx, out, data, len);
  809. fz_drop_buffer(ctx, buf);
  810. buf = NULL;
  811. fz_write_string(ctx, out, "\nendstream\nendobj\n");
  812. }
  813. fz_always(ctx)
  814. {
  815. fz_free(ctx, cb.word_chars);
  816. }
  817. fz_catch(ctx)
  818. {
  819. fz_drop_buffer(ctx, buf);
  820. fz_rethrow(ctx);
  821. }
  822. }
  823. static void
  824. pdfocr_close_band_writer(fz_context *ctx, fz_band_writer *writer_)
  825. {
  826. pdfocr_band_writer *writer = (pdfocr_band_writer *)writer_;
  827. fz_output *out = writer->super.out;
  828. int i;
  829. /* We actually do the trailer writing in the close */
  830. if (writer->xref_max > 2)
  831. {
  832. int64_t t_pos;
  833. /* Catalog */
  834. writer->xref[1] = fz_tell_output(ctx, out);
  835. fz_write_printf(ctx, out, "1 0 obj\n<</Type/Catalog/Pages 2 0 R>>\nendobj\n");
  836. /* Page table */
  837. writer->xref[2] = fz_tell_output(ctx, out);
  838. fz_write_printf(ctx, out, "2 0 obj\n<</Count %d/Kids[", writer->pages);
  839. for (i = 0; i < writer->pages; i++)
  840. {
  841. if (i > 0)
  842. fz_write_byte(ctx, out, ' ');
  843. fz_write_printf(ctx, out, "%d 0 R", writer->page_obj[i]);
  844. }
  845. fz_write_string(ctx, out, "]/Type/Pages>>\nendobj\n");
  846. /* Xref */
  847. t_pos = fz_tell_output(ctx, out);
  848. fz_write_printf(ctx, out, "xref\n0 %d\n0000000000 65535 f \n", writer->obj_num);
  849. for (i = 1; i < writer->obj_num; i++)
  850. fz_write_printf(ctx, out, "%010ld 00000 n \n", writer->xref[i]);
  851. fz_write_printf(ctx, out, "trailer\n<</Size %d/Root 1 0 R>>\nstartxref\n%ld\n%%%%EOF\n", writer->obj_num, t_pos);
  852. }
  853. }
  854. static void
  855. pdfocr_drop_band_writer(fz_context *ctx, fz_band_writer *writer_)
  856. {
  857. pdfocr_band_writer *writer = (pdfocr_band_writer *)writer_;
  858. fz_free(ctx, writer->stripbuf);
  859. fz_free(ctx, writer->compbuf);
  860. fz_free(ctx, writer->page_obj);
  861. fz_free(ctx, writer->xref);
  862. fz_drop_pixmap(ctx, writer->ocrbitmap);
  863. ocr_fin(ctx, writer->tessapi);
  864. }
  865. #endif
  866. fz_band_writer *fz_new_pdfocr_band_writer(fz_context *ctx, fz_output *out, const fz_pdfocr_options *options)
  867. {
  868. #ifdef OCR_DISABLED
  869. fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "No OCR support in this build");
  870. #else
  871. pdfocr_band_writer *writer = fz_new_band_writer(ctx, pdfocr_band_writer, out);
  872. writer->super.header = pdfocr_write_header;
  873. writer->super.band = pdfocr_write_band;
  874. writer->super.trailer = pdfocr_write_trailer;
  875. writer->super.close = pdfocr_close_band_writer;
  876. writer->super.drop = pdfocr_drop_band_writer;
  877. if (options)
  878. writer->options = *options;
  879. else
  880. memset(&writer->options, 0, sizeof(writer->options));
  881. /* Objects:
  882. * 1 reserved for catalog
  883. * 2 for pages tree
  884. * 3 font
  885. * 4 cidfont
  886. * 5 cid to gid map
  887. * 6 tounicode
  888. * 7 font descriptor
  889. * 8 font file
  890. */
  891. writer->obj_num = 9;
  892. fz_try(ctx)
  893. {
  894. writer->tessapi = ocr_init(ctx, writer->options.language, writer->options.datadir);
  895. }
  896. fz_catch(ctx)
  897. {
  898. fz_drop_band_writer(ctx, &writer->super);
  899. fz_rethrow(ctx);
  900. }
  901. return &writer->super;
  902. #endif
  903. }
  904. void
  905. fz_pdfocr_band_writer_set_progress(fz_context *ctx, fz_band_writer *writer_, fz_pdfocr_progress_fn *progress, void *progress_arg)
  906. {
  907. #ifdef OCR_DISABLED
  908. fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "No OCR support in this build");
  909. #else
  910. pdfocr_band_writer *writer = (pdfocr_band_writer *)writer_;
  911. if (writer == NULL)
  912. return;
  913. if (writer->super.header != pdfocr_write_header)
  914. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Not a pdfocr band writer!");
  915. writer->progress = progress;
  916. writer->progress_arg = progress_arg;
  917. #endif
  918. }
  919. void
  920. fz_save_pixmap_as_pdfocr(fz_context *ctx, fz_pixmap *pixmap, char *filename, int append, const fz_pdfocr_options *pdfocr)
  921. {
  922. #ifdef OCR_DISABLED
  923. fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "No OCR support in this build");
  924. #else
  925. fz_output *out = fz_new_output_with_path(ctx, filename, append);
  926. fz_try(ctx)
  927. {
  928. fz_write_pixmap_as_pdfocr(ctx, out, pixmap, pdfocr);
  929. fz_close_output(ctx, out);
  930. }
  931. fz_always(ctx)
  932. fz_drop_output(ctx, out);
  933. fz_catch(ctx)
  934. fz_rethrow(ctx);
  935. #endif
  936. }
  937. /* High-level document writer interface */
  938. #ifndef OCR_DISABLED
  939. typedef struct
  940. {
  941. fz_document_writer super;
  942. fz_draw_options draw;
  943. fz_pdfocr_options pdfocr;
  944. fz_pixmap *pixmap;
  945. fz_band_writer *bander;
  946. fz_output *out;
  947. int pagenum;
  948. } fz_pdfocr_writer;
  949. static fz_device *
  950. pdfocr_begin_page(fz_context *ctx, fz_document_writer *wri_, fz_rect mediabox)
  951. {
  952. fz_pdfocr_writer *wri = (fz_pdfocr_writer*)wri_;
  953. return fz_new_draw_device_with_options(ctx, &wri->draw, mediabox, &wri->pixmap);
  954. }
  955. static void
  956. pdfocr_end_page(fz_context *ctx, fz_document_writer *wri_, fz_device *dev)
  957. {
  958. fz_pdfocr_writer *wri = (fz_pdfocr_writer*)wri_;
  959. fz_pixmap *pix = wri->pixmap;
  960. fz_try(ctx)
  961. {
  962. fz_close_device(ctx, dev);
  963. fz_write_header(ctx, wri->bander, pix->w, pix->h, pix->n, pix->alpha, pix->xres, pix->yres, wri->pagenum++, pix->colorspace, pix->seps);
  964. fz_write_band(ctx, wri->bander, pix->stride, pix->h, pix->samples);
  965. }
  966. fz_always(ctx)
  967. {
  968. fz_drop_device(ctx, dev);
  969. fz_drop_pixmap(ctx, pix);
  970. wri->pixmap = NULL;
  971. }
  972. fz_catch(ctx)
  973. fz_rethrow(ctx);
  974. }
  975. static void
  976. pdfocr_close_writer(fz_context *ctx, fz_document_writer *wri_)
  977. {
  978. fz_pdfocr_writer *wri = (fz_pdfocr_writer*)wri_;
  979. fz_close_band_writer(ctx, wri->bander);
  980. fz_close_output(ctx, wri->out);
  981. }
  982. static void
  983. pdfocr_drop_writer(fz_context *ctx, fz_document_writer *wri_)
  984. {
  985. fz_pdfocr_writer *wri = (fz_pdfocr_writer*)wri_;
  986. fz_drop_pixmap(ctx, wri->pixmap);
  987. fz_drop_band_writer(ctx, wri->bander);
  988. fz_drop_output(ctx, wri->out);
  989. }
  990. #endif
  991. fz_document_writer *
  992. fz_new_pdfocr_writer_with_output(fz_context *ctx, fz_output *out, const char *options)
  993. {
  994. #ifdef OCR_DISABLED
  995. fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "No OCR support in this build");
  996. #else
  997. fz_pdfocr_writer *wri = NULL;
  998. fz_var(wri);
  999. fz_try(ctx)
  1000. {
  1001. wri = fz_new_derived_document_writer(ctx, fz_pdfocr_writer, pdfocr_begin_page, pdfocr_end_page, pdfocr_close_writer, pdfocr_drop_writer);
  1002. fz_parse_draw_options(ctx, &wri->draw, options);
  1003. fz_parse_pdfocr_options(ctx, &wri->pdfocr, options);
  1004. wri->out = out;
  1005. wri->bander = fz_new_pdfocr_band_writer(ctx, wri->out, &wri->pdfocr);
  1006. }
  1007. fz_catch(ctx)
  1008. {
  1009. fz_drop_output(ctx, out);
  1010. fz_free(ctx, wri);
  1011. fz_rethrow(ctx);
  1012. }
  1013. return (fz_document_writer*)wri;
  1014. #endif
  1015. }
  1016. fz_document_writer *
  1017. fz_new_pdfocr_writer(fz_context *ctx, const char *path, const char *options)
  1018. {
  1019. #ifdef OCR_DISABLED
  1020. fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "No OCR support in this build");
  1021. #else
  1022. fz_output *out = fz_new_output_with_path(ctx, path ? path : "out.pdfocr", 0);
  1023. return fz_new_pdfocr_writer_with_output(ctx, out, options);
  1024. #endif
  1025. }
  1026. void
  1027. fz_pdfocr_writer_set_progress(fz_context *ctx, fz_document_writer *writer, fz_pdfocr_progress_fn *progress, void *progress_arg)
  1028. {
  1029. #ifdef OCR_DISABLED
  1030. fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "No OCR support in this build");
  1031. #else
  1032. fz_pdfocr_writer *wri = (fz_pdfocr_writer *)writer;
  1033. if (!writer)
  1034. return;
  1035. if (writer->begin_page != pdfocr_begin_page)
  1036. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Not a pdfocr writer!");
  1037. fz_pdfocr_band_writer_set_progress(ctx, wri->bander, progress, progress_arg);
  1038. #endif
  1039. }