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