mutool_draw.py 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
  1. import getopt
  2. import os
  3. import re
  4. import sys
  5. import time
  6. if os.environ.get('MUPDF_PYTHON') in ('swig', None):
  7. # PYTHONPATH should have been set up to point to a build/shared-*/
  8. # directory containing mupdf.so generated by scripts/mupdfwrap.py and SWIG.
  9. import mupdf
  10. elif os.environ.get('MUPDF_PYTHON') == 'cppyy':
  11. sys.path.insert(0, os.path.abspath(f'{__file__}/../../platform/python'))
  12. import mupdf_cppyy
  13. del sys.path[0]
  14. mupdf = mupdf_cppyy.cppyy.gbl.mupdf
  15. else:
  16. raise Exception(f'Unrecognised $MUPDF_PYTHON: {os.environ.get("MUPDF_PYTHON")}')
  17. # Force stderr to be line-buffered - i.e. python will flush to the underlying
  18. # stderr stream every newline. This ensures that our output interleaves with
  19. # the output of mupdf C code, making it easier to compare our output with that
  20. # of mutool.
  21. #
  22. sys.stderr = os.fdopen( os.dup( sys.stderr.fileno()), 'w', 1)
  23. OUT_NONE = 0
  24. OUT_PNG = 1
  25. OUT_PNM = 2
  26. OUT_PGM = 3
  27. OUT_PPM = 4
  28. OUT_PAM = 5
  29. OUT_PBM = 6
  30. OUT_PKM = 7
  31. OUT_PWG = 8
  32. OUT_PCL = 9
  33. OUT_PS = 10
  34. OUT_PSD = 11
  35. OUT_TEXT = 12
  36. OUT_HTML = 13
  37. OUT_XHTML = 14
  38. OUT_STEXT = 15
  39. OUT_PCLM = 16
  40. OUT_TRACE = 17
  41. OUT_BBOX = 18
  42. OUT_SVG = 19
  43. OUT_XMLTEXT = 20
  44. CS_INVALID = 0
  45. CS_UNSET = 1
  46. CS_MONO = 2
  47. CS_GRAY = 3
  48. CS_GRAY_ALPHA = 4
  49. CS_RGB = 5
  50. CS_RGB_ALPHA = 6
  51. CS_CMYK = 7
  52. CS_CMYK_ALPHA = 8
  53. CS_ICC = 9
  54. CS_INVALID = 0
  55. CS_UNSET = 1
  56. CS_MONO = 2
  57. CS_GRAY = 3
  58. CS_GRAY_ALPHA = 4
  59. CS_RGB = 5
  60. CS_RGB_ALPHA = 6
  61. CS_CMYK = 7
  62. CS_CMYK_ALPHA = 8
  63. CS_ICC = 9
  64. SPOTS_NONE = 0
  65. SPOTS_OVERPRINT_SIM = 1
  66. SPOTS_FULL = 2
  67. class suffix_t:
  68. def __init__( self, suffix, format_, spots):
  69. self.suffix = suffix
  70. self.format = format_
  71. self.spots = spots
  72. suffix_table = [
  73. suffix_t( ".png", OUT_PNG, 0 ),
  74. suffix_t( ".pgm", OUT_PGM, 0 ),
  75. suffix_t( ".ppm", OUT_PPM, 0 ),
  76. suffix_t( ".pnm", OUT_PNM, 0 ),
  77. suffix_t( ".pam", OUT_PAM, 0 ),
  78. suffix_t( ".pbm", OUT_PBM, 0 ),
  79. suffix_t( ".pkm", OUT_PKM, 0 ),
  80. suffix_t( ".svg", OUT_SVG, 0 ),
  81. suffix_t( ".pwg", OUT_PWG, 0 ),
  82. suffix_t( ".pclm", OUT_PCLM, 0 ),
  83. suffix_t( ".pcl", OUT_PCL, 0 ),
  84. suffix_t( ".psd", OUT_PSD, 1 ),
  85. suffix_t( ".ps", OUT_PS, 0 ),
  86. suffix_t( ".txt", OUT_TEXT, 0 ),
  87. suffix_t( ".text", OUT_TEXT, 0 ),
  88. suffix_t( ".html", OUT_HTML, 0 ),
  89. suffix_t( ".xhtml", OUT_XHTML, 0 ),
  90. suffix_t( ".stext", OUT_STEXT, 0 ),
  91. suffix_t( ".trace", OUT_TRACE, 0 ),
  92. suffix_t( ".raw", OUT_XMLTEXT, 0 ),
  93. suffix_t( ".bbox", OUT_BBOX, 0 ),
  94. ]
  95. class cs_name_t:
  96. def __init__( self, name, colorspace):
  97. self.name = name
  98. self.colorspace = colorspace
  99. cs_name_table = dict(
  100. m = CS_MONO,
  101. mono = CS_MONO,
  102. g = CS_GRAY,
  103. gray = CS_GRAY,
  104. grey = CS_GRAY,
  105. ga = CS_GRAY_ALPHA,
  106. grayalpha = CS_GRAY_ALPHA,
  107. greyalpha = CS_GRAY_ALPHA,
  108. rgb = CS_RGB,
  109. rgba = CS_RGB_ALPHA,
  110. rgbalpha = CS_RGB_ALPHA,
  111. cmyk = CS_CMYK,
  112. cmyka = CS_CMYK_ALPHA,
  113. cmykalpha = CS_CMYK_ALPHA,
  114. )
  115. class format_cs_table_t:
  116. def __init__( self, format_, default_cs, permitted_cs):
  117. self.format = format_
  118. self.default_cs = default_cs
  119. self.permitted_cs = permitted_cs
  120. format_cs_table = [
  121. format_cs_table_t( OUT_PNG, CS_RGB, [ CS_GRAY, CS_GRAY_ALPHA, CS_RGB, CS_RGB_ALPHA, CS_ICC ] ),
  122. format_cs_table_t( OUT_PPM, CS_RGB, [ CS_GRAY, CS_RGB ] ),
  123. format_cs_table_t( OUT_PNM, CS_GRAY, [ CS_GRAY, CS_RGB ] ),
  124. format_cs_table_t( OUT_PAM, CS_RGB_ALPHA, [ CS_GRAY, CS_GRAY_ALPHA, CS_RGB, CS_RGB_ALPHA, CS_CMYK, CS_CMYK_ALPHA ] ),
  125. format_cs_table_t( OUT_PGM, CS_GRAY, [ CS_GRAY, CS_RGB ] ),
  126. format_cs_table_t( OUT_PBM, CS_MONO, [ CS_MONO ] ),
  127. format_cs_table_t( OUT_PKM, CS_CMYK, [ CS_CMYK ] ),
  128. format_cs_table_t( OUT_PWG, CS_RGB, [ CS_MONO, CS_GRAY, CS_RGB, CS_CMYK ] ),
  129. format_cs_table_t( OUT_PCL, CS_MONO, [ CS_MONO, CS_RGB ] ),
  130. format_cs_table_t( OUT_PCLM, CS_RGB, [ CS_RGB, CS_GRAY ] ),
  131. format_cs_table_t( OUT_PS, CS_RGB, [ CS_GRAY, CS_RGB, CS_CMYK ] ),
  132. format_cs_table_t( OUT_PSD, CS_CMYK, [ CS_GRAY, CS_GRAY_ALPHA, CS_RGB, CS_RGB_ALPHA, CS_CMYK, CS_CMYK_ALPHA, CS_ICC ] ),
  133. format_cs_table_t( OUT_TRACE, CS_RGB, [ CS_RGB ] ),
  134. format_cs_table_t( OUT_XMLTEXT, CS_RGB, [ CS_RGB ] ),
  135. format_cs_table_t( OUT_BBOX, CS_RGB, [ CS_RGB ] ),
  136. format_cs_table_t( OUT_SVG, CS_RGB, [ CS_RGB ] ),
  137. format_cs_table_t( OUT_TEXT, CS_RGB, [ CS_RGB ] ),
  138. format_cs_table_t( OUT_HTML, CS_RGB, [ CS_RGB ] ),
  139. format_cs_table_t( OUT_XHTML, CS_RGB, [ CS_RGB ] ),
  140. format_cs_table_t( OUT_STEXT, CS_RGB, [ CS_RGB ] ),
  141. ]
  142. def stat_mtime(path):
  143. try:
  144. return os.path.getmtime(path)
  145. except Exception:
  146. return 0
  147. class worker_t:
  148. def __init__( self):
  149. self.num = 0
  150. self.band = 0
  151. self.list = None
  152. self.ctm = None
  153. self.tbounds = None
  154. self.pix = None
  155. self.bit = None
  156. self.cookie = mupdf.FzCookie()
  157. class state:
  158. output = None
  159. out = None
  160. output_pagenum = 0
  161. output_file_per_page = 0
  162. format_ = None
  163. output_format = OUT_NONE
  164. rotation = 0
  165. resolution = 72
  166. res_specified = 0
  167. width = 0
  168. height = 0
  169. fit = 0
  170. layout_w = mupdf.FZ_DEFAULT_LAYOUT_W
  171. layout_h = mupdf.FZ_DEFAULT_LAYOUT_H
  172. layout_em = mupdf.FZ_DEFAULT_LAYOUT_EM
  173. layout_css = None
  174. layout_use_doc_css = 1
  175. min_line_width = 0.0
  176. showfeatures = 0
  177. showtime = 0
  178. showmemory = 0
  179. showmd5 = 0
  180. no_icc = 0
  181. ignore_errors = 0
  182. uselist = 1
  183. alphabits_text = 8
  184. alphabits_graphics = 8
  185. out_cs = CS_UNSET
  186. proof_filename = None
  187. proof_cs = mupdf.FzColorspace()
  188. icc_filename = None
  189. gamma_value = 1
  190. invert = 0
  191. band_height = 0
  192. lowmemory = 0
  193. quiet = 0
  194. errored = 0
  195. colorspace = mupdf.FzColorspace()
  196. oi = None
  197. spots = SPOTS_OVERPRINT_SIM
  198. alpha = 0
  199. useaccel = 1
  200. filename = None
  201. files = 0
  202. num_workers = 0
  203. workers = None
  204. bander = None
  205. layer_config = None
  206. class bgprint:
  207. active = 0
  208. started = 0
  209. pagenum = 0
  210. filename = None
  211. list_ = None
  212. page = None
  213. interptime = 0
  214. seps = None
  215. class timing:
  216. count = 0
  217. total = 0
  218. min_ = 0
  219. max_ = 0
  220. mininterp = 0
  221. maxinterp = 0
  222. minpage = 0
  223. maxpage = 0
  224. minfilename = None
  225. maxfilename = None
  226. layout = 0
  227. minlayout = 0
  228. maxlayout = 0
  229. minlayoutfilename = None
  230. maxlayoutfilename = None
  231. def usage():
  232. sys.stderr.write( f'''
  233. mudraw version {mupdf.FZ_VERSION} "
  234. Usage: mudraw [options] file [pages]
  235. \t-p -\tpassword
  236. \t-o -\toutput file name (%d for page number)
  237. \t-F -\toutput format (default inferred from output file name)
  238. \t\traster: png, pnm, pam, pbm, pkm, pwg, pcl, ps
  239. \t\tvector: svg, pdf, trace
  240. \t\ttext: txt, html, stext
  241. \t-q\tbe quiet (don't print progress messages)
  242. \t-s -\tshow extra information:
  243. \t\tm - show memory use
  244. \t\tt - show timings
  245. \t\tf - show page features
  246. \t\t5 - show md5 checksum of rendered image
  247. \t-R -\trotate clockwise (default: 0 degrees)
  248. \t-r -\tresolution in dpi (default: 72)
  249. \t-w -\twidth (in pixels) (maximum width if -r is specified)
  250. \t-h -\theight (in pixels) (maximum height if -r is specified)
  251. \t-f -\tfit width and/or height exactly; ignore original aspect ratio
  252. \t-B -\tmaximum band_height (pXm, pcl, pclm, ps, psd and png output only)
  253. \t-T -\tnumber of threads to use for rendering (banded mode only)
  254. \t-W -\tpage width for EPUB layout
  255. \t-H -\tpage height for EPUB layout
  256. \t-S -\tfont size for EPUB layout
  257. \t-U -\tfile name of user stylesheet for EPUB layout
  258. \t-X\tdisable document styles for EPUB layout
  259. \t-a\tdisable usage of accelerator file
  260. \t-c -\tcolorspace (mono, gray, grayalpha, rgb, rgba, cmyk, cmykalpha, filename of ICC profile)
  261. \t-e -\tproof icc profile (filename of ICC profile)
  262. \t-G -\tapply gamma correction
  263. \t-I\tinvert colors
  264. \t-A -\tnumber of bits of antialiasing (0 to 8)
  265. \t-A -/-\tnumber of bits of antialiasing (0 to 8) (graphics, text)
  266. \t-l -\tminimum stroked line width (in pixels)
  267. \t-D\tdisable use of display list
  268. \t-i\tignore errors
  269. \t-L\tlow memory mode (avoid caching, clear objects after each page)
  270. \t-P\tparallel interpretation/rendering
  271. \t-N\tdisable ICC workflow (\"N\"o color management)
  272. \t-O -\tControl spot/overprint rendering
  273. \t\t 0 = No spot rendering
  274. \t\t 1 = Overprint simulation (default)
  275. \t\t 2 = Full spot rendering
  276. \t-y l\tList the layer configs to stderr
  277. \t-y -\tSelect layer config (by number)
  278. \t-y -{{,-}}*\tSelect layer config (by number), and toggle the listed entries
  279. \tpages\tcomma separated list of page numbers and ranges
  280. ''')
  281. sys.exit(1)
  282. gettime_first = None
  283. def gettime():
  284. global gettime_first
  285. if gettime_first is None:
  286. gettime_first = time.time()
  287. now = time.time()
  288. return (now - gettime_first) * 1000
  289. def has_percent_d(s):
  290. # find '%[0-9]*d' */
  291. m = re.search( '%[0-9]*d', s)
  292. if m:
  293. return 1
  294. return 0
  295. # Output file level (as opposed to page level) headers
  296. def file_level_headers():
  297. if state.output_format in (OUT_STEXT, OUT_TRACE, OUT_XMLTEXT, OUT_BBOX):
  298. state.out.fz_write_string( "<?xml version=\"1.0\"?>\n")
  299. if state.output_format == OUT_HTML:
  300. state.out.fz_print_stext_header_as_html()
  301. if state.output_format == OUT_XHTML:
  302. state.out.fz_print_stext_header_as_xhtml()
  303. if state.output_format in (OUT_STEXT, OUT_TRACE, OUT_BBOX):
  304. state.out.fz_write_string( f'<document name="{state.filename}">\n')
  305. if state.output_format == OUT_PS:
  306. state.out.fz_write_ps_file_header()
  307. if state.output_format == OUT_PWG:
  308. state.out.fz_write_pwg_file_header()
  309. if state.output_format == OUT_PCLM:
  310. opts = mupdf.FzPclmOptions( 'compression=flate')
  311. state.bander = mupdf.FzBandWriter(state.out, opts)
  312. def file_level_trailers():
  313. if state.output_format in (OUT_STEXT, OUT_TRACE, OUT_BBOX):
  314. state.out.fz_write_string( "</document>\n")
  315. if state.output_format == OUT_HTML:
  316. state.out.fz_print_stext_trailer_as_html()
  317. if state.output_format == OUT_XHTML:
  318. state.out.fz_print_stext_trailer_as_xhtml()
  319. if state.output_format == OUT_PS:
  320. state.out.fz_write_ps_file_trailer( state.output_pagenum)
  321. def drawband( page, list_, ctm, tbounds, cookie, band_start, pix):
  322. bit = None
  323. if pix.alpha():
  324. pix.fz_clear_pixmap()
  325. else:
  326. pix.fz_clear_pixmap_with_value( 255)
  327. dev = mupdf.FzDevice( mupdf.FzMatrix(), pix, state.proof_cs)
  328. if state.lowmemory:
  329. dev.enable_device_hints( mupdf.FZ_NO_CACHE)
  330. if state.alphabits_graphics == 0:
  331. dev.enable_device_hints( mupdf.FZ_DONT_INTERPOLATE_IMAGES)
  332. if list_:
  333. list_.fz_run_display_list( dev, ctm, tbounds, cookie)
  334. else:
  335. page.fz_run_page( dev, ctm, cookie)
  336. dev.fz_close_device()
  337. dev = None # lgtm [py/unused-local-variable]
  338. if state.invert:
  339. pix.fz_invert_pixmap()
  340. if state.gamma_value != 1:
  341. pix.fz_gamma_pixmap( state.gamma_value)
  342. if ((state.output_format == OUT_PCL or state.output_format == OUT_PWG) and state.out_cs == CS_MONO) or (state.output_format == OUT_PBM) or (state.output_format == OUT_PKM):
  343. bit = mupdf.FzBitmap( pix, mupdf.FzHalftone(), band_start)
  344. return bit
  345. def dodrawpage( page, list_, pagenum, cookie, start, interptime, filename, bg, seps):
  346. if state.output_file_per_page:
  347. file_level_headers()
  348. if list_:
  349. mediabox = mupdf.FzRect( list_)
  350. else:
  351. mediabox = page.fz_bound_page()
  352. if state.output_format == OUT_TRACE:
  353. state.out.fz_write_string( "<page mediabox=\"%g %g %g %g\">\n" % (
  354. mediabox.x0, mediabox.y0, mediabox.x1, mediabox.y1))
  355. dev = mupdf.FzDevice( state.out)
  356. if state.lowmemory:
  357. dev.fz_enable_device_hints( mupdf.FZ_NO_CACHE)
  358. if list_:
  359. list_.fz_run_display_list( dev, mupdf.FzMatrix(), mupdf.FzRect(mupdf.fz_infinite_rect), cookie)
  360. else:
  361. page.fz_run_page( dev, fz_identity, cookie)
  362. state.out.fz_write_string( "</page>\n")
  363. dev.fz_close_device()
  364. dev = None # lgtm [py/unused-local-variable]
  365. elif state.output_format == OUT_XMLTEXT:
  366. state.out.fz_write_string( "<page mediabox=\"%g %g %g %g\">\n" % (
  367. mediabox.x0, mediabox.y0, mediabox.x1, mediabox.y1))
  368. dev = mupdf.FzDevice.fz_new_raw_device( state.out)
  369. if list_:
  370. list_.fz_run_display_list( dev, mupdf.FzMatrix(), mupdf.FzRect(mupdf.fz_infinite_rect), cookie)
  371. else:
  372. page.fz_run_page( dev, fz_identity, cookie)
  373. state.out.fz_write_string( "</page>\n")
  374. dev.fz_close_device()
  375. dev = None # lgtm [py/unused-local-variable]
  376. elif state.output_format == OUT_BBOX:
  377. bbox = mupdf.FzRect( mupdf.FzRect.Fixed_EMPTY)
  378. dev = mupdf.FzDevice( bbox)
  379. if state.lowmemory:
  380. dev.fz_enable_device_hints( mupdf.FZ_NO_CACHE)
  381. if list_:
  382. list_.fz_run_display_list( dev, fz_identity, mupdf.FzRect(mupdf.fz_infinite_rect), cookie)
  383. else:
  384. page.fz_run_page( dev, fz_identity, cookie)
  385. dev.fz_close_device()
  386. state.out.fz_write_string( "<page bbox=\"%s %s %s %s\" mediabox=\"%s %s %s %s\" />\n",
  387. bbox.x0,
  388. bbox.y0,
  389. bbox.x1,
  390. bbox.y1,
  391. mediabox.x0,
  392. mediabox.y0,
  393. mediabox.x1,
  394. mediabox.y1,
  395. )
  396. elif state.output_format in (OUT_TEXT, OUT_HTML, OUT_XHTML, OUT_STEXT):
  397. zoom = state.resolution / 72
  398. ctm = mupdf.FzMatrix(mupdf.fz_pre_scale(mupdf.fz_rotate(state.rotation), zoom, zoom))
  399. stext_options = mupdf.FzStextOptions()
  400. stext_options.flags = mupdf.FZ_STEXT_PRESERVE_IMAGES if (state.output_format == OUT_HTML or state.output_format == OUT_XHTML) else 0
  401. text = mupdf.FzStextPage( mediabox)
  402. dev = mupdf.FzDevice( text, stext_options)
  403. if state.lowmemory:
  404. fz_enable_device_hints( dev, FZ_NO_CACHE)
  405. if list_:
  406. list_.fz_run_display_list( dev, ctm, mupdf.FzRect(mupdf.fz_infinite_rect), cookie)
  407. else:
  408. page.fz_run_page( dev, ctm, cookie)
  409. dev.fz_close_device()
  410. dev = None # lgtm [py/unused-local-variable]
  411. if state.output_format == OUT_STEXT:
  412. state.out.fz_print_stext_page_as_xml( text, pagenum)
  413. elif state.output_format == OUT_HTML:
  414. state.out.fz_print_stext_page_as_html( text, pagenum)
  415. elif state.output_format == OUT_XHTML:
  416. state.out.fz_print_stext_page_as_xhtml( text, pagenum)
  417. elif state.output_format == OUT_TEXT:
  418. state.out.fz_print_stext_page_as_text( text)
  419. state.out.fz_write_string( "\f\n")
  420. elif state.output_format == OUT_SVG:
  421. zoom = state.resolution / 72
  422. ctm = mupdf.FzMatrix(zoom, zoom)
  423. ctm.fz_pre_rotate( state.rotation)
  424. tbounds = mupdf.FzRect(mediabox, ctm)
  425. if not state.output or state.output == "-":
  426. state.out = mupdf.FzOutput( mupdf.FzOutput.Fixed_STDOUT)
  427. else:
  428. buf = mupdf.fz_format_output_path( state.output, pagenum)
  429. state.out = mupdf.FzOutput( buf, 0)
  430. dev = mupdf.FzDevice( state.out, tbounds.x1-tbounds.x0, tbounds.y1-tbounds.y0, mupdf.FZ_SVG_TEXT_AS_PATH, 1)
  431. if state.lowmemory:
  432. dev.fz_enable_device_hints( dev, mupdf.FZ_NO_CACHE)
  433. if list_:
  434. list_.fz_run_display_list( dev, ctm, tbounds, cookie)
  435. else:
  436. page.fz_run_page( dev, ctm, cookie)
  437. dev.fz_close_device()
  438. state.out.fz_close_output()
  439. else:
  440. zoom = state.resolution / 72
  441. ctm = mupdf.fz_pre_scale( mupdf.fz_rotate(state.rotation), zoom, zoom)
  442. tbounds = mupdf.FzRect(mediabox, ctm)
  443. ibounds = tbounds.fz_round_rect()
  444. # Make local copies of our width/height
  445. w = state.width
  446. h = state.height
  447. # If a resolution is specified, check to see whether w/h are
  448. # exceeded; if not, unset them. */
  449. if state.res_specified:
  450. t = ibounds.x1 - ibounds.x0
  451. if w and t <= w:
  452. w = 0
  453. t = ibounds.y1 - ibounds.y0
  454. if h and t <= h:
  455. h = 0
  456. # Now w or h will be 0 unless they need to be enforced.
  457. if w or h:
  458. scalex = w / (tbounds.x1 - tbounds.x0)
  459. scaley = h / (tbounds.y1 - tbounds.y0)
  460. if state.fit:
  461. if w == 0:
  462. scalex = 1.0
  463. if h == 0:
  464. scaley = 1.0
  465. else:
  466. if w == 0:
  467. scalex = scaley
  468. if h == 0:
  469. scaley = scalex
  470. if not state.fit:
  471. if scalex > scaley:
  472. scalex = scaley
  473. else:
  474. scaley = scalex
  475. scale_mat = mupdf.fz_scale(scalex, scaley)
  476. ctm = mupdf.fz_concat(ctm, scale_mat)
  477. tbounds = mupdf.FzRect( mediabox, ctm)
  478. ibounds = tbounds.fz_round_rect()
  479. tbounds = ibounds.fz_rect_from_irect()
  480. band_ibounds = ibounds
  481. bands = 1
  482. totalheight = ibounds.y1 - ibounds.y0
  483. drawheight = totalheight
  484. if state.band_height != 0:
  485. # Banded rendering; we'll only render to a
  486. # given height at a time.
  487. drawheight = state.band_height
  488. if totalheight > state.band_height:
  489. band_ibounds.y1 = band_ibounds.y0 + state.band_height
  490. bands = (totalheight + state.band_height-1)/state.band_height
  491. tbounds.y1 = tbounds.y0 + state.band_height + 2
  492. #DEBUG_THREADS(("Using %d Bands\n", bands));
  493. if state.num_workers > 0:
  494. for band in range( min(state.num_workers, bands)):
  495. state.workers[band].band = band
  496. state.workers[band].ctm = ctm
  497. state.workers[band].tbounds = tbounds
  498. state.workers[band].cookie = mupdf.FzCookie()
  499. state.workers[band].list = list_
  500. state.workers[band].pix = mupdf.FzPixmap( state.colorspace, band_ibounds, seps, state.alpha)
  501. state.workers[band].pix.fz_set_pixmap_resolution( state.resolution, state.resolution)
  502. ctm.f -= drawheight
  503. pix = state.workers[0].pix
  504. else:
  505. pix = mupdf.FzPixmap( state.colorspace, band_ibounds, seps, state.alpha)
  506. pix.fz_set_pixmap_resolution( int(state.resolution), int(state.resolution))
  507. # Output any page level headers (for banded formats)
  508. if state.output:
  509. state.bander = None
  510. if state.output_format == OUT_PGM or state.output_format == OUT_PPM or state.output_format == OUT_PNM:
  511. state.bander = mupdf.FzBandWriter( state.out, mupdf.FzBandWriter.PNM)
  512. elif state.output_format == OUT_PAM:
  513. state.bander = mupdf.FzBandWriter( state.out, mupdf.FzBandWriter.PAM)
  514. elif state.output_format == OUT_PNG:
  515. state.bander = mupdf.FzBandWriter( state.out, mupdf.FzBandWriter.PNG)
  516. elif state.output_format == OUT_PBM:
  517. state.bander = mupdf.FzBandWriter( state.out, mupdf.FzBandWriter.PBM)
  518. elif state.output_format == OUT_PKM:
  519. state.bander = mupdf.FzBandWriter( state.out, mupdf.FzBandWriter.PKM)
  520. elif state.output_format == OUT_PS:
  521. state.bander = mupdf.FzBandWriter( state.out, mupdf.FzBandWriter.PS)
  522. elif state.output_format == OUT_PSD:
  523. state.bander = mupdf.FzBandWriter( state.out, mupdf.FzBandWriter.PSD)
  524. elif state.output_format == OUT_PWG:
  525. if state.out_cs == CS_MONO:
  526. state.bander = mupdf.FzBandWriter( state.out, mupdf.FzBandWriter.MONO, mupdf.FzPwgOptions())
  527. else:
  528. state.bander = mupdf.FzBandWriter( state.out, mupdf.FzBandWriter.COLOR, mupdf.FzPwgOptions())
  529. elif state.output_format == OUT_PCL:
  530. if state.out_cs == CS_MONO:
  531. state.bander = mupdf.FzBandWriter( state.out, mupdf.FzBandWriter.MONO, mupdf.FzPclOptions())
  532. else:
  533. state.bander = mupdf.FzBandWriter( state.out, mupdf.FzBandWriter.COLOR, mupdf.FzPclOptions())
  534. if state.bander:
  535. state.bander.fz_write_header( pix.w(), totalheight, pix.n(), pix.alpha(), pix.xres(), pix.yres(), state.output_pagenum, pix.colorspace(), pix.seps())
  536. state.output_pagenum += 1
  537. for band in range( bands):
  538. if state.num_workers > 0:
  539. w = state.workers[band % state.num_workers]
  540. pix = w.pix
  541. bit = w.bit
  542. w.bit = None
  543. cookie.fz_increment_errors(w.cookie.errors())
  544. else:
  545. bit = drawband( page, list_, ctm, tbounds, cookie, band * state.band_height, pix)
  546. if state.output:
  547. if state.bander:
  548. if bit:
  549. state.bander.fz_write_band( bit.stride(), drawheight, bit.samples())
  550. else:
  551. state.bander.fz_write_band( pix.stride(), drawheight, pix.samples())
  552. bit = None
  553. if state.num_workers > 0 and band + state.num_workers < bands:
  554. w = state.workers[band % state.num_workers]
  555. w.band = band + state.num_workers
  556. w.ctm = ctm
  557. w.tbounds = tbounds
  558. w.cookie = mupdf.FzCookie()
  559. ctm.f -= drawheight
  560. # FIXME
  561. if state.showmd5:
  562. digest = pix.fz_md5_pixmap()
  563. sys.stderr.write( ' ')
  564. for i in range(16):
  565. sys.stderr.write( '%02x', digest[i])
  566. if state.output_file_per_page:
  567. file_level_trailers()
  568. if state.showtime:
  569. end = gettime()
  570. diff = end - start
  571. if bg:
  572. if diff + interptime < timing.min:
  573. timing.min = diff + interptime
  574. timing.mininterp = interptime
  575. timing.minpage = pagenum
  576. timing.minfilename = filename
  577. if diff + interptime > timing.max:
  578. timing.max = diff + interptime
  579. timing.maxinterp = interptime
  580. timing.maxpage = pagenum
  581. timing.maxfilename = filename
  582. timing.count += 1
  583. sys.stderr.write( " %dms (interpretation) %dms (rendering) %dms (total)" % (interptime, diff, diff + interptime))
  584. else:
  585. if diff < timing.min:
  586. timing.min = diff
  587. timing.minpage = pagenum
  588. timing.minfilename = filename
  589. if diff > timing.max:
  590. timing.max = diff
  591. timing.maxpage = pagenum
  592. timing.maxfilename = filename
  593. timing.total += diff
  594. timing.count += 1
  595. sys.stderr.write( " %dms" % diff)
  596. if not state.quiet or state.showfeatures or state.showtime or state.showmd5:
  597. sys.stderr.write( "\n")
  598. if state.lowmemory:
  599. mupdf.fz_empty_store()
  600. if state.showmemory:
  601. # Use low-level fn because mupdf.fz_stderr() returns fz_output*, not
  602. # FzOutput.
  603. mupdf.ll_fz_dump_glyph_cache_stats(mupdf.ll_fz_stderr())
  604. mupdf.fz_flush_warnings()
  605. if cookie.errors():
  606. state.errored = 1
  607. def bgprint_flush():
  608. if not bgprint.active or not bgprint.started:
  609. return
  610. bgprint.started = 0
  611. def drawpage( doc, pagenum):
  612. list_ = None
  613. cookie = mupdf.FzCookie()
  614. seps = None
  615. features = ""
  616. start = gettime() if state.showtime else 0
  617. page = mupdf.FzPage( doc, pagenum - 1)
  618. if state.spots != SPOTS_NONE:
  619. seps = page.fz_page_separations()
  620. if seps.m_internal:
  621. n = seps.fz_count_separations()
  622. if state.spots == SPOTS_FULL:
  623. for i in range(n):
  624. seps.fz_set_separation_behavior( i, mupdf.FZ_SEPARATION_SPOT)
  625. else:
  626. for i in range(n):
  627. seps.fz_set_separation_behavior( i, mupdf.FZ_SEPARATION_COMPOSITE)
  628. elif page.fz_page_uses_overprint():
  629. # This page uses overprint, so we need an empty
  630. # sep object to force the overprint simulation on.
  631. seps = mupdf.FzSeparations(0)
  632. elif state.oi and state.oi.m_internal and state.oi.fz_colorspace_n() != state.colorspace.fz_colorspace_n():
  633. # We have an output intent, and it's incompatible
  634. # with the colorspace our device needs. Force the
  635. # overprint simulation on, because this ensures that
  636. # we 'simulate' the output intent too. */
  637. seps = mupdf.FzSeparations(0)
  638. if state.uselist:
  639. list_ = mupdf.FzDisplayList( page.fz_bound_page())
  640. dev = mupdf.FzDevice( list_)
  641. if state.lowmemory:
  642. dev.fz_enable_device_hints( FZ_NO_CACHE)
  643. page.fz_run_page( dev, mupdf.FzMatrix(), cookie)
  644. dev.fz_close_device()
  645. if bgprint.active and state.showtime:
  646. end = gettime()
  647. start = end - start
  648. if state.showfeatures:
  649. # SWIG doesn't appear to handle the out-param is_color in
  650. # mupdf.Device() constructor that wraps fz_new_test_device(), so we use
  651. # the underlying mupdf function() instead.
  652. #
  653. dev, iscolor = mupdf.ll_fz_new_test_device( 0.02, 0, None)
  654. dev = mupdf.FzDevice( dev)
  655. if state.lowmemory:
  656. dev.fz_enable_device_hints( mupdf.FZ_NO_CACHE)
  657. if list_:
  658. list_.fz_run_display_list( dev, mupdf.FzMatrix(mupdf.fz_identity), mupdf.FzRect(mupdf.fz_infinite_rect), mupdf.FzCookie())
  659. else:
  660. page.fz_run_page( dev, fz_identity, cookie)
  661. dev.fz_close_device()
  662. features = " color" if iscolor else " grayscale"
  663. if state.output_file_per_page:
  664. bgprint_flush()
  665. if state.out:
  666. state.out.fz_close_output()
  667. text_buffer = mupdf.fz_format_output_path( state.output, pagenum)
  668. state.out = mupdf.FzOutput( text_buffer, 0)
  669. if bgprint.active:
  670. bgprint_flush()
  671. if bgprint.active:
  672. if not state.quiet or state.showfeatures or state.showtime or state.showmd5:
  673. sys.stderr.write( "page %s %d%s" % (state.filename, pagenum, features))
  674. bgprint.started = 1
  675. bgprint.page = page
  676. bgprint.list = list_
  677. bgprint.seps = seps
  678. bgprint.filename = state.filename
  679. bgprint.pagenum = pagenum
  680. bgprint.interptime = start
  681. else:
  682. if not state.quiet or state.showfeatures or state.showtime or state.showmd5:
  683. sys.stderr.write( "page %s %d%s" % (state.filename, pagenum, features))
  684. dodrawpage( page, list_, pagenum, cookie, start, 0, state.filename, 0, seps)
  685. def drawrange( doc, range_):
  686. pagecount = doc.fz_count_pages()
  687. while 1:
  688. range_, spage, epage = mupdf.fz_parse_page_range( range_, pagecount)
  689. if range_ is None:
  690. break
  691. if spage < epage:
  692. for page in range(spage, epage+1):
  693. drawpage( doc, page)
  694. else:
  695. for page in range( spage, epage-1, -1):
  696. drawpage( doc, page)
  697. def parse_colorspace( name):
  698. ret = cs_name_table.get( name)
  699. if ret:
  700. return ret
  701. state.icc_filename = name
  702. return CS_ICC
  703. class trace_info:
  704. def __init__( self):
  705. self.current = 0
  706. self.peak = 0
  707. self.total = 0
  708. def iswhite(ch):
  709. return (
  710. ch == '\011' or ch == '\012' or
  711. ch == '\014' or ch == '\015' or ch == '\040'
  712. )
  713. def apply_layer_config( doc, lc):
  714. pass
  715. def convert_to_accel_path(absname):
  716. tmpdir = os.getenv('TEMP')
  717. if not tmpdir:
  718. tmpdir = os.getenv('TMP')
  719. if not tmpdir:
  720. tmpdir = '/var/tmp'
  721. if not os.path.isdir(tmpdir):
  722. tmpdir = '/tmp'
  723. if absname.startswith( '/') or absname.startswith( '\\'):
  724. absname = absname[1:]
  725. absname = absname.replace( '/', '%')
  726. absname = absname.replace( '\\', '%')
  727. absname = absname.replace( ':', '%')
  728. return '%s/%s.accel' % (tmpdir, absname)
  729. def get_accelerator_filename( filename):
  730. absname = os.path.realpath( filename)
  731. return convert_to_accel_path( absname)
  732. def save_accelerator(doc, filename):
  733. if not doc.fz_document_supports_accelerator():
  734. return
  735. absname = get_accelerator_filename( filename)
  736. doc.fz_save_accelerator( absname)
  737. def draw( argv):
  738. password = ''
  739. info = trace_info()
  740. items, argv = getopt.getopt( argv, 'qp:o:F:R:r:w:h:fB:c:e:G:Is:A:DiW:H:S:T:U:XLvPl:y:NO:a')
  741. for option, value in items:
  742. if 0: pass
  743. elif option == '-q': state.quiet = 1
  744. elif option == '-p': password = value
  745. elif option == '-o': state.output = value
  746. elif option == '-F': state.format_ = value
  747. elif option == '-R': state.rotation = float( value)
  748. elif option == '-r':
  749. state.resolution = float( value)
  750. state.res_specified = 1
  751. elif option == '-w': state.width = float( value)
  752. elif option == '-h': state.height = float( value)
  753. elif option == '-f': state.fit = 1
  754. elif option == '-B': state.band_height = int( value)
  755. elif option == '-c': state.out_cs = parse_colorspace( value)
  756. elif option == '-e': state.proof_filename = value
  757. elif option == '-G': state.gamma_value = float( value)
  758. elif option == '-I': state.invert += 1
  759. elif option == '-W': state.layout_w = float( value)
  760. elif option == '-H': state.layout_h = float( value)
  761. elif option == '-S': state.layout_em = float( value)
  762. elif option == '-U': state.layout_css = value
  763. elif option == '-X': state.layout_use_doc_css = 0
  764. elif option == '-O':
  765. state.spots = float( value)
  766. if not mupdf.FZ_ENABLE_SPOT_RENDERING:
  767. sys.stderr.write( 'Spot rendering/Overprint/Overprint simulation not enabled in this build\n')
  768. state.spots = SPOTS_NONE
  769. elif option == '-s':
  770. if 't' in value: state.showtime += 1
  771. if 'm' in value: state.showmemory += 1
  772. if 'f' in value: state.showfeatures += 1
  773. if '5' in value: state.showmd5 += 1
  774. elif option == '-A':
  775. state.alphabits_graphics = int(value)
  776. sep = value.find( '/')
  777. if sep >= 0:
  778. state.alphabits_text = int(value[sep+1:])
  779. else:
  780. state.alphabits_text = state.alphabits_graphics
  781. elif option == '-D': state.uselist = 0
  782. elif option == '-l': state.min_line_width = float(value)
  783. elif option == '-i': state.ignore_errors = 1
  784. elif option == '-N': state.no_icc = 1
  785. elif option == '-T': state.num_workers = int(value)
  786. elif option == '-L': state.lowmemory = 1
  787. elif option == '-P': bgprint.active = 1
  788. elif option == '-y': state.layer_config = value
  789. elif option == '-a': state.useaccel = 0
  790. elif option == '-v': sys.stderr.write( f'mudraw version {mupdf.FZ_VERSION}\n')
  791. if not argv:
  792. usage()
  793. if state.num_workers > 0:
  794. if state.uselist == 0:
  795. sys.stderr.write('cannot use multiple threads without using display list\n')
  796. sys.exit(1)
  797. if state.band_height == 0:
  798. sys.stderr.write('Using multiple threads without banding is pointless\n')
  799. if bgprint.active:
  800. if state.uselist == 0:
  801. sys.stderr.write('cannot bgprint without using display list\n')
  802. sys.exit(1)
  803. if state.proof_filename:
  804. proof_buffer = mupdf.FzBuffer( state.proof_filename)
  805. state.proof_cs = mupdf.FzColorspace( FZ_COLORSPACE_NONE, 0, None, proof_buffer)
  806. mupdf.fz_set_text_aa_level( state.alphabits_text)
  807. mupdf.fz_set_graphics_aa_level( state.alphabits_graphics)
  808. mupdf.fz_set_graphics_min_line_width( state.min_line_width)
  809. if state.no_icc:
  810. mupdf.fz_disable_icc()
  811. else:
  812. mupdf.fz_enable_icc()
  813. if state.layout_css:
  814. buf = mupdf.FzBuffer( state.layout_css)
  815. mupdf.fz_set_user_css( buf.string_from_buffer())
  816. mupdf.fz_set_use_document_css( state.layout_use_doc_css)
  817. # Determine output type
  818. if state.band_height < 0:
  819. sys.stderr.write( 'Bandheight must be > 0\n')
  820. sys.exit(1)
  821. state.output_format = OUT_PNG
  822. if state.format_:
  823. for i in range(len(suffix_table)):
  824. if state.format_ == suffix_table[i].suffix[1:]:
  825. state.output_format = suffix_table[i].format
  826. if state.spots == SPOTS_FULL and suffix_table[i].spots == 0:
  827. sys.stderr.write( f'Output format {suffix_table[i].suffix[1:]} does not support spot rendering.\nDoing overprint simulation instead.\n')
  828. state.spots = SPOTS_OVERPRINT_SIM
  829. break
  830. else:
  831. sys.stderr.write( f'Unknown output format {format}\n')
  832. sys.exit(1)
  833. elif state.output:
  834. suffix = state.output
  835. i = 0
  836. while 1:
  837. if i == len(suffix_table):
  838. break
  839. s = suffix.find( suffix_table[i].suffix)
  840. if s != -1:
  841. suffix = suffix_table[i].suffix[s+1:]
  842. state.output_format = suffix_table[i].format
  843. if state.spots == SPOTS_FULL and suffix_table[i].spots == 0:
  844. sys.stderr.write( 'Output format {suffix_table[i].suffix[1:]} does not support spot rendering\nDoing overprint simulation instead.\n')
  845. state.spots = SPOTS_OVERPRINT_SIM
  846. i = 0
  847. else:
  848. i += 1
  849. if state.band_height:
  850. if state.output_format not in ( OUT_PAM, OUT_PGM, OUT_PPM, OUT_PNM, OUT_PNG, OUT_PBM, OUT_PKM, OUT_PCL, OUT_PCLM, OUT_PS, OUT_PSD):
  851. sys.stderr.write( 'Banded operation only possible with PxM, PCL, PCLM, PS, PSD, and PNG outputs\n')
  852. sys.exit(1)
  853. if state.showmd5:
  854. sys.stderr.write( 'Banded operation not compatible with MD5\n')
  855. sys.exit(1)
  856. for i in range(len(format_cs_table)):
  857. if format_cs_table[i].format == state.output_format:
  858. if state.out_cs == CS_UNSET:
  859. state.out_cs = format_cs_table[i].default_cs
  860. for j in range( len(format_cs_table[i].permitted_cs)):
  861. if format_cs_table[i].permitted_cs[j] == state.out_cs:
  862. break
  863. else:
  864. sys.stderr.write( 'Unsupported colorspace for this format\n')
  865. sys.exit(1)
  866. state.alpha = 1
  867. if state.out_cs in ( CS_MONO, CS_GRAY, CS_GRAY_ALPHA):
  868. state.colorspace = mupdf.FzColorspace( mupdf.FzColorspace.Fixed_GRAY)
  869. state.alpha = (state.out_cs == CS_GRAY_ALPHA)
  870. elif state.out_cs in ( CS_RGB, CS_RGB_ALPHA):
  871. state.colorspace = mupdf.FzColorspace( mupdf.FzColorspace.Fixed_RGB)
  872. state.alpha = (state.out_cs == CS_RGB_ALPHA)
  873. elif state.out_cs in ( CS_CMYK, CS_CMYK_ALPHA):
  874. state.colorspace = mupdf.FzColorspace( mupdf.FzColorspace.Fixed_CMYK)
  875. state.alpha = (state.out_cs == CS_CMYK_ALPHA)
  876. elif state.out_cs == CS_ICC:
  877. try:
  878. icc_buffer = mupdf.FzBuffer( state.icc_filename)
  879. state.colorspace = Colorspace( mupdf.FZ_COLORSPACE_NONE, 0, None, icc_buffer)
  880. except Exception as e:
  881. sys.stderr.write( 'Invalid ICC destination color space\n')
  882. sys.exit(1)
  883. if state.colorspace.m_internal is None:
  884. sys.stderr.write( 'Invalid ICC destination color space\n')
  885. sys.exit(1)
  886. state.alpha = 0
  887. else:
  888. sys.stderr.write( 'Unknown colorspace!\n')
  889. sys.exit(1)
  890. if state.out_cs != CS_ICC:
  891. state.colorspace = mupdf.FzColorspace( state.colorspace)
  892. else:
  893. # Check to make sure this icc profile is ok with the output format */
  894. okay = 0
  895. for i in range( len(format_cs_table)):
  896. if format_cs_table[i].format == state.output_format:
  897. for j in range( len(format_cs_table[i].permitted_cs)):
  898. x = format_cs_table[i].permitted_cs[j]
  899. if x in ( CS_MONO, CS_GRAY, CS_GRAY_ALPHA):
  900. if state.colorspace.fz_colorspace_is_gray():
  901. okay = 1
  902. elif x in ( CS_RGB, CS_RGB_ALPHA):
  903. if state.colorspace.fz_colorspace_is_rgb():
  904. okay = 1
  905. elif x in ( CS_CMYK, CS_CMYK_ALPHA):
  906. if state.colorspace.fz_colorspace_is_cmyk():
  907. okay = 1
  908. if not okay:
  909. sys.stderr.write( 'ICC profile uses a colorspace that cannot be used for this format\n')
  910. sys.exit(1)
  911. if state.output_format == OUT_SVG:
  912. # SVG files are always opened for each page. Do not open "output".
  913. pass
  914. elif state.output and (not state.output.startswith('-') or len(state.output) >= 2) and len(state.output) >= 1:
  915. if has_percent_d(state.output):
  916. state.output_file_per_page = 1
  917. else:
  918. state.out = mupdf.FzOutput(state.output, 0)
  919. else:
  920. state.quiet = 1 # automatically be quiet if printing to stdout
  921. if 0: # lgtm [py/unreachable-statement]
  922. # Windows specific code to make stdout binary.
  923. if state.output_format not in( OUT_TEXT, OUT_STEXT, OUT_HTML, OUT_XHTML, OUT_TRACE, OUT_XMLTEXT):
  924. setmode(fileno(stdout), O_BINARY)
  925. state.out = mupdf.FzOutput( mupdf.FzOutput.Fixed_STDOUT)
  926. state.filename = argv[0]
  927. if not state.output_file_per_page:
  928. file_level_headers()
  929. timing.count = 0
  930. timing.total = 0
  931. timing.min = 1 << 30
  932. timing.max = 0
  933. timing.mininterp = 1 << 30
  934. timing.maxinterp = 0
  935. timing.minpage = 0
  936. timing.maxpage = 0
  937. timing.minfilename = ""
  938. timing.maxfilename = ""
  939. timing.layout = 0
  940. timing.minlayout = 1 << 30
  941. timing.maxlayout = 0
  942. timing.minlayoutfilename = ""
  943. timing.maxlayoutfilename = ""
  944. if state.showtime and bgprint.active:
  945. timing.total = gettime()
  946. fz_optind = 0
  947. try:
  948. while fz_optind < len( argv):
  949. try:
  950. accel = None
  951. state.filename = argv[fz_optind]
  952. fz_optind += 1
  953. state.files += 1
  954. if not state.useaccel:
  955. accel = None
  956. # If there was an accelerator to load, what would it be called?
  957. else:
  958. accelpath = get_accelerator_filename( state.filename)
  959. # Check whether that file exists, and isn't older than
  960. # the document.
  961. atime = stat_mtime( accelpath)
  962. dtime = stat_mtime( state.filename)
  963. if atime == 0:
  964. # No accelerator
  965. pass
  966. elif atime > dtime:
  967. accel = accelpath
  968. else:
  969. # Accelerator data is out of date
  970. os.unlink( accelpath)
  971. accel = None # In case we have jumped up from below
  972. # Unfortunately if accel=None, SWIG doesn't seem to think of it
  973. # as a char*, so we end up in fz_open_document_with_stream().
  974. #
  975. # If we try to avoid this by setting accel='', SWIG correctly
  976. # calls Document(const char *filename, const char *accel) =>
  977. # fz_open_accelerated_document(), but the latter function tests
  978. # for NULL not "" so fails.
  979. #
  980. # So we choose the constructor explicitly rather than leaving
  981. # it up to SWIG.
  982. #
  983. if accel:
  984. doc = mupdf.FzDocument(state.filename, accel)
  985. else:
  986. doc = mupdf.FzDocument(state.filename)
  987. if doc.fz_needs_password():
  988. if not doc.fz_authenticate_password( password):
  989. raise Exception( f'cannot authenticate password: {state.filename}')
  990. # Once document is open check for output intent colorspace
  991. state.oi = doc.fz_document_output_intent()
  992. if state.oi.m_internal:
  993. # See if we had explicitly set a profile to render
  994. if state.out_cs != CS_ICC:
  995. # In this case, we want to render to the output intent
  996. # color space if the number of channels is the same
  997. if state.oi.fz_colorspace_n() == state.colorspace.fz_colorspace_n():
  998. state.colorspace = state.oi
  999. layouttime = time.time()
  1000. doc.fz_layout_document( state.layout_w, state.layout_h, state.layout_em)
  1001. doc.fz_count_pages()
  1002. layouttime = time.time() - layouttime
  1003. timing.layout += layouttime
  1004. if layouttime < timing.minlayout:
  1005. timing.minlayout = layouttime
  1006. timing.minlayoutfilename = state.filename
  1007. if layouttime > timing.maxlayout:
  1008. timing.maxlayout = layouttime
  1009. timing.maxlayoutfilename = state.filename
  1010. if state.layer_config:
  1011. apply_layer_config( doc, state.layer_config)
  1012. if fz_optind == len(argv) or not mupdf.fz_is_page_range( argv[fz_optind]):
  1013. drawrange( doc, "1-N")
  1014. if fz_optind < len( argv) and mupdf.fz_is_page_range( argv[fz_optind]):
  1015. drawrange( doc, argv[fz_optind])
  1016. fz_optind += 1
  1017. bgprint_flush()
  1018. if state.useaccel:
  1019. save_accelerator( doc, state.filename)
  1020. except Exception as e:
  1021. if not state.ignore_errors:
  1022. raise
  1023. bgprint_flush()
  1024. sys.stderr.write( f'ignoring error in {state.filename}\n')
  1025. except Exception as e:
  1026. bgprint_flush()
  1027. sys.stderr.write( f'error: cannot draw \'{state.filename}\' because: {e}\n')
  1028. state.errored = 1
  1029. if 0:
  1030. # Enable for debugging.
  1031. import traceback
  1032. traceback.print_exc()
  1033. if not state.output_file_per_page:
  1034. file_level_trailers()
  1035. if state.out:
  1036. state.out.fz_close_output()
  1037. state.out = None
  1038. if state.showtime and timing.count > 0:
  1039. if bgprint.active:
  1040. timing.total = gettime() - timing.total
  1041. if state.files == 1:
  1042. sys.stderr.write( f'total {timing.total:.0f}ms ({timing.layout:.0f}ms layout) / {timing.count} pages for an average of {timing.total / timing.count:.0f}ms\n')
  1043. if bgprint.active:
  1044. sys.stderr.write( f'fastest page {timing.minpage}: {timing.mininterp:.0f}ms (interpretation) {timing.min - timing.mininterp:.0f}ms (rendering) {timing.min:.0f}ms(total)\n')
  1045. sys.stderr.write( f'slowest page {timing.maxpage}: {timing.maxinterp:.0f}ms (interpretation) {timing.max - timing.maxinterp:.0f}ms (rendering) {timing.max:.0f}ms(total)\n')
  1046. else:
  1047. sys.stderr.write( f'fastest page {timing.minpage}: {timing.min:.0f}ms\n')
  1048. sys.stderr.write( f'slowest page {timing.maxpage}: {timing.max:.0f}ms\n')
  1049. else:
  1050. sys.stderr.write( f'total {timing.total:.0f}ms ({timing.layout:.0f}ms layout) / {timing.count} pages for an average of {timing.total / timing.count:.0f}ms in {state.files} files\n')
  1051. sys.stderr.write( f'fastest layout: {timing.minlayout:.0f}ms ({timing.minlayoutfilename})\n')
  1052. sys.stderr.write( f'slowest layout: {timing.maxlayout:.0f}ms ({timing.maxlayoutfilename})\n')
  1053. sys.stderr.write( f'fastest page {timing.minpage}: {timing.min:.0f}ms ({timing.minfilename})\n')
  1054. sys.stderr.write( f'slowest page {timing.maxpage}: {timing.max:.0f}ms ({timing.maxfilename})\n')
  1055. if state.showmemory:
  1056. sys.stderr.write( f'Memory use total={info.total} peak={info.peak} current={info.current}\n')
  1057. return state.errored != 0