1 | //======================================================================== |
2 | // |
3 | // ImageOutputDev.cc |
4 | // |
5 | // Copyright 1998-2003 Glyph & Cog, LLC |
6 | // |
7 | //======================================================================== |
8 | |
9 | //======================================================================== |
10 | // |
11 | // Modified under the Poppler project - http://poppler.freedesktop.org |
12 | // |
13 | // All changes made under the Poppler project to this file are licensed |
14 | // under GPL version 2 or later |
15 | // |
16 | // Copyright (C) 2005, 2007, 2011, 2018, 2019, 2021, 2022 Albert Astals Cid <aacid@kde.org> |
17 | // Copyright (C) 2006 Rainer Keller <class321@gmx.de> |
18 | // Copyright (C) 2008 Timothy Lee <timothy.lee@siriushk.com> |
19 | // Copyright (C) 2008 Vasile Gaburici <gaburici@cs.umd.edu> |
20 | // Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org> |
21 | // Copyright (C) 2009 William Bader <williambader@hotmail.com> |
22 | // Copyright (C) 2010 Jakob Voss <jakob.voss@gbv.de> |
23 | // Copyright (C) 2012, 2013, 2017, 2018 Adrian Johnson <ajohnson@redneon.com> |
24 | // Copyright (C) 2013 Thomas Fischer <fischer@unix-ag.uni-kl.de> |
25 | // Copyright (C) 2013 Hib Eris <hib@hiberis.nl> |
26 | // Copyright (C) 2017 Caolán McNamara <caolanm@redhat.com> |
27 | // Copyright (C) 2018 Andreas Gruenbacher <agruenba@redhat.com> |
28 | // Copyright (C) 2020 mrbax <12640-mrbax@users.noreply.gitlab.freedesktop.org> |
29 | // Copyright (C) 2024 Fernando Herrera <fherrera@onirica.com> |
30 | // Copyright (C) 2024 Sebastian J. Bronner <waschtl@sbronner.com> |
31 | // |
32 | // To see a description of the changes please see the Changelog file that |
33 | // came with your tarball or type make ChangeLog if you are building from git |
34 | // |
35 | //======================================================================== |
36 | |
37 | #include "config.h" |
38 | #include <poppler-config.h> |
39 | |
40 | #include <cstdio> |
41 | #include <cstdlib> |
42 | #include <cstddef> |
43 | #include <cctype> |
44 | #include <cmath> |
45 | #include "goo/gmem.h" |
46 | #include "goo/NetPBMWriter.h" |
47 | #include "goo/PNGWriter.h" |
48 | #include "goo/TiffWriter.h" |
49 | #include "Error.h" |
50 | #include "GfxState.h" |
51 | #include "Object.h" |
52 | #include "Stream.h" |
53 | #include "JBIG2Stream.h" |
54 | #include "ImageOutputDev.h" |
55 | |
56 | ImageOutputDev::ImageOutputDev(char *fileRootA, bool pageNamesA, bool listImagesA) |
57 | { |
58 | listImages = listImagesA; |
59 | if (!listImages) { |
60 | fileRoot = copyString(s: fileRootA); |
61 | fileName = (char *)gmalloc(size: strlen(s: fileRoot) + 45); |
62 | } |
63 | outputPNG = false; |
64 | outputTiff = false; |
65 | dumpJPEG = false; |
66 | dumpJP2 = false; |
67 | dumpJBIG2 = false; |
68 | dumpCCITT = false; |
69 | pageNames = pageNamesA; |
70 | printFilenames = false; |
71 | imgNum = 0; |
72 | pageNum = 0; |
73 | errorCode = 0; |
74 | if (listImages) { |
75 | printf(format: "page num type width height color comp bpc enc interp object ID x-ppi y-ppi size ratio\n" ); |
76 | printf(format: "--------------------------------------------------------------------------------------------\n" ); |
77 | } |
78 | } |
79 | |
80 | ImageOutputDev::~ImageOutputDev() |
81 | { |
82 | if (!listImages) { |
83 | gfree(p: fileName); |
84 | gfree(p: fileRoot); |
85 | } |
86 | } |
87 | |
88 | void ImageOutputDev::setFilename(const char *fileExt) |
89 | { |
90 | if (pageNames) { |
91 | sprintf(s: fileName, format: "%s-%03d-%03d.%s" , fileRoot, pageNum, imgNum, fileExt); |
92 | } else { |
93 | sprintf(s: fileName, format: "%s-%03d.%s" , fileRoot, imgNum, fileExt); |
94 | } |
95 | } |
96 | |
97 | // Print a floating point number between 0 - 9999 using 4 characters |
98 | // eg '1.23', '12.3', ' 123', '1234' |
99 | // |
100 | // We need to be careful to handle the cases where rounding adds an |
101 | // extra digit before the decimal. eg printf("%4.2f", 9.99999) |
102 | // outputs "10.00" instead of "9.99". |
103 | static void printNumber(double d) |
104 | { |
105 | char buf[10]; |
106 | |
107 | if (d < 10.0) { |
108 | sprintf(s: buf, format: "%4.2f" , d); |
109 | buf[4] = 0; |
110 | printf(format: "%s" , buf); |
111 | } else if (d < 100.0) { |
112 | sprintf(s: buf, format: "%4.1f" , d); |
113 | if (!isdigit(buf[3])) { |
114 | buf[3] = 0; |
115 | printf(format: " %s" , buf); |
116 | } else { |
117 | printf(format: "%s" , buf); |
118 | } |
119 | } else { |
120 | printf(format: "%4.0f" , d); |
121 | } |
122 | } |
123 | |
124 | void ImageOutputDev::listImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, bool inlineImg, ImageType imageType) |
125 | { |
126 | const char *type; |
127 | const char *colorspace; |
128 | const char *enc; |
129 | int components, bpc; |
130 | |
131 | printf(format: "%4d %5d " , pageNum, imgNum); |
132 | type = "" ; |
133 | switch (imageType) { |
134 | case imgImage: |
135 | type = "image" ; |
136 | break; |
137 | case imgStencil: |
138 | type = "stencil" ; |
139 | break; |
140 | case imgMask: |
141 | type = "mask" ; |
142 | break; |
143 | case imgSmask: |
144 | type = "smask" ; |
145 | break; |
146 | } |
147 | printf(format: "%-7s %5d %5d " , type, width, height); |
148 | |
149 | colorspace = "-" ; |
150 | /* masks and stencils default to ncomps = 1 and bpc = 1 */ |
151 | components = 1; |
152 | bpc = 1; |
153 | if (colorMap && colorMap->isOk()) { |
154 | switch (colorMap->getColorSpace()->getMode()) { |
155 | case csDeviceGray: |
156 | case csCalGray: |
157 | colorspace = "gray" ; |
158 | break; |
159 | case csDeviceRGB: |
160 | case csCalRGB: |
161 | colorspace = "rgb" ; |
162 | break; |
163 | case csDeviceCMYK: |
164 | colorspace = "cmyk" ; |
165 | break; |
166 | case csLab: |
167 | colorspace = "lab" ; |
168 | break; |
169 | case csICCBased: |
170 | colorspace = "icc" ; |
171 | break; |
172 | case csIndexed: |
173 | colorspace = "index" ; |
174 | break; |
175 | case csSeparation: |
176 | colorspace = "sep" ; |
177 | break; |
178 | case csDeviceN: |
179 | colorspace = "devn" ; |
180 | break; |
181 | case csPattern: |
182 | default: |
183 | colorspace = "-" ; |
184 | break; |
185 | } |
186 | components = colorMap->getNumPixelComps(); |
187 | bpc = colorMap->getBits(); |
188 | } |
189 | printf(format: "%-5s %2d %2d " , colorspace, components, bpc); |
190 | |
191 | switch (str->getKind()) { |
192 | case strCCITTFax: |
193 | enc = "ccitt" ; |
194 | break; |
195 | case strDCT: |
196 | enc = "jpeg" ; |
197 | break; |
198 | case strJPX: |
199 | enc = "jpx" ; |
200 | break; |
201 | case strJBIG2: |
202 | enc = "jbig2" ; |
203 | break; |
204 | case strFile: |
205 | case strFlate: |
206 | case strCachedFile: |
207 | case strASCIIHex: |
208 | case strASCII85: |
209 | case strLZW: |
210 | case strRunLength: |
211 | case strWeird: |
212 | default: |
213 | enc = "image" ; |
214 | break; |
215 | } |
216 | printf(format: "%-5s " , enc); |
217 | |
218 | printf(format: "%-3s " , interpolate ? "yes" : "no" ); |
219 | |
220 | if (inlineImg) { |
221 | printf(format: "[inline] " ); |
222 | } else if (ref->isRef()) { |
223 | const Ref imageRef = ref->getRef(); |
224 | if (imageRef.gen >= 100000) { |
225 | printf(format: "[none] " ); |
226 | } else { |
227 | printf(format: " %6d %2d " , imageRef.num, imageRef.gen); |
228 | } |
229 | } else { |
230 | printf(format: "[none] " ); |
231 | } |
232 | |
233 | const double *mat = state->getCTM(); |
234 | double width2 = sqrt(x: mat[0] * mat[0] + mat[1] * mat[1]); |
235 | double height2 = sqrt(x: mat[2] * mat[2] + mat[3] * mat[3]); |
236 | double xppi = fabs(x: width * 72.0 / width2); |
237 | double yppi = fabs(x: height * 72.0 / height2); |
238 | if (xppi < 1.0) { |
239 | printf(format: "%5.3f " , xppi); |
240 | } else { |
241 | printf(format: "%5.0f " , xppi); |
242 | } |
243 | if (yppi < 1.0) { |
244 | printf(format: "%5.3f " , yppi); |
245 | } else { |
246 | printf(format: "%5.0f " , yppi); |
247 | } |
248 | |
249 | Goffset embedSize = -1; |
250 | if (inlineImg) { |
251 | embedSize = getInlineImageLength(str, width, height, colorMap); |
252 | } else { |
253 | embedSize = str->getBaseStream()->getLength(); |
254 | } |
255 | |
256 | long long imageSize = 0; |
257 | if (colorMap && colorMap->isOk()) { |
258 | imageSize = ((long long)width * height * colorMap->getNumPixelComps() * colorMap->getBits()) / 8; |
259 | } else { |
260 | imageSize = (long long)width * height / 8; // mask |
261 | } |
262 | |
263 | double ratio = -1.0; |
264 | if (imageSize > 0) { |
265 | ratio = 100.0 * embedSize / imageSize; |
266 | } |
267 | |
268 | if (embedSize < 0) { |
269 | printf(format: " - " ); |
270 | } else if (embedSize <= 9999) { |
271 | printf(format: "%4lldB" , embedSize); |
272 | } else { |
273 | double d = embedSize / 1024.0; |
274 | if (d <= 9999.0) { |
275 | printNumber(d); |
276 | putchar(c: 'K'); |
277 | } else { |
278 | d /= 1024.0; |
279 | if (d <= 9999.0) { |
280 | printNumber(d); |
281 | putchar(c: 'M'); |
282 | } else { |
283 | d /= 1024.0; |
284 | printNumber(d); |
285 | putchar(c: 'G'); |
286 | } |
287 | } |
288 | } |
289 | |
290 | if (ratio > 9.9) { |
291 | printf(format: " %3.0f%%\n" , ratio); |
292 | } else if (ratio >= 0.0) { |
293 | printf(format: " %3.1f%%\n" , ratio); |
294 | } else { |
295 | printf(format: " - \n" ); |
296 | } |
297 | |
298 | ++imgNum; |
299 | } |
300 | |
301 | long ImageOutputDev::getInlineImageLength(Stream *str, int width, int height, GfxImageColorMap *colorMap) |
302 | { |
303 | long len; |
304 | |
305 | if (colorMap) { |
306 | ImageStream *imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); |
307 | imgStr->reset(); |
308 | for (int y = 0; y < height; y++) { |
309 | imgStr->getLine(); |
310 | } |
311 | |
312 | imgStr->close(); |
313 | delete imgStr; |
314 | } else { |
315 | str->reset(); |
316 | for (int y = 0; y < height; y++) { |
317 | int size = (width + 7) / 8; |
318 | for (int x = 0; x < size; x++) { |
319 | str->getChar(); |
320 | } |
321 | } |
322 | } |
323 | |
324 | EmbedStream *embedStr = (EmbedStream *)(str->getBaseStream()); |
325 | embedStr->rewind(); |
326 | len = 0; |
327 | while (embedStr->getChar() != EOF) { |
328 | len++; |
329 | } |
330 | |
331 | embedStr->restore(); |
332 | |
333 | return len; |
334 | } |
335 | |
336 | void ImageOutputDev::writeRawImage(Stream *str, const char *ext) |
337 | { |
338 | FILE *f; |
339 | int c; |
340 | |
341 | // open the image file |
342 | setFilename(ext); |
343 | ++imgNum; |
344 | if (!(f = fopen(filename: fileName, modes: "wb" ))) { |
345 | error(category: errIO, pos: -1, msg: "Couldn't open image file '{0:s}'" , fileName); |
346 | errorCode = 2; |
347 | return; |
348 | } |
349 | |
350 | // initialize stream |
351 | str = str->getNextStream(); |
352 | str->reset(); |
353 | |
354 | // copy the stream |
355 | while ((c = str->getChar()) != EOF) { |
356 | fputc(c: c, stream: f); |
357 | } |
358 | |
359 | str->close(); |
360 | fclose(stream: f); |
361 | } |
362 | |
363 | void ImageOutputDev::writeImageFile(ImgWriter *writer, ImageFormat format, const char *ext, Stream *str, int width, int height, GfxImageColorMap *colorMap) |
364 | { |
365 | FILE *f = nullptr; /* squelch bogus compiler warning */ |
366 | ImageStream *imgStr = nullptr; |
367 | unsigned char *row; |
368 | unsigned char *rowp; |
369 | unsigned char *p; |
370 | GfxRGB rgb; |
371 | GfxCMYK cmyk; |
372 | GfxGray gray; |
373 | unsigned char zero[gfxColorMaxComps]; |
374 | int invert_bits; |
375 | |
376 | if (writer) { |
377 | setFilename(ext); |
378 | ++imgNum; |
379 | if (!(f = fopen(filename: fileName, modes: "wb" ))) { |
380 | error(category: errIO, pos: -1, msg: "Couldn't open image file '{0:s}'" , fileName); |
381 | errorCode = 2; |
382 | return; |
383 | } |
384 | |
385 | if (!writer->init(f, width, height, hDPI: 72, vDPI: 72)) { |
386 | error(category: errIO, pos: -1, msg: "Error writing '{0:s}'" , fileName); |
387 | errorCode = 2; |
388 | return; |
389 | } |
390 | } |
391 | |
392 | int pixelSize = sizeof(unsigned int); |
393 | if (format == imgRGB48) { |
394 | pixelSize = 2 * sizeof(unsigned int); |
395 | } |
396 | |
397 | row = (unsigned char *)gmallocn_checkoverflow(count: width, size: pixelSize); |
398 | if (!row) { |
399 | error(category: errIO, pos: -1, msg: "Image data for '{0:s}' is too big. {1:d} width with {2:d} bytes per pixel" , fileName, width, pixelSize); |
400 | errorCode = 99; |
401 | return; |
402 | } |
403 | |
404 | if (format != imgMonochrome) { |
405 | // initialize stream |
406 | imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); |
407 | imgStr->reset(); |
408 | } else { |
409 | // initialize stream |
410 | str->reset(); |
411 | } |
412 | |
413 | // PDF masks use 0 = draw current color, 1 = leave unchanged. |
414 | // We invert this to provide the standard interpretation of alpha |
415 | // (0 = transparent, 1 = opaque). If the colorMap already inverts |
416 | // the mask we leave the data unchanged. |
417 | invert_bits = 0xff; |
418 | if (colorMap) { |
419 | memset(s: zero, c: 0, n: sizeof(zero)); |
420 | colorMap->getGray(x: zero, gray: &gray); |
421 | if (colToByte(x: gray) == 0) { |
422 | invert_bits = 0x00; |
423 | } |
424 | } |
425 | |
426 | // for each line... |
427 | for (int y = 0; y < height; y++) { |
428 | switch (format) { |
429 | case imgRGB: |
430 | p = imgStr->getLine(); |
431 | rowp = row; |
432 | for (int x = 0; x < width; ++x) { |
433 | if (p) { |
434 | colorMap->getRGB(x: p, rgb: &rgb); |
435 | *rowp++ = colToByte(x: rgb.r); |
436 | *rowp++ = colToByte(x: rgb.g); |
437 | *rowp++ = colToByte(x: rgb.b); |
438 | p += colorMap->getNumPixelComps(); |
439 | } else { |
440 | *rowp++ = 0; |
441 | *rowp++ = 0; |
442 | *rowp++ = 0; |
443 | } |
444 | } |
445 | if (writer) { |
446 | writer->writeRow(row: &row); |
447 | } |
448 | break; |
449 | |
450 | case imgRGB48: { |
451 | p = imgStr->getLine(); |
452 | unsigned short *rowp16 = reinterpret_cast<unsigned short *>(row); |
453 | for (int x = 0; x < width; ++x) { |
454 | if (p) { |
455 | colorMap->getRGB(x: p, rgb: &rgb); |
456 | *rowp16++ = colToShort(x: rgb.r); |
457 | *rowp16++ = colToShort(x: rgb.g); |
458 | *rowp16++ = colToShort(x: rgb.b); |
459 | p += colorMap->getNumPixelComps(); |
460 | } else { |
461 | *rowp16++ = 0; |
462 | *rowp16++ = 0; |
463 | *rowp16++ = 0; |
464 | } |
465 | } |
466 | if (writer) { |
467 | writer->writeRow(row: &row); |
468 | } |
469 | break; |
470 | } |
471 | |
472 | case imgCMYK: |
473 | p = imgStr->getLine(); |
474 | rowp = row; |
475 | for (int x = 0; x < width; ++x) { |
476 | if (p) { |
477 | colorMap->getCMYK(x: p, cmyk: &cmyk); |
478 | *rowp++ = colToByte(x: cmyk.c); |
479 | *rowp++ = colToByte(x: cmyk.m); |
480 | *rowp++ = colToByte(x: cmyk.y); |
481 | *rowp++ = colToByte(x: cmyk.k); |
482 | p += colorMap->getNumPixelComps(); |
483 | } else { |
484 | *rowp++ = 0; |
485 | *rowp++ = 0; |
486 | *rowp++ = 0; |
487 | *rowp++ = 0; |
488 | } |
489 | } |
490 | if (writer) { |
491 | writer->writeRow(row: &row); |
492 | } |
493 | break; |
494 | |
495 | case imgGray: |
496 | p = imgStr->getLine(); |
497 | rowp = row; |
498 | for (int x = 0; x < width; ++x) { |
499 | if (p) { |
500 | colorMap->getGray(x: p, gray: &gray); |
501 | *rowp++ = colToByte(x: gray); |
502 | p += colorMap->getNumPixelComps(); |
503 | } else { |
504 | *rowp++ = 0; |
505 | } |
506 | } |
507 | if (writer) { |
508 | writer->writeRow(row: &row); |
509 | } |
510 | break; |
511 | |
512 | case imgMonochrome: |
513 | int size = (width + 7) / 8; |
514 | for (int x = 0; x < size; x++) { |
515 | row[x] = str->getChar() ^ invert_bits; |
516 | } |
517 | if (writer) { |
518 | writer->writeRow(row: &row); |
519 | } |
520 | break; |
521 | } |
522 | } |
523 | |
524 | gfree(p: row); |
525 | if (format != imgMonochrome) { |
526 | imgStr->close(); |
527 | delete imgStr; |
528 | } |
529 | str->close(); |
530 | if (writer) { |
531 | writer->close(); |
532 | fclose(stream: f); |
533 | } |
534 | } |
535 | |
536 | void ImageOutputDev::writeImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool inlineImg) |
537 | { |
538 | ImageFormat format; |
539 | EmbedStream *embedStr; |
540 | |
541 | if (inlineImg) { |
542 | embedStr = (EmbedStream *)(str->getBaseStream()); |
543 | // Record the stream. This determines the size. |
544 | getInlineImageLength(str, width, height, colorMap); |
545 | // Reading the stream again will return EOF at end of recording. |
546 | embedStr->rewind(); |
547 | } |
548 | |
549 | if (dumpJPEG && str->getKind() == strDCT) { |
550 | // dump JPEG file |
551 | writeRawImage(str, ext: "jpg" ); |
552 | |
553 | } else if (dumpJP2 && str->getKind() == strJPX && !inlineImg) { |
554 | // dump JPEG2000 file |
555 | writeRawImage(str, ext: "jp2" ); |
556 | |
557 | } else if (dumpJBIG2 && str->getKind() == strJBIG2 && !inlineImg) { |
558 | // dump JBIG2 globals stream if available |
559 | JBIG2Stream *jb2Str = static_cast<JBIG2Stream *>(str); |
560 | Object *globals = jb2Str->getGlobalsStream(); |
561 | if (globals->isStream()) { |
562 | FILE *f; |
563 | int c; |
564 | Stream *globalsStr = globals->getStream(); |
565 | |
566 | setFilename("jb2g" ); |
567 | if (!(f = fopen(filename: fileName, modes: "wb" ))) { |
568 | error(category: errIO, pos: -1, msg: "Couldn't open image file '{0:s}'" , fileName); |
569 | errorCode = 2; |
570 | return; |
571 | } |
572 | globalsStr->reset(); |
573 | while ((c = globalsStr->getChar()) != EOF) { |
574 | fputc(c: c, stream: f); |
575 | } |
576 | globalsStr->close(); |
577 | fclose(stream: f); |
578 | } |
579 | |
580 | // dump JBIG2 embedded file |
581 | writeRawImage(str, ext: "jb2e" ); |
582 | |
583 | } else if (dumpCCITT && str->getKind() == strCCITTFax) { |
584 | // write CCITT parameters |
585 | CCITTFaxStream *ccittStr = static_cast<CCITTFaxStream *>(str); |
586 | FILE *f; |
587 | setFilename("params" ); |
588 | if (!(f = fopen(filename: fileName, modes: "wb" ))) { |
589 | error(category: errIO, pos: -1, msg: "Couldn't open image file '{0:s}'" , fileName); |
590 | errorCode = 2; |
591 | return; |
592 | } |
593 | if (ccittStr->getEncoding() < 0) { |
594 | fprintf(stream: f, format: "-4 " ); |
595 | } else if (ccittStr->getEncoding() == 0) { |
596 | fprintf(stream: f, format: "-1 " ); |
597 | } else { |
598 | fprintf(stream: f, format: "-2 " ); |
599 | } |
600 | |
601 | if (ccittStr->getEndOfLine()) { |
602 | fprintf(stream: f, format: "-A " ); |
603 | } else { |
604 | fprintf(stream: f, format: "-P " ); |
605 | } |
606 | |
607 | fprintf(stream: f, format: "-X %d " , ccittStr->getColumns()); |
608 | |
609 | if (ccittStr->getBlackIs1()) { |
610 | fprintf(stream: f, format: "-W " ); |
611 | } else { |
612 | fprintf(stream: f, format: "-B " ); |
613 | } |
614 | |
615 | fprintf(stream: f, format: "-M\n" ); // PDF uses MSB first |
616 | |
617 | fclose(stream: f); |
618 | |
619 | // dump CCITT file |
620 | writeRawImage(str, ext: "ccitt" ); |
621 | |
622 | } else if (outputPNG && !(outputTiff && colorMap && (colorMap->getColorSpace()->getMode() == csDeviceCMYK || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 4)))) { |
623 | // output in PNG format |
624 | |
625 | #ifdef ENABLE_LIBPNG |
626 | ImgWriter *writer; |
627 | |
628 | if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { |
629 | writer = new PNGWriter(PNGWriter::MONOCHROME); |
630 | format = imgMonochrome; |
631 | } else if (colorMap->getColorSpace()->getMode() == csDeviceGray || colorMap->getColorSpace()->getMode() == csCalGray) { |
632 | writer = new PNGWriter(PNGWriter::GRAY); |
633 | format = imgGray; |
634 | } else if ((colorMap->getColorSpace()->getMode() == csDeviceRGB || colorMap->getColorSpace()->getMode() == csCalRGB || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 3)) |
635 | && colorMap->getBits() > 8) { |
636 | writer = new PNGWriter(PNGWriter::RGB48); |
637 | format = imgRGB48; |
638 | } else { |
639 | writer = new PNGWriter(PNGWriter::RGB); |
640 | format = imgRGB; |
641 | } |
642 | |
643 | writeImageFile(writer, format, ext: "png" , str, width, height, colorMap); |
644 | |
645 | delete writer; |
646 | #endif |
647 | } else if (outputTiff) { |
648 | // output in TIFF format |
649 | |
650 | #ifdef ENABLE_LIBTIFF |
651 | ImgWriter *writer; |
652 | |
653 | if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { |
654 | writer = new TiffWriter(TiffWriter::MONOCHROME); |
655 | format = imgMonochrome; |
656 | } else if (colorMap->getColorSpace()->getMode() == csDeviceGray || colorMap->getColorSpace()->getMode() == csCalGray) { |
657 | writer = new TiffWriter(TiffWriter::GRAY); |
658 | format = imgGray; |
659 | } else if (colorMap->getColorSpace()->getMode() == csDeviceCMYK || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 4)) { |
660 | writer = new TiffWriter(TiffWriter::CMYK); |
661 | format = imgCMYK; |
662 | } else if ((colorMap->getColorSpace()->getMode() == csDeviceRGB || colorMap->getColorSpace()->getMode() == csCalRGB || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 3)) |
663 | && colorMap->getBits() > 8) { |
664 | writer = new TiffWriter(TiffWriter::RGB48); |
665 | format = imgRGB48; |
666 | } else { |
667 | writer = new TiffWriter(TiffWriter::RGB); |
668 | format = imgRGB; |
669 | } |
670 | |
671 | writeImageFile(writer, format, ext: "tif" , str, width, height, colorMap); |
672 | |
673 | delete writer; |
674 | #endif |
675 | } else { |
676 | // output in PPM/PBM format |
677 | ImgWriter *writer; |
678 | |
679 | if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { |
680 | writer = new NetPBMWriter(NetPBMWriter::MONOCHROME); |
681 | format = imgMonochrome; |
682 | } else { |
683 | writer = new NetPBMWriter(NetPBMWriter::RGB); |
684 | format = imgRGB; |
685 | } |
686 | |
687 | writeImageFile(writer, format, ext: format == imgRGB ? "ppm" : "pbm" , str, width, height, colorMap); |
688 | |
689 | delete writer; |
690 | } |
691 | |
692 | if (inlineImg) { |
693 | embedStr->restore(); |
694 | } |
695 | |
696 | if (printFilenames) { |
697 | printf(format: "%s\n" , fileName); |
698 | } |
699 | } |
700 | |
701 | bool ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep) |
702 | { |
703 | return true; |
704 | // do nothing -- this avoids the potentially slow loop in Gfx.cc |
705 | } |
706 | |
707 | void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) |
708 | { |
709 | if (listImages) { |
710 | listImage(state, ref, str, width, height, colorMap: nullptr, interpolate, inlineImg, imageType: imgStencil); |
711 | } else { |
712 | writeImage(state, ref, str, width, height, colorMap: nullptr, inlineImg); |
713 | } |
714 | } |
715 | |
716 | void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) |
717 | { |
718 | if (listImages) { |
719 | listImage(state, ref, str, width, height, colorMap, interpolate, inlineImg, imageType: imgImage); |
720 | } else { |
721 | writeImage(state, ref, str, width, height, colorMap, inlineImg); |
722 | } |
723 | } |
724 | |
725 | void ImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert, bool maskInterpolate) |
726 | { |
727 | if (listImages) { |
728 | listImage(state, ref, str, width, height, colorMap, interpolate, inlineImg: false, imageType: imgImage); |
729 | listImage(state, ref, str: maskStr, width: maskWidth, height: maskHeight, colorMap: nullptr, interpolate: maskInterpolate, inlineImg: false, imageType: imgMask); |
730 | } else { |
731 | writeImage(state, ref, str, width, height, colorMap, inlineImg: false); |
732 | writeImage(state, ref, str: maskStr, width: maskWidth, height: maskHeight, colorMap: nullptr, inlineImg: false); |
733 | } |
734 | } |
735 | |
736 | void ImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, |
737 | bool maskInterpolate) |
738 | { |
739 | if (listImages) { |
740 | listImage(state, ref, str, width, height, colorMap, interpolate, inlineImg: false, imageType: imgImage); |
741 | listImage(state, ref, str: maskStr, width: maskWidth, height: maskHeight, colorMap: maskColorMap, interpolate: maskInterpolate, inlineImg: false, imageType: imgSmask); |
742 | } else { |
743 | writeImage(state, ref, str, width, height, colorMap, inlineImg: false); |
744 | writeImage(state, ref, str: maskStr, width: maskWidth, height: maskHeight, colorMap: maskColorMap, inlineImg: false); |
745 | } |
746 | } |
747 | |