muraster.c 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787
  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. /*
  23. * muraster -- Convert a document to a raster file.
  24. *
  25. * Deliberately simple. Designed to be a basis for what
  26. * printer customers would need.
  27. *
  28. * Therefore; only supports pgm, ppm, pam, pbm, pkm,
  29. * and then only dependent on the FZ_PLOTTERS_{G,RGB,CMYK}
  30. * flags.
  31. * Only supports banding.
  32. * Supports auto fallback to grey if possible.
  33. * Supports threading.
  34. * Supports fallback in low memory cases.
  35. */
  36. /*
  37. CONFIGURATION SECTION
  38. The first bit of configuration for this is actually in
  39. how the muthreads helper library is built. If muthreads
  40. does not know how to support threading on your system
  41. then it will ensure that DISABLE_MUTHREADS is set. All
  42. the muthreads entrypoints/types will still be defined
  43. (as dummy types/functions), but attempting to use them
  44. will return errors.
  45. Configuration options affecting threading should be
  46. turned off if DISABLE_MUTHREADS is set.
  47. Integrators can/should define the following
  48. MURASTER_CONFIG_ values. If not set, we'll
  49. attempt to set sensible defaults.
  50. */
  51. /*
  52. MURASTER_CONFIG_RENDER_THREADS: The number of render
  53. threads to use. Typically you would set this to the
  54. number of CPU cores - 1 (or -2 if background printing
  55. is used).
  56. If no threading library exists for your OS set this
  57. to 0.
  58. If undefined, we will use a default of
  59. 3 - MURASTER_CONFIG_BGPRINT.
  60. */
  61. /* #define MURASTER_CONFIG_RENDER_THREADS 3 */
  62. /*
  63. MURASTER_CONFIG_BGPRINT: 0 or 1. Set to 1 to
  64. enable background printing. This relies on
  65. a threading library existing for the OS.
  66. If undefined, we will use a default of 1.
  67. */
  68. /* #define MURASTER_CONFIG_BGPRINT 1 */
  69. /*
  70. MURASTER_CONFIG_X_RESOLUTION: The default X resolution
  71. in dots per inch. If undefined, taken to be 300dpi.
  72. */
  73. /* #define MURASTER_CONFIG_X_RESOLUTION 300 */
  74. /*
  75. MURASTER_CONFIG_Y_RESOLUTION: The default Y resolution
  76. in dots per inch. If undefined, taken to be 300dpi.
  77. */
  78. /* #define MURASTER_CONFIG_Y_RESOLUTION 300 */
  79. /*
  80. MURASTER_CONFIG_WIDTH: The printable page width
  81. (in inches)
  82. */
  83. /* #define MURASTER_CONFIG_WIDTH 8.27f */
  84. /*
  85. MURASTER_CONFIG_HEIGHT: The printable page height
  86. (in inches)
  87. */
  88. /* #define MURASTER_CONFIG_HEIGHT 11.69f */
  89. /*
  90. MURASTER_CONFIG_STORE_SIZE: The maximum size to use
  91. for the fz_store.
  92. If undefined, then on Linux we will attempt to guess
  93. the memory size, and we'll use that for the store
  94. size. This will be too large, but it should work OK.
  95. If undefined and NOT linux, then we'll use the default
  96. store size.
  97. */
  98. /* #define MURASTER_CONFIG_STORE_SIZE FZ_STORE_DEFAULT */
  99. /*
  100. MURASTER_CONFIG_MIN_BAND_HEIGHT: The minimum band
  101. height we will ever use. This might correspond to the
  102. number of nozzles on an inkjet head.
  103. By default, we'll use 32.
  104. */
  105. /* #define MURASTER_CONFIG_MIN_BAND_HEIGHT 32 */
  106. /*
  107. MURASTER_CONFIG_BAND_MEMORY: The maximum amount of
  108. memory (in bytes) to use for any given band.
  109. We will need MURASTER_CONFIG_RENDER_THREADS of these,
  110. one for each render thread.
  111. Having this be a multiple of
  112. MURASTER_CONFIG_MIN_BAND_HEIGHT * MURASTER_CONFIG_MAX_WIDTH * MURASTER_CONFIG_X_RESOLUTION * N
  113. would be sensible.
  114. (Where N = 1 for greyscale, 3 for RGB, 4 for CMYK)
  115. */
  116. /* #define MURASTER_CONFIG_BAND_MEMORY (32*10*300*4*16) */
  117. /*
  118. MURASTER_CONFIG_GREY_FALLBACK: 0, 1 or 2.
  119. Set to 1 to fallback to grey rendering if the page
  120. is definitely grey. Any images in colored color
  121. spaces will be assumed to be color. This may refuse
  122. to fallback in some cases when it could have done.
  123. Set to 2 to fallback to grey rendering if the page
  124. is definitely grey. Any images in colored color
  125. spaces will be exhaustively checked. This will
  126. fallback whenever possible, at the expense of some
  127. runtime as more processing is required to check.
  128. */
  129. /* #define MURASTER_CONFIG_GREY_FALLBACK 1 */
  130. /*
  131. END OF CONFIGURATION SECTION
  132. */
  133. #include "mupdf/fitz.h"
  134. #include "mupdf/helpers/mu-threads.h"
  135. #include <string.h>
  136. #include <stdlib.h>
  137. #include <stdio.h>
  138. #ifdef _WIN32
  139. struct timeval;
  140. struct timezone;
  141. int gettimeofday(struct timeval *tv, struct timezone *tz);
  142. #else
  143. #include <sys/time.h>
  144. #endif
  145. /*
  146. After this point, we convert the #defines set (or not set)
  147. above into sensible values we can work with. Don't edit
  148. these for configuration.
  149. */
  150. /* Unless we have specifically disabled threading, enable it. */
  151. #ifndef DISABLE_MUTHREADS
  152. #ifndef MURASTER_THREADS
  153. #define MURASTER_THREADS 1
  154. #endif
  155. #endif
  156. /* If we have threading, and we haven't already configured BGPRINT,
  157. * enable it. */
  158. #if MURASTER_THREADS != 0
  159. #ifndef MURASTER_CONFIG_BGPRINT
  160. #define MURASTER_CONFIG_BGPRINT 1
  161. #endif
  162. #endif
  163. #ifdef MURASTER_CONFIG_X_RESOLUTION
  164. #define X_RESOLUTION MURASTER_CONFIG_X_RESOLUTION
  165. #else
  166. #define X_RESOLUTION 300
  167. #endif
  168. #ifdef MURASTER_CONFIG_Y_RESOLUTION
  169. #define Y_RESOLUTION MURASTER_CONFIG_Y_RESOLUTION
  170. #else
  171. #define Y_RESOLUTION 300
  172. #endif
  173. #ifdef MURASTER_CONFIG_WIDTH
  174. #define PAPER_WIDTH MURASTER_CONFIG_WIDTH
  175. #else
  176. #define PAPER_WIDTH 8.27f
  177. #endif
  178. #ifdef MURASTER_CONFIG_HEIGHT
  179. #define PAPER_HEIGHT MURASTER_CONFIG_HEIGHT
  180. #else
  181. #define PAPER_HEIGHT 11.69f
  182. #endif
  183. #ifdef MURASTER_CONFIG_STORE_SIZE
  184. #define STORE_SIZE MURASTER_CONFIG_STORE_SIZE
  185. #else
  186. #define STORE_SIZE FZ_STORE_SIZE
  187. #endif
  188. #ifdef MURASTER_CONFIG_MIN_BAND_HEIGHT
  189. #define MIN_BAND_HEIGHT MURASTER_CONFIG_MIN_BAND_HEIGHT
  190. #else
  191. #define MIN_BAND_HEIGHT 32
  192. #endif
  193. #ifdef MURASTER_CONFIG_BAND_MEMORY
  194. #define BAND_MEMORY MURASTER_CONFIG_BAND_MEMORY
  195. #else
  196. #if defined(FZ_PLOTTERS_CMYK) || defined(FZ_PLOTTERS_N)
  197. #define BAND_MEMORY (MIN_BAND_HEIGHT * PAPER_WIDTH * X_RESOLUTION * 4 * 16)
  198. #elif defined(FZ_PLOTTERS_RGB)
  199. #define BAND_MEMORY (MIN_BAND_HEIGHT * PAPER_WIDTH * X_RESOLUTION * 3 * 16)
  200. #else
  201. #define BAND_MEMORY (MIN_BAND_HEIGHT * PAPER_WIDTH * X_RESOLUTION * 1 * 16)
  202. #endif
  203. #endif
  204. #ifdef MURASTER_CONFIG_GREY_FALLBACK
  205. #define GREY_FALLBACK MURASTER_CONFIG_GREY_FALLBACK
  206. #else
  207. #ifdef FZ_PLOTTERS_N
  208. #define GREY_FALLBACK 1
  209. #elif defined(FZ_PLOTTERS_G) && (defined(FZ_PLOTTERS_RGB) || defined(FZ_PLOTTERS_CMYK))
  210. #define GREY_FALLBACK 1
  211. #else
  212. #define GREY_FALLBACK 0
  213. #endif
  214. #endif
  215. #if GREY_FALLBACK != 0 && !defined(FZ_PLOTTERS_N) && !defined(FZ_PLOTTERS_G)
  216. #error MURASTER_CONFIG_GREY_FALLBACK requires either FZ_PLOTTERS_N or FZ_PLOTTERS_G
  217. #endif
  218. /* Enable for helpful threading debug */
  219. /* #define DEBUG_THREADS(A) do { printf A; fflush(stdout); } while (0) */
  220. #define DEBUG_THREADS(A) do { } while (0)
  221. enum {
  222. OUT_PGM,
  223. OUT_PPM,
  224. OUT_PAM,
  225. OUT_PBM,
  226. OUT_PKM
  227. };
  228. enum {
  229. CS_GRAY,
  230. CS_RGB,
  231. CS_CMYK
  232. };
  233. typedef struct
  234. {
  235. char *suffix;
  236. int format;
  237. int cs;
  238. } suffix_t;
  239. static const suffix_t suffix_table[] =
  240. {
  241. #if FZ_PLOTTERS_G || FZ_PLOTTERS_N
  242. { ".pgm", OUT_PGM, CS_GRAY },
  243. #endif
  244. #if FZ_PLOTTERS_RGB || FZ_PLOTTERS_N
  245. { ".ppm", OUT_PPM, CS_RGB },
  246. #endif
  247. #if FZ_PLOTTERS_CMYK || FZ_PLOTTERS_N
  248. { ".pam", OUT_PAM, CS_CMYK },
  249. #endif
  250. #if FZ_PLOTTERS_G || FZ_PLOTTERS_N
  251. { ".pbm", OUT_PBM, CS_GRAY },
  252. #endif
  253. #if FZ_PLOTTERS_CMYK || FZ_PLOTTERS_N
  254. { ".pkm", OUT_PKM, CS_CMYK }
  255. #endif
  256. };
  257. #ifndef DISABLE_MUTHREADS
  258. static mu_mutex mutexes[FZ_LOCK_MAX];
  259. static void muraster_lock(void *user, int lock)
  260. {
  261. mu_lock_mutex(&mutexes[lock]);
  262. }
  263. static void muraster_unlock(void *user, int lock)
  264. {
  265. mu_unlock_mutex(&mutexes[lock]);
  266. }
  267. static fz_locks_context muraster_locks =
  268. {
  269. NULL, muraster_lock, muraster_unlock
  270. };
  271. static void fin_muraster_locks(void)
  272. {
  273. int i;
  274. for (i = 0; i < FZ_LOCK_MAX; i++)
  275. mu_destroy_mutex(&mutexes[i]);
  276. }
  277. static fz_locks_context *init_muraster_locks(void)
  278. {
  279. int i;
  280. int failed = 0;
  281. for (i = 0; i < FZ_LOCK_MAX; i++)
  282. failed |= mu_create_mutex(&mutexes[i]);
  283. if (failed)
  284. {
  285. fin_muraster_locks();
  286. return NULL;
  287. }
  288. return &muraster_locks;
  289. }
  290. #endif
  291. #ifdef MURASTER_CONFIG_RENDER_THREADS
  292. #define NUM_RENDER_THREADS MURASTER_CONFIG_RENDER_THREADS
  293. #elif defined(DISABLE_MUTHREADS)
  294. #define NUM_RENDER_THREADS 0
  295. #else
  296. #define NUM_RENDER_THREADS 3
  297. #endif
  298. #if defined(DISABLE_MUTHREADS) && NUM_RENDER_THREADS != 0
  299. #error "Can't have MURASTER_CONFIG_RENDER_THREADS > 0 without having a threading library!"
  300. #endif
  301. #ifdef MURASTER_CONFIG_BGPRINT
  302. #define BGPRINT MURASTER_CONFIG_BGPRINT
  303. #elif MURASTER_THREADS == 0
  304. #define BGPRINT 0
  305. #else
  306. #define BGPRINT 1
  307. #endif
  308. #if defined(DISABLE_MUTHREADS) && BGPRINT != 0
  309. #error "Can't have MURASTER_CONFIG_BGPRINT > 0 without having a threading library!"
  310. #endif
  311. typedef struct worker_t {
  312. fz_context *ctx;
  313. int started;
  314. int status;
  315. int num;
  316. int band_start; /* -1 to shutdown, or offset of band to render */
  317. fz_display_list *list;
  318. fz_matrix ctm;
  319. fz_rect tbounds;
  320. fz_pixmap *pix;
  321. fz_bitmap *bit;
  322. fz_cookie cookie;
  323. mu_semaphore start;
  324. mu_semaphore stop;
  325. mu_thread thread;
  326. } worker_t;
  327. static char *output = NULL;
  328. static fz_output *out = NULL;
  329. static char *format;
  330. static int output_format;
  331. static int output_cs;
  332. static int rotation = -1;
  333. static float x_resolution;
  334. static float y_resolution;
  335. static int width = 0;
  336. static int height = 0;
  337. static int fit = 0;
  338. static float layout_w = FZ_DEFAULT_LAYOUT_W;
  339. static float layout_h = FZ_DEFAULT_LAYOUT_H;
  340. static float layout_em = FZ_DEFAULT_LAYOUT_EM;
  341. static char *layout_css = NULL;
  342. static int layout_use_doc_css = 1;
  343. static int showtime = 0;
  344. static int showmemory = 0;
  345. static int ignore_errors = 0;
  346. static int alphabits_text = 8;
  347. static int alphabits_graphics = 8;
  348. static int min_band_height;
  349. static size_t max_band_memory;
  350. static int errored = 0;
  351. static fz_colorspace *colorspace;
  352. static char *filename;
  353. static int num_workers = 0;
  354. static worker_t *workers;
  355. typedef struct render_details
  356. {
  357. /* Page */
  358. fz_page *page;
  359. /* Display list */
  360. fz_display_list *list;
  361. /* Raw bounds */
  362. fz_rect bounds;
  363. /* Transformed bounds */
  364. fz_rect tbounds;
  365. /* Rounded transformed bounds */
  366. fz_irect ibounds;
  367. /* Transform matrix */
  368. fz_matrix ctm;
  369. /* How many min band heights are we working in? */
  370. int band_height_multiple;
  371. /* What colorspace are we working in? (Adjusted for fallback) */
  372. int colorspace;
  373. /* What output format? (Adjusted for fallback) */
  374. int format;
  375. /* During the course of the rendering, this keeps track of
  376. * how many 'min_band_heights' have been safely rendered. */
  377. int bands_rendered;
  378. /* The maximum number of workers we'll try to use. This
  379. * will start at the maximum value, and may drop to 0
  380. * if we have problems with memory. */
  381. int num_workers;
  382. /* The band writer to output the page */
  383. fz_band_writer *bander;
  384. /* Number of components in image */
  385. int n;
  386. } render_details;
  387. enum
  388. {
  389. RENDER_OK = 0,
  390. RENDER_RETRY = 1,
  391. RENDER_FATAL = 2
  392. };
  393. static struct {
  394. int active;
  395. int started;
  396. int solo;
  397. int status;
  398. fz_context *ctx;
  399. mu_thread thread;
  400. mu_semaphore start;
  401. mu_semaphore stop;
  402. int pagenum;
  403. char *filename;
  404. render_details render;
  405. int interptime;
  406. } bgprint;
  407. static struct {
  408. int count, total;
  409. int min, max;
  410. int mininterp, maxinterp;
  411. int minpage, maxpage;
  412. char *minfilename;
  413. char *maxfilename;
  414. } timing;
  415. static int usage(void)
  416. {
  417. fprintf(stderr,
  418. "muraster version " FZ_VERSION "\n"
  419. "usage: muraster [options] file [pages]\n"
  420. "\t-p -\tpassword\n"
  421. "\n"
  422. "\t-o -\toutput file name\n"
  423. "\t-F -\toutput format (default inferred from output file name)\n"
  424. "\t\tpam, pbm, pgm, pkm, ppm\n"
  425. "\n"
  426. "\t-s -\tshow extra information:\n"
  427. "\t\tm - show memory use\n"
  428. "\t\tt - show timings\n"
  429. "\n"
  430. "\t-R {auto,0,90,180,270}\n"
  431. "\t\trotate clockwise (default: auto)\n"
  432. "\t-r -{,_}\tx and y resolution in dpi (default: %dx%d)\n"
  433. "\t-w -\tprintable width (in inches) (default: %.2f)\n"
  434. "\t-h -\tprintable height (in inches) (default: %.2f)\n"
  435. "\t-f\tfit file to page if too large\n"
  436. "\t-B -\tminimum band height (e.g. 32)\n"
  437. "\t-M -\tmax bandmemory (e.g. 655360)\n"
  438. #ifndef DISABLE_MUTHREADS
  439. "\t-T -\tnumber of threads to use for rendering\n"
  440. "\t-P\tparallel interpretation/rendering\n"
  441. #endif
  442. "\n"
  443. "\t-W -\tpage width for EPUB layout\n"
  444. "\t-H -\tpage height for EPUB layout\n"
  445. "\t-S -\tfont size for EPUB layout\n"
  446. "\t-U -\tfile name of user stylesheet for EPUB layout\n"
  447. "\t-X\tdisable document styles for EPUB layout\n"
  448. "\n"
  449. "\t-A -\tnumber of bits of antialiasing (0 to 8)\n"
  450. "\t-A -/-\tnumber of bits of antialiasing (0 to 8) (graphics, text)\n"
  451. "\n"
  452. "\tpages\tcomma separated list of page numbers and ranges\n",
  453. X_RESOLUTION, Y_RESOLUTION, PAPER_WIDTH, PAPER_HEIGHT
  454. );
  455. return 1;
  456. }
  457. static int gettime(void)
  458. {
  459. static struct timeval first;
  460. static int once = 1;
  461. struct timeval now;
  462. if (once)
  463. {
  464. gettimeofday(&first, NULL);
  465. once = 0;
  466. }
  467. gettimeofday(&now, NULL);
  468. return (now.tv_sec - first.tv_sec) * 1000 + (now.tv_usec - first.tv_usec) / 1000;
  469. }
  470. static int drawband(fz_context *ctx, fz_page *page, fz_display_list *list, fz_matrix ctm, fz_rect tbounds, fz_cookie *cookie, int band_start, fz_pixmap *pix, fz_bitmap **bit)
  471. {
  472. fz_device *dev = NULL;
  473. *bit = NULL;
  474. fz_var(dev);
  475. fz_try(ctx)
  476. {
  477. fz_clear_pixmap_with_value(ctx, pix, 255);
  478. dev = fz_new_draw_device(ctx, fz_identity, pix);
  479. if (alphabits_graphics == 0)
  480. fz_enable_device_hints(ctx, dev, FZ_DONT_INTERPOLATE_IMAGES);
  481. if (list)
  482. fz_run_display_list(ctx, list, dev, ctm, tbounds, cookie);
  483. else
  484. fz_run_page(ctx, page, dev, ctm, cookie);
  485. fz_close_device(ctx, dev);
  486. fz_drop_device(ctx, dev);
  487. dev = NULL;
  488. if ((output_format == OUT_PBM) || (output_format == OUT_PKM))
  489. *bit = fz_new_bitmap_from_pixmap_band(ctx, pix, NULL, band_start);
  490. }
  491. fz_catch(ctx)
  492. {
  493. fz_drop_device(ctx, dev);
  494. return RENDER_RETRY;
  495. }
  496. return RENDER_OK;
  497. }
  498. static int dodrawpage(fz_context *ctx, int pagenum, fz_cookie *cookie, render_details *render)
  499. {
  500. fz_pixmap *pix = NULL;
  501. fz_bitmap *bit = NULL;
  502. int errors_are_fatal = 0;
  503. fz_irect ibounds = render->ibounds;
  504. fz_rect tbounds = render->tbounds;
  505. int total_height = ibounds.y1 - ibounds.y0;
  506. int start_offset = min_band_height * render->bands_rendered;
  507. int remaining_start = ibounds.y0 + start_offset;
  508. int remaining_height = ibounds.y1 - remaining_start;
  509. int band_height = min_band_height * render->band_height_multiple;
  510. int bands = (remaining_height + band_height-1) / band_height;
  511. fz_matrix ctm = render->ctm;
  512. int band;
  513. fz_var(pix);
  514. fz_var(bit);
  515. fz_var(errors_are_fatal);
  516. fz_try(ctx)
  517. {
  518. /* Set up ibounds and tbounds for a single band_height band.
  519. * We will adjust ctm as we go. */
  520. ibounds.y1 = ibounds.y0 + band_height;
  521. tbounds.y1 = tbounds.y0 + band_height + 2;
  522. DEBUG_THREADS(("Using %d Bands\n", bands));
  523. ctm.f += start_offset;
  524. if (render->num_workers > 0)
  525. {
  526. for (band = 0; band < fz_mini(render->num_workers, bands); band++)
  527. {
  528. int band_start = start_offset + band * band_height;
  529. worker_t *w = &workers[band];
  530. w->band_start = band_start;
  531. w->ctm = ctm;
  532. w->tbounds = tbounds;
  533. memset(&w->cookie, 0, sizeof(fz_cookie));
  534. w->list = render->list;
  535. if (remaining_height < band_height)
  536. ibounds.y1 = ibounds.y0 + remaining_height;
  537. remaining_height -= band_height;
  538. w->pix = fz_new_pixmap_with_bbox(ctx, colorspace, ibounds, NULL, 0);
  539. w->pix->y += band * band_height;
  540. fz_set_pixmap_resolution(ctx, w->pix, x_resolution, y_resolution);
  541. DEBUG_THREADS(("Worker %d, Pre-triggering band %d\n", band, band));
  542. w->started = 1;
  543. mu_trigger_semaphore(&w->start);
  544. }
  545. pix = workers[0].pix;
  546. }
  547. else
  548. {
  549. pix = fz_new_pixmap_with_bbox(ctx, colorspace, ibounds, NULL, 0);
  550. fz_set_pixmap_resolution(ctx, pix, x_resolution, y_resolution);
  551. }
  552. for (band = 0; band < bands; band++)
  553. {
  554. int status;
  555. int band_start = start_offset + band * band_height;
  556. int draw_height = total_height - band_start;
  557. if (draw_height > band_height)
  558. draw_height = band_height;
  559. if (render->num_workers > 0)
  560. {
  561. worker_t *w = &workers[band % render->num_workers];
  562. DEBUG_THREADS(("Waiting for worker %d to complete band %d\n", w->num, band));
  563. mu_wait_semaphore(&w->stop);
  564. w->started = 0;
  565. status = w->status;
  566. pix = w->pix;
  567. bit = w->bit;
  568. w->bit = NULL;
  569. cookie->errors += w->cookie.errors;
  570. }
  571. else
  572. status = drawband(ctx, render->page, render->list, ctm, tbounds, cookie, band_start, pix, &bit);
  573. if (status != RENDER_OK)
  574. fz_throw(ctx, FZ_ERROR_GENERIC, "Render failed");
  575. render->bands_rendered += render->band_height_multiple;
  576. if (out)
  577. {
  578. /* If we get any errors while outputting the bands, retrying won't help. */
  579. errors_are_fatal = 1;
  580. fz_write_band(ctx, render->bander, bit ? bit->stride : pix->stride, draw_height, bit ? bit->samples : pix->samples);
  581. errors_are_fatal = 0;
  582. }
  583. fz_drop_bitmap(ctx, bit);
  584. bit = NULL;
  585. if (render->num_workers > 0 && band + render->num_workers < bands)
  586. {
  587. worker_t *w = &workers[band % render->num_workers];
  588. w->band_start = band_start;
  589. w->ctm = ctm;
  590. w->tbounds = tbounds;
  591. memset(&w->cookie, 0, sizeof(fz_cookie));
  592. DEBUG_THREADS(("Triggering worker %d for band_start= %d\n", w->num, w->band_start));
  593. w->started = 1;
  594. mu_trigger_semaphore(&w->start);
  595. }
  596. if (render->num_workers <= 0)
  597. pix += draw_height;
  598. }
  599. }
  600. fz_always(ctx)
  601. {
  602. fz_drop_bitmap(ctx, bit);
  603. bit = NULL;
  604. if (render->num_workers > 0)
  605. {
  606. for (band = 0; band < fz_mini(render->num_workers, bands); band++)
  607. {
  608. worker_t *w = &workers[band];
  609. w->cookie.abort = 1;
  610. if (w->started)
  611. {
  612. mu_wait_semaphore(&w->stop);
  613. w->started = 0;
  614. }
  615. fz_drop_pixmap(ctx, w->pix);
  616. }
  617. }
  618. else
  619. fz_drop_pixmap(ctx, pix);
  620. }
  621. fz_catch(ctx)
  622. {
  623. /* Swallow error */
  624. if (errors_are_fatal)
  625. return RENDER_FATAL;
  626. return RENDER_RETRY;
  627. }
  628. if (cookie->errors)
  629. errored = 1;
  630. return RENDER_OK;
  631. }
  632. /* This functions tries to render a page, falling back repeatedly to try and make it work. */
  633. static int try_render_page(fz_context *ctx, int pagenum, fz_cookie *cookie, int start, int interptime, char *fname, int bg, int solo, render_details *render)
  634. {
  635. int status;
  636. if (out && !(bg && solo))
  637. {
  638. /* Output any page level headers (for banded formats). Don't do this if
  639. * we're running in solo bgprint mode, cos we've already done it once! */
  640. fz_try(ctx)
  641. {
  642. int w = render->ibounds.x1 - render->ibounds.x0;
  643. int h = render->ibounds.y1 - render->ibounds.y0;
  644. fz_write_header(ctx, render->bander, w, h, render->n, 0, 0, 0, 0, 0, NULL);
  645. }
  646. fz_catch(ctx)
  647. {
  648. /* Failure! */
  649. return RENDER_FATAL;
  650. }
  651. }
  652. while (1)
  653. {
  654. status = dodrawpage(ctx, pagenum, cookie, render);
  655. if (status == RENDER_OK || status == RENDER_FATAL)
  656. break;
  657. /* If we are bgprinting, then ask the caller to try us again in solo mode. */
  658. if (bg && !solo)
  659. {
  660. DEBUG_THREADS(("Render failure; trying again in solo mode\n"));
  661. return RENDER_RETRY; /* Avoids all the cleanup below! */
  662. }
  663. /* Try again with fewer threads */
  664. if (render->num_workers > 1)
  665. {
  666. render->num_workers >>= 1;
  667. DEBUG_THREADS(("Render failure; trying again with %d render threads\n", render->num_workers));
  668. continue;
  669. }
  670. /* Halve the band height, if we still can. */
  671. if (render->band_height_multiple > 2)
  672. {
  673. render->band_height_multiple >>= 1;
  674. DEBUG_THREADS(("Render failure; trying again with %d band height multiple\n", render->band_height_multiple));
  675. continue;
  676. }
  677. /* If all else fails, ditch the list and try again. */
  678. if (render->list)
  679. {
  680. fz_drop_display_list(ctx, render->list);
  681. render->list = NULL;
  682. DEBUG_THREADS(("Render failure; trying again with no list\n"));
  683. continue;
  684. }
  685. /* Give up. */
  686. DEBUG_THREADS(("Render failure; nothing else to try\n"));
  687. break;
  688. }
  689. fz_close_band_writer(ctx, render->bander);
  690. fz_drop_page(ctx, render->page);
  691. fz_drop_display_list(ctx, render->list);
  692. fz_drop_band_writer(ctx, render->bander);
  693. if (showtime)
  694. {
  695. int end = gettime();
  696. int diff = end - start;
  697. if (bg)
  698. {
  699. if (diff + interptime < timing.min)
  700. {
  701. timing.min = diff + interptime;
  702. timing.mininterp = interptime;
  703. timing.minpage = pagenum;
  704. timing.minfilename = fname;
  705. }
  706. if (diff + interptime > timing.max)
  707. {
  708. timing.max = diff + interptime;
  709. timing.maxinterp = interptime;
  710. timing.maxpage = pagenum;
  711. timing.maxfilename = fname;
  712. }
  713. timing.total += diff + interptime;
  714. timing.count ++;
  715. fprintf(stderr, " %dms (interpretation) %dms (rendering) %dms (total)\n", interptime, diff, diff + interptime);
  716. }
  717. else
  718. {
  719. if (diff < timing.min)
  720. {
  721. timing.min = diff;
  722. timing.minpage = pagenum;
  723. timing.minfilename = fname;
  724. }
  725. if (diff > timing.max)
  726. {
  727. timing.max = diff;
  728. timing.maxpage = pagenum;
  729. timing.maxfilename = fname;
  730. }
  731. timing.total += diff;
  732. timing.count ++;
  733. fprintf(stderr, " %dms\n", diff);
  734. }
  735. }
  736. if (showmemory)
  737. {
  738. fz_dump_glyph_cache_stats(ctx, fz_stderr(ctx));
  739. }
  740. fz_flush_warnings(ctx);
  741. return status;
  742. }
  743. static int wait_for_bgprint_to_finish(void)
  744. {
  745. if (!bgprint.active || !bgprint.started)
  746. return 0;
  747. mu_wait_semaphore(&bgprint.stop);
  748. bgprint.started = 0;
  749. return bgprint.status;
  750. }
  751. static void
  752. get_page_render_details(fz_context *ctx, fz_page *page, render_details *render)
  753. {
  754. float page_width, page_height;
  755. int rot;
  756. float s_x, s_y;
  757. render->page = page;
  758. render->list = NULL;
  759. render->num_workers = num_workers;
  760. render->bounds = fz_bound_page(ctx, page);
  761. page_width = (render->bounds.x1 - render->bounds.x0)/72;
  762. page_height = (render->bounds.y1 - render->bounds.y0)/72;
  763. s_x = x_resolution / 72;
  764. s_y = y_resolution / 72;
  765. if (rotation == -1)
  766. {
  767. /* Automatic rotation. If we fit, use 0. If we don't, and 90 would be 'better' use that. */
  768. if (page_width <= width && page_height <= height)
  769. {
  770. /* Page fits, so use no rotation. */
  771. rot = 0;
  772. }
  773. else if (fit)
  774. {
  775. /* Use whichever gives the biggest scale */
  776. float sx_0 = width / page_width;
  777. float sy_0 = height / page_height;
  778. float sx_90 = height / page_width;
  779. float sy_90 = width / page_height;
  780. float s_0, s_90;
  781. s_0 = fz_min(sx_0, sy_0);
  782. s_90 = fz_min(sx_90, sy_90);
  783. if (s_0 >= s_90)
  784. {
  785. rot = 0;
  786. if (s_0 < 1)
  787. {
  788. s_x *= s_0;
  789. s_y *= s_0;
  790. }
  791. }
  792. else
  793. {
  794. rot = 90;
  795. if (s_90 < 1)
  796. {
  797. s_x *= s_90;
  798. s_y *= s_90;
  799. }
  800. }
  801. }
  802. else
  803. {
  804. /* Use whichever crops the least area */
  805. float lost0 = 0;
  806. float lost90 = 0;
  807. if (page_width > width)
  808. lost0 += (page_width - width) * (page_height > height ? height : page_height);
  809. if (page_height > height)
  810. lost0 += (page_height - height) * page_width;
  811. if (page_width > height)
  812. lost90 += (page_width - height) * (page_height > width ? width : page_height);
  813. if (page_height > width)
  814. lost90 += (page_height - width) * page_width;
  815. rot = (lost0 <= lost90 ? 0 : 90);
  816. }
  817. }
  818. else
  819. {
  820. rot = rotation;
  821. }
  822. render->ctm = fz_pre_scale(fz_rotate(rot), s_x, s_y);
  823. render->tbounds = fz_transform_rect(render->bounds, render->ctm);;
  824. render->ibounds = fz_round_rect(render->tbounds);
  825. }
  826. static void
  827. initialise_banding(fz_context *ctx, render_details *render, int color)
  828. {
  829. size_t min_band_mem;
  830. int bpp, h, w, reps;
  831. render->colorspace = output_cs;
  832. render->format = output_format;
  833. #if GREY_FALLBACK != 0
  834. if (color == 0)
  835. {
  836. if (render->colorspace == CS_RGB)
  837. {
  838. /* Fallback from PPM to PGM */
  839. render->colorspace = CS_GRAY;
  840. render->format = OUT_PGM;
  841. }
  842. else if (render->colorspace == CS_CMYK)
  843. {
  844. render->colorspace = CS_GRAY;
  845. if (render->format == OUT_PKM)
  846. render->format = OUT_PBM;
  847. else
  848. render->format = OUT_PGM;
  849. }
  850. }
  851. #endif
  852. switch (render->colorspace)
  853. {
  854. case CS_GRAY:
  855. bpp = 1;
  856. break;
  857. case CS_RGB:
  858. bpp = 2;
  859. break;
  860. default:
  861. case CS_CMYK:
  862. bpp = 3;
  863. break;
  864. }
  865. w = render->ibounds.x1 - render->ibounds.x0;
  866. h = render->ibounds.y1 - render->ibounds.y0;
  867. if (w <= 0 || h <= 0)
  868. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Invalid page dimensions");
  869. min_band_mem = (size_t)bpp * w * min_band_height;
  870. if (min_band_mem > 0)
  871. reps = (int)(max_band_memory / min_band_mem);
  872. if (min_band_mem == 0 || reps < 1)
  873. reps = 1;
  874. /* Adjust reps to even out the work between threads */
  875. if (render->num_workers > 0)
  876. {
  877. int runs, num_bands;
  878. num_bands = (h + min_band_height - 1) / min_band_height;
  879. /* num_bands = number of min_band_height bands */
  880. runs = (num_bands + reps-1) / reps;
  881. /* runs = number of worker runs of reps min_band_height bands */
  882. runs = ((runs + render->num_workers - 1) / render->num_workers) * render->num_workers;
  883. /* runs = number of worker runs rounded up to make use of all our threads */
  884. reps = (num_bands + runs - 1) / runs;
  885. }
  886. render->band_height_multiple = reps;
  887. render->bands_rendered = 0;
  888. if (output_format == OUT_PGM || output_format == OUT_PPM)
  889. {
  890. render->bander = fz_new_pnm_band_writer(ctx, out);
  891. render->n = output_format == OUT_PGM ? 1 : 3;
  892. }
  893. else if (output_format == OUT_PAM)
  894. {
  895. render->bander = fz_new_pam_band_writer(ctx, out);
  896. render->n = 4;
  897. }
  898. else if (output_format == OUT_PBM)
  899. {
  900. render->bander = fz_new_pbm_band_writer(ctx, out);
  901. render->n = 1;
  902. }
  903. else if (output_format == OUT_PKM)
  904. {
  905. render->bander = fz_new_pkm_band_writer(ctx, out);
  906. render->n = 4;
  907. }
  908. }
  909. static void drawpage(fz_context *ctx, fz_document *doc, int pagenum)
  910. {
  911. fz_page *page;
  912. fz_display_list *list = NULL;
  913. fz_device *list_dev = NULL;
  914. int start;
  915. fz_cookie cookie = { 0 };
  916. #if GREY_FALLBACK != 0
  917. fz_device *test_dev = NULL;
  918. int is_color = 0;
  919. #else
  920. int is_color = 2;
  921. #endif
  922. render_details render;
  923. int status;
  924. fz_var(list);
  925. fz_var(list_dev);
  926. fz_var(test_dev);
  927. do
  928. {
  929. start = (showtime ? gettime() : 0);
  930. page = fz_load_page(ctx, doc, pagenum - 1);
  931. /* Calculate Page bounds, transform etc */
  932. get_page_render_details(ctx, page, &render);
  933. /* Make the display list, and see if we need color */
  934. fz_try(ctx)
  935. {
  936. list = fz_new_display_list(ctx, render.bounds);
  937. list_dev = fz_new_list_device(ctx, list);
  938. #if GREY_FALLBACK != 0
  939. test_dev = fz_new_test_device(ctx, &is_color, 0.01f, 0, list_dev);
  940. fz_run_page(ctx, page, test_dev, fz_identity, &cookie);
  941. fz_close_device(ctx, test_dev);
  942. #else
  943. fz_run_page(ctx, page, list_dev, fz_identity, &cookie);
  944. #endif
  945. fz_close_device(ctx, list_dev);
  946. }
  947. fz_always(ctx)
  948. {
  949. #if GREY_FALLBACK != 0
  950. fz_drop_device(ctx, test_dev);
  951. #endif
  952. fz_drop_device(ctx, list_dev);
  953. }
  954. fz_catch(ctx)
  955. {
  956. fz_drop_display_list(ctx, list);
  957. list = NULL;
  958. /* Just continue with no list. Also, we can't do multiple
  959. * threads if we have no list. */
  960. render.num_workers = 1;
  961. }
  962. render.list = list;
  963. #if GREY_FALLBACK != 0
  964. if (list == NULL)
  965. {
  966. /* We need to know about color, but the previous test failed
  967. * (presumably) due to the size of the list. Rerun direct
  968. * from file. */
  969. fz_try(ctx)
  970. {
  971. test_dev = fz_new_test_device(ctx, &is_color, 0.01f, 0, NULL);
  972. fz_run_page(ctx, page, test_dev, fz_identity, &cookie);
  973. fz_close_device(ctx, test_dev);
  974. }
  975. fz_always(ctx)
  976. {
  977. fz_drop_device(ctx, test_dev);
  978. }
  979. fz_catch(ctx)
  980. {
  981. /* We failed. Just give up. */
  982. fz_drop_page(ctx, page);
  983. fz_rethrow(ctx);
  984. }
  985. }
  986. #endif
  987. #if GREY_FALLBACK == 2
  988. /* If we 'possibly' need color, find out if we 'really' need color. */
  989. if (is_color == 1)
  990. {
  991. /* We know that the device has images or shadings in
  992. * colored spaces. We have been told to test exhaustively
  993. * so we know whether to use color or grey rendering. */
  994. is_color = 0;
  995. fz_try(ctx)
  996. {
  997. test_dev = fz_new_test_device(ctx, &is_color, 0.01f, FZ_TEST_OPT_IMAGES | FZ_TEST_OPT_SHADINGS, NULL);
  998. if (list)
  999. fz_run_display_list(ctx, list, test_dev, &fz_identity, &fz_infinite_rect, &cookie);
  1000. else
  1001. fz_run_page(ctx, page, test_dev, &fz_identity, &cookie);
  1002. fz_close_device(ctx, test_dev);
  1003. }
  1004. fz_always(ctx)
  1005. {
  1006. fz_drop_device(ctx, test_dev);
  1007. }
  1008. fz_catch(ctx)
  1009. {
  1010. fz_drop_display_list(ctx, list);
  1011. fz_drop_page(ctx, page);
  1012. fz_rethrow(ctx);
  1013. }
  1014. }
  1015. #endif
  1016. /* Figure out banding */
  1017. initialise_banding(ctx, &render, is_color);
  1018. if (bgprint.active && showtime)
  1019. {
  1020. int end = gettime();
  1021. start = end - start;
  1022. }
  1023. /* If we're not using bgprint, then no need to wait */
  1024. if (!bgprint.active)
  1025. break;
  1026. /* If we are using it, then wait for it to finish. */
  1027. status = wait_for_bgprint_to_finish();
  1028. if (status == RENDER_OK)
  1029. {
  1030. /* The background bgprint completed successfully. Drop out of the loop,
  1031. * and carry on with our next page. */
  1032. break;
  1033. }
  1034. /* The bgprint in the background failed! This might have been because
  1035. * we were using memory etc in the foreground. We'd better ditch
  1036. * everything we can and try again. */
  1037. fz_drop_display_list(ctx, list);
  1038. fz_drop_page(ctx, page);
  1039. if (status == RENDER_FATAL)
  1040. {
  1041. /* We failed because of not being able to output. No point in retrying. */
  1042. fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to render page");
  1043. }
  1044. bgprint.started = 1;
  1045. bgprint.solo = 1;
  1046. mu_trigger_semaphore(&bgprint.start);
  1047. status = wait_for_bgprint_to_finish();
  1048. if (status != 0)
  1049. {
  1050. /* Hard failure */
  1051. fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to render page");
  1052. }
  1053. /* Loop back to reload this page */
  1054. }
  1055. while (1);
  1056. if (showtime)
  1057. {
  1058. fprintf(stderr, "page %s %d", filename, pagenum);
  1059. }
  1060. if (bgprint.active)
  1061. {
  1062. bgprint.started = 1;
  1063. bgprint.solo = 0;
  1064. bgprint.render = render;
  1065. bgprint.filename = filename;
  1066. bgprint.pagenum = pagenum;
  1067. bgprint.interptime = start;
  1068. mu_trigger_semaphore(&bgprint.start);
  1069. }
  1070. else
  1071. {
  1072. if (try_render_page(ctx, pagenum, &cookie, start, 0, filename, 0, 0, &render))
  1073. {
  1074. /* Hard failure */
  1075. fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to render page");
  1076. }
  1077. }
  1078. }
  1079. /* Wait for the final page being printed by bgprint to complete,
  1080. * retrying if necessary. */
  1081. static void
  1082. finish_bgprint(fz_context *ctx)
  1083. {
  1084. int status;
  1085. if (!bgprint.active)
  1086. return;
  1087. /* If we are using it, then wait for it to finish. */
  1088. status = wait_for_bgprint_to_finish();
  1089. if (status == RENDER_OK)
  1090. {
  1091. /* The background bgprint completed successfully. */
  1092. return;
  1093. }
  1094. if (status == RENDER_FATAL)
  1095. {
  1096. /* We failed because of not being able to output. No point in retrying. */
  1097. fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to render page");
  1098. }
  1099. bgprint.started = 1;
  1100. bgprint.solo = 1;
  1101. mu_trigger_semaphore(&bgprint.start);
  1102. status = wait_for_bgprint_to_finish();
  1103. if (status != 0)
  1104. {
  1105. /* Hard failure */
  1106. fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to render page");
  1107. }
  1108. }
  1109. static void drawrange(fz_context *ctx, fz_document *doc, const char *range)
  1110. {
  1111. int page, spage, epage, pagecount;
  1112. pagecount = fz_count_pages(ctx, doc);
  1113. while ((range = fz_parse_page_range(ctx, range, &spage, &epage, pagecount)))
  1114. {
  1115. if (spage < epage)
  1116. for (page = spage; page <= epage; page++)
  1117. drawpage(ctx, doc, page);
  1118. else
  1119. for (page = spage; page >= epage; page--)
  1120. drawpage(ctx, doc, page);
  1121. }
  1122. }
  1123. typedef struct
  1124. {
  1125. size_t size;
  1126. #if defined(_M_IA64) || defined(_M_AMD64)
  1127. size_t align;
  1128. #endif
  1129. } trace_header;
  1130. typedef struct
  1131. {
  1132. size_t current;
  1133. size_t peak;
  1134. size_t total;
  1135. } trace_info;
  1136. static void *
  1137. trace_malloc(void *arg, size_t size)
  1138. {
  1139. trace_info *info = (trace_info *) arg;
  1140. trace_header *p;
  1141. if (size == 0)
  1142. return NULL;
  1143. p = malloc(size + sizeof(trace_header));
  1144. if (p == NULL)
  1145. return NULL;
  1146. p[0].size = size;
  1147. info->current += size;
  1148. info->total += size;
  1149. if (info->current > info->peak)
  1150. info->peak = info->current;
  1151. return (void *)&p[1];
  1152. }
  1153. static void
  1154. trace_free(void *arg, void *p_)
  1155. {
  1156. trace_info *info = (trace_info *) arg;
  1157. trace_header *p = (trace_header *)p_;
  1158. if (p == NULL)
  1159. return;
  1160. info->current -= p[-1].size;
  1161. free(&p[-1]);
  1162. }
  1163. static void *
  1164. trace_realloc(void *arg, void *p_, size_t size)
  1165. {
  1166. trace_info *info = (trace_info *) arg;
  1167. trace_header *p = (trace_header *)p_;
  1168. size_t oldsize;
  1169. if (size == 0)
  1170. {
  1171. trace_free(arg, p_);
  1172. return NULL;
  1173. }
  1174. if (p == NULL)
  1175. return trace_malloc(arg, size);
  1176. oldsize = p[-1].size;
  1177. p = realloc(&p[-1], size + sizeof(trace_header));
  1178. if (p == NULL)
  1179. return NULL;
  1180. info->current += size - oldsize;
  1181. if (size > oldsize)
  1182. info->total += size - oldsize;
  1183. if (info->current > info->peak)
  1184. info->peak = info->current;
  1185. p[0].size = size;
  1186. return &p[1];
  1187. }
  1188. #ifndef DISABLE_MUTHREADS
  1189. static void worker_thread(void *arg)
  1190. {
  1191. worker_t *me = (worker_t *)arg;
  1192. int band_start;
  1193. do
  1194. {
  1195. DEBUG_THREADS(("Worker %d waiting\n", me->num));
  1196. mu_wait_semaphore(&me->start);
  1197. band_start = me->band_start;
  1198. DEBUG_THREADS(("Worker %d woken for band_start %d\n", me->num, me->band_start));
  1199. me->status = RENDER_OK;
  1200. if (band_start >= 0)
  1201. me->status = drawband(me->ctx, NULL, me->list, me->ctm, me->tbounds, &me->cookie, band_start, me->pix, &me->bit);
  1202. DEBUG_THREADS(("Worker %d completed band_start %d (status=%d)\n", me->num, band_start, me->status));
  1203. mu_trigger_semaphore(&me->stop);
  1204. }
  1205. while (band_start >= 0);
  1206. }
  1207. static void bgprint_worker(void *arg)
  1208. {
  1209. fz_cookie cookie = { 0 };
  1210. int pagenum;
  1211. (void)arg;
  1212. do
  1213. {
  1214. DEBUG_THREADS(("BGPrint waiting\n"));
  1215. mu_wait_semaphore(&bgprint.start);
  1216. pagenum = bgprint.pagenum;
  1217. DEBUG_THREADS(("BGPrint woken for pagenum %d\n", pagenum));
  1218. if (pagenum >= 0)
  1219. {
  1220. int start = gettime();
  1221. memset(&cookie, 0, sizeof(cookie));
  1222. bgprint.status = try_render_page(bgprint.ctx, pagenum, &cookie, start, bgprint.interptime, bgprint.filename, 1, bgprint.solo, &bgprint.render);
  1223. }
  1224. DEBUG_THREADS(("BGPrint completed page %d\n", pagenum));
  1225. mu_trigger_semaphore(&bgprint.stop);
  1226. }
  1227. while (pagenum >= 0);
  1228. }
  1229. #endif
  1230. static void
  1231. read_resolution(const char *arg)
  1232. {
  1233. char *sep = strchr(arg, ',');
  1234. if (sep == NULL)
  1235. sep = strchr(arg, 'x');
  1236. if (sep == NULL)
  1237. sep = strchr(arg, ':');
  1238. if (sep == NULL)
  1239. sep = strchr(arg, ';');
  1240. x_resolution = fz_atoi(arg);
  1241. if (sep && sep[1])
  1242. y_resolution = fz_atoi(arg);
  1243. else
  1244. y_resolution = x_resolution;
  1245. if (x_resolution <= 0 || y_resolution <= 0)
  1246. {
  1247. fprintf(stderr, "Ignoring invalid resolution\n");
  1248. x_resolution = X_RESOLUTION;
  1249. y_resolution = Y_RESOLUTION;
  1250. }
  1251. }
  1252. static int
  1253. read_rotation(const char *arg)
  1254. {
  1255. int i;
  1256. if (strcmp(arg, "auto"))
  1257. {
  1258. return -1;
  1259. }
  1260. i = fz_atoi(arg);
  1261. i = i % 360;
  1262. if (i % 90 != 0)
  1263. {
  1264. fprintf(stderr, "Ignoring invalid rotation\n");
  1265. i = 0;
  1266. }
  1267. return i;
  1268. }
  1269. int main(int argc, char **argv)
  1270. {
  1271. char *password = "";
  1272. fz_document *doc = NULL;
  1273. int c;
  1274. fz_context *ctx;
  1275. trace_info info = { 0, 0, 0 };
  1276. fz_alloc_context alloc_ctx = { &info, trace_malloc, trace_realloc, trace_free };
  1277. fz_locks_context *locks = NULL;
  1278. fz_var(doc);
  1279. bgprint.active = 0; /* set by -P */
  1280. min_band_height = MIN_BAND_HEIGHT;
  1281. max_band_memory = BAND_MEMORY;
  1282. width = 0;
  1283. height = 0;
  1284. num_workers = NUM_RENDER_THREADS;
  1285. x_resolution = X_RESOLUTION;
  1286. y_resolution = Y_RESOLUTION;
  1287. while ((c = fz_getopt(argc, argv, "p:o:F:R:r:w:h:fB:M:s:A:iW:H:S:T:U:XvP")) != -1)
  1288. {
  1289. switch (c)
  1290. {
  1291. default: return usage();
  1292. case 'p': password = fz_optarg; break;
  1293. case 'o': output = fz_optarg; break;
  1294. case 'F': format = fz_optarg; break;
  1295. case 'R': rotation = read_rotation(fz_optarg); break;
  1296. case 'r': read_resolution(fz_optarg); break;
  1297. case 'w': width = fz_atof(fz_optarg); break;
  1298. case 'h': height = fz_atof(fz_optarg); break;
  1299. case 'f': fit = 1; break;
  1300. case 'B': min_band_height = atoi(fz_optarg); break;
  1301. case 'M': max_band_memory = atoi(fz_optarg); break;
  1302. case 'W': layout_w = fz_atof(fz_optarg); break;
  1303. case 'H': layout_h = fz_atof(fz_optarg); break;
  1304. case 'S': layout_em = fz_atof(fz_optarg); break;
  1305. case 'U': layout_css = fz_optarg; break;
  1306. case 'X': layout_use_doc_css = 0; break;
  1307. case 's':
  1308. if (strchr(fz_optarg, 't')) ++showtime;
  1309. if (strchr(fz_optarg, 'm')) ++showmemory;
  1310. break;
  1311. case 'A':
  1312. {
  1313. char *sep;
  1314. alphabits_graphics = atoi(fz_optarg);
  1315. sep = strchr(fz_optarg, '/');
  1316. if (sep)
  1317. alphabits_text = atoi(sep+1);
  1318. else
  1319. alphabits_text = alphabits_graphics;
  1320. break;
  1321. }
  1322. case 'i': ignore_errors = 1; break;
  1323. case 'T':
  1324. #if MURASTER_THREADS != 0
  1325. num_workers = atoi(fz_optarg); break;
  1326. #else
  1327. fprintf(stderr, "Threads not enabled in this build\n");
  1328. break;
  1329. #endif
  1330. case 'P':
  1331. #if MURASTER_THREADS != 0
  1332. bgprint.active = 1; break;
  1333. #else
  1334. fprintf(stderr, "Threads not enabled in this build\n");
  1335. break;
  1336. #endif
  1337. case 'v': fprintf(stderr, "muraster version %s\n", FZ_VERSION); return 1;
  1338. }
  1339. }
  1340. if (width == 0)
  1341. width = x_resolution * PAPER_WIDTH;
  1342. if (height == 0)
  1343. height = y_resolution * PAPER_HEIGHT;
  1344. if (fz_optind == argc)
  1345. return usage();
  1346. if (min_band_height <= 0)
  1347. {
  1348. fprintf(stderr, "Require a positive minimum band height\n");
  1349. exit(1);
  1350. }
  1351. #ifndef DISABLE_MUTHREADS
  1352. locks = init_muraster_locks();
  1353. if (locks == NULL)
  1354. {
  1355. fprintf(stderr, "cannot initialise mutexes\n");
  1356. exit(1);
  1357. }
  1358. #endif
  1359. ctx = fz_new_context((showmemory == 0 ? NULL : &alloc_ctx), locks, FZ_STORE_DEFAULT);
  1360. if (!ctx)
  1361. {
  1362. fprintf(stderr, "cannot initialise context\n");
  1363. exit(1);
  1364. }
  1365. fz_set_text_aa_level(ctx, alphabits_text);
  1366. fz_set_graphics_aa_level(ctx, alphabits_graphics);
  1367. #ifndef DISABLE_MUTHREADS
  1368. if (bgprint.active)
  1369. {
  1370. int fail = 0;
  1371. bgprint.ctx = fz_clone_context(ctx);
  1372. fail |= mu_create_semaphore(&bgprint.start);
  1373. fail |= mu_create_semaphore(&bgprint.stop);
  1374. fail |= mu_create_thread(&bgprint.thread, bgprint_worker, NULL);
  1375. if (fail)
  1376. {
  1377. fprintf(stderr, "bgprint startup failed\n");
  1378. exit(1);
  1379. }
  1380. }
  1381. if (num_workers > 0)
  1382. {
  1383. int i;
  1384. int fail = 0;
  1385. workers = fz_calloc(ctx, num_workers, sizeof(*workers));
  1386. for (i = 0; i < num_workers; i++)
  1387. {
  1388. workers[i].ctx = fz_clone_context(ctx);
  1389. workers[i].num = i;
  1390. fail |= mu_create_semaphore(&workers[i].start);
  1391. fail |= mu_create_semaphore(&workers[i].stop);
  1392. fail |= mu_create_thread(&workers[i].thread, worker_thread, &workers[i]);
  1393. }
  1394. if (fail)
  1395. {
  1396. fprintf(stderr, "worker startup failed\n");
  1397. exit(1);
  1398. }
  1399. }
  1400. #endif /* DISABLE_MUTHREADS */
  1401. if (layout_css)
  1402. fz_load_user_css(ctx, layout_css);
  1403. fz_set_use_document_css(ctx, layout_use_doc_css);
  1404. output_format = suffix_table[0].format;
  1405. output_cs = suffix_table[0].cs;
  1406. if (format)
  1407. {
  1408. int i;
  1409. for (i = 0; i < (int)nelem(suffix_table); i++)
  1410. {
  1411. if (!strcmp(format, suffix_table[i].suffix+1))
  1412. {
  1413. output_format = suffix_table[i].format;
  1414. output_cs = suffix_table[i].cs;
  1415. break;
  1416. }
  1417. }
  1418. if (i == (int)nelem(suffix_table))
  1419. {
  1420. fprintf(stderr, "Unknown output format '%s'\n", format);
  1421. exit(1);
  1422. }
  1423. }
  1424. else if (output)
  1425. {
  1426. char *suffix = output;
  1427. int i;
  1428. for (i = 0; i < (int)nelem(suffix_table); i++)
  1429. {
  1430. char *s = strstr(suffix, suffix_table[i].suffix);
  1431. if (s != NULL)
  1432. {
  1433. suffix = s+1;
  1434. output_format = suffix_table[i].format;
  1435. output_cs = suffix_table[i].cs;
  1436. i = 0;
  1437. }
  1438. }
  1439. }
  1440. switch (output_cs)
  1441. {
  1442. case CS_GRAY:
  1443. colorspace = fz_device_gray(ctx);
  1444. break;
  1445. case CS_RGB:
  1446. colorspace = fz_device_rgb(ctx);
  1447. break;
  1448. case CS_CMYK:
  1449. colorspace = fz_device_cmyk(ctx);
  1450. break;
  1451. }
  1452. if (output && (output[0] != '-' || output[1] != 0) && *output != 0)
  1453. {
  1454. out = fz_new_output_with_path(ctx, output, 0);
  1455. }
  1456. else
  1457. out = fz_stdout(ctx);
  1458. timing.count = 0;
  1459. timing.total = 0;
  1460. timing.min = 1 << 30;
  1461. timing.max = 0;
  1462. timing.mininterp = 1 << 30;
  1463. timing.maxinterp = 0;
  1464. timing.minpage = 0;
  1465. timing.maxpage = 0;
  1466. timing.minfilename = "";
  1467. timing.maxfilename = "";
  1468. fz_try(ctx)
  1469. {
  1470. fz_register_document_handlers(ctx);
  1471. while (fz_optind < argc)
  1472. {
  1473. fz_try(ctx)
  1474. {
  1475. filename = argv[fz_optind++];
  1476. doc = fz_open_document(ctx, filename);
  1477. if (fz_needs_password(ctx, doc))
  1478. {
  1479. if (!fz_authenticate_password(ctx, doc, password))
  1480. fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot authenticate password: %s", filename);
  1481. }
  1482. fz_layout_document(ctx, doc, layout_w, layout_h, layout_em);
  1483. if (fz_optind == argc || !fz_is_page_range(ctx, argv[fz_optind]))
  1484. drawrange(ctx, doc, "1-N");
  1485. if (fz_optind < argc && fz_is_page_range(ctx, argv[fz_optind]))
  1486. drawrange(ctx, doc, argv[fz_optind++]);
  1487. fz_drop_document(ctx, doc);
  1488. doc = NULL;
  1489. }
  1490. fz_catch(ctx)
  1491. {
  1492. if (!ignore_errors)
  1493. fz_rethrow(ctx);
  1494. fz_drop_document(ctx, doc);
  1495. doc = NULL;
  1496. fz_report_error(ctx);
  1497. fz_warn(ctx, "ignoring error in '%s'", filename);
  1498. }
  1499. }
  1500. finish_bgprint(ctx);
  1501. }
  1502. fz_catch(ctx)
  1503. {
  1504. fz_drop_document(ctx, doc);
  1505. fz_log_error_printf(ctx, "cannot draw '%s'", filename);
  1506. errored = 1;
  1507. }
  1508. if (showtime && timing.count > 0)
  1509. {
  1510. fprintf(stderr, "total %dms / %d pages for an average of %dms\n",
  1511. timing.total, timing.count, timing.total / timing.count);
  1512. fprintf(stderr, "fastest page %d: %dms\n", timing.minpage, timing.min);
  1513. fprintf(stderr, "slowest page %d: %dms\n", timing.maxpage, timing.max);
  1514. }
  1515. #ifndef DISABLE_MUTHREADS
  1516. if (num_workers > 0)
  1517. {
  1518. int i;
  1519. for (i = 0; i < num_workers; i++)
  1520. {
  1521. workers[i].band_start = -1;
  1522. mu_trigger_semaphore(&workers[i].start);
  1523. mu_wait_semaphore(&workers[i].stop);
  1524. mu_destroy_semaphore(&workers[i].start);
  1525. mu_destroy_semaphore(&workers[i].stop);
  1526. mu_destroy_thread(&workers[i].thread);
  1527. fz_drop_context(workers[i].ctx);
  1528. }
  1529. fz_free(ctx, workers);
  1530. }
  1531. if (bgprint.active)
  1532. {
  1533. bgprint.pagenum = -1;
  1534. mu_trigger_semaphore(&bgprint.start);
  1535. mu_wait_semaphore(&bgprint.stop);
  1536. mu_destroy_semaphore(&bgprint.start);
  1537. mu_destroy_semaphore(&bgprint.stop);
  1538. mu_destroy_thread(&bgprint.thread);
  1539. fz_drop_context(bgprint.ctx);
  1540. }
  1541. #endif /* DISABLE_MUTHREADS */
  1542. fz_close_output(ctx, out);
  1543. fz_drop_output(ctx, out);
  1544. out = NULL;
  1545. fz_drop_context(ctx);
  1546. #ifndef DISABLE_MUTHREADS
  1547. fin_muraster_locks();
  1548. #endif /* DISABLE_MUTHREADS */
  1549. if (showmemory)
  1550. {
  1551. char buf[100];
  1552. fz_snprintf(buf, sizeof buf, "Memory use total=%zu peak=%zu current=%zu", info.total, info.peak, info.current);
  1553. fprintf(stderr, "%s\n", buf);
  1554. }
  1555. return (errored != 0);
  1556. }