1 //========================================================================
2 //
3 // PSOutputDev.cc
4 //
5 // Copyright 1996-2013 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 Martin Kretzschmar <martink@gnome.org>
17 // Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com>
18 // Copyright (C) 2006-2009, 2011-2013, 2015-2021 Albert Astals Cid <aacid@kde.org>
19 // Copyright (C) 2006 Jeff Muizelaar <jeff@infidigm.net>
20 // Copyright (C) 2007, 2008 Brad Hards <bradh@kde.org>
21 // Copyright (C) 2008, 2009 Koji Otani <sho@bbr.jp>
22 // Copyright (C) 2008, 2010 Hib Eris <hib@hiberis.nl>
23 // Copyright (C) 2009-2013 Thomas Freitag <Thomas.Freitag@alfa.de>
24 // Copyright (C) 2009 Till Kamppeter <till.kamppeter@gmail.com>
25 // Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org>
26 // Copyright (C) 2009, 2011, 2012, 2014-2017, 2019, 2020 William Bader <williambader@hotmail.com>
27 // Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net>
28 // Copyright (C) 2009-2011, 2013-2015, 2017, 2020 Adrian Johnson <ajohnson@redneon.com>
29 // Copyright (C) 2012, 2014 Fabio D'Urso <fabiodurso@hotmail.it>
30 // Copyright (C) 2012 Lu Wang <coolwanglu@gmail.com>
31 // Copyright (C) 2014 Till Kamppeter <till.kamppeter@gmail.com>
32 // Copyright (C) 2015 Marek Kasik <mkasik@redhat.com>
33 // Copyright (C) 2016 Caolán McNamara <caolanm@redhat.com>
34 // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich
35 // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
36 // Copyright (C) 2018 Philipp Knechtges <philipp-dev@knechtges.com>
37 // Copyright (C) 2019, 2021 Christian Persch <chpe@src.gnome.org>
38 // Copyright (C) 2019 Oliver Sander <oliver.sander@tu-dresden.de>
39 // Copyright (C) 2020, 2021 Philipp Knechtges <philipp-dev@knechtges.com>
40 // Copyright (C) 2021 Hubert Figuiere <hub@figuiere.net>
41 //
42 // To see a description of the changes please see the Changelog file that
43 // came with your tarball or type make ChangeLog if you are building from git
44 //
45 //========================================================================
46
47 #include <config.h>
48
49 #include <cstdio>
50 #include <cstddef>
51 #include <cstdarg>
52 #include <csignal>
53 #include <cmath>
54 #include <climits>
55 #include <algorithm>
56 #include <array>
57 #include "goo/GooString.h"
58 #include "poppler-config.h"
59 #include "GlobalParams.h"
60 #include "Object.h"
61 #include "Error.h"
62 #include "Function.h"
63 #include "Gfx.h"
64 #include "GfxState.h"
65 #include "GfxFont.h"
66 #include "UnicodeMap.h"
67 #include <fofi/FoFiType1C.h>
68 #include <fofi/FoFiTrueType.h>
69 #include "Catalog.h"
70 #include "Page.h"
71 #include "Stream.h"
72 #ifdef ENABLE_ZLIB
73 # include "FlateEncoder.h"
74 #endif
75 #ifdef ENABLE_ZLIB_UNCOMPRESS
76 # include "FlateStream.h"
77 #endif
78 #include "Annot.h"
79 #include "XRef.h"
80 #include "PreScanOutputDev.h"
81 #include "FileSpec.h"
82 #include "CharCodeToUnicode.h"
83 #include "splash/Splash.h"
84 #include "splash/SplashBitmap.h"
85 #include "SplashOutputDev.h"
86 #include "PSOutputDev.h"
87 #include "PDFDoc.h"
88
89 #ifdef USE_CMS
90 # include <lcms2.h>
91 #endif
92
93 // the MSVC math.h doesn't define this
94 #ifndef M_PI
95 # define M_PI 3.14159265358979323846
96 #endif
97
98 //------------------------------------------------------------------------
99
100 // Max size of a slice when rasterizing pages, in pixels.
101 #define rasterizationSliceSize 20000000
102
103 //------------------------------------------------------------------------
104 // PostScript prolog and setup
105 //------------------------------------------------------------------------
106
107 // The '~' escapes mark prolog code that is emitted only in certain
108 // levels:
109 //
110 // ~[123][sn]
111 // ^ ^----- s=psLevel*Sep, n=psLevel*
112 // +----- 1=psLevel1*, 2=psLevel2*, 3=psLevel3*
113
114 static const char *prolog[] = { "/xpdf 75 dict def xpdf begin",
115 "% PDF special state",
116 "/pdfDictSize 15 def",
117 "~1sn",
118 "/pdfStates 64 array def",
119 " 0 1 63 {",
120 " pdfStates exch pdfDictSize dict",
121 " dup /pdfStateIdx 3 index put",
122 " put",
123 " } for",
124 "~123sn",
125 "/pdfSetup {",
126 " /setpagedevice where {",
127 " pop 2 dict begin",
128 " /Policies 1 dict dup begin /PageSize 6 def end def",
129 " { /Duplex true def } if",
130 " currentdict end setpagedevice",
131 " } {",
132 " pop",
133 " } ifelse",
134 "} def",
135 "/pdfSetupPaper {",
136 " % Change paper size, but only if different from previous paper size otherwise",
137 " % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size",
138 " % so we use the same when checking if the size changes.",
139 " /setpagedevice where {",
140 " pop currentpagedevice",
141 " /PageSize known {",
142 " 2 copy",
143 " currentpagedevice /PageSize get aload pop",
144 " exch 4 1 roll",
145 " sub abs 5 gt",
146 " 3 1 roll",
147 " sub abs 5 gt",
148 " or",
149 " } {",
150 " true",
151 " } ifelse",
152 " {",
153 " 2 array astore",
154 " 2 dict begin",
155 " /PageSize exch def",
156 " /ImagingBBox null def",
157 " currentdict end",
158 " setpagedevice",
159 " } {",
160 " pop pop",
161 " } ifelse",
162 " } {",
163 " pop",
164 " } ifelse",
165 "} def",
166 "~1sn",
167 "/pdfOpNames [",
168 " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke",
169 " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender /pdfPatternCS",
170 " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath",
171 "] def",
172 "~123sn",
173 "/pdfStartPage {",
174 "~1sn",
175 " pdfStates 0 get begin",
176 "~23sn",
177 " pdfDictSize dict begin",
178 "~23n",
179 " /pdfFillCS [] def",
180 " /pdfFillXform {} def",
181 " /pdfStrokeCS [] def",
182 " /pdfStrokeXform {} def",
183 "~1n",
184 " /pdfFill 0 def",
185 " /pdfStroke 0 def",
186 "~1s",
187 " /pdfFill [0 0 0 1] def",
188 " /pdfStroke [0 0 0 1] def",
189 "~23sn",
190 " /pdfFill [0] def",
191 " /pdfStroke [0] def",
192 " /pdfFillOP false def",
193 " /pdfStrokeOP false def",
194 "~3sn",
195 " /pdfOPM false def",
196 "~123sn",
197 " /pdfLastFill false def",
198 " /pdfLastStroke false def",
199 " /pdfTextMat [1 0 0 1 0 0] def",
200 " /pdfFontSize 0 def",
201 " /pdfCharSpacing 0 def",
202 " /pdfTextRender 0 def",
203 " /pdfPatternCS false def",
204 " /pdfTextRise 0 def",
205 " /pdfWordSpacing 0 def",
206 " /pdfHorizScaling 1 def",
207 " /pdfTextClipPath [] def",
208 "} def",
209 "/pdfEndPage { end } def",
210 "~23s",
211 "% separation convention operators",
212 "/findcmykcustomcolor where {",
213 " pop",
214 "}{",
215 " /findcmykcustomcolor { 5 array astore } def",
216 "} ifelse",
217 "/setcustomcolor where {",
218 " pop",
219 "}{",
220 " /setcustomcolor {",
221 " exch",
222 " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
223 " 0 4 getinterval cvx",
224 " [ exch /dup load exch { mul exch dup } /forall load",
225 " /pop load dup ] cvx",
226 " ] setcolorspace setcolor",
227 " } def",
228 "} ifelse",
229 "/customcolorimage where {",
230 " pop",
231 "}{",
232 " /customcolorimage {",
233 " gsave",
234 " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
235 " 0 4 getinterval",
236 " [ exch /dup load exch { mul exch dup } /forall load",
237 " /pop load dup ] cvx",
238 " ] setcolorspace",
239 " 10 dict begin",
240 " /ImageType 1 def",
241 " /DataSource exch def",
242 " /ImageMatrix exch def",
243 " /BitsPerComponent exch def",
244 " /Height exch def",
245 " /Width exch def",
246 " /Decode [1 0] def",
247 " currentdict end",
248 " image",
249 " grestore",
250 " } def",
251 "} ifelse",
252 "~123sn",
253 "% PDF color state",
254 "~1n",
255 "/g { dup /pdfFill exch def setgray",
256 " /pdfLastFill true def /pdfLastStroke false def } def",
257 "/G { dup /pdfStroke exch def setgray",
258 " /pdfLastStroke true def /pdfLastFill false def } def",
259 "/fCol {",
260 " pdfLastFill not {",
261 " pdfFill setgray",
262 " /pdfLastFill true def /pdfLastStroke false def",
263 " } if",
264 "} def",
265 "/sCol {",
266 " pdfLastStroke not {",
267 " pdfStroke setgray",
268 " /pdfLastStroke true def /pdfLastFill false def",
269 " } if",
270 "} def",
271 "~1s",
272 "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
273 " /pdfLastFill true def /pdfLastStroke false def } def",
274 "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
275 " /pdfLastStroke true def /pdfLastFill false def } def",
276 "/fCol {",
277 " pdfLastFill not {",
278 " pdfFill aload pop setcmykcolor",
279 " /pdfLastFill true def /pdfLastStroke false def",
280 " } if",
281 "} def",
282 "/sCol {",
283 " pdfLastStroke not {",
284 " pdfStroke aload pop setcmykcolor",
285 " /pdfLastStroke true def /pdfLastFill false def",
286 " } if",
287 "} def",
288 "~3n",
289 "/opm { dup /pdfOPM exch def",
290 " /setoverprintmode where{pop setoverprintmode}{pop}ifelse } def",
291 "~23n",
292 "/cs { /pdfFillXform exch def dup /pdfFillCS exch def",
293 " setcolorspace } def",
294 "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def",
295 " setcolorspace } def",
296 "/sc { pdfLastFill not { pdfFillCS setcolorspace } if",
297 " dup /pdfFill exch def aload pop pdfFillXform setcolor",
298 " /pdfLastFill true def /pdfLastStroke false def } def",
299 "/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } if",
300 " dup /pdfStroke exch def aload pop pdfStrokeXform setcolor",
301 " /pdfLastStroke true def /pdfLastFill false def } def",
302 "/op { /pdfFillOP exch def",
303 " pdfLastFill { pdfFillOP setoverprint } if } def",
304 "/OP { /pdfStrokeOP exch def",
305 " pdfLastStroke { pdfStrokeOP setoverprint } if } def",
306 "/fCol {",
307 " pdfLastFill not {",
308 " pdfFillCS setcolorspace",
309 " pdfFill aload pop pdfFillXform setcolor",
310 " pdfFillOP setoverprint",
311 " /pdfLastFill true def /pdfLastStroke false def",
312 " } if",
313 "} def",
314 "/sCol {",
315 " pdfLastStroke not {",
316 " pdfStrokeCS setcolorspace",
317 " pdfStroke aload pop pdfStrokeXform setcolor",
318 " pdfStrokeOP setoverprint",
319 " /pdfLastStroke true def /pdfLastFill false def",
320 " } if",
321 "} def",
322 "~3s",
323 "/opm { dup /pdfOPM exch def",
324 " /setoverprintmode where{pop setoverprintmode}{pop}ifelse } def",
325 "~23s",
326 "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
327 " /pdfLastFill true def /pdfLastStroke false def } def",
328 "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
329 " /pdfLastStroke true def /pdfLastFill false def } def",
330 "/ck { 6 copy 6 array astore /pdfFill exch def",
331 " findcmykcustomcolor exch setcustomcolor",
332 " /pdfLastFill true def /pdfLastStroke false def } def",
333 "/CK { 6 copy 6 array astore /pdfStroke exch def",
334 " findcmykcustomcolor exch setcustomcolor",
335 " /pdfLastStroke true def /pdfLastFill false def } def",
336 "/op { /pdfFillOP exch def",
337 " pdfLastFill { pdfFillOP setoverprint } if } def",
338 "/OP { /pdfStrokeOP exch def",
339 " pdfLastStroke { pdfStrokeOP setoverprint } if } def",
340 "/fCol {",
341 " pdfLastFill not {",
342 " pdfFill aload length 4 eq {",
343 " setcmykcolor",
344 " }{",
345 " findcmykcustomcolor exch setcustomcolor",
346 " } ifelse",
347 " pdfFillOP setoverprint",
348 " /pdfLastFill true def /pdfLastStroke false def",
349 " } if",
350 "} def",
351 "/sCol {",
352 " pdfLastStroke not {",
353 " pdfStroke aload length 4 eq {",
354 " setcmykcolor",
355 " }{",
356 " findcmykcustomcolor exch setcustomcolor",
357 " } ifelse",
358 " pdfStrokeOP setoverprint",
359 " /pdfLastStroke true def /pdfLastFill false def",
360 " } if",
361 "} def",
362 "~123sn",
363 "% build a font",
364 "/pdfMakeFont {",
365 " 4 3 roll findfont",
366 " 4 2 roll matrix scale makefont",
367 " dup length dict begin",
368 " { 1 index /FID ne { def } { pop pop } ifelse } forall",
369 " /Encoding exch def",
370 " currentdict",
371 " end",
372 " definefont pop",
373 "} def",
374 "/pdfMakeFont16 {",
375 " exch findfont",
376 " dup length dict begin",
377 " { 1 index /FID ne { def } { pop pop } ifelse } forall",
378 " /WMode exch def",
379 " currentdict",
380 " end",
381 " definefont pop",
382 "} def",
383 "~3sn",
384 "/pdfMakeFont16L3 {",
385 " 1 index /CIDFont resourcestatus {",
386 " pop pop 1 index /CIDFont findresource /CIDFontType known",
387 " } {",
388 " false",
389 " } ifelse",
390 " {",
391 " 0 eq { /Identity-H } { /Identity-V } ifelse",
392 " exch 1 array astore composefont pop",
393 " } {",
394 " pdfMakeFont16",
395 " } ifelse",
396 "} def",
397 "~123sn",
398 "% graphics state operators",
399 "~1sn",
400 "/q {",
401 " gsave",
402 " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for",
403 " pdfStates pdfStateIdx 1 add get begin",
404 " pdfOpNames { exch def } forall",
405 "} def",
406 "/Q { end grestore } def",
407 "~23sn",
408 "/q { gsave pdfDictSize dict begin } def",
409 "/Q {",
410 " end grestore",
411 " /pdfLastFill where {",
412 " pop",
413 " pdfLastFill {",
414 " pdfFillOP setoverprint",
415 " } {",
416 " pdfStrokeOP setoverprint",
417 " } ifelse",
418 " } if",
419 "~3sn",
420 " /pdfOPM where {",
421 " pop",
422 " pdfOPM /setoverprintmode where{pop setoverprintmode}{pop}ifelse ",
423 " } if",
424 "~23sn",
425 "} def",
426 "~123sn",
427 "/cm { concat } def",
428 "/d { setdash } def",
429 "/i { setflat } def",
430 "/j { setlinejoin } def",
431 "/J { setlinecap } def",
432 "/M { setmiterlimit } def",
433 "/w { setlinewidth } def",
434 "% path segment operators",
435 "/m { moveto } def",
436 "/l { lineto } def",
437 "/c { curveto } def",
438 "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
439 " neg 0 rlineto closepath } def",
440 "/h { closepath } def",
441 "% path painting operators",
442 "/S { sCol stroke } def",
443 "/Sf { fCol stroke } def",
444 "/f { fCol fill } def",
445 "/f* { fCol eofill } def",
446 "% clipping operators",
447 "/W { clip newpath } def",
448 "/W* { eoclip newpath } def",
449 "/Ws { strokepath clip newpath } def",
450 "% text state operators",
451 "/Tc { /pdfCharSpacing exch def } def",
452 "/Tf { dup /pdfFontSize exch def",
453 " dup pdfHorizScaling mul exch matrix scale",
454 " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
455 " exch findfont exch makefont setfont } def",
456 "/Tr { /pdfTextRender exch def } def",
457 "/Tp { /pdfPatternCS exch def } def",
458 "/Ts { /pdfTextRise exch def } def",
459 "/Tw { /pdfWordSpacing exch def } def",
460 "/Tz { /pdfHorizScaling exch def } def",
461 "% text positioning operators",
462 "/Td { pdfTextMat transform moveto } def",
463 "/Tm { /pdfTextMat exch def } def",
464 "% text string operators",
465 "/xyshow where {",
466 " pop",
467 " /xyshow2 {",
468 " dup length array",
469 " 0 2 2 index length 1 sub {",
470 " 2 index 1 index 2 copy get 3 1 roll 1 add get",
471 " pdfTextMat dtransform",
472 " 4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put",
473 " } for",
474 " exch pop",
475 " xyshow",
476 " } def",
477 "}{",
478 " /xyshow2 {",
479 " currentfont /FontType get 0 eq {",
480 " 0 2 3 index length 1 sub {",
481 " currentpoint 4 index 3 index 2 getinterval show moveto",
482 " 2 copy get 2 index 3 2 roll 1 add get",
483 " pdfTextMat dtransform rmoveto",
484 " } for",
485 " } {",
486 " 0 1 3 index length 1 sub {",
487 " currentpoint 4 index 3 index 1 getinterval show moveto",
488 " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get",
489 " pdfTextMat dtransform rmoveto",
490 " } for",
491 " } ifelse",
492 " pop pop",
493 " } def",
494 "} ifelse",
495 "/cshow where {",
496 " pop",
497 " /xycp {", // xycharpath
498 " 0 3 2 roll",
499 " {",
500 " pop pop currentpoint 3 2 roll",
501 " 1 string dup 0 4 3 roll put false charpath moveto",
502 " 2 copy get 2 index 2 index 1 add get",
503 " pdfTextMat dtransform rmoveto",
504 " 2 add",
505 " } exch cshow",
506 " pop pop",
507 " } def",
508 "}{",
509 " /xycp {", // xycharpath
510 " currentfont /FontType get 0 eq {",
511 " 0 2 3 index length 1 sub {",
512 " currentpoint 4 index 3 index 2 getinterval false charpath moveto",
513 " 2 copy get 2 index 3 2 roll 1 add get",
514 " pdfTextMat dtransform rmoveto",
515 " } for",
516 " } {",
517 " 0 1 3 index length 1 sub {",
518 " currentpoint 4 index 3 index 1 getinterval false charpath moveto",
519 " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get",
520 " pdfTextMat dtransform rmoveto",
521 " } for",
522 " } ifelse",
523 " pop pop",
524 " } def",
525 "} ifelse",
526 "/Tj {",
527 " fCol", // because stringwidth has to draw Type 3 chars
528 " 0 pdfTextRise pdfTextMat dtransform rmoveto",
529 " currentpoint 4 2 roll",
530 " pdfTextRender 1 and 0 eq {",
531 " 2 copy xyshow2",
532 " } if",
533 " pdfTextRender 3 and dup 1 eq exch 2 eq or {",
534 " 3 index 3 index moveto",
535 " 2 copy",
536 " currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
537 " xycp currentpoint stroke moveto",
538 " } if",
539 " pdfTextRender 4 and 0 ne {",
540 " 4 2 roll moveto xycp",
541 " /pdfTextClipPath [ pdfTextClipPath aload pop",
542 " {/moveto cvx}",
543 " {/lineto cvx}",
544 " {/curveto cvx}",
545 " {/closepath cvx}",
546 " pathforall ] def",
547 " currentpoint newpath moveto",
548 " } {",
549 " pop pop pop pop",
550 " } ifelse",
551 " 0 pdfTextRise neg pdfTextMat dtransform rmoveto",
552 "} def",
553 "/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0",
554 " pdfTextMat dtransform rmoveto } def",
555 "/TJmV { 0.001 mul pdfFontSize mul neg 0 exch",
556 " pdfTextMat dtransform rmoveto } def",
557 "/Tclip { pdfTextClipPath cvx exec clip newpath",
558 " /pdfTextClipPath [] def } def",
559 "/Tclip* { pdfTextClipPath cvx exec eoclip newpath",
560 " /pdfTextClipPath [] def } def",
561 "~1ns",
562 "% Level 1 image operators",
563 "/pdfIm1 {",
564 " /pdfImBuf1 4 index string def",
565 " { currentfile pdfImBuf1 readhexstring pop } image",
566 "} def",
567 "/pdfIm1Bin {",
568 " /pdfImBuf1 4 index string def",
569 " { currentfile pdfImBuf1 readstring pop } image",
570 "} def",
571 "~1s",
572 "/pdfIm1Sep {",
573 " /pdfImBuf1 4 index string def",
574 " /pdfImBuf2 4 index string def",
575 " /pdfImBuf3 4 index string def",
576 " /pdfImBuf4 4 index string def",
577 " { currentfile pdfImBuf1 readhexstring pop }",
578 " { currentfile pdfImBuf2 readhexstring pop }",
579 " { currentfile pdfImBuf3 readhexstring pop }",
580 " { currentfile pdfImBuf4 readhexstring pop }",
581 " true 4 colorimage",
582 "} def",
583 "/pdfIm1SepBin {",
584 " /pdfImBuf1 4 index string def",
585 " /pdfImBuf2 4 index string def",
586 " /pdfImBuf3 4 index string def",
587 " /pdfImBuf4 4 index string def",
588 " { currentfile pdfImBuf1 readstring pop }",
589 " { currentfile pdfImBuf2 readstring pop }",
590 " { currentfile pdfImBuf3 readstring pop }",
591 " { currentfile pdfImBuf4 readstring pop }",
592 " true 4 colorimage",
593 "} def",
594 "~1ns",
595 "/pdfImM1 {",
596 " fCol /pdfImBuf1 4 index 7 add 8 idiv string def",
597 " { currentfile pdfImBuf1 readhexstring pop } imagemask",
598 "} def",
599 "/pdfImM1Bin {",
600 " fCol /pdfImBuf1 4 index 7 add 8 idiv string def",
601 " { currentfile pdfImBuf1 readstring pop } imagemask",
602 "} def",
603 "/pdfImStr {",
604 " 2 copy exch length lt {",
605 " 2 copy get exch 1 add exch",
606 " } {",
607 " ()",
608 " } ifelse",
609 "} def",
610 "/pdfImM1a {",
611 " { pdfImStr } imagemask",
612 " pop pop",
613 "} def",
614 "~23sn",
615 "% Level 2/3 image operators",
616 "/pdfImBuf 100 string def",
617 "/pdfImStr {",
618 " 2 copy exch length lt {",
619 " 2 copy get exch 1 add exch",
620 " } {",
621 " ()",
622 " } ifelse",
623 "} def",
624 "/skipEOD {",
625 " { currentfile pdfImBuf readline",
626 " not { pop exit } if",
627 " (%-EOD-) eq { exit } if } loop",
628 "} def",
629 "/pdfIm { image skipEOD } def",
630 "~3sn",
631 "/pdfMask {",
632 " /ReusableStreamDecode filter",
633 " skipEOD",
634 " /maskStream exch def",
635 "} def",
636 "/pdfMaskEnd { maskStream closefile } def",
637 "/pdfMaskInit {",
638 " /maskArray exch def",
639 " /maskIdx 0 def",
640 "} def",
641 "/pdfMaskSrc {",
642 " maskIdx maskArray length lt {",
643 " maskArray maskIdx get",
644 " /maskIdx maskIdx 1 add def",
645 " } {",
646 " ()",
647 " } ifelse",
648 "} def",
649 "~23s",
650 "/pdfImSep {",
651 " findcmykcustomcolor exch",
652 " dup /Width get /pdfImBuf1 exch string def",
653 " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def",
654 " /pdfImDecodeLow exch def",
655 " begin Width Height BitsPerComponent ImageMatrix DataSource end",
656 " /pdfImData exch def",
657 " { pdfImData pdfImBuf1 readstring pop",
658 " 0 1 2 index length 1 sub {",
659 " 1 index exch 2 copy get",
660 " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi",
661 " 255 exch sub put",
662 " } for }",
663 " 6 5 roll customcolorimage",
664 " skipEOD",
665 "} def",
666 "~23sn",
667 "/pdfImM { fCol imagemask skipEOD } def",
668 "~123sn",
669 "/pr { 2 index 2 index 3 2 roll putinterval 4 add } def",
670 "/pdfImClip {",
671 " gsave",
672 " 0 2 4 index length 1 sub {",
673 " dup 4 index exch 2 copy",
674 " get 5 index div put",
675 " 1 add 3 index exch 2 copy",
676 " get 3 index div put",
677 " } for",
678 " pop pop rectclip",
679 "} def",
680 "/pdfImClipEnd { grestore } def",
681 "~23sn",
682 "% shading operators",
683 "/colordelta {",
684 " false 0 1 3 index length 1 sub {",
685 " dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {",
686 " pop true",
687 " } if",
688 " } for",
689 " exch pop exch pop",
690 "} def",
691 "/funcCol { func n array astore } def",
692 "/funcSH {",
693 " dup 0 eq {",
694 " true",
695 " } {",
696 " dup 6 eq {",
697 " false",
698 " } {",
699 " 4 index 4 index funcCol dup",
700 " 6 index 4 index funcCol dup",
701 " 3 1 roll colordelta 3 1 roll",
702 " 5 index 5 index funcCol dup",
703 " 3 1 roll colordelta 3 1 roll",
704 " 6 index 8 index funcCol dup",
705 " 3 1 roll colordelta 3 1 roll",
706 " colordelta or or or",
707 " } ifelse",
708 " } ifelse",
709 " {",
710 " 1 add",
711 " 4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch",
712 " 6 index 6 index 4 index 4 index 4 index funcSH",
713 " 2 index 6 index 6 index 4 index 4 index funcSH",
714 " 6 index 2 index 4 index 6 index 4 index funcSH",
715 " 5 3 roll 3 2 roll funcSH pop pop",
716 " } {",
717 " pop 3 index 2 index add 0.5 mul 3 index 2 index add 0.5 mul",
718 "~23n",
719 " funcCol sc",
720 "~23s",
721 " funcCol aload pop k",
722 "~23sn",
723 " dup 4 index exch mat transform m",
724 " 3 index 3 index mat transform l",
725 " 1 index 3 index mat transform l",
726 " mat transform l pop pop h f*",
727 " } ifelse",
728 "} def",
729 "/axialCol {",
730 " dup 0 lt {",
731 " pop t0",
732 " } {",
733 " dup 1 gt {",
734 " pop t1",
735 " } {",
736 " dt mul t0 add",
737 " } ifelse",
738 " } ifelse",
739 " func n array astore",
740 "} def",
741 "/axialSH {",
742 " dup 0 eq {",
743 " true",
744 " } {",
745 " dup 8 eq {",
746 " false",
747 " } {",
748 " 2 index axialCol 2 index axialCol colordelta",
749 " } ifelse",
750 " } ifelse",
751 " {",
752 " 1 add 3 1 roll 2 copy add 0.5 mul",
753 " dup 4 3 roll exch 4 index axialSH",
754 " exch 3 2 roll axialSH",
755 " } {",
756 " pop 2 copy add 0.5 mul",
757 "~23n",
758 " axialCol sc",
759 "~23s",
760 " axialCol aload pop k",
761 "~23sn",
762 " exch dup dx mul x0 add exch dy mul y0 add",
763 " 3 2 roll dup dx mul x0 add exch dy mul y0 add",
764 " dx abs dy abs ge {",
765 " 2 copy yMin sub dy mul dx div add yMin m",
766 " yMax sub dy mul dx div add yMax l",
767 " 2 copy yMax sub dy mul dx div add yMax l",
768 " yMin sub dy mul dx div add yMin l",
769 " h f*",
770 " } {",
771 " exch 2 copy xMin sub dx mul dy div add xMin exch m",
772 " xMax sub dx mul dy div add xMax exch l",
773 " exch 2 copy xMax sub dx mul dy div add xMax exch l",
774 " xMin sub dx mul dy div add xMin exch l",
775 " h f*",
776 " } ifelse",
777 " } ifelse",
778 "} def",
779 "/radialCol {",
780 " dup t0 lt {",
781 " pop t0",
782 " } {",
783 " dup t1 gt {",
784 " pop t1",
785 " } if",
786 " } ifelse",
787 " func n array astore",
788 "} def",
789 "/radialSH {",
790 " dup 0 eq {",
791 " true",
792 " } {",
793 " dup 8 eq {",
794 " false",
795 " } {",
796 " 2 index dt mul t0 add radialCol",
797 " 2 index dt mul t0 add radialCol colordelta",
798 " } ifelse",
799 " } ifelse",
800 " {",
801 " 1 add 3 1 roll 2 copy add 0.5 mul",
802 " dup 4 3 roll exch 4 index radialSH",
803 " exch 3 2 roll radialSH",
804 " } {",
805 " pop 2 copy add 0.5 mul dt mul t0 add",
806 "~23n",
807 " radialCol sc",
808 "~23s",
809 " radialCol aload pop k",
810 "~23sn",
811 " encl {",
812 " exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
813 " 0 360 arc h",
814 " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
815 " 360 0 arcn h f",
816 " } {",
817 " 2 copy",
818 " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
819 " a1 a2 arcn",
820 " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
821 " a2 a1 arcn h",
822 " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
823 " a1 a2 arc",
824 " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
825 " a2 a1 arc h f",
826 " } ifelse",
827 " } ifelse",
828 "} def",
829 "~123sn",
830 "end",
831 nullptr };
832
833 static const char *cmapProlog[] = { "/CIDInit /ProcSet findresource begin",
834 "10 dict begin",
835 " begincmap",
836 " /CMapType 1 def",
837 " /CMapName /Identity-H def",
838 " /CIDSystemInfo 3 dict dup begin",
839 " /Registry (Adobe) def",
840 " /Ordering (Identity) def",
841 " /Supplement 0 def",
842 " end def",
843 " 1 begincodespacerange",
844 " <0000> <ffff>",
845 " endcodespacerange",
846 " 0 usefont",
847 " 1 begincidrange",
848 " <0000> <ffff> 0",
849 " endcidrange",
850 " endcmap",
851 " currentdict CMapName exch /CMap defineresource pop",
852 "end",
853 "10 dict begin",
854 " begincmap",
855 " /CMapType 1 def",
856 " /CMapName /Identity-V def",
857 " /CIDSystemInfo 3 dict dup begin",
858 " /Registry (Adobe) def",
859 " /Ordering (Identity) def",
860 " /Supplement 0 def",
861 " end def",
862 " /WMode 1 def",
863 " 1 begincodespacerange",
864 " <0000> <ffff>",
865 " endcodespacerange",
866 " 0 usefont",
867 " 1 begincidrange",
868 " <0000> <ffff> 0",
869 " endcidrange",
870 " endcmap",
871 " currentdict CMapName exch /CMap defineresource pop",
872 "end",
873 "end",
874 nullptr };
875
876 //------------------------------------------------------------------------
877 // Fonts
878 //------------------------------------------------------------------------
879
880 struct PSSubstFont
881 {
882 const char *psName; // PostScript name
883 double mWidth; // width of 'm' character
884 };
885
886 // NB: must be in same order as base14SubstFonts in GfxFont.cc
887 static const PSSubstFont psBase14SubstFonts[14] = { { "Courier", 0.600 },
888 { "Courier-Oblique", 0.600 },
889 { "Courier-Bold", 0.600 },
890 { "Courier-BoldOblique", 0.600 },
891 { "Helvetica", 0.833 },
892 { "Helvetica-Oblique", 0.833 },
893 { "Helvetica-Bold", 0.889 },
894 { "Helvetica-BoldOblique", 0.889 },
895 { "Times-Roman", 0.788 },
896 { "Times-Italic", 0.722 },
897 { "Times-Bold", 0.833 },
898 { "Times-BoldItalic", 0.778 },
899 // the last two are never used for substitution
900 { "Symbol", 0 },
901 { "ZapfDingbats", 0 } };
902
903 // Mapping from Type 1/1C font file to PS font name.
904 struct PST1FontName
905 {
906 Ref fontFileID;
907 GooString *psName; // PostScript font name used for this
908 // embedded font file
909 };
910
911 // Info for 8-bit fonts
912 struct PSFont8Info
913 {
914 Ref fontID;
915 int *codeToGID; // code-to-GID mapping for TrueType fonts
916 };
917
918 // Encoding info for substitute 16-bit font
919 struct PSFont16Enc
920 {
921 Ref fontID;
922 GooString *enc;
923 };
924
925 //------------------------------------------------------------------------
926 // process colors
927 //------------------------------------------------------------------------
928
929 #define psProcessCyan 1
930 #define psProcessMagenta 2
931 #define psProcessYellow 4
932 #define psProcessBlack 8
933 #define psProcessCMYK 15
934
935 //------------------------------------------------------------------------
936 // PSOutCustomColor
937 //------------------------------------------------------------------------
938
939 class PSOutCustomColor
940 {
941 public:
942 PSOutCustomColor(double cA, double mA, double yA, double kA, GooString *nameA);
943 ~PSOutCustomColor();
944
945 PSOutCustomColor(const PSOutCustomColor &) = delete;
946 PSOutCustomColor &operator=(const PSOutCustomColor &) = delete;
947
948 double c, m, y, k;
949 GooString *name;
950 PSOutCustomColor *next;
951 };
952
PSOutCustomColor(double cA,double mA,double yA,double kA,GooString * nameA)953 PSOutCustomColor::PSOutCustomColor(double cA, double mA, double yA, double kA, GooString *nameA)
954 {
955 c = cA;
956 m = mA;
957 y = yA;
958 k = kA;
959 name = nameA;
960 next = nullptr;
961 }
962
~PSOutCustomColor()963 PSOutCustomColor::~PSOutCustomColor()
964 {
965 delete name;
966 }
967
968 //------------------------------------------------------------------------
969
970 struct PSOutImgClipRect
971 {
972 int x0, x1, y0, y1;
973 };
974
975 //------------------------------------------------------------------------
976
977 struct PSOutPaperSize
978 {
PSOutPaperSizePSOutPaperSize979 PSOutPaperSize(GooString *nameA, int wA, int hA)
980 {
981 name = nameA;
982 w = wA;
983 h = hA;
984 }
~PSOutPaperSizePSOutPaperSize985 ~PSOutPaperSize() { delete name; }
986 PSOutPaperSize(const PSOutPaperSize &) = delete;
987 PSOutPaperSize &operator=(const PSOutPaperSize &) = delete;
988 GooString *name;
989 int w, h;
990 };
991
992 //------------------------------------------------------------------------
993 // DeviceNRecoder
994 //------------------------------------------------------------------------
995
996 class DeviceNRecoder : public FilterStream
997 {
998 public:
999 DeviceNRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA);
1000 ~DeviceNRecoder() override;
getKind() const1001 StreamKind getKind() const override { return strWeird; }
1002 void reset() override;
getChar()1003 int getChar() override { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; }
lookChar()1004 int lookChar() override { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; }
getPSFilter(int psLevel,const char * indent)1005 GooString *getPSFilter(int psLevel, const char *indent) override { return nullptr; }
isBinary(bool last=true) const1006 bool isBinary(bool last = true) const override { return true; }
isEncoder() const1007 bool isEncoder() const override { return true; }
1008
1009 private:
1010 bool fillBuf();
1011
1012 int width, height;
1013 GfxImageColorMap *colorMap;
1014 const Function *func;
1015 ImageStream *imgStr;
1016 int buf[gfxColorMaxComps];
1017 int pixelIdx;
1018 int bufIdx;
1019 int bufSize;
1020 };
1021
DeviceNRecoder(Stream * strA,int widthA,int heightA,GfxImageColorMap * colorMapA)1022 DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA) : FilterStream(strA)
1023 {
1024 width = widthA;
1025 height = heightA;
1026 colorMap = colorMapA;
1027 imgStr = nullptr;
1028 pixelIdx = 0;
1029 bufIdx = gfxColorMaxComps;
1030 bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getAlt()->getNComps();
1031 func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getTintTransformFunc();
1032 }
1033
~DeviceNRecoder()1034 DeviceNRecoder::~DeviceNRecoder()
1035 {
1036 if (imgStr) {
1037 delete imgStr;
1038 }
1039 if (str->isEncoder()) {
1040 delete str;
1041 }
1042 }
1043
reset()1044 void DeviceNRecoder::reset()
1045 {
1046 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits());
1047 imgStr->reset();
1048 }
1049
fillBuf()1050 bool DeviceNRecoder::fillBuf()
1051 {
1052 unsigned char pixBuf[gfxColorMaxComps];
1053 GfxColor color;
1054 double x[gfxColorMaxComps], y[gfxColorMaxComps];
1055 int i;
1056
1057 if (pixelIdx >= width * height) {
1058 return false;
1059 }
1060 imgStr->getPixel(pixBuf);
1061 colorMap->getColor(pixBuf, &color);
1062 for (i = 0; i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps(); ++i) {
1063 x[i] = colToDbl(color.c[i]);
1064 }
1065 func->transform(x, y);
1066 for (i = 0; i < bufSize; ++i) {
1067 buf[i] = (int)(y[i] * 255 + 0.5);
1068 }
1069 bufIdx = 0;
1070 ++pixelIdx;
1071 return true;
1072 }
1073
1074 //------------------------------------------------------------------------
1075 // PSOutputDev
1076 //------------------------------------------------------------------------
1077
1078 extern "C" {
1079 typedef void (*SignalFunc)(int);
1080 }
1081
outputToFile(void * stream,const char * data,int len)1082 static void outputToFile(void *stream, const char *data, int len)
1083 {
1084 fwrite(data, 1, len, (FILE *)stream);
1085 }
1086
PSOutputDev(const char * fileName,PDFDoc * docA,char * psTitleA,const std::vector<int> & pagesA,PSOutMode modeA,int paperWidthA,int paperHeightA,bool noCropA,bool duplexA,int imgLLXA,int imgLLYA,int imgURXA,int imgURYA,PSForceRasterize forceRasterizeA,bool manualCtrlA,PSOutCustomCodeCbk customCodeCbkA,void * customCodeCbkDataA,PSLevel levelA)1087 PSOutputDev::PSOutputDev(const char *fileName, PDFDoc *docA, char *psTitleA, const std::vector<int> &pagesA, PSOutMode modeA, int paperWidthA, int paperHeightA, bool noCropA, bool duplexA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
1088 PSForceRasterize forceRasterizeA, bool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA, PSLevel levelA)
1089 {
1090 FILE *f;
1091 PSFileType fileTypeA;
1092
1093 underlayCbk = nullptr;
1094 underlayCbkData = nullptr;
1095 overlayCbk = nullptr;
1096 overlayCbkData = nullptr;
1097 customCodeCbk = customCodeCbkA;
1098 customCodeCbkData = customCodeCbkDataA;
1099
1100 fontIDs = nullptr;
1101 t1FontNames = nullptr;
1102 font8Info = nullptr;
1103 font16Enc = nullptr;
1104 imgIDs = nullptr;
1105 formIDs = nullptr;
1106 paperSizes = nullptr;
1107 embFontList = nullptr;
1108 customColors = nullptr;
1109 haveTextClip = false;
1110 t3String = nullptr;
1111 forceRasterize = forceRasterizeA;
1112 psTitle = nullptr;
1113
1114 // open file or pipe
1115 if (!strcmp(fileName, "-")) {
1116 fileTypeA = psStdout;
1117 f = stdout;
1118 } else if (fileName[0] == '|') {
1119 fileTypeA = psPipe;
1120 #ifdef HAVE_POPEN
1121 # ifndef _WIN32
1122 signal(SIGPIPE, (SignalFunc)SIG_IGN);
1123 # endif
1124 if (!(f = popen(fileName + 1, "w"))) {
1125 error(errIO, -1, "Couldn't run print command '{0:s}'", fileName);
1126 ok = false;
1127 return;
1128 }
1129 #else
1130 error(errIO, -1, "Print commands are not supported ('{0:s}')", fileName);
1131 ok = false;
1132 return;
1133 #endif
1134 } else {
1135 fileTypeA = psFile;
1136 if (!(f = openFile(fileName, "w"))) {
1137 error(errIO, -1, "Couldn't open PostScript file '{0:s}'", fileName);
1138 ok = false;
1139 return;
1140 }
1141 }
1142
1143 init(outputToFile, f, fileTypeA, psTitleA, docA, pagesA, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, paperWidthA, paperHeightA, noCropA, duplexA, levelA);
1144 }
1145
PSOutputDev(int fdA,PDFDoc * docA,char * psTitleA,const std::vector<int> & pagesA,PSOutMode modeA,int paperWidthA,int paperHeightA,bool noCropA,bool duplexA,int imgLLXA,int imgLLYA,int imgURXA,int imgURYA,PSForceRasterize forceRasterizeA,bool manualCtrlA,PSOutCustomCodeCbk customCodeCbkA,void * customCodeCbkDataA,PSLevel levelA)1146 PSOutputDev::PSOutputDev(int fdA, PDFDoc *docA, char *psTitleA, const std::vector<int> &pagesA, PSOutMode modeA, int paperWidthA, int paperHeightA, bool noCropA, bool duplexA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
1147 PSForceRasterize forceRasterizeA, bool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA, PSLevel levelA)
1148 {
1149 FILE *f;
1150 PSFileType fileTypeA;
1151
1152 underlayCbk = nullptr;
1153 underlayCbkData = nullptr;
1154 overlayCbk = nullptr;
1155 overlayCbkData = nullptr;
1156 customCodeCbk = customCodeCbkA;
1157 customCodeCbkData = customCodeCbkDataA;
1158
1159 fontIDs = nullptr;
1160 t1FontNames = nullptr;
1161 font8Info = nullptr;
1162 font16Enc = nullptr;
1163 imgIDs = nullptr;
1164 formIDs = nullptr;
1165 paperSizes = nullptr;
1166 embFontList = nullptr;
1167 customColors = nullptr;
1168 haveTextClip = false;
1169 t3String = nullptr;
1170 forceRasterize = forceRasterizeA;
1171 psTitle = nullptr;
1172
1173 // open file or pipe
1174 if (fdA == fileno(stdout)) {
1175 fileTypeA = psStdout;
1176 f = stdout;
1177 } else {
1178 fileTypeA = psFile;
1179 if (!(f = fdopen(fdA, "w"))) {
1180 error(errIO, -1, "Couldn't open PostScript file descriptor '{0:d}'", fdA);
1181 ok = false;
1182 return;
1183 }
1184 }
1185
1186 init(outputToFile, f, fileTypeA, psTitleA, docA, pagesA, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, paperWidthA, paperHeightA, noCropA, duplexA, levelA);
1187 }
1188
PSOutputDev(PSOutputFunc outputFuncA,void * outputStreamA,char * psTitleA,PDFDoc * docA,const std::vector<int> & pagesA,PSOutMode modeA,int paperWidthA,int paperHeightA,bool noCropA,bool duplexA,int imgLLXA,int imgLLYA,int imgURXA,int imgURYA,PSForceRasterize forceRasterizeA,bool manualCtrlA,PSOutCustomCodeCbk customCodeCbkA,void * customCodeCbkDataA,PSLevel levelA)1189 PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, char *psTitleA, PDFDoc *docA, const std::vector<int> &pagesA, PSOutMode modeA, int paperWidthA, int paperHeightA, bool noCropA, bool duplexA, int imgLLXA, int imgLLYA,
1190 int imgURXA, int imgURYA, PSForceRasterize forceRasterizeA, bool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA, PSLevel levelA)
1191 {
1192 underlayCbk = nullptr;
1193 underlayCbkData = nullptr;
1194 overlayCbk = nullptr;
1195 overlayCbkData = nullptr;
1196 customCodeCbk = customCodeCbkA;
1197 customCodeCbkData = customCodeCbkDataA;
1198
1199 fontIDs = nullptr;
1200 t1FontNames = nullptr;
1201 font8Info = nullptr;
1202 font16Enc = nullptr;
1203 imgIDs = nullptr;
1204 formIDs = nullptr;
1205 paperSizes = nullptr;
1206 embFontList = nullptr;
1207 customColors = nullptr;
1208 haveTextClip = false;
1209 t3String = nullptr;
1210 forceRasterize = forceRasterizeA;
1211 psTitle = nullptr;
1212
1213 init(outputFuncA, outputStreamA, psGeneric, psTitleA, docA, pagesA, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, paperWidthA, paperHeightA, noCropA, duplexA, levelA);
1214 }
1215
1216 struct StandardMedia
1217 {
1218 const char *name;
1219 int width;
1220 int height;
1221 };
1222
1223 static const StandardMedia standardMedia[] = { { "A0", 2384, 3371 }, { "A1", 1685, 2384 }, { "A2", 1190, 1684 }, { "A3", 842, 1190 }, { "A4", 595, 842 }, { "A5", 420, 595 },
1224 { "B4", 729, 1032 }, { "B5", 516, 729 }, { "Letter", 612, 792 }, { "Tabloid", 792, 1224 }, { "Ledger", 1224, 792 }, { "Legal", 612, 1008 },
1225 { "Statement", 396, 612 }, { "Executive", 540, 720 }, { "Folio", 612, 936 }, { "Quarto", 610, 780 }, { "10x14", 720, 1008 }, { nullptr, 0, 0 } };
1226
1227 /* PLRM specifies a tolerance of 5 points when matching page sizes */
pageDimensionEqual(int a,int b)1228 static bool pageDimensionEqual(int a, int b)
1229 {
1230 int aux;
1231 if (unlikely(checkedSubtraction(a, b, &aux))) {
1232 return false;
1233 }
1234 return (abs(aux) < 5);
1235 }
1236
1237 // Shared initialization of PSOutputDev members.
1238 // Store the values but do not process them so the function that
1239 // created the PSOutputDev can use the various setters to change defaults.
1240
init(PSOutputFunc outputFuncA,void * outputStreamA,PSFileType fileTypeA,char * psTitleA,PDFDoc * docA,const std::vector<int> & pagesA,PSOutMode modeA,int imgLLXA,int imgLLYA,int imgURXA,int imgURYA,bool manualCtrlA,int paperWidthA,int paperHeightA,bool noCropA,bool duplexA,PSLevel levelA)1241 void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA, PSFileType fileTypeA, char *psTitleA, PDFDoc *docA, const std::vector<int> &pagesA, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, bool manualCtrlA,
1242 int paperWidthA, int paperHeightA, bool noCropA, bool duplexA, PSLevel levelA)
1243 {
1244
1245 if (pagesA.empty()) {
1246 ok = false;
1247 return;
1248 }
1249
1250 // initialize
1251 postInitDone = false;
1252 embedType1 = true;
1253 embedTrueType = true;
1254 embedCIDPostScript = true;
1255 embedCIDTrueType = true;
1256 fontPassthrough = false;
1257 optimizeColorSpace = false;
1258 passLevel1CustomColor = false;
1259 preloadImagesForms = false;
1260 generateOPI = false;
1261 useASCIIHex = false;
1262 useBinary = false;
1263 enableLZW = true;
1264 enableFlate = true;
1265 rasterResolution = 300;
1266 uncompressPreloadedImages = false;
1267 psCenter = true;
1268 rasterAntialias = false;
1269 displayText = true;
1270 ok = true;
1271 outputFunc = outputFuncA;
1272 outputStream = outputStreamA;
1273 fileType = fileTypeA;
1274 psTitle = (psTitleA ? strdup(psTitleA) : nullptr);
1275 doc = docA;
1276 level = levelA;
1277 pages = pagesA;
1278 mode = modeA;
1279 paperWidth = paperWidthA;
1280 paperHeight = paperHeightA;
1281 noCrop = noCropA;
1282 duplex = duplexA;
1283 imgLLX = imgLLXA;
1284 imgLLY = imgLLYA;
1285 imgURX = imgURXA;
1286 imgURY = imgURYA;
1287 manualCtrl = manualCtrlA;
1288
1289 xref = nullptr;
1290
1291 processColors = 0;
1292 inType3Char = false;
1293 inUncoloredPattern = false;
1294 t3FillColorOnly = false;
1295
1296 #ifdef OPI_SUPPORT
1297 // initialize OPI nesting levels
1298 opi13Nest = 0;
1299 opi20Nest = 0;
1300 #endif
1301
1302 tx0 = ty0 = -1;
1303 xScale0 = yScale0 = 0;
1304 rotate0 = -1;
1305 clipLLX0 = clipLLY0 = 0;
1306 clipURX0 = clipURY0 = -1;
1307
1308 processColorFormatSpecified = false;
1309
1310 // initialize sequential page number
1311 seqPage = 1;
1312 }
1313
1314 // Complete the initialization after the function that created the PSOutputDev
1315 // has had a chance to modify default values with the various setters.
1316
postInit()1317 void PSOutputDev::postInit()
1318 {
1319 Catalog *catalog;
1320 PDFRectangle *box;
1321 PSOutPaperSize *size;
1322 int w, h, i;
1323
1324 if (postInitDone || !ok) {
1325 return;
1326 }
1327
1328 postInitDone = true;
1329
1330 xref = doc->getXRef();
1331 catalog = doc->getCatalog();
1332
1333 if (paperWidth < 0 || paperHeight < 0) {
1334 paperMatch = true;
1335 } else {
1336 paperMatch = false;
1337 }
1338
1339 paperSizes = new std::vector<PSOutPaperSize *>();
1340 for (const int pg : pages) {
1341 Page *page = catalog->getPage(pg);
1342 if (page == nullptr)
1343 paperMatch = false;
1344 if (!paperMatch) {
1345 w = paperWidth;
1346 h = paperHeight;
1347 if (w < 0 || h < 0) {
1348 // Unable to obtain a paper size from the document and no page size
1349 // specified. In this case use A4 as the page size to ensure the PS output is
1350 // valid. This will only occur if the PDF is very broken.
1351 w = 595;
1352 h = 842;
1353 }
1354 } else if (noCrop) {
1355 w = (int)ceil(page->getMediaWidth());
1356 h = (int)ceil(page->getMediaHeight());
1357 } else {
1358 w = (int)ceil(page->getCropWidth());
1359 h = (int)ceil(page->getCropHeight());
1360 }
1361 if (paperMatch) {
1362 const int pageRotate = page->getRotate();
1363 if (pageRotate == 90 || pageRotate == 270)
1364 std::swap(w, h);
1365 }
1366 if (w > paperWidth)
1367 paperWidth = w;
1368 if (h > paperHeight)
1369 paperHeight = h;
1370 for (i = 0; i < (int)paperSizes->size(); ++i) {
1371 size = (*paperSizes)[i];
1372 if (pageDimensionEqual(w, size->w) && pageDimensionEqual(h, size->h))
1373 break;
1374 }
1375 if (i == (int)paperSizes->size()) {
1376 const StandardMedia *media = standardMedia;
1377 GooString *name = nullptr;
1378 while (media->name) {
1379 if (pageDimensionEqual(w, media->width) && pageDimensionEqual(h, media->height)) {
1380 name = new GooString(media->name);
1381 w = media->width;
1382 h = media->height;
1383 break;
1384 }
1385 media++;
1386 }
1387 if (!name)
1388 name = GooString::format("{0:d}x{1:d}mm", int(w * 25.4 / 72), int(h * 25.4 / 72));
1389 paperSizes->push_back(new PSOutPaperSize(name, w, h));
1390 }
1391 pagePaperSize.insert(std::pair<int, int>(pg, i));
1392 if (!paperMatch)
1393 break; // we only need one entry when all pages are the same size
1394 }
1395 if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) {
1396 imgLLX = imgLLY = 0;
1397 imgURX = paperWidth;
1398 imgURY = paperHeight;
1399 }
1400 std::vector<int> pageList;
1401 if (mode == psModeForm) {
1402 pageList.push_back(pages[0]);
1403 } else {
1404 pageList = pages;
1405 }
1406
1407 // initialize fontIDs, fontFileIDs, and fontFileNames lists
1408 fontIDSize = 64;
1409 fontIDLen = 0;
1410 fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref));
1411 for (i = 0; i < 14; ++i) {
1412 fontNames.emplace(psBase14SubstFonts[i].psName);
1413 }
1414 t1FontNameSize = 64;
1415 t1FontNameLen = 0;
1416 t1FontNames = (PST1FontName *)gmallocn(t1FontNameSize, sizeof(PST1FontName));
1417 font8InfoLen = 0;
1418 font8InfoSize = 0;
1419 font16EncLen = 0;
1420 font16EncSize = 0;
1421 imgIDLen = 0;
1422 imgIDSize = 0;
1423 formIDLen = 0;
1424 formIDSize = 0;
1425
1426 numSaves = 0;
1427 numTilingPatterns = 0;
1428 nextFunc = 0;
1429
1430 // set some default process color format if none is set
1431 if (!processColorFormatSpecified) {
1432 if (level == psLevel1) {
1433 processColorFormat = splashModeMono8;
1434 } else if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep || overprintPreview) {
1435 processColorFormat = splashModeCMYK8;
1436 }
1437 #ifdef USE_CMS
1438 else if (getDisplayProfile()) {
1439 auto processcolorspace = cmsGetColorSpace(getDisplayProfile().get());
1440 if (processcolorspace == cmsSigCmykData) {
1441 processColorFormat = splashModeCMYK8;
1442 } else if (processcolorspace == cmsSigGrayData) {
1443 processColorFormat = splashModeMono8;
1444 } else {
1445 processColorFormat = splashModeRGB8;
1446 }
1447 }
1448 #endif
1449 else {
1450 processColorFormat = splashModeRGB8;
1451 }
1452 }
1453
1454 // check for consistency between the processColorFormat the LanguageLevel and other settings
1455 if (level == psLevel1 && processColorFormat != splashModeMono8) {
1456 error(errConfig, -1,
1457 "Conflicting settings between LanguageLevel=psLevel1 and processColorFormat."
1458 " Resetting processColorFormat to MONO8.");
1459 processColorFormat = splashModeMono8;
1460 } else if ((level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep || overprintPreview) && processColorFormat != splashModeCMYK8) {
1461 error(errConfig, -1,
1462 "Conflicting settings between LanguageLevel and/or overprint simulation, and processColorFormat."
1463 " Resetting processColorFormat to CMYK8.");
1464 processColorFormat = splashModeCMYK8;
1465 }
1466 #ifdef USE_CMS
1467 if (getDisplayProfile()) {
1468 auto processcolorspace = cmsGetColorSpace(getDisplayProfile().get());
1469 if (processColorFormat == splashModeCMYK8) {
1470 if (processcolorspace != cmsSigCmykData) {
1471 error(errConfig, -1, "Mismatch between processColorFormat=CMYK8 and ICC profile color format.");
1472 }
1473 } else if (processColorFormat == splashModeMono8) {
1474 if (processcolorspace != cmsSigGrayData) {
1475 error(errConfig, -1, "Mismatch between processColorFormat=MONO8 and ICC profile color format.");
1476 }
1477 } else if (processColorFormat == splashModeRGB8) {
1478 if (processcolorspace != cmsSigRgbData) {
1479 error(errConfig, -1, "Mismatch between processColorFormat=RGB8 and ICC profile color format.");
1480 }
1481 }
1482 }
1483 #endif
1484
1485 // initialize embedded font resource comment list
1486 embFontList = new GooString();
1487
1488 if (!manualCtrl) {
1489 Page *page;
1490 // this check is needed in case the document has zero pages
1491 if ((page = doc->getPage(pageList[0]))) {
1492 writeHeader(pageList.size(), page->getMediaBox(), page->getCropBox(), page->getRotate(), psTitle);
1493 } else {
1494 error(errSyntaxError, -1, "Invalid page {0:d}", pageList[0]);
1495 box = new PDFRectangle(0, 0, 1, 1);
1496 writeHeader(pageList.size(), box, box, 0, psTitle);
1497 delete box;
1498 }
1499 if (mode != psModeForm) {
1500 writePS("%%BeginProlog\n");
1501 }
1502 writeXpdfProcset();
1503 if (mode != psModeForm) {
1504 writePS("%%EndProlog\n");
1505 writePS("%%BeginSetup\n");
1506 }
1507 writeDocSetup(catalog, pageList, duplex);
1508 if (mode != psModeForm) {
1509 writePS("%%EndSetup\n");
1510 }
1511 }
1512 }
1513
~PSOutputDev()1514 PSOutputDev::~PSOutputDev()
1515 {
1516 PSOutCustomColor *cc;
1517 int i;
1518
1519 if (ok) {
1520 if (!postInitDone) {
1521 postInit();
1522 }
1523 if (!manualCtrl) {
1524 writePS("%%Trailer\n");
1525 writeTrailer();
1526 if (mode != psModeForm) {
1527 writePS("%%EOF\n");
1528 }
1529 }
1530 if (fileType == psFile) {
1531 fclose((FILE *)outputStream);
1532 }
1533 #ifdef HAVE_POPEN
1534 else if (fileType == psPipe) {
1535 pclose((FILE *)outputStream);
1536 # ifndef _WIN32
1537 signal(SIGPIPE, (SignalFunc)SIG_DFL);
1538 # endif
1539 }
1540 #endif
1541 }
1542 if (paperSizes) {
1543 for (auto entry : *paperSizes) {
1544 delete entry;
1545 }
1546 delete paperSizes;
1547 }
1548 if (embFontList) {
1549 delete embFontList;
1550 }
1551 if (fontIDs) {
1552 gfree(fontIDs);
1553 }
1554 if (t1FontNames) {
1555 for (i = 0; i < t1FontNameLen; ++i) {
1556 delete t1FontNames[i].psName;
1557 }
1558 gfree(t1FontNames);
1559 }
1560 if (font8Info) {
1561 for (i = 0; i < font8InfoLen; ++i) {
1562 gfree(font8Info[i].codeToGID);
1563 }
1564 gfree(font8Info);
1565 }
1566 if (font16Enc) {
1567 for (i = 0; i < font16EncLen; ++i) {
1568 if (font16Enc[i].enc) {
1569 delete font16Enc[i].enc;
1570 }
1571 }
1572 gfree(font16Enc);
1573 }
1574 gfree(imgIDs);
1575 gfree(formIDs);
1576 while (customColors) {
1577 cc = customColors;
1578 customColors = cc->next;
1579 delete cc;
1580 }
1581 gfree(psTitle);
1582 delete t3String;
1583 }
1584
writeHeader(int nPages,const PDFRectangle * mediaBox,const PDFRectangle * cropBox,int pageRotate,const char * title)1585 void PSOutputDev::writeHeader(int nPages, const PDFRectangle *mediaBox, const PDFRectangle *cropBox, int pageRotate, const char *title)
1586 {
1587 PSOutPaperSize *size;
1588 double x1, y1, x2, y2;
1589
1590 switch (mode) {
1591 case psModePS:
1592 writePS("%!PS-Adobe-3.0\n");
1593 break;
1594 case psModeEPS:
1595 writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
1596 break;
1597 case psModeForm:
1598 writePS("%!PS-Adobe-3.0 Resource-Form\n");
1599 break;
1600 }
1601 writePSFmt("%Produced by poppler pdftops version: {0:s} (http://poppler.freedesktop.org)\n", PACKAGE_VERSION);
1602 Object info = xref->getDocInfo();
1603 if (info.isDict()) {
1604 Object obj1 = info.dictLookup("Creator");
1605 if (obj1.isString()) {
1606 writePS("%%Creator: ");
1607 writePSTextLine(obj1.getString());
1608 }
1609 }
1610 if (title) {
1611 char *sanitizedTitle = strdup(title);
1612 for (size_t i = 0; i < strlen(sanitizedTitle); ++i) {
1613 if (sanitizedTitle[i] == '\n' || sanitizedTitle[i] == '\r') {
1614 sanitizedTitle[i] = ' ';
1615 }
1616 }
1617 writePSFmt("%%Title: {0:s}\n", sanitizedTitle);
1618 free(sanitizedTitle);
1619 }
1620 writePSFmt("%%LanguageLevel: {0:d}\n", (level == psLevel1 || level == psLevel1Sep) ? 1 : (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
1621 if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
1622 writePS("%%DocumentProcessColors: (atend)\n");
1623 writePS("%%DocumentCustomColors: (atend)\n");
1624 }
1625 writePS("%%DocumentSuppliedResources: (atend)\n");
1626 if ((level == psLevel1 || level == psLevel1Sep) && useBinary) {
1627 writePS("%%DocumentData: Binary\n");
1628 }
1629
1630 switch (mode) {
1631 case psModePS:
1632 for (std::size_t i = 0; i < paperSizes->size(); ++i) {
1633 size = (*paperSizes)[i];
1634 writePSFmt("%%{0:s} {1:t} {2:d} {3:d} 0 () ()\n", i == 0 ? "DocumentMedia:" : "+", size->name, size->w, size->h);
1635 }
1636 writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight);
1637 writePSFmt("%%Pages: {0:d}\n", nPages);
1638 writePS("%%EndComments\n");
1639 if (!paperMatch) {
1640 size = (*paperSizes)[0];
1641 writePS("%%BeginDefaults\n");
1642 writePSFmt("%%PageMedia: {0:t}\n", size->name);
1643 writePS("%%EndDefaults\n");
1644 }
1645 break;
1646 case psModeEPS:
1647 epsX1 = cropBox->x1;
1648 epsY1 = cropBox->y1;
1649 epsX2 = cropBox->x2;
1650 epsY2 = cropBox->y2;
1651 if (pageRotate == 0 || pageRotate == 180) {
1652 x1 = epsX1;
1653 y1 = epsY1;
1654 x2 = epsX2;
1655 y2 = epsY2;
1656 } else { // pageRotate == 90 || pageRotate == 270
1657 x1 = 0;
1658 y1 = 0;
1659 x2 = epsY2 - epsY1;
1660 y2 = epsX2 - epsX1;
1661 }
1662 writePSFmt("%%BoundingBox: {0:d} {1:d} {2:d} {3:d}\n", (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2));
1663 writePSFmt("%%HiResBoundingBox: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", x1, y1, x2, y2);
1664 writePS("%%DocumentSuppliedResources: (atend)\n");
1665 writePS("%%EndComments\n");
1666 break;
1667 case psModeForm:
1668 writePS("%%EndComments\n");
1669 writePS("32 dict dup begin\n");
1670 writePSFmt("/BBox [{0:d} {1:d} {2:d} {3:d}] def\n", (int)floor(mediaBox->x1), (int)floor(mediaBox->y1), (int)ceil(mediaBox->x2), (int)ceil(mediaBox->y2));
1671 writePS("/FormType 1 def\n");
1672 writePS("/Matrix [1 0 0 1 0 0] def\n");
1673 break;
1674 }
1675 }
1676
writeXpdfProcset()1677 void PSOutputDev::writeXpdfProcset()
1678 {
1679 bool lev1, lev2, lev3, sep, nonSep;
1680 const char **p;
1681 const char *q;
1682
1683 writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", "3.00");
1684 writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright);
1685 lev1 = lev2 = lev3 = sep = nonSep = true;
1686 for (p = prolog; *p; ++p) {
1687 if ((*p)[0] == '~') {
1688 lev1 = lev2 = lev3 = sep = nonSep = false;
1689 for (q = *p + 1; *q; ++q) {
1690 switch (*q) {
1691 case '1':
1692 lev1 = true;
1693 break;
1694 case '2':
1695 lev2 = true;
1696 break;
1697 case '3':
1698 lev3 = true;
1699 break;
1700 case 's':
1701 sep = true;
1702 break;
1703 case 'n':
1704 nonSep = true;
1705 break;
1706 }
1707 }
1708 } else if ((level == psLevel1 && lev1 && nonSep) || (level == psLevel1Sep && lev1 && sep) || (level == psLevel1Sep && lev2 && sep && getPassLevel1CustomColor()) || (level == psLevel2 && lev2 && nonSep)
1709 || (level == psLevel2Sep && lev2 && sep) || (level == psLevel3 && lev3 && nonSep) || (level == psLevel3Sep && lev3 && sep)) {
1710 writePSFmt("{0:s}\n", *p);
1711 }
1712 }
1713 writePS("%%EndResource\n");
1714
1715 if (level >= psLevel3) {
1716 for (p = cmapProlog; *p; ++p) {
1717 writePSFmt("{0:s}\n", *p);
1718 }
1719 }
1720 }
1721
writeDocSetup(Catalog * catalog,const std::vector<int> & pageList,bool duplexA)1722 void PSOutputDev::writeDocSetup(Catalog *catalog, const std::vector<int> &pageList, bool duplexA)
1723 {
1724 Page *page;
1725 Dict *resDict;
1726 Annots *annots;
1727 Object *acroForm;
1728 GooString *s;
1729
1730 if (mode == psModeForm) {
1731 // swap the form and xpdf dicts
1732 writePS("xpdf end begin dup begin\n");
1733 } else {
1734 writePS("xpdf begin\n");
1735 }
1736 for (const int pg : pageList) {
1737 page = doc->getPage(pg);
1738 if (!page) {
1739 error(errSyntaxError, -1, "Failed writing resources for page {0:d}", pg);
1740 continue;
1741 }
1742 if ((resDict = page->getResourceDict())) {
1743 setupResources(resDict);
1744 }
1745 annots = page->getAnnots();
1746 for (int i = 0; i < annots->getNumAnnots(); ++i) {
1747 Object obj1 = annots->getAnnot(i)->getAppearanceResDict();
1748 if (obj1.isDict()) {
1749 setupResources(obj1.getDict());
1750 }
1751 }
1752 }
1753 if ((acroForm = catalog->getAcroForm()) && acroForm->isDict()) {
1754 Object obj1 = acroForm->dictLookup("DR");
1755 if (obj1.isDict()) {
1756 setupResources(obj1.getDict());
1757 }
1758 obj1 = acroForm->dictLookup("Fields");
1759 if (obj1.isArray()) {
1760 for (int i = 0; i < obj1.arrayGetLength(); ++i) {
1761 Object obj2 = obj1.arrayGet(i);
1762 if (obj2.isDict()) {
1763 Object obj3 = obj2.dictLookup("DR");
1764 if (obj3.isDict()) {
1765 setupResources(obj3.getDict());
1766 }
1767 }
1768 }
1769 }
1770 }
1771 if (mode != psModeForm) {
1772 if (mode != psModeEPS && !manualCtrl) {
1773 writePSFmt("{0:s} pdfSetup\n", duplexA ? "true" : "false");
1774 if (!paperMatch) {
1775 writePSFmt("{0:d} {1:d} pdfSetupPaper\n", paperWidth, paperHeight);
1776 }
1777 }
1778 #ifdef OPI_SUPPORT
1779 if (generateOPI) {
1780 writePS("/opiMatrix matrix currentmatrix def\n");
1781 }
1782 #endif
1783 }
1784 if (customCodeCbk) {
1785 if ((s = (*customCodeCbk)(this, psOutCustomDocSetup, 0, customCodeCbkData))) {
1786 writePS(s->c_str());
1787 delete s;
1788 }
1789 }
1790 }
1791
writePageTrailer()1792 void PSOutputDev::writePageTrailer()
1793 {
1794 if (mode != psModeForm) {
1795 writePS("pdfEndPage\n");
1796 }
1797 }
1798
writeTrailer()1799 void PSOutputDev::writeTrailer()
1800 {
1801 PSOutCustomColor *cc;
1802
1803 if (mode == psModeForm) {
1804 writePS("/Foo exch /Form defineresource pop\n");
1805 } else {
1806 writePS("end\n");
1807 writePS("%%DocumentSuppliedResources:\n");
1808 writePS(embFontList->c_str());
1809 if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
1810 writePS("%%DocumentProcessColors:");
1811 if (processColors & psProcessCyan) {
1812 writePS(" Cyan");
1813 }
1814 if (processColors & psProcessMagenta) {
1815 writePS(" Magenta");
1816 }
1817 if (processColors & psProcessYellow) {
1818 writePS(" Yellow");
1819 }
1820 if (processColors & psProcessBlack) {
1821 writePS(" Black");
1822 }
1823 writePS("\n");
1824 writePS("%%DocumentCustomColors:");
1825 for (cc = customColors; cc; cc = cc->next) {
1826 writePS(" ");
1827 writePSString(cc->name->toStr());
1828 }
1829 writePS("\n");
1830 writePS("%%CMYKCustomColor:\n");
1831 for (cc = customColors; cc; cc = cc->next) {
1832 writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", cc->c, cc->m, cc->y, cc->k);
1833 writePSString(cc->name->toStr());
1834 writePS("\n");
1835 }
1836 }
1837 }
1838 }
1839
setupResources(Dict * resDict)1840 void PSOutputDev::setupResources(Dict *resDict)
1841 {
1842 bool skip;
1843
1844 setupFonts(resDict);
1845 setupImages(resDict);
1846 setupForms(resDict);
1847
1848 //----- recursively scan XObjects
1849 Object xObjDict = resDict->lookup("XObject");
1850 if (xObjDict.isDict()) {
1851 for (int i = 0; i < xObjDict.dictGetLength(); ++i) {
1852
1853 // avoid infinite recursion on XObjects
1854 skip = false;
1855 const Object &xObjRef = xObjDict.dictGetValNF(i);
1856 if (xObjRef.isRef()) {
1857 Ref ref0 = xObjRef.getRef();
1858 if (resourceIDs.find(ref0.num) != resourceIDs.end()) {
1859 skip = true;
1860 } else {
1861 resourceIDs.insert(ref0.num);
1862 }
1863 }
1864 if (!skip) {
1865
1866 // process the XObject's resource dictionary
1867 Object xObj = xObjDict.dictGetVal(i);
1868 if (xObj.isStream()) {
1869 Ref resObjRef;
1870 Object resObj = xObj.streamGetDict()->lookup("Resources", &resObjRef);
1871 if (resObj.isDict()) {
1872 if (resObjRef != Ref::INVALID()) {
1873 const int numObj = resObjRef.num;
1874 if (resourceIDs.find(numObj) != resourceIDs.end()) {
1875 error(errSyntaxError, -1, "loop in Resources (numObj: {0:d})", numObj);
1876 continue;
1877 }
1878 resourceIDs.insert(numObj);
1879 }
1880 setupResources(resObj.getDict());
1881 }
1882 }
1883 }
1884 }
1885 }
1886
1887 //----- recursively scan Patterns
1888 Object patDict = resDict->lookup("Pattern");
1889 if (patDict.isDict()) {
1890 inType3Char = true;
1891 for (int i = 0; i < patDict.dictGetLength(); ++i) {
1892
1893 // avoid infinite recursion on Patterns
1894 skip = false;
1895 const Object &patRef = patDict.dictGetValNF(i);
1896 if (patRef.isRef()) {
1897 Ref ref0 = patRef.getRef();
1898 if (resourceIDs.find(ref0.num) != resourceIDs.end()) {
1899 skip = true;
1900 } else {
1901 resourceIDs.insert(ref0.num);
1902 }
1903 }
1904 if (!skip) {
1905
1906 // process the Pattern's resource dictionary
1907 Object pat = patDict.dictGetVal(i);
1908 if (pat.isStream()) {
1909 Object resObj = pat.streamGetDict()->lookup("Resources");
1910 if (resObj.isDict()) {
1911 setupResources(resObj.getDict());
1912 }
1913 }
1914 }
1915 }
1916 inType3Char = false;
1917 }
1918 }
1919
setupFonts(Dict * resDict)1920 void PSOutputDev::setupFonts(Dict *resDict)
1921 {
1922 Ref r;
1923 GfxFontDict *gfxFontDict;
1924 GfxFont *font;
1925 int i;
1926
1927 gfxFontDict = nullptr;
1928 const Object &obj1 = resDict->lookupNF("Font");
1929 if (obj1.isRef()) {
1930 Object obj2 = obj1.fetch(xref);
1931 if (obj2.isDict()) {
1932 r = obj1.getRef();
1933 gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict());
1934 }
1935 } else if (obj1.isDict()) {
1936 gfxFontDict = new GfxFontDict(xref, nullptr, obj1.getDict());
1937 }
1938 if (gfxFontDict) {
1939 for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
1940 if ((font = gfxFontDict->getFont(i))) {
1941 setupFont(font, resDict);
1942 }
1943 }
1944 delete gfxFontDict;
1945 }
1946 }
1947
setupFont(GfxFont * font,Dict * parentResDict)1948 void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict)
1949 {
1950 GfxFontLoc *fontLoc;
1951 GooString *psName;
1952 char buf[16];
1953 bool subst;
1954 const char *charName;
1955 double xs, ys;
1956 int code;
1957 double w1, w2;
1958 int i, j;
1959
1960 // check if font is already set up
1961 for (i = 0; i < fontIDLen; ++i) {
1962 if (fontIDs[i] == *font->getID()) {
1963 return;
1964 }
1965 }
1966
1967 // add entry to fontIDs list
1968 if (fontIDLen >= fontIDSize) {
1969 fontIDSize += 64;
1970 fontIDs = (Ref *)greallocn(fontIDs, fontIDSize, sizeof(Ref));
1971 }
1972 fontIDs[fontIDLen++] = *font->getID();
1973
1974 psName = nullptr;
1975 xs = ys = 1;
1976 subst = false;
1977
1978 if (font->getType() == fontType3) {
1979 psName = GooString::format("T3_{0:d}_{1:d}", font->getID()->num, font->getID()->gen);
1980 setupType3Font(font, psName, parentResDict);
1981 } else {
1982 fontLoc = font->locateFont(xref, this);
1983 if (fontLoc != nullptr) {
1984 switch (fontLoc->locType) {
1985 case gfxFontLocEmbedded:
1986 switch (fontLoc->fontType) {
1987 case fontType1:
1988 // this assumes that the PS font name matches the PDF font name
1989 psName = font->getEmbeddedFontName() ? font->getEmbeddedFontName()->copy() : new GooString();
1990 setupEmbeddedType1Font(&fontLoc->embFontID, psName);
1991 break;
1992 case fontType1C:
1993 psName = makePSFontName(font, &fontLoc->embFontID);
1994 setupEmbeddedType1CFont(font, &fontLoc->embFontID, psName);
1995 break;
1996 case fontType1COT:
1997 psName = makePSFontName(font, &fontLoc->embFontID);
1998 setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID, psName);
1999 break;
2000 case fontTrueType:
2001 case fontTrueTypeOT:
2002 psName = makePSFontName(font, font->getID());
2003 setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID, psName);
2004 break;
2005 case fontCIDType0C:
2006 psName = makePSFontName(font, &fontLoc->embFontID);
2007 setupEmbeddedCIDType0Font(font, &fontLoc->embFontID, psName);
2008 break;
2009 case fontCIDType2:
2010 case fontCIDType2OT:
2011 psName = makePSFontName(font, font->getID());
2012 //~ should check to see if font actually uses vertical mode
2013 setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID, psName, true);
2014 break;
2015 case fontCIDType0COT:
2016 psName = makePSFontName(font, &fontLoc->embFontID);
2017 setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID, psName);
2018 break;
2019 default:
2020 break;
2021 }
2022 break;
2023 case gfxFontLocExternal:
2024 //~ add cases for external 16-bit fonts
2025 switch (fontLoc->fontType) {
2026 case fontType1:
2027 if (font->getEmbeddedFontName()) {
2028 // this assumes that the PS font name matches the PDF font name
2029 psName = font->getEmbeddedFontName()->copy();
2030 } else {
2031 //~ this won't work -- the PS font name won't match
2032 psName = makePSFontName(font, font->getID());
2033 }
2034 setupExternalType1Font(fontLoc->path, psName);
2035 break;
2036 case fontTrueType:
2037 case fontTrueTypeOT:
2038 psName = makePSFontName(font, font->getID());
2039 setupExternalTrueTypeFont(font, fontLoc->path, psName);
2040 break;
2041 case fontCIDType2:
2042 case fontCIDType2OT:
2043 psName = makePSFontName(font, font->getID());
2044 //~ should check to see if font actually uses vertical mode
2045 setupExternalCIDTrueTypeFont(font, fontLoc->path, psName, true);
2046 break;
2047 default:
2048 break;
2049 }
2050 break;
2051 case gfxFontLocResident:
2052 psName = fontLoc->path->copy();
2053 break;
2054 }
2055 }
2056
2057 if (!psName) {
2058 if (font->isCIDFont()) {
2059 error(errSyntaxError, -1, "Couldn't find a font to substitute for '{0:s}' ('{1:s}' character collection)", font->getName() ? font->getName()->c_str() : "(unnamed)",
2060 ((GfxCIDFont *)font)->getCollection() ? ((GfxCIDFont *)font)->getCollection()->c_str() : "(unknown)");
2061 if (font16EncLen >= font16EncSize) {
2062 font16EncSize += 16;
2063 font16Enc = (PSFont16Enc *)greallocn(font16Enc, font16EncSize, sizeof(PSFont16Enc));
2064 }
2065 font16Enc[font16EncLen].fontID = *font->getID();
2066 font16Enc[font16EncLen].enc = nullptr;
2067 ++font16EncLen;
2068 } else {
2069 error(errSyntaxError, -1, "Couldn't find a font to substitute for '{0:s}'", font->getName() ? font->getName()->c_str() : "(unnamed)");
2070 }
2071 delete fontLoc;
2072 return;
2073 }
2074
2075 // scale substituted 8-bit fonts
2076 if (fontLoc->locType == gfxFontLocResident && fontLoc->substIdx >= 0) {
2077 subst = true;
2078 for (code = 0; code < 256; ++code) {
2079 if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) && charName[0] == 'm' && charName[1] == '\0') {
2080 break;
2081 }
2082 }
2083 if (code < 256) {
2084 w1 = ((Gfx8BitFont *)font)->getWidth(code);
2085 } else {
2086 w1 = 0;
2087 }
2088 w2 = psBase14SubstFonts[fontLoc->substIdx].mWidth;
2089 xs = w1 / w2;
2090 if (xs < 0.1) {
2091 xs = 1;
2092 }
2093 }
2094
2095 delete fontLoc;
2096 }
2097
2098 // generate PostScript code to set up the font
2099 if (font->isCIDFont()) {
2100 if (level == psLevel3 || level == psLevel3Sep) {
2101 writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n", font->getID()->num, font->getID()->gen, psName, font->getWMode());
2102 } else {
2103 writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n", font->getID()->num, font->getID()->gen, psName, font->getWMode());
2104 }
2105 } else {
2106 writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n", font->getID()->num, font->getID()->gen, psName, xs, ys);
2107 for (i = 0; i < 256; i += 8) {
2108 writePS((char *)((i == 0) ? "[ " : " "));
2109 for (j = 0; j < 8; ++j) {
2110 if (font->getType() == fontTrueType && !subst && !((Gfx8BitFont *)font)->getHasEncoding()) {
2111 sprintf(buf, "c%02x", i + j);
2112 charName = buf;
2113 } else {
2114 charName = ((Gfx8BitFont *)font)->getCharName(i + j);
2115 }
2116 writePS("/");
2117 writePSName(charName ? charName : (char *)".notdef");
2118 // the empty name is legal in PDF and PostScript, but PostScript
2119 // uses a double-slash (//...) for "immediately evaluated names",
2120 // so we need to add a space character here
2121 if (charName && !charName[0]) {
2122 writePS(" ");
2123 }
2124 }
2125 writePS((i == 256 - 8) ? (char *)"]\n" : (char *)"\n");
2126 }
2127 writePS("pdfMakeFont\n");
2128 }
2129
2130 delete psName;
2131 }
2132
setupEmbeddedType1Font(Ref * id,GooString * psName)2133 void PSOutputDev::setupEmbeddedType1Font(Ref *id, GooString *psName)
2134 {
2135 static const char hexChar[17] = "0123456789abcdef";
2136 Dict *dict;
2137 long length1, length2, length3, i;
2138 int c;
2139 int start[4];
2140 bool binMode;
2141 bool writePadding = true;
2142
2143 // check if font is already embedded
2144 if (!fontNames.emplace(psName->toStr()).second) {
2145 return;
2146 }
2147
2148 // get the font stream and info
2149 Object obj1, obj2, obj3;
2150 Object refObj(*id);
2151 Object strObj = refObj.fetch(xref);
2152 if (!strObj.isStream()) {
2153 error(errSyntaxError, -1, "Embedded font file object is not a stream");
2154 goto err1;
2155 }
2156 if (!(dict = strObj.streamGetDict())) {
2157 error(errSyntaxError, -1, "Embedded font stream is missing its dictionary");
2158 goto err1;
2159 }
2160 obj1 = dict->lookup("Length1");
2161 obj2 = dict->lookup("Length2");
2162 obj3 = dict->lookup("Length3");
2163 if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {
2164 error(errSyntaxError, -1, "Missing length fields in embedded font stream dictionary");
2165 goto err1;
2166 }
2167 length1 = obj1.getInt();
2168 length2 = obj2.getInt();
2169 length3 = obj3.getInt();
2170
2171 // beginning comment
2172 writePSFmt("%%BeginResource: font {0:t}\n", psName);
2173 embFontList->append("%%+ font ");
2174 embFontList->append(psName->c_str());
2175 embFontList->append("\n");
2176
2177 strObj.streamReset();
2178 if (strObj.streamGetChar() == 0x80 && strObj.streamGetChar() == 1) {
2179 // PFB format
2180 length1 = strObj.streamGetChar() | (strObj.streamGetChar() << 8) | (strObj.streamGetChar() << 16) | (strObj.streamGetChar() << 24);
2181 } else {
2182 strObj.streamReset();
2183 }
2184 // copy ASCII portion of font
2185 for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
2186 writePSChar(c);
2187 }
2188
2189 // figure out if encrypted portion is binary or ASCII
2190 binMode = false;
2191 for (i = 0; i < 4; ++i) {
2192 start[i] = strObj.streamGetChar();
2193 if (start[i] == EOF) {
2194 error(errSyntaxError, -1, "Unexpected end of file in embedded font stream");
2195 goto err1;
2196 }
2197 if (!((start[i] >= '0' && start[i] <= '9') || (start[i] >= 'A' && start[i] <= 'F') || (start[i] >= 'a' && start[i] <= 'f')))
2198 binMode = true;
2199 }
2200
2201 if (length2 == 0) {
2202 // length2 == 0 is an error
2203 // trying to solve it by just piping all
2204 // the stream data
2205 error(errSyntaxWarning, -1, "Font has length2 as 0, trying to overcome the problem reading the stream until the end");
2206 length2 = INT_MAX;
2207 writePadding = false;
2208 }
2209
2210 // convert binary data to ASCII
2211 if (binMode) {
2212 if (start[0] == 0x80 && start[1] == 2) {
2213 length2 = start[2] | (start[3] << 8) | (strObj.streamGetChar() << 16) | (strObj.streamGetChar() << 24);
2214 i = 0;
2215 } else {
2216 for (i = 0; i < 4; ++i) {
2217 writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
2218 writePSChar(hexChar[start[i] & 0x0f]);
2219 }
2220 }
2221 #if 0 // this causes trouble for various PostScript printers
2222 // if Length2 is incorrect (too small), font data gets chopped, so
2223 // we take a few extra characters from the trailer just in case
2224 length2 += length3 >= 8 ? 8 : length3;
2225 #endif
2226 while (i < length2) {
2227 if ((c = strObj.streamGetChar()) == EOF) {
2228 break;
2229 }
2230 writePSChar(hexChar[(c >> 4) & 0x0f]);
2231 writePSChar(hexChar[c & 0x0f]);
2232 if (++i % 32 == 0) {
2233 writePSChar('\n');
2234 }
2235 }
2236 if (i % 32 > 0) {
2237 writePSChar('\n');
2238 }
2239
2240 // already in ASCII format -- just copy it
2241 } else {
2242 for (i = 0; i < 4; ++i) {
2243 writePSChar(start[i]);
2244 }
2245 for (i = 4; i < length2; ++i) {
2246 if ((c = strObj.streamGetChar()) == EOF) {
2247 break;
2248 }
2249 writePSChar(c);
2250 }
2251 }
2252
2253 if (writePadding) {
2254 if (length3 > 0) {
2255 // write fixed-content portion
2256 c = strObj.streamGetChar();
2257 if (c == 0x80) {
2258 c = strObj.streamGetChar();
2259 if (c == 1) {
2260 length3 = strObj.streamGetChar() | (strObj.streamGetChar() << 8) | (strObj.streamGetChar() << 16) | (strObj.streamGetChar() << 24);
2261
2262 i = 0;
2263 while (i < length3) {
2264 if ((c = strObj.streamGetChar()) == EOF) {
2265 break;
2266 }
2267 writePSChar(c);
2268 ++i;
2269 }
2270 }
2271 } else {
2272 if (c != EOF) {
2273 writePSChar(c);
2274
2275 while ((c = strObj.streamGetChar()) != EOF) {
2276 writePSChar(c);
2277 }
2278 }
2279 }
2280 } else {
2281 // write padding and "cleartomark"
2282 for (i = 0; i < 8; ++i) {
2283 writePS("00000000000000000000000000000000"
2284 "00000000000000000000000000000000\n");
2285 }
2286 writePS("cleartomark\n");
2287 }
2288 }
2289
2290 // ending comment
2291 writePS("%%EndResource\n");
2292
2293 err1:
2294 if (strObj.isStream())
2295 strObj.streamClose();
2296 }
2297
setupExternalType1Font(GooString * fileName,GooString * psName)2298 void PSOutputDev::setupExternalType1Font(GooString *fileName, GooString *psName)
2299 {
2300 static const char hexChar[17] = "0123456789abcdef";
2301 FILE *fontFile;
2302 int c;
2303
2304 if (!fontNames.emplace(psName->toStr()).second) {
2305 return;
2306 }
2307
2308 // beginning comment
2309 writePSFmt("%%BeginResource: font {0:t}\n", psName);
2310 embFontList->append("%%+ font ");
2311 embFontList->append(psName->c_str());
2312 embFontList->append("\n");
2313
2314 // copy the font file
2315 if (!(fontFile = openFile(fileName->c_str(), "rb"))) {
2316 error(errIO, -1, "Couldn't open external font file");
2317 return;
2318 }
2319
2320 c = fgetc(fontFile);
2321 if (c == 0x80) {
2322 // PFB file
2323 ungetc(c, fontFile);
2324 while (!feof(fontFile)) {
2325 fgetc(fontFile); // skip start of segment byte (0x80)
2326 int segType = fgetc(fontFile);
2327 long segLen = fgetc(fontFile) | (fgetc(fontFile) << 8) | (fgetc(fontFile) << 16) | (fgetc(fontFile) << 24);
2328 if (feof(fontFile))
2329 break;
2330
2331 if (segType == 1) {
2332 // ASCII segment
2333 for (long i = 0; i < segLen; i++) {
2334 c = fgetc(fontFile);
2335 if (c == EOF)
2336 break;
2337 writePSChar(c);
2338 }
2339 } else if (segType == 2) {
2340 // binary segment
2341 for (long i = 0; i < segLen; i++) {
2342 c = fgetc(fontFile);
2343 if (c == EOF)
2344 break;
2345 writePSChar(hexChar[(c >> 4) & 0x0f]);
2346 writePSChar(hexChar[c & 0x0f]);
2347 if (i % 36 == 35)
2348 writePSChar('\n');
2349 }
2350 } else {
2351 // end of file
2352 break;
2353 }
2354 }
2355 } else if (c != EOF) {
2356 writePSChar(c);
2357 while ((c = fgetc(fontFile)) != EOF)
2358 writePSChar(c);
2359 }
2360 fclose(fontFile);
2361
2362 // ending comment
2363 writePS("%%EndResource\n");
2364 }
2365
setupEmbeddedType1CFont(GfxFont * font,Ref * id,GooString * psName)2366 void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id, GooString *psName)
2367 {
2368 char *fontBuf;
2369 int fontLen;
2370 FoFiType1C *ffT1C;
2371 int i;
2372
2373 // check if font is already embedded
2374 for (i = 0; i < t1FontNameLen; ++i) {
2375 if (t1FontNames[i].fontFileID == *id) {
2376 psName->clear();
2377 psName->insert(0, t1FontNames[i].psName);
2378 return;
2379 }
2380 }
2381 if (t1FontNameLen == t1FontNameSize) {
2382 t1FontNameSize *= 2;
2383 t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, sizeof(PST1FontName));
2384 }
2385 t1FontNames[t1FontNameLen].fontFileID = *id;
2386 t1FontNames[t1FontNameLen].psName = psName->copy();
2387 ++t1FontNameLen;
2388
2389 // beginning comment
2390 writePSFmt("%%BeginResource: font {0:t}\n", psName);
2391 embFontList->append("%%+ font ");
2392 embFontList->append(psName->c_str());
2393 embFontList->append("\n");
2394
2395 // convert it to a Type 1 font
2396 if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2397 if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
2398 ffT1C->convertToType1(psName->c_str(), nullptr, true, outputFunc, outputStream);
2399 delete ffT1C;
2400 }
2401 gfree(fontBuf);
2402 }
2403
2404 // ending comment
2405 writePS("%%EndResource\n");
2406 }
2407
setupEmbeddedOpenTypeT1CFont(GfxFont * font,Ref * id,GooString * psName)2408 void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GooString *psName)
2409 {
2410 char *fontBuf;
2411 int fontLen;
2412 FoFiTrueType *ffTT;
2413 int i;
2414
2415 // check if font is already embedded
2416 for (i = 0; i < t1FontNameLen; ++i) {
2417 if (t1FontNames[i].fontFileID == *id) {
2418 psName->clear();
2419 psName->insert(0, t1FontNames[i].psName);
2420 return;
2421 }
2422 }
2423 if (t1FontNameLen == t1FontNameSize) {
2424 t1FontNameSize *= 2;
2425 t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, sizeof(PST1FontName));
2426 }
2427 t1FontNames[t1FontNameLen].fontFileID = *id;
2428 t1FontNames[t1FontNameLen].psName = psName->copy();
2429 ++t1FontNameLen;
2430
2431 // beginning comment
2432 writePSFmt("%%BeginResource: font {0:t}\n", psName);
2433 embFontList->append("%%+ font ");
2434 embFontList->append(psName->c_str());
2435 embFontList->append("\n");
2436
2437 // convert it to a Type 1 font
2438 if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2439 if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2440 if (ffTT->isOpenTypeCFF()) {
2441 ffTT->convertToType1(psName->c_str(), nullptr, true, outputFunc, outputStream);
2442 }
2443 delete ffTT;
2444 }
2445 gfree(fontBuf);
2446 }
2447
2448 // ending comment
2449 writePS("%%EndResource\n");
2450 }
2451
setupEmbeddedTrueTypeFont(GfxFont * font,Ref * id,GooString * psName)2452 void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GooString *psName)
2453 {
2454 char *fontBuf;
2455 int fontLen;
2456 FoFiTrueType *ffTT;
2457 int *codeToGID;
2458
2459 // beginning comment
2460 writePSFmt("%%BeginResource: font {0:t}\n", psName);
2461 embFontList->append("%%+ font ");
2462 embFontList->append(psName->c_str());
2463 embFontList->append("\n");
2464
2465 // convert it to a Type 42 font
2466 if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2467 if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2468 codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
2469 ffTT->convertToType42(psName->c_str(), ((Gfx8BitFont *)font)->getHasEncoding() ? ((Gfx8BitFont *)font)->getEncoding() : nullptr, codeToGID, outputFunc, outputStream);
2470 if (codeToGID) {
2471 if (font8InfoLen >= font8InfoSize) {
2472 font8InfoSize += 16;
2473 font8Info = (PSFont8Info *)greallocn(font8Info, font8InfoSize, sizeof(PSFont8Info));
2474 }
2475 font8Info[font8InfoLen].fontID = *font->getID();
2476 font8Info[font8InfoLen].codeToGID = codeToGID;
2477 ++font8InfoLen;
2478 }
2479 delete ffTT;
2480 }
2481 gfree(fontBuf);
2482 }
2483
2484 // ending comment
2485 writePS("%%EndResource\n");
2486 }
2487
setupExternalTrueTypeFont(GfxFont * font,GooString * fileName,GooString * psName)2488 void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GooString *fileName, GooString *psName)
2489 {
2490 FoFiTrueType *ffTT;
2491 int *codeToGID;
2492
2493 // beginning comment
2494 writePSFmt("%%BeginResource: font {0:t}\n", psName);
2495 embFontList->append("%%+ font ");
2496 embFontList->append(psName->c_str());
2497 embFontList->append("\n");
2498
2499 // convert it to a Type 42 font
2500 if ((ffTT = FoFiTrueType::load(fileName->c_str()))) {
2501 codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
2502 ffTT->convertToType42(psName->c_str(), ((Gfx8BitFont *)font)->getHasEncoding() ? ((Gfx8BitFont *)font)->getEncoding() : nullptr, codeToGID, outputFunc, outputStream);
2503 if (codeToGID) {
2504 if (font8InfoLen >= font8InfoSize) {
2505 font8InfoSize += 16;
2506 font8Info = (PSFont8Info *)greallocn(font8Info, font8InfoSize, sizeof(PSFont8Info));
2507 }
2508 font8Info[font8InfoLen].fontID = *font->getID();
2509 font8Info[font8InfoLen].codeToGID = codeToGID;
2510 ++font8InfoLen;
2511 }
2512 delete ffTT;
2513 }
2514
2515 // ending comment
2516 writePS("%%EndResource\n");
2517 }
2518
updateFontMaxValidGlyph(GfxFont * font,int maxValidGlyph)2519 void PSOutputDev::updateFontMaxValidGlyph(GfxFont *font, int maxValidGlyph)
2520 {
2521 if (maxValidGlyph >= 0 && font->getName()) {
2522 auto &fontMaxValidGlyph = perFontMaxValidGlyph[font->getName()->toStr()];
2523 if (fontMaxValidGlyph < maxValidGlyph) {
2524 fontMaxValidGlyph = maxValidGlyph;
2525 }
2526 }
2527 }
2528
setupExternalCIDTrueTypeFont(GfxFont * font,GooString * fileName,GooString * psName,bool needVerticalMetrics)2529 void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font, GooString *fileName, GooString *psName, bool needVerticalMetrics)
2530 {
2531 FoFiTrueType *ffTT;
2532 int *codeToGID;
2533 int codeToGIDLen;
2534
2535 // beginning comment
2536 writePSFmt("%%BeginResource: font {0:t}\n", psName);
2537 embFontList->append("%%+ font ");
2538 embFontList->append(psName->c_str());
2539 embFontList->append("\n");
2540
2541 // convert it to a Type 0 font
2542 //~ this should use fontNum to load the correct font
2543 if ((ffTT = FoFiTrueType::load(fileName->c_str()))) {
2544
2545 // check for embedding permission
2546 if (ffTT->getEmbeddingRights() >= 1) {
2547 codeToGID = nullptr;
2548 codeToGIDLen = 0;
2549 if (((GfxCIDFont *)font)->getCIDToGID()) {
2550 codeToGIDLen = ((GfxCIDFont *)font)->getCIDToGIDLen();
2551 if (codeToGIDLen) {
2552 codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
2553 memcpy(codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), codeToGIDLen * sizeof(int));
2554 }
2555 } else {
2556 codeToGID = ((GfxCIDFont *)font)->getCodeToGIDMap(ffTT, &codeToGIDLen);
2557 }
2558 if (ffTT->isOpenTypeCFF()) {
2559 ffTT->convertToCIDType0(psName->c_str(), codeToGID, codeToGIDLen, outputFunc, outputStream);
2560 } else if (level >= psLevel3) {
2561 // Level 3: use a CID font
2562 ffTT->convertToCIDType2(psName->c_str(), codeToGID, codeToGIDLen, needVerticalMetrics, outputFunc, outputStream);
2563 } else {
2564 // otherwise: use a non-CID composite font
2565 int maxValidGlyph = -1;
2566 ffTT->convertToType0(psName->c_str(), codeToGID, codeToGIDLen, needVerticalMetrics, &maxValidGlyph, outputFunc, outputStream);
2567 updateFontMaxValidGlyph(font, maxValidGlyph);
2568 }
2569 gfree(codeToGID);
2570 } else {
2571 error(errSyntaxError, -1, "TrueType font '{0:s}' does not allow embedding", font->getName() ? font->getName()->c_str() : "(unnamed)");
2572 }
2573 delete ffTT;
2574 }
2575
2576 // ending comment
2577 writePS("%%EndResource\n");
2578 }
2579
setupEmbeddedCIDType0Font(GfxFont * font,Ref * id,GooString * psName)2580 void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, GooString *psName)
2581 {
2582 char *fontBuf;
2583 int fontLen;
2584 FoFiType1C *ffT1C;
2585 int i;
2586
2587 // check if font is already embedded
2588 for (i = 0; i < t1FontNameLen; ++i) {
2589 if (t1FontNames[i].fontFileID == *id) {
2590 psName->clear();
2591 psName->insert(0, t1FontNames[i].psName);
2592 return;
2593 }
2594 }
2595 if (t1FontNameLen == t1FontNameSize) {
2596 t1FontNameSize *= 2;
2597 t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, sizeof(PST1FontName));
2598 }
2599 t1FontNames[t1FontNameLen].fontFileID = *id;
2600 t1FontNames[t1FontNameLen].psName = psName->copy();
2601 ++t1FontNameLen;
2602
2603 // beginning comment
2604 writePSFmt("%%BeginResource: font {0:t}\n", psName);
2605 embFontList->append("%%+ font ");
2606 embFontList->append(psName->c_str());
2607 embFontList->append("\n");
2608
2609 // convert it to a Type 0 font
2610 if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2611 if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
2612 if (level >= psLevel3) {
2613 // Level 3: use a CID font
2614 ffT1C->convertToCIDType0(psName->c_str(), nullptr, 0, outputFunc, outputStream);
2615 } else {
2616 // otherwise: use a non-CID composite font
2617 ffT1C->convertToType0(psName->c_str(), nullptr, 0, outputFunc, outputStream);
2618 }
2619 delete ffT1C;
2620 }
2621 gfree(fontBuf);
2622 }
2623
2624 // ending comment
2625 writePS("%%EndResource\n");
2626 }
2627
setupEmbeddedCIDTrueTypeFont(GfxFont * font,Ref * id,GooString * psName,bool needVerticalMetrics)2628 void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GooString *psName, bool needVerticalMetrics)
2629 {
2630 char *fontBuf;
2631 int fontLen;
2632 FoFiTrueType *ffTT;
2633
2634 // beginning comment
2635 writePSFmt("%%BeginResource: font {0:t}\n", psName);
2636 embFontList->append("%%+ font ");
2637 embFontList->append(psName->c_str());
2638 embFontList->append("\n");
2639
2640 // convert it to a Type 0 font
2641 if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2642 if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2643 if (level >= psLevel3) {
2644 // Level 3: use a CID font
2645 ffTT->convertToCIDType2(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), needVerticalMetrics, outputFunc, outputStream);
2646 } else {
2647 // otherwise: use a non-CID composite font
2648 int maxValidGlyph = -1;
2649 ffTT->convertToType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), needVerticalMetrics, &maxValidGlyph, outputFunc, outputStream);
2650 updateFontMaxValidGlyph(font, maxValidGlyph);
2651 }
2652 delete ffTT;
2653 }
2654 gfree(fontBuf);
2655 }
2656
2657 // ending comment
2658 writePS("%%EndResource\n");
2659 }
2660
setupEmbeddedOpenTypeCFFFont(GfxFont * font,Ref * id,GooString * psName)2661 void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GooString *psName)
2662 {
2663 char *fontBuf;
2664 int fontLen;
2665 FoFiTrueType *ffTT;
2666 int i;
2667
2668 // check if font is already embedded
2669 for (i = 0; i < t1FontNameLen; ++i) {
2670 if (t1FontNames[i].fontFileID == *id) {
2671 psName->clear();
2672 psName->insert(0, t1FontNames[i].psName);
2673 return;
2674 }
2675 }
2676 if (t1FontNameLen == t1FontNameSize) {
2677 t1FontNameSize *= 2;
2678 t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, sizeof(PST1FontName));
2679 }
2680 t1FontNames[t1FontNameLen].fontFileID = *id;
2681 t1FontNames[t1FontNameLen].psName = psName->copy();
2682 ++t1FontNameLen;
2683
2684 // beginning comment
2685 writePSFmt("%%BeginResource: font {0:t}\n", psName);
2686 embFontList->append("%%+ font ");
2687 embFontList->append(psName->c_str());
2688 embFontList->append("\n");
2689
2690 // convert it to a Type 0 font
2691 if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2692 if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2693 if (ffTT->isOpenTypeCFF()) {
2694 if (level >= psLevel3) {
2695 // Level 3: use a CID font
2696 ffTT->convertToCIDType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream);
2697 } else {
2698 // otherwise: use a non-CID composite font
2699 ffTT->convertToType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream);
2700 }
2701 }
2702 delete ffTT;
2703 }
2704 gfree(fontBuf);
2705 }
2706
2707 // ending comment
2708 writePS("%%EndResource\n");
2709 }
2710
setupType3Font(GfxFont * font,GooString * psName,Dict * parentResDict)2711 void PSOutputDev::setupType3Font(GfxFont *font, GooString *psName, Dict *parentResDict)
2712 {
2713 Dict *resDict;
2714 Dict *charProcs;
2715 Gfx *gfx;
2716 PDFRectangle box;
2717 const double *m;
2718 GooString *buf;
2719 int i;
2720
2721 // set up resources used by font
2722 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2723 inType3Char = true;
2724 setupResources(resDict);
2725 inType3Char = false;
2726 } else {
2727 resDict = parentResDict;
2728 }
2729
2730 // beginning comment
2731 writePSFmt("%%BeginResource: font {0:t}\n", psName);
2732 embFontList->append("%%+ font ");
2733 embFontList->append(psName->c_str());
2734 embFontList->append("\n");
2735
2736 // font dictionary
2737 writePS("8 dict begin\n");
2738 writePS("/FontType 3 def\n");
2739 m = font->getFontMatrix();
2740 writePSFmt("/FontMatrix [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", m[0], m[1], m[2], m[3], m[4], m[5]);
2741 m = font->getFontBBox();
2742 writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", m[0], m[1], m[2], m[3]);
2743 writePS("/Encoding 256 array def\n");
2744 writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
2745 writePS("/BuildGlyph {\n");
2746 writePS(" exch /CharProcs get exch\n");
2747 writePS(" 2 copy known not { pop /.notdef } if\n");
2748 writePS(" get exec\n");
2749 writePS("} bind def\n");
2750 writePS("/BuildChar {\n");
2751 writePS(" 1 index /Encoding get exch get\n");
2752 writePS(" 1 index /BuildGlyph get exec\n");
2753 writePS("} bind def\n");
2754 if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
2755 writePSFmt("/CharProcs {0:d} dict def\n", charProcs->getLength());
2756 writePS("CharProcs begin\n");
2757 box.x1 = m[0];
2758 box.y1 = m[1];
2759 box.x2 = m[2];
2760 box.y2 = m[3];
2761 gfx = new Gfx(doc, this, resDict, &box, nullptr);
2762 inType3Char = true;
2763 for (i = 0; i < charProcs->getLength(); ++i) {
2764 t3FillColorOnly = false;
2765 t3Cacheable = false;
2766 t3NeedsRestore = false;
2767 writePS("/");
2768 writePSName(charProcs->getKey(i));
2769 writePS(" {\n");
2770 Object charProc = charProcs->getVal(i);
2771 gfx->display(&charProc);
2772 if (t3String) {
2773 if (t3Cacheable) {
2774 buf = GooString::format("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} setcachedevice\n", t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
2775 } else {
2776 buf = GooString::format("{0:.6g} {1:.6g} setcharwidth\n", t3WX, t3WY);
2777 }
2778 (*outputFunc)(outputStream, buf->c_str(), buf->getLength());
2779 delete buf;
2780 (*outputFunc)(outputStream, t3String->c_str(), t3String->getLength());
2781 delete t3String;
2782 t3String = nullptr;
2783 }
2784 if (t3NeedsRestore) {
2785 (*outputFunc)(outputStream, "Q\n", 2);
2786 }
2787 writePS("} def\n");
2788 }
2789 inType3Char = false;
2790 delete gfx;
2791 writePS("end\n");
2792 }
2793 writePS("currentdict end\n");
2794 writePSFmt("/{0:t} exch definefont pop\n", psName);
2795
2796 // ending comment
2797 writePS("%%EndResource\n");
2798 }
2799
2800 // Make a unique PS font name, based on the names given in the PDF
2801 // font object, and an object ID (font file object for
makePSFontName(GfxFont * font,const Ref * id)2802 GooString *PSOutputDev::makePSFontName(GfxFont *font, const Ref *id)
2803 {
2804 GooString *psName;
2805 const GooString *s;
2806
2807 if ((s = font->getEmbeddedFontName())) {
2808 psName = filterPSName(s);
2809 if (fontNames.emplace(psName->toStr()).second) {
2810 return psName;
2811 }
2812 delete psName;
2813 }
2814 if ((s = font->getName())) {
2815 psName = filterPSName(s);
2816 if (fontNames.emplace(psName->toStr()).second) {
2817 return psName;
2818 }
2819 delete psName;
2820 }
2821 psName = GooString::format("FF{0:d}_{1:d}", id->num, id->gen);
2822 if ((s = font->getEmbeddedFontName())) {
2823 s = filterPSName(s);
2824 psName->append('_')->append(s);
2825 delete s;
2826 } else if ((s = font->getName())) {
2827 s = filterPSName(s);
2828 psName->append('_')->append(s);
2829 delete s;
2830 }
2831 fontNames.emplace(psName->toStr());
2832 return psName;
2833 }
2834
setupImages(Dict * resDict)2835 void PSOutputDev::setupImages(Dict *resDict)
2836 {
2837 Ref imgID;
2838
2839 if (!(mode == psModeForm || inType3Char || preloadImagesForms)) {
2840 return;
2841 }
2842
2843 //----- recursively scan XObjects
2844 Object xObjDict = resDict->lookup("XObject");
2845 if (xObjDict.isDict()) {
2846 for (int i = 0; i < xObjDict.dictGetLength(); ++i) {
2847 const Object &xObjRef = xObjDict.dictGetValNF(i);
2848 Object xObj = xObjDict.dictGetVal(i);
2849 if (xObj.isStream()) {
2850 Object subtypeObj = xObj.streamGetDict()->lookup("Subtype");
2851 if (subtypeObj.isName("Image")) {
2852 if (xObjRef.isRef()) {
2853 imgID = xObjRef.getRef();
2854 int j;
2855 for (j = 0; j < imgIDLen; ++j) {
2856 if (imgIDs[j] == imgID) {
2857 break;
2858 }
2859 }
2860 if (j == imgIDLen) {
2861 if (imgIDLen >= imgIDSize) {
2862 if (imgIDSize == 0) {
2863 imgIDSize = 64;
2864 } else {
2865 imgIDSize *= 2;
2866 }
2867 imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref));
2868 }
2869 imgIDs[imgIDLen++] = imgID;
2870 setupImage(imgID, xObj.getStream(), false);
2871 if (level >= psLevel3) {
2872 Object maskObj = xObj.streamGetDict()->lookup("Mask");
2873 if (maskObj.isStream()) {
2874 setupImage(imgID, maskObj.getStream(), true);
2875 }
2876 }
2877 }
2878 } else {
2879 error(errSyntaxError, -1, "Image in resource dict is not an indirect reference");
2880 }
2881 }
2882 }
2883 }
2884 }
2885 }
2886
setupImage(Ref id,Stream * str,bool mask)2887 void PSOutputDev::setupImage(Ref id, Stream *str, bool mask)
2888 {
2889 bool useFlate, useLZW, useRLE, useCompressed, doUseASCIIHex;
2890 GooString *s;
2891 int c;
2892 int size, line, col, i;
2893 int outerSize, outer;
2894
2895 // filters
2896 //~ this does not correctly handle the DeviceN color space
2897 //~ -- need to use DeviceNRecoder
2898
2899 useFlate = useLZW = useRLE = false;
2900 useCompressed = false;
2901 doUseASCIIHex = false;
2902
2903 if (level < psLevel2) {
2904 doUseASCIIHex = true;
2905 } else {
2906 if (uncompressPreloadedImages) {
2907 /* nothing to do */;
2908 } else {
2909 s = str->getPSFilter(level < psLevel3 ? 2 : 3, "");
2910 if (s) {
2911 useCompressed = true;
2912 delete s;
2913 } else {
2914 if (level >= psLevel3 && getEnableFlate()) {
2915 useFlate = true;
2916 } else if (getEnableLZW()) {
2917 useLZW = true;
2918 } else {
2919 useRLE = true;
2920 }
2921 }
2922 }
2923 doUseASCIIHex = useASCIIHex;
2924 }
2925 if (useCompressed) {
2926 str = str->getUndecodedStream();
2927 }
2928 #ifdef ENABLE_ZLIB
2929 if (useFlate) {
2930 str = new FlateEncoder(str);
2931 } else
2932 #endif
2933 if (useLZW) {
2934 str = new LZWEncoder(str);
2935 } else if (useRLE) {
2936 str = new RunLengthEncoder(str);
2937 }
2938 if (doUseASCIIHex) {
2939 str = new ASCIIHexEncoder(str);
2940 } else {
2941 str = new ASCII85Encoder(str);
2942 }
2943
2944 // compute image data size
2945 str->reset();
2946 col = size = 0;
2947 do {
2948 do {
2949 c = str->getChar();
2950 } while (c == '\n' || c == '\r');
2951 if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
2952 break;
2953 }
2954 if (c == 'z') {
2955 ++col;
2956 } else {
2957 ++col;
2958 for (i = 1; i <= (doUseASCIIHex ? 1 : 4); ++i) {
2959 do {
2960 c = str->getChar();
2961 } while (c == '\n' || c == '\r');
2962 if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
2963 break;
2964 }
2965 ++col;
2966 }
2967 if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
2968 break;
2969 }
2970 }
2971 if (col > 225) {
2972 ++size;
2973 col = 0;
2974 }
2975 } while (c != (doUseASCIIHex ? '>' : '~') && c != EOF);
2976 // add one entry for the final line of data; add another entry
2977 // because the LZWDecode/RunLengthDecode filter may read past the end
2978 ++size;
2979 if (useLZW || useRLE) {
2980 ++size;
2981 }
2982 outerSize = size / 65535 + 1;
2983
2984 writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n", outerSize, mask ? "Mask" : "Im", id.num, id.gen);
2985 str->close();
2986
2987 // write the data into the array
2988 str->reset();
2989 for (outer = 0; outer < outerSize; outer++) {
2990 int innerSize = size > 65535 ? 65535 : size;
2991
2992 // put the inner array into the outer array
2993 writePSFmt("{0:d} array 1 index {1:d} 2 index put\n", innerSize, outer);
2994 line = col = 0;
2995 writePS((char *)(doUseASCIIHex ? "dup 0 <" : "dup 0 <~"));
2996 for (;;) {
2997 do {
2998 c = str->getChar();
2999 } while (c == '\n' || c == '\r');
3000 if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
3001 break;
3002 }
3003 if (c == 'z') {
3004 writePSChar(c);
3005 ++col;
3006 } else {
3007 writePSChar(c);
3008 ++col;
3009 for (i = 1; i <= (doUseASCIIHex ? 1 : 4); ++i) {
3010 do {
3011 c = str->getChar();
3012 } while (c == '\n' || c == '\r');
3013 if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
3014 break;
3015 }
3016 writePSChar(c);
3017 ++col;
3018 }
3019 }
3020 if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
3021 break;
3022 }
3023 // each line is: "dup nnnnn <~...data...~> put<eol>"
3024 // so max data length = 255 - 20 = 235
3025 // chunks are 1 or 4 bytes each, so we have to stop at 232
3026 // but make it 225 just to be safe
3027 if (col > 225) {
3028 writePS((char *)(doUseASCIIHex ? "> put\n" : "~> put\n"));
3029 ++line;
3030 if (line >= innerSize)
3031 break;
3032 writePSFmt((char *)(doUseASCIIHex ? "dup {0:d} <" : "dup {0:d} <~"), line);
3033 col = 0;
3034 }
3035 }
3036 if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
3037 writePS((char *)(doUseASCIIHex ? "> put\n" : "~> put\n"));
3038 if (useLZW || useRLE) {
3039 ++line;
3040 writePSFmt("{0:d} <> put\n", line);
3041 } else {
3042 writePS("pop\n");
3043 }
3044 break;
3045 }
3046 writePS("pop\n");
3047 size -= innerSize;
3048 }
3049 writePS("pop\n");
3050 str->close();
3051
3052 delete str;
3053 }
3054
setupForms(Dict * resDict)3055 void PSOutputDev::setupForms(Dict *resDict)
3056 {
3057 if (!preloadImagesForms) {
3058 return;
3059 }
3060
3061 Object xObjDict = resDict->lookup("XObject");
3062 if (xObjDict.isDict()) {
3063 for (int i = 0; i < xObjDict.dictGetLength(); ++i) {
3064 const Object &xObjRef = xObjDict.dictGetValNF(i);
3065 Object xObj = xObjDict.dictGetVal(i);
3066 if (xObj.isStream()) {
3067 Object subtypeObj = xObj.streamGetDict()->lookup("Subtype");
3068 if (subtypeObj.isName("Form")) {
3069 if (xObjRef.isRef()) {
3070 setupForm(xObjRef.getRef(), &xObj);
3071 } else {
3072 error(errSyntaxError, -1, "Form in resource dict is not an indirect reference");
3073 }
3074 }
3075 }
3076 }
3077 }
3078 }
3079
setupForm(Ref id,Object * strObj)3080 void PSOutputDev::setupForm(Ref id, Object *strObj)
3081 {
3082 Dict *dict, *resDict;
3083 double m[6], bbox[4];
3084 PDFRectangle box;
3085 Gfx *gfx;
3086
3087 // check if form is already defined
3088 for (int i = 0; i < formIDLen; ++i) {
3089 if (formIDs[i] == id) {
3090 return;
3091 }
3092 }
3093
3094 // add entry to formIDs list
3095 if (formIDLen >= formIDSize) {
3096 if (formIDSize == 0) {
3097 formIDSize = 64;
3098 } else {
3099 formIDSize *= 2;
3100 }
3101 formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref));
3102 }
3103 formIDs[formIDLen++] = id;
3104
3105 dict = strObj->streamGetDict();
3106
3107 // get bounding box
3108 Object bboxObj = dict->lookup("BBox");
3109 if (!bboxObj.isArray()) {
3110 error(errSyntaxError, -1, "Bad form bounding box");
3111 return;
3112 }
3113 for (int i = 0; i < 4; ++i) {
3114 Object obj1 = bboxObj.arrayGet(i);
3115 bbox[i] = obj1.getNum();
3116 }
3117
3118 // get matrix
3119 Object matrixObj = dict->lookup("Matrix");
3120 if (matrixObj.isArray()) {
3121 for (int i = 0; i < 6; ++i) {
3122 Object obj1 = matrixObj.arrayGet(i);
3123 m[i] = obj1.getNum();
3124 }
3125 } else {
3126 m[0] = 1;
3127 m[1] = 0;
3128 m[2] = 0;
3129 m[3] = 1;
3130 m[4] = 0;
3131 m[5] = 0;
3132 }
3133
3134 // get resources
3135 Object resObj = dict->lookup("Resources");
3136 resDict = resObj.isDict() ? resObj.getDict() : nullptr;
3137
3138 writePSFmt("/f_{0:d}_{1:d} {{\n", id.num, id.gen);
3139 writePS("q\n");
3140 writePSFmt("[{0:.6gs} {1:.6gs} {2:.6gs} {3:.6gs} {4:.6gs} {5:.6gs}] cm\n", m[0], m[1], m[2], m[3], m[4], m[5]);
3141
3142 box.x1 = bbox[0];
3143 box.y1 = bbox[1];
3144 box.x2 = bbox[2];
3145 box.y2 = bbox[3];
3146 gfx = new Gfx(doc, this, resDict, &box, &box);
3147 gfx->display(strObj);
3148 delete gfx;
3149
3150 writePS("Q\n");
3151 writePS("} def\n");
3152 }
3153
checkPageSlice(Page * page,double,double,int rotateA,bool useMediaBox,bool crop,int sliceX,int sliceY,int sliceW,int sliceH,bool printing,bool (* abortCheckCbk)(void * data),void * abortCheckCbkData,bool (* annotDisplayDecideCbk)(Annot * annot,void * user_data),void * annotDisplayDecideCbkData)3154 bool PSOutputDev::checkPageSlice(Page *page, double /*hDPI*/, double /*vDPI*/, int rotateA, bool useMediaBox, bool crop, int sliceX, int sliceY, int sliceW, int sliceH, bool printing, bool (*abortCheckCbk)(void *data),
3155 void *abortCheckCbkData, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), void *annotDisplayDecideCbkData)
3156 {
3157 PreScanOutputDev *scan;
3158 bool rasterize;
3159 bool useFlate, useLZW;
3160 SplashOutputDev *splashOut;
3161 SplashColor paperColor;
3162 PDFRectangle box;
3163 GfxState *state;
3164 SplashBitmap *bitmap;
3165 Stream *str0, *str;
3166 unsigned char *p;
3167 unsigned char col[4];
3168 double hDPI2, vDPI2;
3169 double m0, m1, m2, m3, m4, m5;
3170 int nStripes, stripeH, stripeY;
3171 int c, w, h, x, y, comp, i;
3172 int numComps, initialNumComps;
3173 char hexBuf[32 * 2 + 2]; // 32 values X 2 chars/value + line ending + null
3174 unsigned char digit;
3175 bool isOptimizedGray;
3176 bool overprint;
3177 SplashColorMode internalColorFormat;
3178
3179 if (!postInitDone) {
3180 postInit();
3181 }
3182 if (forceRasterize == psAlwaysRasterize) {
3183 rasterize = true;
3184 } else if (forceRasterize == psNeverRasterize) {
3185 rasterize = false;
3186 } else {
3187 scan = new PreScanOutputDev(level);
3188 page->displaySlice(scan, 72, 72, rotateA, useMediaBox, crop, sliceX, sliceY, sliceW, sliceH, printing, abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData);
3189 rasterize = scan->usesTransparency() || scan->usesPatternImageMask();
3190 delete scan;
3191 }
3192 if (!rasterize) {
3193 return true;
3194 }
3195
3196 // get the rasterization parameters
3197 useFlate = getEnableFlate() && level >= psLevel3;
3198 useLZW = getEnableLZW();
3199 // start the PS page
3200 page->makeBox(rasterResolution, rasterResolution, rotateA, useMediaBox, false, sliceX, sliceY, sliceW, sliceH, &box, &crop);
3201 rotateA += page->getRotate();
3202 if (rotateA >= 360) {
3203 rotateA -= 360;
3204 } else if (rotateA < 0) {
3205 rotateA += 360;
3206 }
3207 state = new GfxState(rasterResolution, rasterResolution, &box, rotateA, false);
3208 startPage(page->getNum(), state, xref);
3209 delete state;
3210
3211 // If we would not rasterize this page, we would emit the overprint code anyway for language level 2 and upwards.
3212 // As such it is safe to assume for a CMYK printer that it would respect the overprint operands.
3213 overprint = overprintPreview || (processColorFormat == splashModeCMYK8 && level >= psLevel2);
3214
3215 // set up the SplashOutputDev
3216 internalColorFormat = processColorFormat;
3217 if (processColorFormat == splashModeMono8) {
3218 numComps = 1;
3219 paperColor[0] = 0xff;
3220 } else if (processColorFormat == splashModeCMYK8) {
3221 numComps = 4;
3222 splashClearColor(paperColor);
3223
3224 // If overprinting is emulated, it is not sufficient to just store the CMYK values in a bitmap.
3225 // All separation channels need to be stored and collapsed at the end.
3226 // Cf. PDF32000_2008 Section 11.7.4.5 and Tables 148, 149
3227 if (overprint) {
3228 internalColorFormat = splashModeDeviceN8;
3229 }
3230 } else if (processColorFormat == splashModeRGB8) {
3231 numComps = 3;
3232 paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
3233 } else {
3234 error(errUnimplemented, -1, "Unsupported processColorMode. Falling back to RGB8.");
3235 processColorFormat = splashModeRGB8;
3236 internalColorFormat = processColorFormat;
3237 numComps = 3;
3238 paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
3239 }
3240 splashOut = new SplashOutputDev(internalColorFormat, 1, false, paperColor, false, splashThinLineDefault, overprint);
3241 splashOut->setFontAntialias(rasterAntialias);
3242 splashOut->setVectorAntialias(rasterAntialias);
3243 #ifdef USE_CMS
3244 splashOut->setDisplayProfile(getDisplayProfile());
3245 splashOut->setDefaultGrayProfile(getDefaultGrayProfile());
3246 splashOut->setDefaultRGBProfile(getDefaultRGBProfile());
3247 splashOut->setDefaultCMYKProfile(getDefaultCMYKProfile());
3248 #endif
3249 splashOut->startDoc(doc);
3250
3251 // break the page into stripes
3252 hDPI2 = xScale * rasterResolution;
3253 vDPI2 = yScale * rasterResolution;
3254 if (sliceW < 0 || sliceH < 0) {
3255 if (useMediaBox) {
3256 box = *page->getMediaBox();
3257 } else {
3258 box = *page->getCropBox();
3259 }
3260 sliceX = sliceY = 0;
3261 sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0);
3262 sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0);
3263 }
3264 int sliceArea;
3265 if (checkedMultiply(sliceW, sliceH, &sliceArea)) {
3266 delete splashOut;
3267 return false;
3268 }
3269 nStripes = (int)ceil((double)(sliceArea) / (double)rasterizationSliceSize);
3270 if (unlikely(nStripes == 0)) {
3271 delete splashOut;
3272 return false;
3273 }
3274 stripeH = (sliceH + nStripes - 1) / nStripes;
3275
3276 // render the stripes
3277 initialNumComps = numComps;
3278 for (stripeY = sliceY; stripeY < sliceH; stripeY += stripeH) {
3279
3280 // rasterize a stripe
3281 page->makeBox(hDPI2, vDPI2, 0, useMediaBox, false, sliceX, stripeY, sliceW, stripeH, &box, &crop);
3282 m0 = box.x2 - box.x1;
3283 m1 = 0;
3284 m2 = 0;
3285 m3 = box.y2 - box.y1;
3286 m4 = box.x1;
3287 m5 = box.y1;
3288 page->displaySlice(splashOut, hDPI2, vDPI2, (360 - page->getRotate()) % 360, useMediaBox, crop, sliceX, stripeY, sliceW, stripeH, printing, abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData);
3289
3290 // draw the rasterized image
3291 bitmap = splashOut->getBitmap();
3292 numComps = initialNumComps;
3293 w = bitmap->getWidth();
3294 h = bitmap->getHeight();
3295 writePS("gsave\n");
3296 writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", m0, m1, m2, m3, m4, m5);
3297 switch (level) {
3298 case psLevel1:
3299 writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}\n", w, h, w, -h, h, useBinary ? "Bin" : "");
3300 p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
3301 i = 0;
3302 if (useBinary) {
3303 for (y = 0; y < h; ++y) {
3304 for (x = 0; x < w; ++x) {
3305 hexBuf[i++] = *p++;
3306 if (i >= 64) {
3307 writePSBuf(hexBuf, i);
3308 i = 0;
3309 }
3310 }
3311 }
3312 } else {
3313 for (y = 0; y < h; ++y) {
3314 for (x = 0; x < w; ++x) {
3315 digit = *p / 16;
3316 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3317 digit = *p++ % 16;
3318 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3319 if (i >= 64) {
3320 hexBuf[i++] = '\n';
3321 writePSBuf(hexBuf, i);
3322 i = 0;
3323 }
3324 }
3325 }
3326 }
3327 if (i != 0) {
3328 if (!useBinary) {
3329 hexBuf[i++] = '\n';
3330 }
3331 writePSBuf(hexBuf, i);
3332 }
3333 break;
3334 case psLevel1Sep:
3335 p = bitmap->getDataPtr();
3336 // Check for an all gray image
3337 if (getOptimizeColorSpace()) {
3338 isOptimizedGray = true;
3339 for (y = 0; y < h; ++y) {
3340 for (x = 0; x < w; ++x) {
3341 if (p[4 * x] != p[4 * x + 1] || p[4 * x] != p[4 * x + 2]) {
3342 isOptimizedGray = false;
3343 y = h;
3344 break;
3345 }
3346 }
3347 p += bitmap->getRowSize();
3348 }
3349 } else {
3350 isOptimizedGray = false;
3351 }
3352 writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}{6:s}\n", w, h, w, -h, h, isOptimizedGray ? "" : "Sep", useBinary ? "Bin" : "");
3353 p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
3354 i = 0;
3355 col[0] = col[1] = col[2] = col[3] = 0;
3356 if (isOptimizedGray) {
3357 int g;
3358 if ((psProcessBlack & processColors) == 0) {
3359 // Check if the image uses black
3360 for (y = 0; y < h; ++y) {
3361 for (x = 0; x < w; ++x) {
3362 if (p[4 * x] > 0 || p[4 * x + 3] > 0) {
3363 col[3] = 1;
3364 y = h;
3365 break;
3366 }
3367 }
3368 p -= bitmap->getRowSize();
3369 }
3370 p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
3371 }
3372 for (y = 0; y < h; ++y) {
3373 if (useBinary) {
3374 // Binary gray image
3375 for (x = 0; x < w; ++x) {
3376 g = p[4 * x] + p[4 * x + 3];
3377 g = 255 - g;
3378 if (g < 0)
3379 g = 0;
3380 hexBuf[i++] = (unsigned char)g;
3381 if (i >= 64) {
3382 writePSBuf(hexBuf, i);
3383 i = 0;
3384 }
3385 }
3386 } else {
3387 // Hex gray image
3388 for (x = 0; x < w; ++x) {
3389 g = p[4 * x] + p[4 * x + 3];
3390 g = 255 - g;
3391 if (g < 0)
3392 g = 0;
3393 digit = g / 16;
3394 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3395 digit = g % 16;
3396 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3397 if (i >= 64) {
3398 hexBuf[i++] = '\n';
3399 writePSBuf(hexBuf, i);
3400 i = 0;
3401 }
3402 }
3403 }
3404 p -= bitmap->getRowSize();
3405 }
3406 } else if (((psProcessCyan | psProcessMagenta | psProcessYellow | psProcessBlack) & ~processColors) != 0) {
3407 // Color image, need to check color flags for each dot
3408 for (y = 0; y < h; ++y) {
3409 for (comp = 0; comp < 4; ++comp) {
3410 if (useBinary) {
3411 // Binary color image
3412 for (x = 0; x < w; ++x) {
3413 col[comp] |= p[4 * x + comp];
3414 hexBuf[i++] = p[4 * x + comp];
3415 if (i >= 64) {
3416 writePSBuf(hexBuf, i);
3417 i = 0;
3418 }
3419 }
3420 } else {
3421 // Gray color image
3422 for (x = 0; x < w; ++x) {
3423 col[comp] |= p[4 * x + comp];
3424 digit = p[4 * x + comp] / 16;
3425 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3426 digit = p[4 * x + comp] % 16;
3427 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3428 if (i >= 64) {
3429 hexBuf[i++] = '\n';
3430 writePSBuf(hexBuf, i);
3431 i = 0;
3432 }
3433 }
3434 }
3435 }
3436 p -= bitmap->getRowSize();
3437 }
3438 } else {
3439 // Color image, do not need to check color flags
3440 for (y = 0; y < h; ++y) {
3441 for (comp = 0; comp < 4; ++comp) {
3442 if (useBinary) {
3443 // Binary color image
3444 for (x = 0; x < w; ++x) {
3445 hexBuf[i++] = p[4 * x + comp];
3446 if (i >= 64) {
3447 writePSBuf(hexBuf, i);
3448 i = 0;
3449 }
3450 }
3451 } else {
3452 // Hex color image
3453 for (x = 0; x < w; ++x) {
3454 digit = p[4 * x + comp] / 16;
3455 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3456 digit = p[4 * x + comp] % 16;
3457 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3458 if (i >= 64) {
3459 hexBuf[i++] = '\n';
3460 writePSBuf(hexBuf, i);
3461 i = 0;
3462 }
3463 }
3464 }
3465 }
3466 p -= bitmap->getRowSize();
3467 }
3468 }
3469 if (i != 0) {
3470 if (!useBinary) {
3471 hexBuf[i++] = '\n';
3472 }
3473 writePSBuf(hexBuf, i);
3474 }
3475 if (col[0]) {
3476 processColors |= psProcessCyan;
3477 }
3478 if (col[1]) {
3479 processColors |= psProcessMagenta;
3480 }
3481 if (col[2]) {
3482 processColors |= psProcessYellow;
3483 }
3484 if (col[3]) {
3485 processColors |= psProcessBlack;
3486 }
3487 break;
3488 case psLevel2:
3489 case psLevel2Sep:
3490 case psLevel3:
3491 case psLevel3Sep:
3492 p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
3493 if (processColorFormat == splashModeCMYK8 && internalColorFormat != splashModeCMYK8) {
3494 str0 = new SplashBitmapCMYKEncoder(bitmap);
3495 } else {
3496 str0 = new MemStream((char *)p, 0, w * h * numComps, Object(objNull));
3497 }
3498 // Check for a color image that uses only gray
3499 if (!getOptimizeColorSpace()) {
3500 isOptimizedGray = false;
3501 } else if (numComps == 4) {
3502 int compCyan;
3503 isOptimizedGray = true;
3504 while ((compCyan = str0->getChar()) != EOF) {
3505 if (str0->getChar() != compCyan || str0->getChar() != compCyan) {
3506 isOptimizedGray = false;
3507 break;
3508 }
3509 str0->getChar();
3510 }
3511 } else if (numComps == 3) {
3512 int compRed;
3513 isOptimizedGray = true;
3514 while ((compRed = str0->getChar()) != EOF) {
3515 if (str0->getChar() != compRed || str0->getChar() != compRed) {
3516 isOptimizedGray = false;
3517 break;
3518 }
3519 }
3520 } else {
3521 isOptimizedGray = false;
3522 }
3523 str0->reset();
3524 #ifdef ENABLE_ZLIB
3525 if (useFlate) {
3526 if (isOptimizedGray && numComps == 4) {
3527 str = new FlateEncoder(new CMYKGrayEncoder(str0));
3528 numComps = 1;
3529 } else if (isOptimizedGray && numComps == 3) {
3530 str = new FlateEncoder(new RGBGrayEncoder(str0));
3531 numComps = 1;
3532 } else {
3533 str = new FlateEncoder(str0);
3534 }
3535 } else
3536 #endif
3537 if (useLZW) {
3538 if (isOptimizedGray && numComps == 4) {
3539 str = new LZWEncoder(new CMYKGrayEncoder(str0));
3540 numComps = 1;
3541 } else if (isOptimizedGray && numComps == 3) {
3542 str = new LZWEncoder(new RGBGrayEncoder(str0));
3543 numComps = 1;
3544 } else {
3545 str = new LZWEncoder(str0);
3546 }
3547 } else {
3548 if (isOptimizedGray && numComps == 4) {
3549 str = new RunLengthEncoder(new CMYKGrayEncoder(str0));
3550 numComps = 1;
3551 } else if (isOptimizedGray && numComps == 3) {
3552 str = new RunLengthEncoder(new RGBGrayEncoder(str0));
3553 numComps = 1;
3554 } else {
3555 str = new RunLengthEncoder(str0);
3556 }
3557 }
3558 if (numComps == 1) {
3559 writePS("/DeviceGray setcolorspace\n");
3560 } else if (numComps == 3) {
3561 writePS("/DeviceRGB setcolorspace\n");
3562 } else {
3563 writePS("/DeviceCMYK setcolorspace\n");
3564 }
3565 writePS("<<\n /ImageType 1\n");
3566 writePSFmt(" /Width {0:d}\n", bitmap->getWidth());
3567 writePSFmt(" /Height {0:d}\n", bitmap->getHeight());
3568 writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h);
3569 writePS(" /BitsPerComponent 8\n");
3570 if (numComps == 1) {
3571 // the optimized gray variants are implemented as a subtractive color space,
3572 // such that the range is flipped for them
3573 if (isOptimizedGray) {
3574 writePS(" /Decode [1 0]\n");
3575 } else {
3576 writePS(" /Decode [0 1]\n");
3577 }
3578 } else if (numComps == 3) {
3579 writePS(" /Decode [0 1 0 1 0 1]\n");
3580 } else {
3581 writePS(" /Decode [0 1 0 1 0 1 0 1]\n");
3582 }
3583 writePS(" /DataSource currentfile\n");
3584 if (useBinary) {
3585 /* nothing to do */;
3586 } else if (useASCIIHex) {
3587 writePS(" /ASCIIHexDecode filter\n");
3588 } else {
3589 writePS(" /ASCII85Decode filter\n");
3590 }
3591 if (useFlate) {
3592 writePS(" /FlateDecode filter\n");
3593 } else if (useLZW) {
3594 writePS(" /LZWDecode filter\n");
3595 } else {
3596 writePS(" /RunLengthDecode filter\n");
3597 }
3598 writePS(">>\n");
3599 if (useBinary) {
3600 /* nothing to do */;
3601 } else if (useASCIIHex) {
3602 str = new ASCIIHexEncoder(str);
3603 } else {
3604 str = new ASCII85Encoder(str);
3605 }
3606 str->reset();
3607 if (useBinary) {
3608 // Count the bytes to write a document comment
3609 int len = 0;
3610 while (str->getChar() != EOF) {
3611 len++;
3612 }
3613 str->reset();
3614 writePSFmt("%%BeginData: {0:d} Binary Bytes\n", len + 6 + 1);
3615 }
3616 writePS("image\n");
3617 while ((c = str->getChar()) != EOF) {
3618 writePSChar(c);
3619 }
3620 str->close();
3621 delete str;
3622 delete str0;
3623 writePSChar('\n');
3624 if (useBinary) {
3625 writePS("%%EndData\n");
3626 }
3627 processColors |= (numComps == 1) ? psProcessBlack : psProcessCMYK;
3628 break;
3629 }
3630 writePS("grestore\n");
3631 }
3632
3633 delete splashOut;
3634
3635 // finish the PS page
3636 endPage();
3637
3638 return false;
3639 }
3640
startPage(int pageNum,GfxState * state,XRef * xrefA)3641 void PSOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA)
3642 {
3643 Page *page;
3644 int x1, y1, x2, y2, width, height, t;
3645 int imgWidth, imgHeight, imgWidth2, imgHeight2;
3646 bool landscape;
3647 GooString *s;
3648 PSOutPaperSize *paperSize;
3649
3650 if (!postInitDone) {
3651 postInit();
3652 }
3653 xref = xrefA;
3654 if (mode == psModePS) {
3655 GooString pageLabel;
3656 const bool gotLabel = doc->getCatalog()->indexToLabel(pageNum - 1, &pageLabel);
3657 if (gotLabel) {
3658 // See bug13338 for why we try to avoid parentheses...
3659 bool needParens;
3660 GooString *filteredString = filterPSLabel(&pageLabel, &needParens);
3661 if (needParens) {
3662 writePSFmt("%%Page: ({0:t}) {1:d}\n", filteredString, seqPage);
3663 } else {
3664 writePSFmt("%%Page: {0:t} {1:d}\n", filteredString, seqPage);
3665 }
3666 delete filteredString;
3667 } else {
3668 writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage);
3669 }
3670 if (paperMatch) {
3671 page = doc->getCatalog()->getPage(pageNum);
3672 imgLLX = imgLLY = 0;
3673 if (noCrop) {
3674 imgURX = (int)ceil(page->getMediaWidth());
3675 imgURY = (int)ceil(page->getMediaHeight());
3676 } else {
3677 imgURX = (int)ceil(page->getCropWidth());
3678 imgURY = (int)ceil(page->getCropHeight());
3679 }
3680 if (state->getRotate() == 90 || state->getRotate() == 270) {
3681 t = imgURX;
3682 imgURX = imgURY;
3683 imgURY = t;
3684 }
3685 }
3686 }
3687
3688 // underlays
3689 if (underlayCbk) {
3690 (*underlayCbk)(this, underlayCbkData);
3691 }
3692 if (overlayCbk) {
3693 saveState(nullptr);
3694 }
3695
3696 xScale = yScale = 1;
3697 switch (mode) {
3698
3699 case psModePS:
3700 // rotate, translate, and scale page
3701 imgWidth = imgURX - imgLLX;
3702 imgHeight = imgURY - imgLLY;
3703 x1 = (int)floor(state->getX1());
3704 y1 = (int)floor(state->getY1());
3705 x2 = (int)ceil(state->getX2());
3706 y2 = (int)ceil(state->getY2());
3707 if (unlikely(checkedSubtraction(x2, x1, &width))) {
3708 error(errSyntaxError, -1, "width too big");
3709 return;
3710 }
3711 height = y2 - y1;
3712 tx = ty = 0;
3713 // rotation and portrait/landscape mode
3714 if (paperMatch) {
3715 rotate = (360 - state->getRotate()) % 360;
3716 landscape = false;
3717 } else if (rotate0 >= 0) {
3718 rotate = (360 - rotate0) % 360;
3719 landscape = false;
3720 } else {
3721 rotate = (360 - state->getRotate()) % 360;
3722 if (rotate == 0 || rotate == 180) {
3723 if ((width < height && imgWidth > imgHeight && height > imgHeight) || (width > height && imgWidth < imgHeight && width > imgWidth)) {
3724 rotate += 90;
3725 landscape = true;
3726 } else {
3727 landscape = false;
3728 }
3729 } else { // rotate == 90 || rotate == 270
3730 if ((height < width && imgWidth > imgHeight && width > imgHeight) || (height > width && imgWidth < imgHeight && height > imgWidth)) {
3731 rotate = 270 - rotate;
3732 landscape = true;
3733 } else {
3734 landscape = false;
3735 }
3736 }
3737 }
3738 if (rotate == 0) {
3739 imgWidth2 = imgWidth;
3740 imgHeight2 = imgHeight;
3741 } else if (rotate == 90) {
3742 ty = -imgWidth;
3743 imgWidth2 = imgHeight;
3744 imgHeight2 = imgWidth;
3745 } else if (rotate == 180) {
3746 imgWidth2 = imgWidth;
3747 imgHeight2 = imgHeight;
3748 tx = -imgWidth;
3749 ty = -imgHeight;
3750 } else { // rotate == 270
3751 tx = -imgHeight;
3752 imgWidth2 = imgHeight;
3753 imgHeight2 = imgWidth;
3754 }
3755 // shrink or expand
3756 if (xScale0 > 0 && yScale0 > 0) {
3757 xScale = xScale0;
3758 yScale = yScale0;
3759 } else if ((psShrinkLarger && (width > imgWidth2 || height > imgHeight2)) || (psExpandSmaller && (width < imgWidth2 && height < imgHeight2))) {
3760 if (unlikely(width == 0)) {
3761 error(errSyntaxError, -1, "width 0, xScale would be infinite");
3762 return;
3763 }
3764 xScale = (double)imgWidth2 / (double)width;
3765 yScale = (double)imgHeight2 / (double)height;
3766 if (yScale < xScale) {
3767 xScale = yScale;
3768 } else {
3769 yScale = xScale;
3770 }
3771 }
3772 // deal with odd bounding boxes or clipping
3773 if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
3774 tx -= xScale * clipLLX0;
3775 ty -= yScale * clipLLY0;
3776 } else {
3777 tx -= xScale * x1;
3778 ty -= yScale * y1;
3779 }
3780 // center
3781 if (tx0 >= 0 && ty0 >= 0) {
3782 tx += (rotate == 0 || rotate == 180) ? tx0 : ty0;
3783 ty += (rotate == 0 || rotate == 180) ? ty0 : -tx0;
3784 } else if (psCenter) {
3785 if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
3786 tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2;
3787 ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2;
3788 } else {
3789 tx += (imgWidth2 - xScale * width) / 2;
3790 ty += (imgHeight2 - yScale * height) / 2;
3791 }
3792 }
3793 tx += (rotate == 0 || rotate == 180) ? imgLLX : imgLLY;
3794 ty += (rotate == 0 || rotate == 180) ? imgLLY : -imgLLX;
3795
3796 if (paperMatch) {
3797 paperSize = (*paperSizes)[pagePaperSize[pageNum]];
3798 writePSFmt("%%PageMedia: {0:t}\n", paperSize->name);
3799 }
3800
3801 // Create a matrix with the same transform that will be output to PS
3802 Matrix m;
3803 switch (rotate) {
3804 default:
3805 case 0:
3806 m.init(1, 0, 0, 1, 0, 0);
3807 break;
3808 case 90:
3809 m.init(0, 1, -1, 0, 0, 0);
3810 break;
3811 case 180:
3812 m.init(-1, 0, 0, -1, 0, 0);
3813 break;
3814 case 270:
3815 m.init(0, -1, 1, 0, 0, 0);
3816 break;
3817 }
3818 m.translate(tx, ty);
3819 m.scale(xScale, yScale);
3820
3821 double bboxX1, bboxY1, bboxX2, bboxY2;
3822 m.transform(0, 0, &bboxX1, &bboxY1);
3823 m.transform(width, height, &bboxX2, &bboxY2);
3824
3825 writePSFmt("%%PageBoundingBox: {0:g} {1:g} {2:g} {3:g}\n", floor(std::min(bboxX1, bboxX2)), floor(std::min(bboxY1, bboxY2)), ceil(std::max(bboxX1, bboxX2)), ceil(std::max(bboxY1, bboxY2)));
3826
3827 writePSFmt("%%PageOrientation: {0:s}\n", landscape ? "Landscape" : "Portrait");
3828 writePS("%%BeginPageSetup\n");
3829 if (paperMatch) {
3830 writePSFmt("{0:d} {1:d} pdfSetupPaper\n", imgURX, imgURY);
3831 }
3832 writePS("pdfStartPage\n");
3833 if (rotate)
3834 writePSFmt("{0:d} rotate\n", rotate);
3835 if (tx != 0 || ty != 0) {
3836 writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty);
3837 }
3838 if (xScale != 1 || yScale != 1) {
3839 writePSFmt("{0:.6f} {1:.6f} scale\n", xScale, yScale);
3840 }
3841 if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
3842 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re W\n", clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0);
3843 } else {
3844 writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1);
3845 }
3846
3847 ++seqPage;
3848 break;
3849
3850 case psModeEPS:
3851 writePS("pdfStartPage\n");
3852 tx = ty = 0;
3853 rotate = (360 - state->getRotate()) % 360;
3854 if (rotate == 0) {
3855 } else if (rotate == 90) {
3856 writePS("90 rotate\n");
3857 tx = -epsX1;
3858 ty = -epsY2;
3859 } else if (rotate == 180) {
3860 writePS("180 rotate\n");
3861 tx = -(epsX1 + epsX2);
3862 ty = -(epsY1 + epsY2);
3863 } else { // rotate == 270
3864 writePS("270 rotate\n");
3865 tx = -epsX2;
3866 ty = -epsY1;
3867 }
3868 if (tx != 0 || ty != 0) {
3869 writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty);
3870 }
3871 break;
3872
3873 case psModeForm:
3874 writePS("/PaintProc {\n");
3875 writePS("begin xpdf begin\n");
3876 writePS("pdfStartPage\n");
3877 tx = ty = 0;
3878 rotate = 0;
3879 break;
3880 }
3881
3882 if (customCodeCbk) {
3883 if ((s = (*customCodeCbk)(this, psOutCustomPageSetup, pageNum, customCodeCbkData))) {
3884 writePS(s->c_str());
3885 delete s;
3886 }
3887 }
3888
3889 writePS("%%EndPageSetup\n");
3890 }
3891
endPage()3892 void PSOutputDev::endPage()
3893 {
3894 if (overlayCbk) {
3895 restoreState(nullptr);
3896 (*overlayCbk)(this, overlayCbkData);
3897 }
3898
3899 for (const auto &item : iccEmitted) {
3900 writePSFmt("userdict /{0:s} undef\n", item.c_str());
3901 }
3902 iccEmitted.clear();
3903
3904 if (mode == psModeForm) {
3905 writePS("pdfEndPage\n");
3906 writePS("end end\n");
3907 writePS("} def\n");
3908 writePS("end end\n");
3909 } else {
3910 if (!manualCtrl) {
3911 writePS("showpage\n");
3912 }
3913 writePS("%%PageTrailer\n");
3914 writePageTrailer();
3915 }
3916 }
3917
saveState(GfxState * state)3918 void PSOutputDev::saveState(GfxState *state)
3919 {
3920 writePS("q\n");
3921 ++numSaves;
3922 }
3923
restoreState(GfxState * state)3924 void PSOutputDev::restoreState(GfxState *state)
3925 {
3926 writePS("Q\n");
3927 --numSaves;
3928 }
3929
updateCTM(GfxState * state,double m11,double m12,double m21,double m22,double m31,double m32)3930 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32)
3931 {
3932 writePSFmt("[{0:.6gs} {1:.6gs} {2:.6gs} {3:.6gs} {4:.6gs} {5:.6gs}] cm\n", m11, m12, m21, m22, m31, m32);
3933 }
3934
updateLineDash(GfxState * state)3935 void PSOutputDev::updateLineDash(GfxState *state)
3936 {
3937 double *dash;
3938 double start;
3939 int length, i;
3940
3941 state->getLineDash(&dash, &length, &start);
3942 writePS("[");
3943 for (i = 0; i < length; ++i) {
3944 writePSFmt("{0:.6g}{1:w}", dash[i] < 0 ? 0 : dash[i], (i == length - 1) ? 0 : 1);
3945 }
3946 writePSFmt("] {0:.6g} d\n", start);
3947 }
3948
updateFlatness(GfxState * state)3949 void PSOutputDev::updateFlatness(GfxState *state)
3950 {
3951 writePSFmt("{0:d} i\n", state->getFlatness());
3952 }
3953
updateLineJoin(GfxState * state)3954 void PSOutputDev::updateLineJoin(GfxState *state)
3955 {
3956 writePSFmt("{0:d} j\n", state->getLineJoin());
3957 }
3958
updateLineCap(GfxState * state)3959 void PSOutputDev::updateLineCap(GfxState *state)
3960 {
3961 writePSFmt("{0:d} J\n", state->getLineCap());
3962 }
3963
updateMiterLimit(GfxState * state)3964 void PSOutputDev::updateMiterLimit(GfxState *state)
3965 {
3966 writePSFmt("{0:.6g} M\n", state->getMiterLimit());
3967 }
3968
updateLineWidth(GfxState * state)3969 void PSOutputDev::updateLineWidth(GfxState *state)
3970 {
3971 writePSFmt("{0:.6g} w\n", state->getLineWidth());
3972 }
3973
updateFillColorSpace(GfxState * state)3974 void PSOutputDev::updateFillColorSpace(GfxState *state)
3975 {
3976 if (inUncoloredPattern) {
3977 return;
3978 }
3979 switch (level) {
3980 case psLevel1:
3981 case psLevel1Sep:
3982 break;
3983 case psLevel2:
3984 case psLevel3:
3985 if (state->getFillColorSpace()->getMode() != csPattern) {
3986 dumpColorSpaceL2(state, state->getFillColorSpace(), true, false, false);
3987 writePS(" cs\n");
3988 }
3989 break;
3990 case psLevel2Sep:
3991 case psLevel3Sep:
3992 break;
3993 }
3994 }
3995
updateStrokeColorSpace(GfxState * state)3996 void PSOutputDev::updateStrokeColorSpace(GfxState *state)
3997 {
3998 if (inUncoloredPattern) {
3999 return;
4000 }
4001 switch (level) {
4002 case psLevel1:
4003 case psLevel1Sep:
4004 break;
4005 case psLevel2:
4006 case psLevel3:
4007 if (state->getStrokeColorSpace()->getMode() != csPattern) {
4008 dumpColorSpaceL2(state, state->getStrokeColorSpace(), true, false, false);
4009 writePS(" CS\n");
4010 }
4011 break;
4012 case psLevel2Sep:
4013 case psLevel3Sep:
4014 break;
4015 }
4016 }
4017
updateFillColor(GfxState * state)4018 void PSOutputDev::updateFillColor(GfxState *state)
4019 {
4020 GfxColor color;
4021 GfxGray gray;
4022 GfxCMYK cmyk;
4023 GfxSeparationColorSpace *sepCS;
4024 double c, m, y, k;
4025 int i;
4026
4027 if (inUncoloredPattern) {
4028 return;
4029 }
4030 switch (level) {
4031 case psLevel1:
4032 state->getFillGray(&gray);
4033 writePSFmt("{0:.4g} g\n", colToDbl(gray));
4034 break;
4035 case psLevel2:
4036 case psLevel3:
4037 if (state->getFillColorSpace()->getMode() != csPattern) {
4038 const GfxColor *colorPtr = state->getFillColor();
4039 writePS("[");
4040 for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) {
4041 if (i > 0) {
4042 writePS(" ");
4043 }
4044 writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i]));
4045 }
4046 writePS("] sc\n");
4047 }
4048 break;
4049 case psLevel1Sep:
4050 case psLevel2Sep:
4051 case psLevel3Sep:
4052 if (state->getFillColorSpace()->getMode() == csSeparation && (level > psLevel1Sep || getPassLevel1CustomColor())) {
4053 sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
4054 color.c[0] = gfxColorComp1;
4055 sepCS->getCMYK(&color, &cmyk);
4056 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) ck\n", colToDbl(state->getFillColor()->c[0]), colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName());
4057 addCustomColor(sepCS);
4058 } else {
4059 state->getFillCMYK(&cmyk);
4060 c = colToDbl(cmyk.c);
4061 m = colToDbl(cmyk.m);
4062 y = colToDbl(cmyk.y);
4063 k = colToDbl(cmyk.k);
4064 if (getOptimizeColorSpace()) {
4065 double g;
4066 g = 0.299 * c + 0.587 * m + 0.114 * y;
4067 if ((fabs(m - c) < 0.01 && fabs(m - y) < 0.01) || (fabs(m - c) < 0.2 && fabs(m - y) < 0.2 && k + g > 1.5)) {
4068 c = m = y = 0.0;
4069 k += g;
4070 if (k > 1.0)
4071 k = 1.0;
4072 }
4073 }
4074 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k);
4075 addProcessColor(c, m, y, k);
4076 }
4077 break;
4078 }
4079 t3Cacheable = false;
4080 }
4081
updateStrokeColor(GfxState * state)4082 void PSOutputDev::updateStrokeColor(GfxState *state)
4083 {
4084 GfxColor color;
4085 GfxGray gray;
4086 GfxCMYK cmyk;
4087 GfxSeparationColorSpace *sepCS;
4088 double c, m, y, k;
4089 int i;
4090
4091 if (inUncoloredPattern) {
4092 return;
4093 }
4094 switch (level) {
4095 case psLevel1:
4096 state->getStrokeGray(&gray);
4097 writePSFmt("{0:.4g} G\n", colToDbl(gray));
4098 break;
4099 case psLevel2:
4100 case psLevel3:
4101 if (state->getStrokeColorSpace()->getMode() != csPattern) {
4102 const GfxColor *colorPtr = state->getStrokeColor();
4103 writePS("[");
4104 for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) {
4105 if (i > 0) {
4106 writePS(" ");
4107 }
4108 writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i]));
4109 }
4110 writePS("] SC\n");
4111 }
4112 break;
4113 case psLevel1Sep:
4114 case psLevel2Sep:
4115 case psLevel3Sep:
4116 if (state->getStrokeColorSpace()->getMode() == csSeparation && (level > psLevel1Sep || getPassLevel1CustomColor())) {
4117 sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
4118 color.c[0] = gfxColorComp1;
4119 sepCS->getCMYK(&color, &cmyk);
4120 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) CK\n", colToDbl(state->getStrokeColor()->c[0]), colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName());
4121 addCustomColor(sepCS);
4122 } else {
4123 state->getStrokeCMYK(&cmyk);
4124 c = colToDbl(cmyk.c);
4125 m = colToDbl(cmyk.m);
4126 y = colToDbl(cmyk.y);
4127 k = colToDbl(cmyk.k);
4128 if (getOptimizeColorSpace()) {
4129 double g;
4130 g = 0.299 * c + 0.587 * m + 0.114 * y;
4131 if ((fabs(m - c) < 0.01 && fabs(m - y) < 0.01) || (fabs(m - c) < 0.2 && fabs(m - y) < 0.2 && k + g > 1.5)) {
4132 c = m = y = 0.0;
4133 k += g;
4134 if (k > 1.0)
4135 k = 1.0;
4136 }
4137 }
4138 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k);
4139 addProcessColor(c, m, y, k);
4140 }
4141 break;
4142 }
4143 t3Cacheable = false;
4144 }
4145
addProcessColor(double c,double m,double y,double k)4146 void PSOutputDev::addProcessColor(double c, double m, double y, double k)
4147 {
4148 if (c > 0) {
4149 processColors |= psProcessCyan;
4150 }
4151 if (m > 0) {
4152 processColors |= psProcessMagenta;
4153 }
4154 if (y > 0) {
4155 processColors |= psProcessYellow;
4156 }
4157 if (k > 0) {
4158 processColors |= psProcessBlack;
4159 }
4160 }
4161
addCustomColor(GfxSeparationColorSpace * sepCS)4162 void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS)
4163 {
4164 PSOutCustomColor *cc;
4165 GfxColor color;
4166 GfxCMYK cmyk;
4167
4168 if (!sepCS->getName()->cmp("Black")) {
4169 processColors |= psProcessBlack;
4170 return;
4171 }
4172 if (!sepCS->getName()->cmp("Cyan")) {
4173 processColors |= psProcessCyan;
4174 return;
4175 }
4176 if (!sepCS->getName()->cmp("Yellow")) {
4177 processColors |= psProcessYellow;
4178 return;
4179 }
4180 if (!sepCS->getName()->cmp("Magenta")) {
4181 processColors |= psProcessMagenta;
4182 return;
4183 }
4184 if (!sepCS->getName()->cmp("All"))
4185 return;
4186 if (!sepCS->getName()->cmp("None"))
4187 return;
4188 for (cc = customColors; cc; cc = cc->next) {
4189 if (!cc->name->cmp(sepCS->getName())) {
4190 return;
4191 }
4192 }
4193 color.c[0] = gfxColorComp1;
4194 sepCS->getCMYK(&color, &cmyk);
4195 cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()->copy());
4196 cc->next = customColors;
4197 customColors = cc;
4198 }
4199
updateFillOverprint(GfxState * state)4200 void PSOutputDev::updateFillOverprint(GfxState *state)
4201 {
4202 if (level >= psLevel2) {
4203 writePSFmt("{0:s} op\n", state->getFillOverprint() ? "true" : "false");
4204 }
4205 }
4206
updateStrokeOverprint(GfxState * state)4207 void PSOutputDev::updateStrokeOverprint(GfxState *state)
4208 {
4209 if (level >= psLevel2) {
4210 writePSFmt("{0:s} OP\n", state->getStrokeOverprint() ? "true" : "false");
4211 }
4212 }
4213
updateOverprintMode(GfxState * state)4214 void PSOutputDev::updateOverprintMode(GfxState *state)
4215 {
4216 if (level >= psLevel3) {
4217 writePSFmt("{0:s} opm\n", state->getOverprintMode() ? "true" : "false");
4218 }
4219 }
4220
updateTransfer(GfxState * state)4221 void PSOutputDev::updateTransfer(GfxState *state)
4222 {
4223 Function **funcs;
4224 int i;
4225
4226 funcs = state->getTransfer();
4227 if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) {
4228 if (level >= psLevel2) {
4229 for (i = 0; i < 4; ++i) {
4230 cvtFunction(funcs[i]);
4231 }
4232 writePS("setcolortransfer\n");
4233 } else {
4234 cvtFunction(funcs[3]);
4235 writePS("settransfer\n");
4236 }
4237 } else if (funcs[0]) {
4238 cvtFunction(funcs[0]);
4239 writePS("settransfer\n");
4240 } else {
4241 writePS("{} settransfer\n");
4242 }
4243 }
4244
updateFont(GfxState * state)4245 void PSOutputDev::updateFont(GfxState *state)
4246 {
4247 if (state->getFont()) {
4248 writePSFmt("/F{0:d}_{1:d} {2:.6g} Tf\n", state->getFont()->getID()->num, state->getFont()->getID()->gen, fabs(state->getFontSize()) < 0.0001 ? 0.0001 : state->getFontSize());
4249 }
4250 }
4251
updateTextMat(GfxState * state)4252 void PSOutputDev::updateTextMat(GfxState *state)
4253 {
4254 const double *mat = state->getTextMat();
4255 if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.00001) {
4256 // avoid a singular (or close-to-singular) matrix
4257 writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", mat[4], mat[5]);
4258 } else {
4259 writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] Tm\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
4260 }
4261 }
4262
updateCharSpace(GfxState * state)4263 void PSOutputDev::updateCharSpace(GfxState *state)
4264 {
4265 writePSFmt("{0:.6g} Tc\n", state->getCharSpace());
4266 }
4267
updateRender(GfxState * state)4268 void PSOutputDev::updateRender(GfxState *state)
4269 {
4270 int rm;
4271
4272 rm = state->getRender();
4273 writePSFmt("{0:d} Tr\n", rm);
4274 rm &= 3;
4275 if (rm != 0 && rm != 3) {
4276 t3Cacheable = false;
4277 }
4278 }
4279
updateRise(GfxState * state)4280 void PSOutputDev::updateRise(GfxState *state)
4281 {
4282 writePSFmt("{0:.6g} Ts\n", state->getRise());
4283 }
4284
updateWordSpace(GfxState * state)4285 void PSOutputDev::updateWordSpace(GfxState *state)
4286 {
4287 writePSFmt("{0:.6g} Tw\n", state->getWordSpace());
4288 }
4289
updateHorizScaling(GfxState * state)4290 void PSOutputDev::updateHorizScaling(GfxState *state)
4291 {
4292 double h;
4293
4294 h = state->getHorizScaling();
4295 if (fabs(h) < 0.01) {
4296 h = 0.01;
4297 }
4298 writePSFmt("{0:.6g} Tz\n", h);
4299 }
4300
updateTextPos(GfxState * state)4301 void PSOutputDev::updateTextPos(GfxState *state)
4302 {
4303 writePSFmt("{0:.6g} {1:.6g} Td\n", state->getLineX(), state->getLineY());
4304 }
4305
updateTextShift(GfxState * state,double shift)4306 void PSOutputDev::updateTextShift(GfxState *state, double shift)
4307 {
4308 if (state->getFont()->getWMode()) {
4309 writePSFmt("{0:.6g} TJmV\n", shift);
4310 } else {
4311 writePSFmt("{0:.6g} TJm\n", shift);
4312 }
4313 }
4314
saveTextPos(GfxState * state)4315 void PSOutputDev::saveTextPos(GfxState *state)
4316 {
4317 writePS("currentpoint\n");
4318 }
4319
restoreTextPos(GfxState * state)4320 void PSOutputDev::restoreTextPos(GfxState *state)
4321 {
4322 writePS("m\n");
4323 }
4324
stroke(GfxState * state)4325 void PSOutputDev::stroke(GfxState *state)
4326 {
4327 doPath(state->getPath());
4328 if (inType3Char && t3FillColorOnly) {
4329 // if we're constructing a cacheable Type 3 glyph, we need to do
4330 // everything in the fill color
4331 writePS("Sf\n");
4332 } else {
4333 writePS("S\n");
4334 }
4335 }
4336
fill(GfxState * state)4337 void PSOutputDev::fill(GfxState *state)
4338 {
4339 doPath(state->getPath());
4340 writePS("f\n");
4341 }
4342
eoFill(GfxState * state)4343 void PSOutputDev::eoFill(GfxState *state)
4344 {
4345 doPath(state->getPath());
4346 writePS("f*\n");
4347 }
4348
tilingPatternFillL1(GfxState * state,Catalog * cat,Object * str,const double * pmat,int paintType,int tilingType,Dict * resDict,const double * mat,const double * bbox,int x0,int y0,int x1,int y1,double xStep,double yStep)4349 bool PSOutputDev::tilingPatternFillL1(GfxState *state, Catalog *cat, Object *str, const double *pmat, int paintType, int tilingType, Dict *resDict, const double *mat, const double *bbox, int x0, int y0, int x1, int y1, double xStep,
4350 double yStep)
4351 {
4352 PDFRectangle box;
4353 Gfx *gfx;
4354
4355 // define a Type 3 font
4356 writePS("8 dict begin\n");
4357 writePS("/FontType 3 def\n");
4358 writePS("/FontMatrix [1 0 0 1 0 0] def\n");
4359 writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", bbox[0], bbox[1], bbox[2], bbox[3]);
4360 writePS("/Encoding 256 array def\n");
4361 writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
4362 writePS(" Encoding 120 /x put\n");
4363 writePS("/BuildGlyph {\n");
4364 writePS(" exch /CharProcs get exch\n");
4365 writePS(" 2 copy known not { pop /.notdef } if\n");
4366 writePS(" get exec\n");
4367 writePS("} bind def\n");
4368 writePS("/BuildChar {\n");
4369 writePS(" 1 index /Encoding get exch get\n");
4370 writePS(" 1 index /BuildGlyph get exec\n");
4371 writePS("} bind def\n");
4372 writePS("/CharProcs 1 dict def\n");
4373 writePS("CharProcs begin\n");
4374 box.x1 = bbox[0];
4375 box.y1 = bbox[1];
4376 box.x2 = bbox[2];
4377 box.y2 = bbox[3];
4378 gfx = new Gfx(doc, this, resDict, &box, nullptr);
4379 writePS("/x {\n");
4380 if (paintType == 2) {
4381 writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n", xStep, bbox[0], bbox[1], bbox[2], bbox[3]);
4382 t3FillColorOnly = true;
4383 } else {
4384 if (x1 - 1 <= x0) {
4385 writePS("1 0 setcharwidth\n");
4386 } else {
4387 writePSFmt("{0:.6g} 0 setcharwidth\n", xStep);
4388 }
4389 t3FillColorOnly = false;
4390 }
4391 inType3Char = true;
4392 if (paintType == 2) {
4393 inUncoloredPattern = true;
4394 // ensure any PS procedures that contain sCol or fCol do not change the color
4395 writePS("/pdfLastFill true def\n");
4396 writePS("/pdfLastStroke true def\n");
4397 }
4398 ++numTilingPatterns;
4399 gfx->display(str);
4400 --numTilingPatterns;
4401 if (paintType == 2) {
4402 inUncoloredPattern = false;
4403 // ensure the next PS procedures that uses sCol or fCol will update the color
4404 writePS("/pdfLastFill false def\n");
4405 writePS("/pdfLastStroke false def\n");
4406 }
4407 inType3Char = false;
4408 writePS("} def\n");
4409 delete gfx;
4410 writePS("end\n");
4411 writePS("currentdict end\n");
4412 writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns);
4413
4414 // draw the tiles
4415 writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns);
4416 writePS("fCol\n");
4417 writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
4418 writePSFmt("{0:d} 1 {1:d} {{ {2:.6g} exch {3:.6g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n", y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1);
4419 writePS("grestore\n");
4420
4421 return true;
4422 }
4423
tilingPatternFillL2(GfxState * state,Catalog * cat,Object * str,const double * pmat,int paintType,int tilingType,Dict * resDict,const double * mat,const double * bbox,int x0,int y0,int x1,int y1,double xStep,double yStep)4424 bool PSOutputDev::tilingPatternFillL2(GfxState *state, Catalog *cat, Object *str, const double *pmat, int paintType, int tilingType, Dict *resDict, const double *mat, const double *bbox, int x0, int y0, int x1, int y1, double xStep,
4425 double yStep)
4426 {
4427 PDFRectangle box;
4428 Gfx *gfx;
4429
4430 if (paintType == 2) {
4431 // setpattern with PaintType 2 needs the paint color
4432 writePS("currentcolor\n");
4433 }
4434 writePS("<<\n /PatternType 1\n");
4435 writePSFmt(" /PaintType {0:d}\n", paintType);
4436 writePSFmt(" /TilingType {0:d}\n", tilingType);
4437 writePSFmt(" /BBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}]\n", bbox[0], bbox[1], bbox[2], bbox[3]);
4438 writePSFmt(" /XStep {0:.6g}\n", xStep);
4439 writePSFmt(" /YStep {0:.6g}\n", yStep);
4440 writePS(" /PaintProc { \n");
4441 box.x1 = bbox[0];
4442 box.y1 = bbox[1];
4443 box.x2 = bbox[2];
4444 box.y2 = bbox[3];
4445 gfx = new Gfx(doc, this, resDict, &box, nullptr);
4446 inType3Char = true;
4447 if (paintType == 2) {
4448 inUncoloredPattern = true;
4449 // ensure any PS procedures that contain sCol or fCol do not change the color
4450 writePS("/pdfLastFill true def\n");
4451 writePS("/pdfLastStroke true def\n");
4452 }
4453 gfx->display(str);
4454 if (paintType == 2) {
4455 inUncoloredPattern = false;
4456 // ensure the next PS procedures that uses sCol or fCol will update the color
4457 writePS("/pdfLastFill false def\n");
4458 writePS("/pdfLastStroke false def\n");
4459 }
4460 inType3Char = false;
4461 delete gfx;
4462 writePS(" }\n");
4463 writePS(">>\n");
4464 writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}]\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
4465 writePS("makepattern setpattern\n");
4466 writePS("clippath fill\n"); // Gfx sets up a clip before calling out->tilingPatternFill()
4467
4468 return true;
4469 }
4470
tilingPatternFill(GfxState * state,Gfx * gfxA,Catalog * cat,GfxTilingPattern * tPat,const double * mat,int x0,int y0,int x1,int y1,double xStep,double yStep)4471 bool PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *cat, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep)
4472 {
4473 std::set<int>::iterator patternRefIt;
4474 const int patternRefNum = tPat->getPatternRefNum();
4475 if (patternRefNum != -1) {
4476 if (patternsBeingTiled.find(patternRefNum) == patternsBeingTiled.end()) {
4477 patternRefIt = patternsBeingTiled.insert(patternRefNum).first;
4478 } else {
4479 // pretend we drew it anyway
4480 error(errSyntaxError, -1, "Loop in pattern fills");
4481 return true;
4482 }
4483 }
4484
4485 const double *bbox = tPat->getBBox();
4486 const double *pmat = tPat->getMatrix();
4487 const int paintType = tPat->getPaintType();
4488 const int tilingType = tPat->getTilingType();
4489 Dict *resDict = tPat->getResDict();
4490 Object *str = tPat->getContentStream();
4491
4492 bool res;
4493 if (x1 - x0 == 1 && y1 - y0 == 1) {
4494 // Don't need to use patterns if only one instance of the pattern is used
4495 PDFRectangle box;
4496 Gfx *gfx;
4497
4498 const double singleStep_x = x0 * xStep;
4499 const double singleStep_y = y0 * yStep;
4500 const double singleStep_tx = singleStep_x * mat[0] + singleStep_y * mat[2] + mat[4];
4501 const double singleStep_ty = singleStep_x * mat[1] + singleStep_y * mat[3] + mat[5];
4502 box.x1 = bbox[0];
4503 box.y1 = bbox[1];
4504 box.x2 = bbox[2];
4505 box.y2 = bbox[3];
4506 gfx = new Gfx(doc, this, resDict, &box, nullptr, nullptr, nullptr, gfxA);
4507 writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n", mat[0], mat[1], mat[2], mat[3], singleStep_tx, singleStep_ty);
4508 inType3Char = true;
4509 gfx->display(str);
4510 inType3Char = false;
4511 delete gfx;
4512 res = true;
4513 } else if (level == psLevel1 || level == psLevel1Sep) {
4514 res = tilingPatternFillL1(state, cat, str, pmat, paintType, tilingType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
4515 } else {
4516 res = tilingPatternFillL2(state, cat, str, pmat, paintType, tilingType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
4517 }
4518
4519 if (patternRefNum != -1) {
4520 patternsBeingTiled.erase(patternRefIt);
4521 }
4522
4523 return res;
4524 }
4525
functionShadedFill(GfxState * state,GfxFunctionShading * shading)4526 bool PSOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
4527 {
4528 double x0, y0, x1, y1;
4529 int i;
4530
4531 if (level == psLevel2Sep || level == psLevel3Sep) {
4532 if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
4533 return false;
4534 }
4535 processColors |= psProcessCMYK;
4536 }
4537
4538 shading->getDomain(&x0, &y0, &x1, &y1);
4539 const double *mat = shading->getMatrix();
4540 writePSFmt("/mat [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
4541 writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
4542 if (shading->getNFuncs() == 1) {
4543 writePS("/func ");
4544 cvtFunction(shading->getFunc(0));
4545 writePS("def\n");
4546 } else {
4547 writePS("/func {\n");
4548 for (i = 0; i < shading->getNFuncs(); ++i) {
4549 if (i < shading->getNFuncs() - 1) {
4550 writePS("2 copy\n");
4551 }
4552 cvtFunction(shading->getFunc(i));
4553 writePS("exec\n");
4554 if (i < shading->getNFuncs() - 1) {
4555 writePS("3 1 roll\n");
4556 }
4557 }
4558 writePS("} def\n");
4559 }
4560 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} 0 funcSH\n", x0, y0, x1, y1);
4561
4562 return true;
4563 }
4564
axialShadedFill(GfxState * state,GfxAxialShading * shading,double,double)4565 bool PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double /*tMin*/, double /*tMax*/)
4566 {
4567 double xMin, yMin, xMax, yMax;
4568 double x0, y0, x1, y1, dx, dy, mul;
4569 double tMin, tMax, t, t0, t1;
4570 int i;
4571
4572 if (level == psLevel2Sep || level == psLevel3Sep) {
4573 if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
4574 return false;
4575 }
4576 processColors |= psProcessCMYK;
4577 }
4578
4579 // get the clip region bbox
4580 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
4581
4582 // compute min and max t values, based on the four corners of the
4583 // clip region bbox
4584 shading->getCoords(&x0, &y0, &x1, &y1);
4585 dx = x1 - x0;
4586 dy = y1 - y0;
4587 if (fabs(dx) < 0.01 && fabs(dy) < 0.01) {
4588 return true;
4589 } else {
4590 mul = 1 / (dx * dx + dy * dy);
4591 tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
4592 t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
4593 if (t < tMin) {
4594 tMin = t;
4595 } else if (t > tMax) {
4596 tMax = t;
4597 }
4598 t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
4599 if (t < tMin) {
4600 tMin = t;
4601 } else if (t > tMax) {
4602 tMax = t;
4603 }
4604 t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
4605 if (t < tMin) {
4606 tMin = t;
4607 } else if (t > tMax) {
4608 tMax = t;
4609 }
4610 if (tMin < 0 && !shading->getExtend0()) {
4611 tMin = 0;
4612 }
4613 if (tMax > 1 && !shading->getExtend1()) {
4614 tMax = 1;
4615 }
4616 }
4617
4618 // get the function domain
4619 t0 = shading->getDomain0();
4620 t1 = shading->getDomain1();
4621
4622 // generate the PS code
4623 writePSFmt("/t0 {0:.6g} def\n", t0);
4624 writePSFmt("/t1 {0:.6g} def\n", t1);
4625 writePSFmt("/dt {0:.6g} def\n", t1 - t0);
4626 writePSFmt("/x0 {0:.6g} def\n", x0);
4627 writePSFmt("/y0 {0:.6g} def\n", y0);
4628 writePSFmt("/dx {0:.6g} def\n", x1 - x0);
4629 writePSFmt("/x1 {0:.6g} def\n", x1);
4630 writePSFmt("/y1 {0:.6g} def\n", y1);
4631 writePSFmt("/dy {0:.6g} def\n", y1 - y0);
4632 writePSFmt("/xMin {0:.6g} def\n", xMin);
4633 writePSFmt("/yMin {0:.6g} def\n", yMin);
4634 writePSFmt("/xMax {0:.6g} def\n", xMax);
4635 writePSFmt("/yMax {0:.6g} def\n", yMax);
4636 writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
4637 if (shading->getNFuncs() == 1) {
4638 writePS("/func ");
4639 cvtFunction(shading->getFunc(0));
4640 writePS("def\n");
4641 } else {
4642 writePS("/func {\n");
4643 for (i = 0; i < shading->getNFuncs(); ++i) {
4644 if (i < shading->getNFuncs() - 1) {
4645 writePS("dup\n");
4646 }
4647 cvtFunction(shading->getFunc(i));
4648 writePS("exec\n");
4649 if (i < shading->getNFuncs() - 1) {
4650 writePS("exch\n");
4651 }
4652 }
4653 writePS("} def\n");
4654 }
4655 writePSFmt("{0:.6g} {1:.6g} 0 axialSH\n", tMin, tMax);
4656
4657 return true;
4658 }
4659
radialShadedFill(GfxState * state,GfxRadialShading * shading,double,double)4660 bool PSOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double /*sMin*/, double /*sMax*/)
4661 {
4662 double xMin, yMin, xMax, yMax;
4663 double x0, y0, r0, x1, y1, r1, t0, t1;
4664 double xa, ya, ra;
4665 double sMin, sMax, h, ta;
4666 double sLeft, sRight, sTop, sBottom, sZero, sDiag;
4667 bool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero;
4668 bool haveSMin, haveSMax;
4669 double theta, alpha, a1, a2;
4670 bool enclosed;
4671 int i;
4672
4673 if (level == psLevel2Sep || level == psLevel3Sep) {
4674 if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
4675 return false;
4676 }
4677 processColors |= psProcessCMYK;
4678 }
4679
4680 // get the shading info
4681 shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
4682 t0 = shading->getDomain0();
4683 t1 = shading->getDomain1();
4684
4685 // Compute the point at which r(s) = 0; check for the enclosed
4686 // circles case; and compute the angles for the tangent lines.
4687 // Compute the point at which r(s) = 0; check for the enclosed
4688 // circles case; and compute the angles for the tangent lines.
4689 h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
4690 if (h == 0) {
4691 enclosed = true;
4692 theta = 0; // make gcc happy
4693 } else if (r1 - r0 == 0) {
4694 enclosed = false;
4695 theta = 0;
4696 } else if (fabs(r1 - r0) >= h) {
4697 enclosed = true;
4698 theta = 0; // make gcc happy
4699 } else {
4700 enclosed = false;
4701 theta = asin((r1 - r0) / h);
4702 }
4703 if (enclosed) {
4704 a1 = 0;
4705 a2 = 360;
4706 } else {
4707 alpha = atan2(y1 - y0, x1 - x0);
4708 a1 = (180 / M_PI) * (alpha + theta) + 90;
4709 a2 = (180 / M_PI) * (alpha - theta) - 90;
4710 while (a2 < a1) {
4711 a2 += 360;
4712 }
4713 }
4714
4715 // compute the (possibly extended) s range
4716 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
4717 if (enclosed) {
4718 sMin = 0;
4719 sMax = 1;
4720 } else {
4721 // solve x(sLeft) + r(sLeft) = xMin
4722 if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) {
4723 sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
4724 } else {
4725 sLeft = 0; // make gcc happy
4726 }
4727 // solve x(sRight) - r(sRight) = xMax
4728 if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) {
4729 sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
4730 } else {
4731 sRight = 0; // make gcc happy
4732 }
4733 // solve y(sBottom) + r(sBottom) = yMin
4734 if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) {
4735 sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
4736 } else {
4737 sBottom = 0; // make gcc happy
4738 }
4739 // solve y(sTop) - r(sTop) = yMax
4740 if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) {
4741 sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
4742 } else {
4743 sTop = 0; // make gcc happy
4744 }
4745 // solve r(sZero) = 0
4746 if ((haveSZero = fabs(r1 - r0) > 0.000001)) {
4747 sZero = -r0 / (r1 - r0);
4748 } else {
4749 sZero = 0; // make gcc happy
4750 }
4751 // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2)
4752 if (haveSZero) {
4753 sDiag = (sqrt((xMax - xMin) * (xMax - xMin) + (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
4754 } else {
4755 sDiag = 0; // make gcc happy
4756 }
4757 // compute sMin
4758 if (shading->getExtend0()) {
4759 sMin = 0;
4760 haveSMin = false;
4761 if (x0 < x1 && haveSLeft && sLeft < 0) {
4762 sMin = sLeft;
4763 haveSMin = true;
4764 } else if (x0 > x1 && haveSRight && sRight < 0) {
4765 sMin = sRight;
4766 haveSMin = true;
4767 }
4768 if (y0 < y1 && haveSBottom && sBottom < 0) {
4769 if (!haveSMin || sBottom > sMin) {
4770 sMin = sBottom;
4771 haveSMin = true;
4772 }
4773 } else if (y0 > y1 && haveSTop && sTop < 0) {
4774 if (!haveSMin || sTop > sMin) {
4775 sMin = sTop;
4776 haveSMin = true;
4777 }
4778 }
4779 if (haveSZero && sZero < 0) {
4780 if (!haveSMin || sZero > sMin) {
4781 sMin = sZero;
4782 }
4783 }
4784 } else {
4785 sMin = 0;
4786 }
4787 // compute sMax
4788 if (shading->getExtend1()) {
4789 sMax = 1;
4790 haveSMax = false;
4791 if (x1 < x0 && haveSLeft && sLeft > 1) {
4792 sMax = sLeft;
4793 haveSMax = true;
4794 } else if (x1 > x0 && haveSRight && sRight > 1) {
4795 sMax = sRight;
4796 haveSMax = true;
4797 }
4798 if (y1 < y0 && haveSBottom && sBottom > 1) {
4799 if (!haveSMax || sBottom < sMax) {
4800 sMax = sBottom;
4801 haveSMax = true;
4802 }
4803 } else if (y1 > y0 && haveSTop && sTop > 1) {
4804 if (!haveSMax || sTop < sMax) {
4805 sMax = sTop;
4806 haveSMax = true;
4807 }
4808 }
4809 if (haveSZero && sDiag > 1) {
4810 if (!haveSMax || sDiag < sMax) {
4811 sMax = sDiag;
4812 }
4813 }
4814 } else {
4815 sMax = 1;
4816 }
4817 }
4818
4819 // generate the PS code
4820 writePSFmt("/x0 {0:.6g} def\n", x0);
4821 writePSFmt("/x1 {0:.6g} def\n", x1);
4822 writePSFmt("/dx {0:.6g} def\n", x1 - x0);
4823 writePSFmt("/y0 {0:.6g} def\n", y0);
4824 writePSFmt("/y1 {0:.6g} def\n", y1);
4825 writePSFmt("/dy {0:.6g} def\n", y1 - y0);
4826 writePSFmt("/r0 {0:.6g} def\n", r0);
4827 writePSFmt("/r1 {0:.6g} def\n", r1);
4828 writePSFmt("/dr {0:.6g} def\n", r1 - r0);
4829 writePSFmt("/t0 {0:.6g} def\n", t0);
4830 writePSFmt("/t1 {0:.6g} def\n", t1);
4831 writePSFmt("/dt {0:.6g} def\n", t1 - t0);
4832 writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
4833 writePSFmt("/encl {0:s} def\n", enclosed ? "true" : "false");
4834 writePSFmt("/a1 {0:.6g} def\n", a1);
4835 writePSFmt("/a2 {0:.6g} def\n", a2);
4836 if (shading->getNFuncs() == 1) {
4837 writePS("/func ");
4838 cvtFunction(shading->getFunc(0));
4839 writePS("def\n");
4840 } else {
4841 writePS("/func {\n");
4842 for (i = 0; i < shading->getNFuncs(); ++i) {
4843 if (i < shading->getNFuncs() - 1) {
4844 writePS("dup\n");
4845 }
4846 cvtFunction(shading->getFunc(i));
4847 writePS("exec\n");
4848 if (i < shading->getNFuncs() - 1) {
4849 writePS("exch\n");
4850 }
4851 }
4852 writePS("} def\n");
4853 }
4854 writePSFmt("{0:.6g} {1:.6g} 0 radialSH\n", sMin, sMax);
4855
4856 // extend the 'enclosed' case
4857 if (enclosed) {
4858 // extend the smaller circle
4859 if ((shading->getExtend0() && r0 <= r1) || (shading->getExtend1() && r1 < r0)) {
4860 if (r0 <= r1) {
4861 ta = t0;
4862 ra = r0;
4863 xa = x0;
4864 ya = y0;
4865 } else {
4866 ta = t1;
4867 ra = r1;
4868 xa = x1;
4869 ya = y1;
4870 }
4871 if (level == psLevel2Sep || level == psLevel3Sep) {
4872 writePSFmt("{0:.6g} radialCol aload pop k\n", ta);
4873 } else {
4874 writePSFmt("{0:.6g} radialCol sc\n", ta);
4875 }
4876 writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h f*\n", xa, ya, ra);
4877 }
4878
4879 // extend the larger circle
4880 if ((shading->getExtend0() && r0 > r1) || (shading->getExtend1() && r1 >= r0)) {
4881 if (r0 > r1) {
4882 ta = t0;
4883 ra = r0;
4884 xa = x0;
4885 ya = y0;
4886 } else {
4887 ta = t1;
4888 ra = r1;
4889 xa = x1;
4890 ya = y1;
4891 }
4892 if (level == psLevel2Sep || level == psLevel3Sep) {
4893 writePSFmt("{0:.6g} radialCol aload pop k\n", ta);
4894 } else {
4895 writePSFmt("{0:.6g} radialCol sc\n", ta);
4896 }
4897 writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h\n", xa, ya, ra);
4898 writePSFmt("{0:.6g} {1:.6g} m {2:.6g} {3:.6g} l {4:.6g} {5:.6g} l {6:.6g} {7:.6g} l h f*\n", xMin, yMin, xMin, yMax, xMax, yMax, xMax, yMin);
4899 }
4900 }
4901
4902 return true;
4903 }
4904
patchMeshShadedFill(GfxState * state,GfxPatchMeshShading * shading)4905 bool PSOutputDev::patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading)
4906 {
4907 // TODO: support parametrized shading
4908 if (level < psLevel3 || shading->isParameterized()) {
4909 return false;
4910 }
4911
4912 writePS("%% Begin patchMeshShadedFill\n");
4913
4914 // ShadingType 7 shadings are pretty much the same for pdf and ps.
4915 // As such, we basically just need to invert GfxPatchMeshShading::parse here.
4916
4917 writePS("<<\n");
4918 writePS(" /ShadingType 7\n");
4919 writePS(" /ColorSpace ");
4920 dumpColorSpaceL2(state, shading->getColorSpace(), false, false, false);
4921 writePS("\n");
4922 writePS(" /DataSource [\n");
4923
4924 const int ncomps = shading->getColorSpace()->getNComps();
4925
4926 for (int i = 0; i < shading->getNPatches(); ++i) {
4927 const auto &patch = *shading->getPatch(i);
4928 // Print Flag, for us always f = 0
4929 writePS(" 0 \n");
4930
4931 // Print coordinates
4932 const std::array<std::pair<int, int>, 16> coordindices = { { { 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 3, 2 }, { 3, 1 }, { 3, 0 }, { 2, 0 }, { 1, 0 }, { 1, 1 }, { 1, 2 }, { 2, 2 }, { 2, 1 } } };
4933 for (const auto &index : coordindices) {
4934 writePSFmt(" {0:.6g} {1:.6g}\n", patch.x[index.first][index.second], patch.y[index.first][index.second]);
4935 }
4936
4937 // Print colors
4938 const std::array<std::pair<int, int>, 4> colindices = { { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 1, 0 } } };
4939 for (const auto &index : colindices) {
4940 writePS(" ");
4941 for (int comp = 0; comp < ncomps; ++comp) {
4942 writePSFmt(" {0:.6g}", colToDbl(patch.color[index.first][index.second].c[comp]));
4943 }
4944 writePS("\n");
4945 }
4946 }
4947
4948 writePS(" ]\n");
4949
4950 writePS(">> shfill\n");
4951 writePS("%% End patchMeshShadedFill\n");
4952 return true;
4953 }
4954
clip(GfxState * state)4955 void PSOutputDev::clip(GfxState *state)
4956 {
4957 doPath(state->getPath());
4958 writePS("W\n");
4959 }
4960
eoClip(GfxState * state)4961 void PSOutputDev::eoClip(GfxState *state)
4962 {
4963 doPath(state->getPath());
4964 writePS("W*\n");
4965 }
4966
clipToStrokePath(GfxState * state)4967 void PSOutputDev::clipToStrokePath(GfxState *state)
4968 {
4969 doPath(state->getPath());
4970 writePS("Ws\n");
4971 }
4972
doPath(const GfxPath * path)4973 void PSOutputDev::doPath(const GfxPath *path)
4974 {
4975 double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
4976 int n, m, i, j;
4977
4978 n = path->getNumSubpaths();
4979
4980 if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
4981 const GfxSubpath *subpath = path->getSubpath(0);
4982 x0 = subpath->getX(0);
4983 y0 = subpath->getY(0);
4984 x4 = subpath->getX(4);
4985 y4 = subpath->getY(4);
4986 if (x4 == x0 && y4 == y0) {
4987 x1 = subpath->getX(1);
4988 y1 = subpath->getY(1);
4989 x2 = subpath->getX(2);
4990 y2 = subpath->getY(2);
4991 x3 = subpath->getX(3);
4992 y3 = subpath->getY(3);
4993 if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
4994 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1, fabs(x2 - x0), fabs(y1 - y0));
4995 return;
4996 } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
4997 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2, fabs(x1 - x0), fabs(y2 - y0));
4998 return;
4999 }
5000 }
5001 }
5002
5003 for (i = 0; i < n; ++i) {
5004 const GfxSubpath *subpath = path->getSubpath(i);
5005 m = subpath->getNumPoints();
5006 writePSFmt("{0:.6g} {1:.6g} m\n", subpath->getX(0), subpath->getY(0));
5007 j = 1;
5008 while (j < m) {
5009 if (subpath->getCurve(j)) {
5010 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} c\n", subpath->getX(j), subpath->getY(j), subpath->getX(j + 1), subpath->getY(j + 1), subpath->getX(j + 2), subpath->getY(j + 2));
5011 j += 3;
5012 } else {
5013 writePSFmt("{0:.6g} {1:.6g} l\n", subpath->getX(j), subpath->getY(j));
5014 ++j;
5015 }
5016 }
5017 if (subpath->isClosed()) {
5018 writePS("h\n");
5019 }
5020 }
5021 }
5022
drawString(GfxState * state,const GooString * s)5023 void PSOutputDev::drawString(GfxState *state, const GooString *s)
5024 {
5025 GfxFont *font;
5026 int wMode;
5027 int *codeToGID;
5028 GooString *s2;
5029 double dx, dy, originX, originY;
5030 const char *p;
5031 const UnicodeMap *uMap;
5032 CharCode code;
5033 const Unicode *u;
5034 char buf[8];
5035 double *dxdy;
5036 int dxdySize, len, nChars, uLen, n, m, i, j;
5037 int maxGlyphInt;
5038 CharCode maxGlyph;
5039
5040 // for pdftohtml, output PS without text
5041 if (displayText == false)
5042 return;
5043
5044 // check for invisible text -- this is used by Acrobat Capture
5045 if (state->getRender() == 3) {
5046 return;
5047 }
5048
5049 // ignore empty strings
5050 if (s->getLength() == 0) {
5051 return;
5052 }
5053
5054 // get the font
5055 if (!(font = state->getFont())) {
5056 return;
5057 }
5058 maxGlyphInt = (font->getName() ? perFontMaxValidGlyph[font->getName()->toStr()] : 0);
5059 if (maxGlyphInt < 0)
5060 maxGlyphInt = 0;
5061 maxGlyph = (CharCode)maxGlyphInt;
5062 wMode = font->getWMode();
5063
5064 // check for a subtitute 16-bit font
5065 uMap = nullptr;
5066 codeToGID = nullptr;
5067 if (font->isCIDFont()) {
5068 for (i = 0; i < font16EncLen; ++i) {
5069 if (*font->getID() == font16Enc[i].fontID) {
5070 if (!font16Enc[i].enc) {
5071 // font substitution failed, so don't output any text
5072 return;
5073 }
5074 uMap = globalParams->getUnicodeMap(font16Enc[i].enc->toStr());
5075 break;
5076 }
5077 }
5078
5079 // check for a code-to-GID map
5080 } else {
5081 for (i = 0; i < font8InfoLen; ++i) {
5082 if (*font->getID() == font8Info[i].fontID) {
5083 codeToGID = font8Info[i].codeToGID;
5084 break;
5085 }
5086 }
5087 }
5088
5089 // compute the positioning (dx, dy) for each char in the string
5090 nChars = 0;
5091 p = s->c_str();
5092 len = s->getLength();
5093 s2 = new GooString();
5094 dxdySize = font->isCIDFont() ? 8 : s->getLength();
5095 dxdy = (double *)gmallocn(2 * dxdySize, sizeof(double));
5096 while (len > 0) {
5097 n = font->getNextChar(p, len, &code, &u, &uLen, &dx, &dy, &originX, &originY);
5098 dx *= state->getFontSize();
5099 dy *= state->getFontSize();
5100 if (wMode) {
5101 dy += state->getCharSpace();
5102 if (n == 1 && *p == ' ') {
5103 dy += state->getWordSpace();
5104 }
5105 } else {
5106 dx += state->getCharSpace();
5107 if (n == 1 && *p == ' ') {
5108 dx += state->getWordSpace();
5109 }
5110 }
5111 dx *= state->getHorizScaling();
5112 if (font->isCIDFont()) {
5113 if (uMap) {
5114 if (nChars + uLen > dxdySize) {
5115 do {
5116 dxdySize *= 2;
5117 } while (nChars + uLen > dxdySize);
5118 dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double));
5119 }
5120 for (i = 0; i < uLen; ++i) {
5121 m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf));
5122 for (j = 0; j < m; ++j) {
5123 s2->append(buf[j]);
5124 }
5125 //~ this really needs to get the number of chars in the target
5126 //~ encoding - which may be more than the number of Unicode
5127 //~ chars
5128 dxdy[2 * nChars] = dx;
5129 dxdy[2 * nChars + 1] = dy;
5130 ++nChars;
5131 }
5132 } else if (maxGlyph > 0 && code > maxGlyph) {
5133 // Ignore this code.
5134 // Using it will exceed the number of glyphs in the font and generate
5135 // /rangecheck in --xyshow--
5136 if (nChars > 0) {
5137 dxdy[2 * (nChars - 1)] += dx;
5138 dxdy[2 * (nChars - 1) + 1] += dy;
5139 }
5140 } else {
5141 if (nChars + 1 > dxdySize) {
5142 dxdySize *= 2;
5143 dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double));
5144 }
5145 s2->append((char)((code >> 8) & 0xff));
5146 s2->append((char)(code & 0xff));
5147 dxdy[2 * nChars] = dx;
5148 dxdy[2 * nChars + 1] = dy;
5149 ++nChars;
5150 }
5151 } else {
5152 if (!codeToGID || codeToGID[code] >= 0) {
5153 s2->append((char)code);
5154 dxdy[2 * nChars] = dx;
5155 dxdy[2 * nChars + 1] = dy;
5156 ++nChars;
5157 }
5158 }
5159 p += n;
5160 len -= n;
5161 }
5162
5163 if (nChars > 0) {
5164 writePSString(s2->toStr());
5165 writePS("\n[");
5166 for (i = 0; i < 2 * nChars; ++i) {
5167 if (i > 0) {
5168 writePS("\n");
5169 }
5170 writePSFmt("{0:.6g}", dxdy[i]);
5171 }
5172 writePS("] Tj\n");
5173 }
5174 gfree(dxdy);
5175 delete s2;
5176
5177 if (state->getRender() & 4) {
5178 haveTextClip = true;
5179 }
5180 }
5181
beginTextObject(GfxState * state)5182 void PSOutputDev::beginTextObject(GfxState *state) { }
5183
endTextObject(GfxState * state)5184 void PSOutputDev::endTextObject(GfxState *state)
5185 {
5186 if (haveTextClip) {
5187 writePS("Tclip\n");
5188 haveTextClip = false;
5189 }
5190 }
5191
drawImageMask(GfxState * state,Object * ref,Stream * str,int width,int height,bool invert,bool interpolate,bool inlineImg)5192 void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg)
5193 {
5194 int len;
5195
5196 len = height * ((width + 7) / 8);
5197 switch (level) {
5198 case psLevel1:
5199 case psLevel1Sep:
5200 doImageL1(ref, nullptr, invert, inlineImg, str, width, height, len, nullptr, nullptr, 0, 0, false);
5201 break;
5202 case psLevel2:
5203 case psLevel2Sep:
5204 doImageL2(state, ref, nullptr, invert, inlineImg, str, width, height, len, nullptr, nullptr, 0, 0, false);
5205 break;
5206 case psLevel3:
5207 case psLevel3Sep:
5208 doImageL3(state, ref, nullptr, invert, inlineImg, str, width, height, len, nullptr, nullptr, 0, 0, false);
5209 break;
5210 }
5211 }
5212
setSoftMaskFromImageMask(GfxState * state,Object * ref,Stream * str,int width,int height,bool invert,bool inlineImg,double * baseMatrix)5213 void PSOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg, double *baseMatrix)
5214 {
5215 if (level != psLevel1 && level != psLevel1Sep) {
5216 maskToClippingPath(str, width, height, invert);
5217 }
5218 }
5219
unsetSoftMaskFromImageMask(GfxState * state,double * baseMatrix)5220 void PSOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix)
5221 {
5222 if (level != psLevel1 && level != psLevel1Sep) {
5223 writePS("pdfImClipEnd\n");
5224 }
5225 }
5226
drawImage(GfxState * state,Object * ref,Stream * str,int width,int height,GfxImageColorMap * colorMap,bool interpolate,const int * maskColors,bool inlineImg)5227 void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg)
5228 {
5229 int len;
5230
5231 len = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8);
5232 switch (level) {
5233 case psLevel1:
5234 doImageL1(ref, colorMap, false, inlineImg, str, width, height, len, maskColors, nullptr, 0, 0, false);
5235 break;
5236 case psLevel1Sep:
5237 //~ handle indexed, separation, ... color spaces
5238 doImageL1Sep(ref, colorMap, false, inlineImg, str, width, height, len, maskColors, nullptr, 0, 0, false);
5239 break;
5240 case psLevel2:
5241 case psLevel2Sep:
5242 doImageL2(state, ref, colorMap, false, inlineImg, str, width, height, len, maskColors, nullptr, 0, 0, false);
5243 break;
5244 case psLevel3:
5245 case psLevel3Sep:
5246 doImageL3(state, ref, colorMap, false, inlineImg, str, width, height, len, maskColors, nullptr, 0, 0, false);
5247 break;
5248 }
5249 t3Cacheable = false;
5250 }
5251
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)5252 void PSOutputDev::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)
5253 {
5254 int len;
5255
5256 len = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8);
5257 switch (level) {
5258 case psLevel1:
5259 doImageL1(ref, colorMap, false, false, str, width, height, len, nullptr, maskStr, maskWidth, maskHeight, maskInvert);
5260 break;
5261 case psLevel1Sep:
5262 //~ handle indexed, separation, ... color spaces
5263 doImageL1Sep(ref, colorMap, false, false, str, width, height, len, nullptr, maskStr, maskWidth, maskHeight, maskInvert);
5264 break;
5265 case psLevel2:
5266 case psLevel2Sep:
5267 doImageL2(state, ref, colorMap, false, false, str, width, height, len, nullptr, maskStr, maskWidth, maskHeight, maskInvert);
5268 break;
5269 case psLevel3:
5270 case psLevel3Sep:
5271 doImageL3(state, ref, colorMap, false, false, str, width, height, len, nullptr, maskStr, maskWidth, maskHeight, maskInvert);
5272 break;
5273 }
5274 t3Cacheable = false;
5275 }
5276
doImageL1(Object * ref,GfxImageColorMap * colorMap,bool invert,bool inlineImg,Stream * str,int width,int height,int len,const int * maskColors,Stream * maskStr,int maskWidth,int maskHeight,bool maskInvert)5277 void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap, bool invert, bool inlineImg, Stream *str, int width, int height, int len, const int *maskColors, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert)
5278 {
5279 ImageStream *imgStr;
5280 unsigned char pixBuf[gfxColorMaxComps];
5281 GfxGray gray;
5282 int col, x, y, c, i;
5283 char hexBuf[32 * 2 + 2]; // 32 values X 2 chars/value + line ending + null
5284 unsigned char digit, grayValue;
5285
5286 // explicit masking
5287 if (maskStr && !(maskColors && colorMap)) {
5288 maskToClippingPath(maskStr, maskWidth, maskHeight, maskInvert);
5289 }
5290
5291 if ((inType3Char || preloadImagesForms) && !colorMap) {
5292 if (inlineImg) {
5293 // create an array
5294 str = new FixedLengthEncoder(str, len);
5295 str = new ASCIIHexEncoder(str);
5296 str->reset();
5297 col = 0;
5298 writePS("[<");
5299 do {
5300 do {
5301 c = str->getChar();
5302 } while (c == '\n' || c == '\r');
5303 if (c == '>' || c == EOF) {
5304 break;
5305 }
5306 writePSChar(c);
5307 ++col;
5308 // each line is: "<...data...><eol>"
5309 // so max data length = 255 - 4 = 251
5310 // but make it 240 just to be safe
5311 // chunks are 2 bytes each, so we need to stop on an even col number
5312 if (col == 240) {
5313 writePS(">\n<");
5314 col = 0;
5315 }
5316 } while (c != '>' && c != EOF);
5317 writePS(">]\n");
5318 writePS("0\n");
5319 str->close();
5320 delete str;
5321 } else {
5322 // make sure the image is setup, it sometimes is not like on bug #17645
5323 setupImage(ref->getRef(), str, false);
5324 // set up to use the array already created by setupImages()
5325 writePSFmt("ImData_{0:d}_{1:d} 0 0\n", ref->getRefNum(), ref->getRefGen());
5326 }
5327 }
5328
5329 // image/imagemask command
5330 if ((inType3Char || preloadImagesForms) && !colorMap) {
5331 writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1a\n", width, height, invert ? "true" : "false", width, -height, height);
5332 } else if (colorMap) {
5333 writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}\n", width, height, width, -height, height, useBinary ? "Bin" : "");
5334 } else {
5335 writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1{6:s}\n", width, height, invert ? "true" : "false", width, -height, height, useBinary ? "Bin" : "");
5336 }
5337
5338 // image data
5339 if (!((inType3Char || preloadImagesForms) && !colorMap)) {
5340
5341 if (colorMap) {
5342
5343 // set up to process the data stream
5344 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits());
5345 imgStr->reset();
5346
5347 // process the data stream
5348 i = 0;
5349 for (y = 0; y < height; ++y) {
5350
5351 // write the line
5352 for (x = 0; x < width; ++x) {
5353 imgStr->getPixel(pixBuf);
5354 colorMap->getGray(pixBuf, &gray);
5355 grayValue = colToByte(gray);
5356 if (useBinary) {
5357 hexBuf[i++] = grayValue;
5358 } else {
5359 digit = grayValue / 16;
5360 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
5361 digit = grayValue % 16;
5362 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
5363 }
5364 if (i >= 64) {
5365 if (!useBinary) {
5366 hexBuf[i++] = '\n';
5367 }
5368 writePSBuf(hexBuf, i);
5369 i = 0;
5370 }
5371 }
5372 }
5373 if (i != 0) {
5374 if (!useBinary) {
5375 hexBuf[i++] = '\n';
5376 }
5377 writePSBuf(hexBuf, i);
5378 }
5379 str->close();
5380 delete imgStr;
5381
5382 // imagemask
5383 } else {
5384 str->reset();
5385 i = 0;
5386 for (y = 0; y < height; ++y) {
5387 for (x = 0; x < width; x += 8) {
5388 grayValue = str->getChar();
5389 if (useBinary) {
5390 hexBuf[i++] = grayValue;
5391 } else {
5392 digit = grayValue / 16;
5393 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
5394 digit = grayValue % 16;
5395 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
5396 }
5397 if (i >= 64) {
5398 if (!useBinary) {
5399 hexBuf[i++] = '\n';
5400 }
5401 writePSBuf(hexBuf, i);
5402 i = 0;
5403 }
5404 }
5405 }
5406 if (i != 0) {
5407 if (!useBinary) {
5408 hexBuf[i++] = '\n';
5409 }
5410 writePSBuf(hexBuf, i);
5411 }
5412 str->close();
5413 }
5414 }
5415
5416 if (maskStr && !(maskColors && colorMap)) {
5417 writePS("pdfImClipEnd\n");
5418 }
5419 }
5420
doImageL1Sep(Object * ref,GfxImageColorMap * colorMap,bool invert,bool inlineImg,Stream * str,int width,int height,int len,const int * maskColors,Stream * maskStr,int maskWidth,int maskHeight,bool maskInvert)5421 void PSOutputDev::doImageL1Sep(Object *ref, GfxImageColorMap *colorMap, bool invert, bool inlineImg, Stream *str, int width, int height, int len, const int *maskColors, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert)
5422 {
5423 ImageStream *imgStr;
5424 unsigned char *lineBuf;
5425 unsigned char pixBuf[gfxColorMaxComps];
5426 GfxCMYK cmyk;
5427 int x, y, i, comp;
5428 bool checkProcessColor;
5429 char hexBuf[32 * 2 + 2]; // 32 values X 2 chars/value + line ending + null
5430 unsigned char digit;
5431 bool isGray;
5432
5433 // explicit masking
5434 if (maskStr && !(maskColors && colorMap)) {
5435 maskToClippingPath(maskStr, maskWidth, maskHeight, maskInvert);
5436 }
5437
5438 // allocate a line buffer
5439 lineBuf = (unsigned char *)gmallocn(width, 4);
5440
5441 // scan for all gray
5442 if (getOptimizeColorSpace()) {
5443 ImageStream *imgCheckStr;
5444 imgCheckStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits());
5445 imgCheckStr->reset();
5446 isGray = true;
5447 for (y = 0; y < height; ++y) {
5448 for (x = 0; x < width; ++x) {
5449 imgCheckStr->getPixel(pixBuf);
5450 colorMap->getCMYK(pixBuf, &cmyk);
5451 if (colToByte(cmyk.c) != colToByte(cmyk.m) || colToByte(cmyk.c) != colToByte(cmyk.y)) {
5452 isGray = false;
5453 y = height; // end outer loop
5454 break;
5455 }
5456 }
5457 }
5458 imgCheckStr->close();
5459 delete imgCheckStr;
5460 } else {
5461 isGray = false;
5462 }
5463
5464 // set up to process the data stream
5465 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits());
5466 imgStr->reset();
5467
5468 // width, height, matrix, bits per component
5469 writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}{6:s}\n", width, height, width, -height, height, isGray ? "" : "Sep", useBinary ? "Bin" : "");
5470
5471 // process the data stream
5472 checkProcessColor = true;
5473 i = 0;
5474
5475 if (isGray) {
5476 int g;
5477 for (y = 0; y < height; ++y) {
5478
5479 // read the line
5480 if (checkProcessColor) {
5481 checkProcessColor = ((psProcessBlack & processColors) == 0);
5482 }
5483 for (x = 0; x < width; ++x) {
5484 imgStr->getPixel(pixBuf);
5485 colorMap->getCMYK(pixBuf, &cmyk);
5486 g = colToByte(cmyk.c) + colToByte(cmyk.k);
5487 if (checkProcessColor && g > 0) {
5488 processColors |= psProcessBlack;
5489 }
5490 g = 255 - g;
5491 if (g < 0)
5492 g = 0;
5493 if (useBinary) {
5494 hexBuf[i++] = g;
5495 } else {
5496 digit = g / 16;
5497 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
5498 digit = g % 16;
5499 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
5500 }
5501 if (i >= 64) {
5502 if (!useBinary) {
5503 hexBuf[i++] = '\n';
5504 }
5505 writePSBuf(hexBuf, i);
5506 i = 0;
5507 }
5508 }
5509 }
5510 } else {
5511 for (y = 0; y < height; ++y) {
5512
5513 // read the line
5514 if (checkProcessColor) {
5515 checkProcessColor = (((psProcessCyan | psProcessMagenta | psProcessYellow | psProcessBlack) & ~processColors) != 0);
5516 }
5517 if (checkProcessColor) {
5518 for (x = 0; x < width; ++x) {
5519 imgStr->getPixel(pixBuf);
5520 colorMap->getCMYK(pixBuf, &cmyk);
5521 lineBuf[4 * x + 0] = colToByte(cmyk.c);
5522 lineBuf[4 * x + 1] = colToByte(cmyk.m);
5523 lineBuf[4 * x + 2] = colToByte(cmyk.y);
5524 lineBuf[4 * x + 3] = colToByte(cmyk.k);
5525 addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k));
5526 }
5527 } else {
5528 for (x = 0; x < width; ++x) {
5529 imgStr->getPixel(pixBuf);
5530 colorMap->getCMYK(pixBuf, &cmyk);
5531 lineBuf[4 * x + 0] = colToByte(cmyk.c);
5532 lineBuf[4 * x + 1] = colToByte(cmyk.m);
5533 lineBuf[4 * x + 2] = colToByte(cmyk.y);
5534 lineBuf[4 * x + 3] = colToByte(cmyk.k);
5535 }
5536 }
5537
5538 // write one line of each color component
5539 if (useBinary) {
5540 for (comp = 0; comp < 4; ++comp) {
5541 for (x = 0; x < width; ++x) {
5542 hexBuf[i++] = lineBuf[4 * x + comp];
5543 if (i >= 64) {
5544 writePSBuf(hexBuf, i);
5545 i = 0;
5546 }
5547 }
5548 }
5549 } else {
5550 for (comp = 0; comp < 4; ++comp) {
5551 for (x = 0; x < width; ++x) {
5552 digit = lineBuf[4 * x + comp] / 16;
5553 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
5554 digit = lineBuf[4 * x + comp] % 16;
5555 hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
5556 if (i >= 64) {
5557 hexBuf[i++] = '\n';
5558 writePSBuf(hexBuf, i);
5559 i = 0;
5560 }
5561 }
5562 }
5563 }
5564 }
5565 }
5566
5567 if (i != 0) {
5568 if (!useBinary) {
5569 hexBuf[i++] = '\n';
5570 }
5571 writePSBuf(hexBuf, i);
5572 }
5573
5574 str->close();
5575 delete imgStr;
5576 gfree(lineBuf);
5577
5578 if (maskStr && !(maskColors && colorMap)) {
5579 writePS("pdfImClipEnd\n");
5580 }
5581 }
5582
maskToClippingPath(Stream * maskStr,int maskWidth,int maskHeight,bool maskInvert)5583 void PSOutputDev::maskToClippingPath(Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert)
5584 {
5585 ImageStream *imgStr;
5586 unsigned char *line;
5587 PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut;
5588 int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize;
5589 bool emitRect, addRect, extendRect;
5590 int i, x0, x1, y, maskXor;
5591
5592 imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
5593 imgStr->reset();
5594 rects0Len = rects1Len = rectsOutLen = 0;
5595 rectsSize = rectsOutSize = 64;
5596 rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
5597 rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
5598 rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, sizeof(PSOutImgClipRect));
5599 maskXor = maskInvert ? 1 : 0;
5600 for (y = 0; y < maskHeight; ++y) {
5601 if (!(line = imgStr->getLine())) {
5602 break;
5603 }
5604 i = 0;
5605 rects1Len = 0;
5606 for (x0 = 0; x0 < maskWidth && (line[x0] ^ maskXor); ++x0)
5607 ;
5608 for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1)
5609 ;
5610 while (x0 < maskWidth || i < rects0Len) {
5611 emitRect = addRect = extendRect = false;
5612 if (x0 >= maskWidth) {
5613 emitRect = true;
5614 } else if (i >= rects0Len) {
5615 addRect = true;
5616 } else if (rects0[i].x0 < x0) {
5617 emitRect = true;
5618 } else if (x0 < rects0[i].x0) {
5619 addRect = true;
5620 } else if (rects0[i].x1 == x1) {
5621 extendRect = true;
5622 } else {
5623 emitRect = addRect = true;
5624 }
5625 if (emitRect) {
5626 if (rectsOutLen == rectsOutSize) {
5627 rectsOutSize *= 2;
5628 rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect));
5629 }
5630 rectsOut[rectsOutLen].x0 = rects0[i].x0;
5631 rectsOut[rectsOutLen].x1 = rects0[i].x1;
5632 rectsOut[rectsOutLen].y0 = maskHeight - y;
5633 rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0;
5634 ++rectsOutLen;
5635 ++i;
5636 }
5637 if (addRect || extendRect) {
5638 if (rects1Len == rectsSize) {
5639 rectsSize *= 2;
5640 rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize, sizeof(PSOutImgClipRect));
5641 rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize, sizeof(PSOutImgClipRect));
5642 }
5643 rects1[rects1Len].x0 = x0;
5644 rects1[rects1Len].x1 = x1;
5645 if (addRect) {
5646 rects1[rects1Len].y0 = y;
5647 }
5648 if (extendRect) {
5649 rects1[rects1Len].y0 = rects0[i].y0;
5650 ++i;
5651 }
5652 ++rects1Len;
5653 for (x0 = x1; x0 < maskWidth && (line[x0] ^ maskXor); ++x0)
5654 ;
5655 for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1)
5656 ;
5657 }
5658 }
5659 rectsTmp = rects0;
5660 rects0 = rects1;
5661 rects1 = rectsTmp;
5662 i = rects0Len;
5663 rects0Len = rects1Len;
5664 rects1Len = i;
5665 }
5666 for (i = 0; i < rects0Len; ++i) {
5667 if (rectsOutLen == rectsOutSize) {
5668 rectsOutSize *= 2;
5669 rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect));
5670 }
5671 rectsOut[rectsOutLen].x0 = rects0[i].x0;
5672 rectsOut[rectsOutLen].x1 = rects0[i].x1;
5673 rectsOut[rectsOutLen].y0 = maskHeight - y;
5674 rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0;
5675 ++rectsOutLen;
5676 }
5677 if (rectsOutLen < 65536 / 4) {
5678 writePSFmt("{0:d} array 0\n", rectsOutLen * 4);
5679 for (i = 0; i < rectsOutLen; ++i) {
5680 writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n", rectsOut[i].x0, rectsOut[i].y0, rectsOut[i].x1 - rectsOut[i].x0, rectsOut[i].y1 - rectsOut[i].y0);
5681 }
5682 writePSFmt("pop {0:d} {1:d} pdfImClip\n", maskWidth, maskHeight);
5683 } else {
5684 // would be over the limit of array size.
5685 // make each rectangle path and clip.
5686 writePS("gsave newpath\n");
5687 for (i = 0; i < rectsOutLen; ++i) {
5688 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", ((double)rectsOut[i].x0) / maskWidth, ((double)rectsOut[i].y0) / maskHeight, ((double)(rectsOut[i].x1 - rectsOut[i].x0)) / maskWidth,
5689 ((double)(rectsOut[i].y1 - rectsOut[i].y0)) / maskHeight);
5690 }
5691 writePS("clip\n");
5692 }
5693 gfree(rectsOut);
5694 gfree(rects0);
5695 gfree(rects1);
5696 delete imgStr;
5697 maskStr->close();
5698 }
5699
doImageL2(GfxState * state,Object * ref,GfxImageColorMap * colorMap,bool invert,bool inlineImg,Stream * str,int width,int height,int len,const int * maskColors,Stream * maskStr,int maskWidth,int maskHeight,bool maskInvert)5700 void PSOutputDev::doImageL2(GfxState *state, Object *ref, GfxImageColorMap *colorMap, bool invert, bool inlineImg, Stream *str, int width, int height, int len, const int *maskColors, Stream *maskStr, int maskWidth, int maskHeight,
5701 bool maskInvert)
5702 {
5703 Stream *str2;
5704 ImageStream *imgStr;
5705 unsigned char *line;
5706 PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut;
5707 int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize;
5708 bool emitRect, addRect, extendRect;
5709 GooString *s;
5710 int n, numComps;
5711 bool useLZW, useRLE, useASCII, useCompressed;
5712 GfxSeparationColorSpace *sepCS;
5713 GfxColor color;
5714 GfxCMYK cmyk;
5715 int c;
5716 int col, i, j, x0, x1, y;
5717 char dataBuf[4096];
5718
5719 rectsOutLen = 0;
5720
5721 // color key masking
5722 if (maskColors && colorMap && !inlineImg) {
5723 // can't read the stream twice for inline images -- but masking
5724 // isn't allowed with inline images anyway
5725 numComps = colorMap->getNumPixelComps();
5726 imgStr = new ImageStream(str, width, numComps, colorMap->getBits());
5727 imgStr->reset();
5728 rects0Len = rects1Len = 0;
5729 rectsSize = rectsOutSize = 64;
5730 rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
5731 rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
5732 rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, sizeof(PSOutImgClipRect));
5733 for (y = 0; y < height; ++y) {
5734 if (!(line = imgStr->getLine())) {
5735 break;
5736 }
5737 i = 0;
5738 rects1Len = 0;
5739 for (x0 = 0; x0 < width; ++x0) {
5740 for (j = 0; j < numComps; ++j) {
5741 if (line[x0 * numComps + j] < maskColors[2 * j] || line[x0 * numComps + j] > maskColors[2 * j + 1]) {
5742 break;
5743 }
5744 }
5745 if (j < numComps) {
5746 break;
5747 }
5748 }
5749 for (x1 = x0; x1 < width; ++x1) {
5750 for (j = 0; j < numComps; ++j) {
5751 if (line[x1 * numComps + j] < maskColors[2 * j] || line[x1 * numComps + j] > maskColors[2 * j + 1]) {
5752 break;
5753 }
5754 }
5755 if (j == numComps) {
5756 break;
5757 }
5758 }
5759 while (x0 < width || i < rects0Len) {
5760 emitRect = addRect = extendRect = false;
5761 if (x0 >= width) {
5762 emitRect = true;
5763 } else if (i >= rects0Len) {
5764 addRect = true;
5765 } else if (rects0[i].x0 < x0) {
5766 emitRect = true;
5767 } else if (x0 < rects0[i].x0) {
5768 addRect = true;
5769 } else if (rects0[i].x1 == x1) {
5770 extendRect = true;
5771 } else {
5772 emitRect = addRect = true;
5773 }
5774 if (emitRect) {
5775 if (rectsOutLen == rectsOutSize) {
5776 rectsOutSize *= 2;
5777 rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect));
5778 }
5779 rectsOut[rectsOutLen].x0 = rects0[i].x0;
5780 rectsOut[rectsOutLen].x1 = rects0[i].x1;
5781 rectsOut[rectsOutLen].y0 = height - y;
5782 rectsOut[rectsOutLen].y1 = height - rects0[i].y0;
5783 ++rectsOutLen;
5784 ++i;
5785 }
5786 if (addRect || extendRect) {
5787 if (rects1Len == rectsSize) {
5788 rectsSize *= 2;
5789 rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize, sizeof(PSOutImgClipRect));
5790 rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize, sizeof(PSOutImgClipRect));
5791 }
5792 rects1[rects1Len].x0 = x0;
5793 rects1[rects1Len].x1 = x1;
5794 if (addRect) {
5795 rects1[rects1Len].y0 = y;
5796 }
5797 if (extendRect) {
5798 rects1[rects1Len].y0 = rects0[i].y0;
5799 ++i;
5800 }
5801 ++rects1Len;
5802 for (x0 = x1; x0 < width; ++x0) {
5803 for (j = 0; j < numComps; ++j) {
5804 if (line[x0 * numComps + j] < maskColors[2 * j] || line[x0 * numComps + j] > maskColors[2 * j + 1]) {
5805 break;
5806 }
5807 }
5808 if (j < numComps) {
5809 break;
5810 }
5811 }
5812 for (x1 = x0; x1 < width; ++x1) {
5813 for (j = 0; j < numComps; ++j) {
5814 if (line[x1 * numComps + j] < maskColors[2 * j] || line[x1 * numComps + j] > maskColors[2 * j + 1]) {
5815 break;
5816 }
5817 }
5818 if (j == numComps) {
5819 break;
5820 }
5821 }
5822 }
5823 }
5824 rectsTmp = rects0;
5825 rects0 = rects1;
5826 rects1 = rectsTmp;
5827 i = rects0Len;
5828 rects0Len = rects1Len;
5829 rects1Len = i;
5830 }
5831 for (i = 0; i < rects0Len; ++i) {
5832 if (rectsOutLen == rectsOutSize) {
5833 rectsOutSize *= 2;
5834 rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect));
5835 }
5836 rectsOut[rectsOutLen].x0 = rects0[i].x0;
5837 rectsOut[rectsOutLen].x1 = rects0[i].x1;
5838 rectsOut[rectsOutLen].y0 = height - y;
5839 rectsOut[rectsOutLen].y1 = height - rects0[i].y0;
5840 ++rectsOutLen;
5841 }
5842 if (rectsOutLen < 65536 / 4) {
5843 writePSFmt("{0:d} array 0\n", rectsOutLen * 4);
5844 for (i = 0; i < rectsOutLen; ++i) {
5845 writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n", rectsOut[i].x0, rectsOut[i].y0, rectsOut[i].x1 - rectsOut[i].x0, rectsOut[i].y1 - rectsOut[i].y0);
5846 }
5847 writePSFmt("pop {0:d} {1:d} pdfImClip\n", width, height);
5848 } else {
5849 // would be over the limit of array size.
5850 // make each rectangle path and clip.
5851 writePS("gsave newpath\n");
5852 for (i = 0; i < rectsOutLen; ++i) {
5853 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", ((double)rectsOut[i].x0) / width, ((double)rectsOut[i].y0) / height, ((double)(rectsOut[i].x1 - rectsOut[i].x0)) / width,
5854 ((double)(rectsOut[i].y1 - rectsOut[i].y0)) / height);
5855 }
5856 writePS("clip\n");
5857 }
5858 gfree(rectsOut);
5859 gfree(rects0);
5860 gfree(rects1);
5861 delete imgStr;
5862 str->close();
5863
5864 // explicit masking
5865 } else if (maskStr) {
5866 maskToClippingPath(maskStr, maskWidth, maskHeight, maskInvert);
5867 }
5868
5869 // color space
5870 if (colorMap) {
5871 // Do not update the process color list for custom colors
5872 bool isCustomColor = (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) && colorMap->getColorSpace()->getMode() == csDeviceN;
5873 dumpColorSpaceL2(state, colorMap->getColorSpace(), false, !isCustomColor, false);
5874 writePS(" setcolorspace\n");
5875 }
5876
5877 // set up the image data
5878 if (mode == psModeForm || inType3Char || preloadImagesForms) {
5879 if (inlineImg) {
5880 // create an array
5881 str2 = new FixedLengthEncoder(str, len);
5882 if (getEnableLZW()) {
5883 str2 = new LZWEncoder(str2);
5884 } else {
5885 str2 = new RunLengthEncoder(str2);
5886 }
5887 if (useASCIIHex) {
5888 str2 = new ASCIIHexEncoder(str2);
5889 } else {
5890 str2 = new ASCII85Encoder(str2);
5891 }
5892 str2->reset();
5893 col = 0;
5894 writePS((char *)(useASCIIHex ? "[<" : "[<~"));
5895 do {
5896 do {
5897 c = str2->getChar();
5898 } while (c == '\n' || c == '\r');
5899 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
5900 break;
5901 }
5902 if (c == 'z') {
5903 writePSChar(c);
5904 ++col;
5905 } else {
5906 writePSChar(c);
5907 ++col;
5908 for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
5909 do {
5910 c = str2->getChar();
5911 } while (c == '\n' || c == '\r');
5912 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
5913 break;
5914 }
5915 writePSChar(c);
5916 ++col;
5917 }
5918 }
5919 // each line is: "<~...data...~><eol>"
5920 // so max data length = 255 - 6 = 249
5921 // chunks are 1 or 5 bytes each, so we have to stop at 245
5922 // but make it 240 just to be safe
5923 if (col > 240) {
5924 writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
5925 col = 0;
5926 }
5927 } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
5928 writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
5929 // add an extra entry because the LZWDecode/RunLengthDecode filter may
5930 // read past the end
5931 writePS("<>]\n");
5932 writePS("0\n");
5933 str2->close();
5934 delete str2;
5935 } else {
5936 // make sure the image is setup, it sometimes is not like on bug #17645
5937 setupImage(ref->getRef(), str, false);
5938 // set up to use the array already created by setupImages()
5939 writePSFmt("ImData_{0:d}_{1:d} 0 0\n", ref->getRefNum(), ref->getRefGen());
5940 }
5941 }
5942
5943 // image dictionary
5944 writePS("<<\n /ImageType 1\n");
5945
5946 // width, height, matrix, bits per component
5947 writePSFmt(" /Width {0:d}\n", width);
5948 writePSFmt(" /Height {0:d}\n", height);
5949 writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", width, -height, height);
5950 if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
5951 writePS(" /BitsPerComponent 8\n");
5952 } else {
5953 writePSFmt(" /BitsPerComponent {0:d}\n", colorMap ? colorMap->getBits() : 1);
5954 }
5955
5956 // decode
5957 if (colorMap) {
5958 writePS(" /Decode [");
5959 if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap->getColorSpace()->getMode() == csSeparation) {
5960 // this matches up with the code in the pdfImSep operator
5961 n = (1 << colorMap->getBits()) - 1;
5962 writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n, colorMap->getDecodeHigh(0) * n);
5963 } else if (colorMap->getColorSpace()->getMode() == csDeviceN) {
5964 numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getAlt()->getNComps();
5965 for (i = 0; i < numComps; ++i) {
5966 if (i > 0) {
5967 writePS(" ");
5968 }
5969 writePS("0 1");
5970 }
5971 } else {
5972 numComps = colorMap->getNumPixelComps();
5973 for (i = 0; i < numComps; ++i) {
5974 if (i > 0) {
5975 writePS(" ");
5976 }
5977 writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i));
5978 }
5979 }
5980 writePS("]\n");
5981 } else {
5982 writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1);
5983 }
5984
5985 // data source
5986 if (mode == psModeForm || inType3Char || preloadImagesForms) {
5987 if (inlineImg) {
5988 writePS(" /DataSource { pdfImStr }\n");
5989 } else {
5990 writePS(" /DataSource { dup 65535 ge { pop 1 add 0 } if 2 index 2"
5991 " index get 1 index get exch 1 add exch }\n");
5992 }
5993 } else {
5994 writePS(" /DataSource currentfile\n");
5995 }
5996
5997 // filters
5998 if ((mode == psModeForm || inType3Char || preloadImagesForms) && uncompressPreloadedImages) {
5999 s = nullptr;
6000 useLZW = useRLE = false;
6001 useCompressed = false;
6002 useASCII = false;
6003 } else {
6004 s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, " ");
6005 if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || inlineImg || !s) {
6006 if (getEnableLZW()) {
6007 useLZW = true;
6008 useRLE = false;
6009 } else {
6010 useRLE = true;
6011 useLZW = false;
6012 }
6013 useASCII = !(mode == psModeForm || inType3Char || preloadImagesForms);
6014 useCompressed = false;
6015 } else {
6016 useLZW = useRLE = false;
6017 useASCII = str->isBinary() && !(mode == psModeForm || inType3Char || preloadImagesForms);
6018 useCompressed = true;
6019 }
6020 }
6021 if (useASCII) {
6022 writePSFmt(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85");
6023 }
6024 if (useLZW) {
6025 writePS(" /LZWDecode filter\n");
6026 } else if (useRLE) {
6027 writePS(" /RunLengthDecode filter\n");
6028 }
6029 if (useCompressed) {
6030 writePS(s->c_str());
6031 }
6032 if (s) {
6033 delete s;
6034 }
6035
6036 if (mode == psModeForm || inType3Char || preloadImagesForms) {
6037
6038 // end of image dictionary
6039 writePSFmt(">>\n{0:s}\n", colorMap ? "image" : "imagemask");
6040
6041 // get rid of the array and index
6042 if (!inlineImg)
6043 writePS("pop ");
6044 writePS("pop pop\n");
6045
6046 } else {
6047
6048 // cut off inline image streams at appropriate length
6049 if (inlineImg) {
6050 str = new FixedLengthEncoder(str, len);
6051 } else if (useCompressed) {
6052 str = str->getUndecodedStream();
6053 }
6054
6055 // recode DeviceN data
6056 if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
6057 str = new DeviceNRecoder(str, width, height, colorMap);
6058 }
6059
6060 // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
6061 if (useLZW) {
6062 str = new LZWEncoder(str);
6063 } else if (useRLE) {
6064 str = new RunLengthEncoder(str);
6065 }
6066 if (useASCII) {
6067 if (useASCIIHex) {
6068 str = new ASCIIHexEncoder(str);
6069 } else {
6070 str = new ASCII85Encoder(str);
6071 }
6072 }
6073
6074 // end of image dictionary
6075 writePS(">>\n");
6076 #ifdef OPI_SUPPORT
6077 if (opi13Nest) {
6078 if (inlineImg) {
6079 // this can't happen -- OPI dictionaries are in XObjects
6080 error(errSyntaxError, -1, "OPI in inline image");
6081 n = 0;
6082 } else {
6083 // need to read the stream to count characters -- the length
6084 // is data-dependent (because of ASCII and LZW/RLE filters)
6085 str->reset();
6086 n = 0;
6087 while ((c = str->getChar()) != EOF) {
6088 ++n;
6089 }
6090 str->close();
6091 }
6092 // +6/7 for "pdfIm\n" / "pdfImM\n"
6093 // +8 for newline + trailer
6094 n += colorMap ? 14 : 15;
6095 writePSFmt("%%BeginData: {0:d} Hex Bytes\n", n);
6096 }
6097 #endif
6098 if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap && colorMap->getColorSpace()->getMode() == csSeparation && colorMap->getBits() == 8) {
6099 color.c[0] = gfxColorComp1;
6100 sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
6101 sepCS->getCMYK(&color, &cmyk);
6102 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n", colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName());
6103 } else {
6104 writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM");
6105 }
6106
6107 // copy the stream data
6108 str->reset();
6109 i = 0;
6110 while ((c = str->getChar()) != EOF) {
6111 dataBuf[i++] = c;
6112 if (i >= (int)sizeof(dataBuf)) {
6113 writePSBuf(dataBuf, i);
6114 i = 0;
6115 }
6116 }
6117 if (i > 0) {
6118 writePSBuf(dataBuf, i);
6119 }
6120 str->close();
6121
6122 // add newline and trailer to the end
6123 writePSChar('\n');
6124 writePS("%-EOD-\n");
6125 #ifdef OPI_SUPPORT
6126 if (opi13Nest) {
6127 writePS("%%EndData\n");
6128 }
6129 #endif
6130
6131 // delete encoders
6132 if (useLZW || useRLE || useASCII || inlineImg) {
6133 delete str;
6134 }
6135 }
6136
6137 if ((maskColors && colorMap && !inlineImg) || maskStr) {
6138 if (rectsOutLen < 65536 / 4) {
6139 writePS("pdfImClipEnd\n");
6140 } else {
6141 writePS("grestore\n");
6142 }
6143 }
6144 }
6145
6146 //~ this doesn't currently support OPI
doImageL3(GfxState * state,Object * ref,GfxImageColorMap * colorMap,bool invert,bool inlineImg,Stream * str,int width,int height,int len,const int * maskColors,Stream * maskStr,int maskWidth,int maskHeight,bool maskInvert)6147 void PSOutputDev::doImageL3(GfxState *state, Object *ref, GfxImageColorMap *colorMap, bool invert, bool inlineImg, Stream *str, int width, int height, int len, const int *maskColors, Stream *maskStr, int maskWidth, int maskHeight,
6148 bool maskInvert)
6149 {
6150 Stream *str2;
6151 GooString *s;
6152 int n, numComps;
6153 bool useFlate, useLZW, useRLE, useASCII, useCompressed;
6154 bool maskUseFlate, maskUseLZW, maskUseRLE, maskUseASCII, maskUseCompressed;
6155 GooString *maskFilters;
6156 GfxSeparationColorSpace *sepCS;
6157 GfxColor color;
6158 GfxCMYK cmyk;
6159 int c;
6160 int col, i;
6161
6162 useFlate = useLZW = useRLE = useASCII = useCompressed = false;
6163 maskUseFlate = maskUseLZW = maskUseRLE = maskUseASCII = maskUseCompressed = false;
6164 maskFilters = nullptr; // make gcc happy
6165
6166 // explicit masking
6167 if (maskStr) {
6168
6169 // mask data source
6170 if ((mode == psModeForm || inType3Char || preloadImagesForms) && uncompressPreloadedImages) {
6171 s = nullptr;
6172 } else {
6173 s = maskStr->getPSFilter(3, " ");
6174 if (!s) {
6175 if (getEnableFlate()) {
6176 maskUseFlate = true;
6177 } else if (getEnableLZW()) {
6178 maskUseLZW = true;
6179 } else {
6180 maskUseRLE = true;
6181 }
6182 maskUseASCII = !(mode == psModeForm || inType3Char || preloadImagesForms);
6183 } else {
6184 maskUseASCII = maskStr->isBinary() && !(mode == psModeForm || inType3Char || preloadImagesForms);
6185 maskUseCompressed = true;
6186 }
6187 }
6188 maskFilters = new GooString();
6189 if (maskUseASCII) {
6190 maskFilters->appendf(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85");
6191 }
6192 if (maskUseFlate) {
6193 maskFilters->append(" /FlateDecode filter\n");
6194 } else if (maskUseLZW) {
6195 maskFilters->append(" /LZWDecode filter\n");
6196 } else if (maskUseRLE) {
6197 maskFilters->append(" /RunLengthDecode filter\n");
6198 }
6199 if (maskUseCompressed) {
6200 maskFilters->append(s);
6201 }
6202 if (s) {
6203 delete s;
6204 }
6205 if (mode == psModeForm || inType3Char || preloadImagesForms) {
6206 writePSFmt("MaskData_{0:d}_{1:d} pdfMaskInit\n", ref->getRefNum(), ref->getRefGen());
6207 } else {
6208 writePS("currentfile\n");
6209 writePS(maskFilters->c_str());
6210 writePS("pdfMask\n");
6211
6212 // add FlateEncode/LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
6213 if (maskUseCompressed) {
6214 maskStr = maskStr->getUndecodedStream();
6215 }
6216 #ifdef ENABLE_ZLIB
6217 if (maskUseFlate) {
6218 maskStr = new FlateEncoder(maskStr);
6219 } else
6220 #endif
6221 if (maskUseLZW) {
6222 maskStr = new LZWEncoder(maskStr);
6223 } else if (maskUseRLE) {
6224 maskStr = new RunLengthEncoder(maskStr);
6225 }
6226 if (maskUseASCII) {
6227 if (useASCIIHex) {
6228 maskStr = new ASCIIHexEncoder(maskStr);
6229 } else {
6230 maskStr = new ASCII85Encoder(maskStr);
6231 }
6232 }
6233
6234 // copy the stream data
6235 maskStr->reset();
6236 while ((c = maskStr->getChar()) != EOF) {
6237 writePSChar(c);
6238 }
6239 maskStr->close();
6240 writePSChar('\n');
6241 writePS("%-EOD-\n");
6242
6243 // delete encoders
6244 if (maskUseFlate || maskUseLZW || maskUseRLE || maskUseASCII) {
6245 delete maskStr;
6246 }
6247 }
6248 }
6249
6250 // color space
6251 if (colorMap) {
6252 // Do not update the process color list for custom colors
6253 bool isCustomColor = (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) && colorMap->getColorSpace()->getMode() == csDeviceN;
6254 dumpColorSpaceL2(state, colorMap->getColorSpace(), false, !isCustomColor, false);
6255 writePS(" setcolorspace\n");
6256 }
6257
6258 // set up the image data
6259 if (mode == psModeForm || inType3Char || preloadImagesForms) {
6260 if (inlineImg) {
6261 // create an array
6262 str2 = new FixedLengthEncoder(str, len);
6263 #ifdef ENABLE_ZLIB
6264 if (getEnableFlate()) {
6265 str2 = new FlateEncoder(str2);
6266 } else
6267 #endif
6268 if (getEnableLZW()) {
6269 str2 = new LZWEncoder(str2);
6270 } else {
6271 str2 = new RunLengthEncoder(str2);
6272 }
6273 if (useASCIIHex) {
6274 str2 = new ASCIIHexEncoder(str2);
6275 } else {
6276 str2 = new ASCII85Encoder(str2);
6277 }
6278 str2->reset();
6279 col = 0;
6280 writePS((char *)(useASCIIHex ? "[<" : "[<~"));
6281 do {
6282 do {
6283 c = str2->getChar();
6284 } while (c == '\n' || c == '\r');
6285 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
6286 break;
6287 }
6288 if (c == 'z') {
6289 writePSChar(c);
6290 ++col;
6291 } else {
6292 writePSChar(c);
6293 ++col;
6294 for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
6295 do {
6296 c = str2->getChar();
6297 } while (c == '\n' || c == '\r');
6298 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
6299 break;
6300 }
6301 writePSChar(c);
6302 ++col;
6303 }
6304 }
6305 // each line is: "<~...data...~><eol>"
6306 // so max data length = 255 - 6 = 249
6307 // chunks are 1 or 5 bytes each, so we have to stop at 245
6308 // but make it 240 just to be safe
6309 if (col > 240) {
6310 writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
6311 col = 0;
6312 }
6313 } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
6314 writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
6315 // add an extra entry because the FlateEncode/LZWDecode/RunLengthDecode filter may
6316 // read past the end
6317 writePS("<>]\n");
6318 writePS("0\n");
6319 str2->close();
6320 delete str2;
6321 } else {
6322 // make sure the image is setup, it sometimes is not like on bug #17645
6323 setupImage(ref->getRef(), str, false);
6324 // set up to use the array already created by setupImages()
6325 writePSFmt("ImData_{0:d}_{1:d} 0 0\n", ref->getRefNum(), ref->getRefGen());
6326 }
6327 }
6328
6329 // explicit masking
6330 if (maskStr) {
6331 writePS("<<\n /ImageType 3\n");
6332 writePS(" /InterleaveType 3\n");
6333 writePS(" /DataDict\n");
6334 }
6335
6336 // image (data) dictionary
6337 writePSFmt("<<\n /ImageType {0:d}\n", (maskColors && colorMap) ? 4 : 1);
6338
6339 // color key masking
6340 if (maskColors && colorMap) {
6341 writePS(" /MaskColor [\n");
6342 numComps = colorMap->getNumPixelComps();
6343 for (i = 0; i < 2 * numComps; i += 2) {
6344 writePSFmt(" {0:d} {1:d}\n", maskColors[i], maskColors[i + 1]);
6345 }
6346 writePS(" ]\n");
6347 }
6348
6349 // width, height, matrix, bits per component
6350 writePSFmt(" /Width {0:d}\n", width);
6351 writePSFmt(" /Height {0:d}\n", height);
6352 writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", width, -height, height);
6353 if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
6354 writePS(" /BitsPerComponent 8\n");
6355 } else {
6356 writePSFmt(" /BitsPerComponent {0:d}\n", colorMap ? colorMap->getBits() : 1);
6357 }
6358
6359 // decode
6360 if (colorMap) {
6361 writePS(" /Decode [");
6362 if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap->getColorSpace()->getMode() == csSeparation) {
6363 // this matches up with the code in the pdfImSep operator
6364 n = (1 << colorMap->getBits()) - 1;
6365 writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n, colorMap->getDecodeHigh(0) * n);
6366 } else {
6367 numComps = colorMap->getNumPixelComps();
6368 for (i = 0; i < numComps; ++i) {
6369 if (i > 0) {
6370 writePS(" ");
6371 }
6372 writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i));
6373 }
6374 }
6375 writePS("]\n");
6376 } else {
6377 writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1);
6378 }
6379
6380 // data source
6381 if (mode == psModeForm || inType3Char || preloadImagesForms) {
6382 if (inlineImg) {
6383 writePS(" /DataSource { pdfImStr }\n");
6384 } else {
6385 writePS(" /DataSource { dup 65535 ge { pop 1 add 0 } if 2 index 2"
6386 " index get 1 index get exch 1 add exch }\n");
6387 }
6388 } else {
6389 writePS(" /DataSource currentfile\n");
6390 }
6391
6392 // filters
6393
6394 useFlate = useLZW = useRLE = false;
6395 useCompressed = false;
6396 useASCII = false;
6397
6398 if ((mode == psModeForm || inType3Char || preloadImagesForms) && uncompressPreloadedImages) {
6399 s = nullptr;
6400 } else {
6401 s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, " ");
6402 if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || inlineImg || !s) {
6403 if (getEnableFlate()) {
6404 useFlate = true;
6405 } else if (getEnableLZW()) {
6406 useLZW = true;
6407 } else {
6408 useRLE = true;
6409 }
6410 useASCII = !(mode == psModeForm || inType3Char || preloadImagesForms);
6411 } else {
6412 useASCII = str->isBinary() && !(mode == psModeForm || inType3Char || preloadImagesForms);
6413 useCompressed = true;
6414 }
6415 }
6416 if (useASCII) {
6417 writePSFmt(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85");
6418 }
6419 if (useFlate) {
6420 writePS(" /FlateDecode filter\n");
6421 } else if (useLZW) {
6422 writePS(" /LZWDecode filter\n");
6423 } else if (useRLE) {
6424 writePS(" /RunLengthDecode filter\n");
6425 }
6426 if (useCompressed) {
6427 writePS(s->c_str());
6428 }
6429 if (s) {
6430 delete s;
6431 }
6432
6433 // end of image (data) dictionary
6434 writePS(">>\n");
6435
6436 // explicit masking
6437 if (maskStr) {
6438 writePS(" /MaskDict\n");
6439 writePS("<<\n");
6440 writePS(" /ImageType 1\n");
6441 writePSFmt(" /Width {0:d}\n", maskWidth);
6442 writePSFmt(" /Height {0:d}\n", maskHeight);
6443 writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", maskWidth, -maskHeight, maskHeight);
6444 writePS(" /BitsPerComponent 1\n");
6445 writePSFmt(" /Decode [{0:d} {1:d}]\n", maskInvert ? 1 : 0, maskInvert ? 0 : 1);
6446
6447 // mask data source
6448 if (mode == psModeForm || inType3Char || preloadImagesForms) {
6449 writePS(" /DataSource {pdfMaskSrc}\n");
6450 writePS(maskFilters->c_str());
6451 } else {
6452 writePS(" /DataSource maskStream\n");
6453 }
6454 delete maskFilters;
6455
6456 writePS(">>\n");
6457 writePS(">>\n");
6458 }
6459
6460 if (mode == psModeForm || inType3Char || preloadImagesForms) {
6461
6462 // image command
6463 writePSFmt("{0:s}\n", colorMap ? "image" : "imagemask");
6464
6465 } else {
6466
6467 if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap && colorMap->getColorSpace()->getMode() == csSeparation && colorMap->getBits() == 8) {
6468 color.c[0] = gfxColorComp1;
6469 sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
6470 sepCS->getCMYK(&color, &cmyk);
6471 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n", colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName());
6472 } else {
6473 writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM");
6474 }
6475 }
6476
6477 // get rid of the array and index
6478 if (mode == psModeForm || inType3Char || preloadImagesForms) {
6479 if (!inlineImg)
6480 writePS("pop ");
6481 writePS("pop pop\n");
6482
6483 // image data
6484 } else {
6485
6486 // cut off inline image streams at appropriate length
6487 if (inlineImg) {
6488 str = new FixedLengthEncoder(str, len);
6489 } else if (useCompressed) {
6490 str = str->getUndecodedStream();
6491 }
6492
6493 // add FlateEncode/LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
6494 #ifdef ENABLE_ZLIB
6495 if (useFlate) {
6496 str = new FlateEncoder(str);
6497 } else
6498 #endif
6499 if (useLZW) {
6500 str = new LZWEncoder(str);
6501 } else if (useRLE) {
6502 str = new RunLengthEncoder(str);
6503 }
6504 if (useASCII) {
6505 if (useASCIIHex) {
6506 str = new ASCIIHexEncoder(str);
6507 } else {
6508 str = new ASCII85Encoder(str);
6509 }
6510 }
6511
6512 // copy the stream data
6513 str->reset();
6514 while ((c = str->getChar()) != EOF) {
6515 writePSChar(c);
6516 }
6517 str->close();
6518
6519 // add newline and trailer to the end
6520 writePSChar('\n');
6521 writePS("%-EOD-\n");
6522
6523 // delete encoders
6524 if (useFlate || useLZW || useRLE || useASCII || inlineImg) {
6525 delete str;
6526 }
6527 }
6528
6529 // close the mask stream
6530 if (maskStr) {
6531 if (!(mode == psModeForm || inType3Char || preloadImagesForms)) {
6532 writePS("pdfMaskEnd\n");
6533 }
6534 }
6535 }
6536
dumpColorSpaceL2(GfxState * state,GfxColorSpace * colorSpace,bool genXform,bool updateColors,bool map01)6537 void PSOutputDev::dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace, bool genXform, bool updateColors, bool map01)
6538 {
6539 GfxCalGrayColorSpace *calGrayCS;
6540 GfxCalRGBColorSpace *calRGBCS;
6541 GfxLabColorSpace *labCS;
6542 GfxIndexedColorSpace *indexedCS;
6543 GfxSeparationColorSpace *separationCS;
6544 GfxDeviceNColorSpace *deviceNCS;
6545 GfxColorSpace *baseCS;
6546 unsigned char *lookup, *p;
6547 double x[gfxColorMaxComps], y[gfxColorMaxComps];
6548 double low[gfxColorMaxComps], range[gfxColorMaxComps];
6549 GfxColor color;
6550 GfxCMYK cmyk;
6551 int n, numComps, numAltComps;
6552 int byte;
6553 int i, j, k;
6554
6555 switch (colorSpace->getMode()) {
6556
6557 case csDeviceGray:
6558 writePS("/DeviceGray");
6559 if (genXform) {
6560 writePS(" {}");
6561 }
6562 if (updateColors) {
6563 processColors |= psProcessBlack;
6564 }
6565 break;
6566
6567 case csCalGray:
6568 calGrayCS = (GfxCalGrayColorSpace *)colorSpace;
6569 writePS("[/CIEBasedA <<\n");
6570 writePSFmt(" /DecodeA {{{0:.4g} exp}} bind\n", calGrayCS->getGamma());
6571 writePSFmt(" /MatrixA [{0:.4g} {1:.4g} {2:.4g}]\n", calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), calGrayCS->getWhiteZ());
6572 writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), calGrayCS->getWhiteZ());
6573 writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", calGrayCS->getBlackX(), calGrayCS->getBlackY(), calGrayCS->getBlackZ());
6574 writePS(">>]");
6575 if (genXform) {
6576 writePS(" {}");
6577 }
6578 if (updateColors) {
6579 processColors |= psProcessBlack;
6580 }
6581 break;
6582
6583 case csDeviceRGB:
6584 writePS("/DeviceRGB");
6585 if (genXform) {
6586 writePS(" {}");
6587 }
6588 if (updateColors) {
6589 processColors |= psProcessCMYK;
6590 }
6591 break;
6592
6593 case csCalRGB:
6594 calRGBCS = (GfxCalRGBColorSpace *)colorSpace;
6595 writePS("[/CIEBasedABC <<\n");
6596 writePSFmt(" /DecodeABC [{{{0:.4g} exp}} bind {{{1:.4g} exp}} bind {{{2:.4g} exp}} bind]\n", calRGBCS->getGammaR(), calRGBCS->getGammaG(), calRGBCS->getGammaB());
6597 writePSFmt(" /MatrixABC [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g} {8:.4g}]\n", calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1], calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3], calRGBCS->getMatrix()[4],
6598 calRGBCS->getMatrix()[5], calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7], calRGBCS->getMatrix()[8]);
6599 writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", calRGBCS->getWhiteX(), calRGBCS->getWhiteY(), calRGBCS->getWhiteZ());
6600 writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", calRGBCS->getBlackX(), calRGBCS->getBlackY(), calRGBCS->getBlackZ());
6601 writePS(">>]");
6602 if (genXform) {
6603 writePS(" {}");
6604 }
6605 if (updateColors) {
6606 processColors |= psProcessCMYK;
6607 }
6608 break;
6609
6610 case csDeviceCMYK:
6611 writePS("/DeviceCMYK");
6612 if (genXform) {
6613 writePS(" {}");
6614 }
6615 if (updateColors) {
6616 processColors |= psProcessCMYK;
6617 }
6618 break;
6619
6620 case csLab:
6621 labCS = (GfxLabColorSpace *)colorSpace;
6622 writePS("[/CIEBasedABC <<\n");
6623 if (map01) {
6624 writePS(" /RangeABC [0 1 0 1 0 1]\n");
6625 writePSFmt(" /DecodeABC [{{100 mul 16 add 116 div}} bind {{{0:.4g} mul {1:.4g} add}} bind {{{2:.4g} mul {3:.4g} add}} bind]\n", (labCS->getAMax() - labCS->getAMin()) / 500.0, labCS->getAMin() / 500.0,
6626 (labCS->getBMax() - labCS->getBMin()) / 200.0, labCS->getBMin() / 200.0);
6627 } else {
6628 writePSFmt(" /RangeABC [0 100 {0:.4g} {1:.4g} {2:.4g} {3:.4g}]\n", labCS->getAMin(), labCS->getAMax(), labCS->getBMin(), labCS->getBMax());
6629 writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
6630 }
6631 writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
6632 writePS(" /DecodeLMN\n");
6633 writePS(" [{dup 6 29 div ge {dup dup mul mul}\n");
6634 writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n", labCS->getWhiteX());
6635 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
6636 writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n", labCS->getWhiteY());
6637 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
6638 writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind]\n", labCS->getWhiteZ());
6639 writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());
6640 writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());
6641 writePS(">>]");
6642 if (genXform) {
6643 writePS(" {}");
6644 }
6645 if (updateColors) {
6646 processColors |= psProcessCMYK;
6647 }
6648 break;
6649
6650 case csICCBased:
6651 #ifdef USE_CMS
6652 {
6653 GfxICCBasedColorSpace *iccBasedCS;
6654 iccBasedCS = (GfxICCBasedColorSpace *)colorSpace;
6655 Ref ref = iccBasedCS->getRef();
6656 const bool validref = ref != Ref::INVALID();
6657 int intent = state->getCmsRenderingIntent();
6658 GooString *name;
6659 if (validref) {
6660 name = GooString::format("ICCBased-{0:d}-{1:d}-{2:d}", ref.num, ref.gen, intent);
6661 } else {
6662 const unsigned long long hash = std::hash<GfxLCMSProfilePtr> {}(iccBasedCS->getProfile());
6663 name = GooString::format("ICCBased-hashed-{0:ullX}-{1:d}", hash, intent);
6664 }
6665 const auto &it = iccEmitted.find(name->toStr());
6666 if (it != iccEmitted.end()) {
6667 writePSFmt("{0:t}", name);
6668 if (genXform) {
6669 writePS(" {}");
6670 }
6671 } else {
6672 char *csa = iccBasedCS->getPostScriptCSA();
6673 if (csa) {
6674 writePSFmt("userdict /{0:t} {1:s} put\n", name, csa);
6675 iccEmitted.emplace(name->toStr());
6676 writePSFmt("{0:t}", name);
6677 if (genXform) {
6678 writePS(" {}");
6679 }
6680 } else {
6681 dumpColorSpaceL2(state, ((GfxICCBasedColorSpace *)colorSpace)->getAlt(), genXform, updateColors, false);
6682 }
6683 }
6684 delete name;
6685 }
6686 #else
6687 // there is no transform function to the alternate color space, so
6688 // we can use it directly
6689 dumpColorSpaceL2(state, ((GfxICCBasedColorSpace *)colorSpace)->getAlt(), genXform, updateColors, false);
6690 #endif
6691 break;
6692
6693 case csIndexed:
6694 indexedCS = (GfxIndexedColorSpace *)colorSpace;
6695 baseCS = indexedCS->getBase();
6696 writePS("[/Indexed ");
6697 dumpColorSpaceL2(state, baseCS, false, false, true);
6698 n = indexedCS->getIndexHigh();
6699 numComps = baseCS->getNComps();
6700 lookup = indexedCS->getLookup();
6701 writePSFmt(" {0:d} <\n", n);
6702 if (baseCS->getMode() == csDeviceN && level != psLevel3 && level != psLevel3Sep) {
6703 const Function *func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();
6704 baseCS->getDefaultRanges(low, range, indexedCS->getIndexHigh());
6705 if (((GfxDeviceNColorSpace *)baseCS)->getAlt()->getMode() == csLab) {
6706 labCS = (GfxLabColorSpace *)((GfxDeviceNColorSpace *)baseCS)->getAlt();
6707 } else {
6708 labCS = nullptr;
6709 }
6710 numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps();
6711 p = lookup;
6712 for (i = 0; i <= n; i += 8) {
6713 writePS(" ");
6714 for (j = i; j < i + 8 && j <= n; ++j) {
6715 for (k = 0; k < numComps; ++k) {
6716 x[k] = low[k] + (*p++ / 255.0) * range[k];
6717 }
6718 func->transform(x, y);
6719 if (labCS) {
6720 y[0] /= 100.0;
6721 y[1] = (y[1] - labCS->getAMin()) / (labCS->getAMax() - labCS->getAMin());
6722 y[2] = (y[2] - labCS->getBMin()) / (labCS->getBMax() - labCS->getBMin());
6723 }
6724 for (k = 0; k < numAltComps; ++k) {
6725 byte = (int)(y[k] * 255 + 0.5);
6726 if (byte < 0) {
6727 byte = 0;
6728 } else if (byte > 255) {
6729 byte = 255;
6730 }
6731 writePSFmt("{0:02x}", byte);
6732 }
6733 if (updateColors) {
6734 color.c[0] = dblToCol(j);
6735 indexedCS->getCMYK(&color, &cmyk);
6736 addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k));
6737 }
6738 }
6739 writePS("\n");
6740 }
6741 } else {
6742 for (i = 0; i <= n; i += 8) {
6743 writePS(" ");
6744 for (j = i; j < i + 8 && j <= n; ++j) {
6745 for (k = 0; k < numComps; ++k) {
6746 writePSFmt("{0:02x}", lookup[j * numComps + k]);
6747 }
6748 if (updateColors) {
6749 color.c[0] = dblToCol(j);
6750 indexedCS->getCMYK(&color, &cmyk);
6751 addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k));
6752 }
6753 }
6754 writePS("\n");
6755 }
6756 }
6757 writePS(">]");
6758 if (genXform) {
6759 writePS(" {}");
6760 }
6761 break;
6762
6763 case csSeparation:
6764 separationCS = (GfxSeparationColorSpace *)colorSpace;
6765 writePS("[/Separation ");
6766 writePSString(separationCS->getName()->toStr());
6767 writePS(" ");
6768 dumpColorSpaceL2(state, separationCS->getAlt(), false, false, false);
6769 writePS("\n");
6770 cvtFunction(separationCS->getFunc());
6771 writePS("]");
6772 if (genXform) {
6773 writePS(" {}");
6774 }
6775 if (updateColors) {
6776 addCustomColor(separationCS);
6777 }
6778 break;
6779
6780 case csDeviceN:
6781 deviceNCS = (GfxDeviceNColorSpace *)colorSpace;
6782 if (level == psLevel3 || level == psLevel3Sep) {
6783 writePS("[/DeviceN\n");
6784 writePS(" [ ");
6785 for (i = 0; i < deviceNCS->getNComps(); i++) {
6786 writePSString(deviceNCS->getColorantName(i));
6787 writePS(" ");
6788 }
6789 writePS("]\n");
6790 dumpColorSpaceL2(state, deviceNCS->getAlt(), false, updateColors, false);
6791 writePS("\n");
6792 cvtFunction(deviceNCS->getTintTransformFunc(), map01 && deviceNCS->getAlt()->getMode() == csLab);
6793 writePS("]\n");
6794 if (genXform) {
6795 writePS(" {}");
6796 }
6797 } else {
6798 // DeviceN color spaces are a Level 3 PostScript feature.
6799 dumpColorSpaceL2(state, deviceNCS->getAlt(), false, updateColors, map01);
6800 if (genXform) {
6801 writePS(" ");
6802 cvtFunction(deviceNCS->getTintTransformFunc());
6803 }
6804 }
6805 break;
6806
6807 case csPattern:
6808 //~ unimplemented
6809 break;
6810 }
6811 }
6812
6813 #ifdef OPI_SUPPORT
opiBegin(GfxState * state,Dict * opiDict)6814 void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict)
6815 {
6816 if (generateOPI) {
6817 Object dict = opiDict->lookup("2.0");
6818 if (dict.isDict()) {
6819 opiBegin20(state, dict.getDict());
6820 } else {
6821 dict = opiDict->lookup("1.3");
6822 if (dict.isDict()) {
6823 opiBegin13(state, dict.getDict());
6824 }
6825 }
6826 }
6827 }
6828
opiBegin20(GfxState * state,Dict * dict)6829 void PSOutputDev::opiBegin20(GfxState *state, Dict *dict)
6830 {
6831 double width, height, left, right, top, bottom;
6832 int w, h;
6833
6834 writePS("%%BeginOPI: 2.0\n");
6835 writePS("%%Distilled\n");
6836
6837 Object obj1 = dict->lookup("F");
6838 Object obj2 = getFileSpecName(&obj1);
6839 if (obj2.isString()) {
6840 writePSFmt("%%ImageFileName: {0:t}\n", obj2.getString());
6841 }
6842
6843 obj1 = dict->lookup("MainImage");
6844 if (obj1.isString()) {
6845 writePSFmt("%%MainImage: {0:t}\n", obj1.getString());
6846 }
6847
6848 //~ ignoring 'Tags' entry
6849 //~ need to use writePSString() and deal with >255-char lines
6850
6851 obj1 = dict->lookup("Size");
6852 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
6853 obj2 = obj1.arrayGet(0);
6854 width = obj2.getNum();
6855 obj2 = obj1.arrayGet(1);
6856 height = obj2.getNum();
6857 writePSFmt("%%ImageDimensions: {0:.6g} {1:.6g}\n", width, height);
6858 }
6859
6860 obj1 = dict->lookup("CropRect");
6861 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
6862 obj2 = obj1.arrayGet(0);
6863 left = obj2.getNum();
6864 obj2 = obj1.arrayGet(1);
6865 top = obj2.getNum();
6866 obj2 = obj1.arrayGet(2);
6867 right = obj2.getNum();
6868 obj2 = obj1.arrayGet(3);
6869 bottom = obj2.getNum();
6870 writePSFmt("%%ImageCropRect: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", left, top, right, bottom);
6871 }
6872
6873 obj1 = dict->lookup("Overprint");
6874 if (obj1.isBool()) {
6875 writePSFmt("%%ImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false");
6876 }
6877
6878 obj1 = dict->lookup("Inks");
6879 if (obj1.isName()) {
6880 writePSFmt("%%ImageInks: {0:s}\n", obj1.getName());
6881 } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
6882 obj2 = obj1.arrayGet(0);
6883 if (obj2.isName()) {
6884 writePSFmt("%%ImageInks: {0:s} {1:d}", obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
6885 for (int i = 1; i + 1 < obj1.arrayGetLength(); i += 2) {
6886 Object obj3 = obj1.arrayGet(i);
6887 Object obj4 = obj1.arrayGet(i + 1);
6888 if (obj3.isString() && obj4.isNum()) {
6889 writePS(" ");
6890 writePSString(obj3.getString()->toStr());
6891 writePSFmt(" {0:.6g}", obj4.getNum());
6892 }
6893 }
6894 writePS("\n");
6895 }
6896 }
6897
6898 writePS("gsave\n");
6899
6900 writePS("%%BeginIncludedImage\n");
6901
6902 obj1 = dict->lookup("IncludedImageDimensions");
6903 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
6904 obj2 = obj1.arrayGet(0);
6905 w = obj2.getInt();
6906 obj2 = obj1.arrayGet(1);
6907 h = obj2.getInt();
6908 writePSFmt("%%IncludedImageDimensions: {0:d} {1:d}\n", w, h);
6909 }
6910
6911 obj1 = dict->lookup("IncludedImageQuality");
6912 if (obj1.isNum()) {
6913 writePSFmt("%%IncludedImageQuality: {0:.6g}\n", obj1.getNum());
6914 }
6915
6916 ++opi20Nest;
6917 }
6918
opiBegin13(GfxState * state,Dict * dict)6919 void PSOutputDev::opiBegin13(GfxState *state, Dict *dict)
6920 {
6921 int left, right, top, bottom, samples, bits, width, height;
6922 double c, m, y, k;
6923 double llx, lly, ulx, uly, urx, ury, lrx, lry;
6924 double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
6925 double horiz, vert;
6926 int i, j;
6927
6928 writePS("save\n");
6929 writePS("/opiMatrix2 matrix currentmatrix def\n");
6930 writePS("opiMatrix setmatrix\n");
6931
6932 Object obj1 = dict->lookup("F");
6933 Object obj2 = getFileSpecName(&obj1);
6934 if (obj2.isString()) {
6935 writePSFmt("%ALDImageFileName: {0:t}\n", obj2.getString());
6936 }
6937
6938 obj1 = dict->lookup("CropRect");
6939 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
6940 obj2 = obj1.arrayGet(0);
6941 left = obj2.getInt();
6942 obj2 = obj1.arrayGet(1);
6943 top = obj2.getInt();
6944 obj2 = obj1.arrayGet(2);
6945 right = obj2.getInt();
6946 obj2 = obj1.arrayGet(3);
6947 bottom = obj2.getInt();
6948 writePSFmt("%ALDImageCropRect: {0:d} {1:d} {2:d} {3:d}\n", left, top, right, bottom);
6949 }
6950
6951 obj1 = dict->lookup("Color");
6952 if (obj1.isArray() && obj1.arrayGetLength() == 5) {
6953 obj2 = obj1.arrayGet(0);
6954 c = obj2.getNum();
6955 obj2 = obj1.arrayGet(1);
6956 m = obj2.getNum();
6957 obj2 = obj1.arrayGet(2);
6958 y = obj2.getNum();
6959 obj2 = obj1.arrayGet(3);
6960 k = obj2.getNum();
6961 obj2 = obj1.arrayGet(4);
6962 if (obj2.isString()) {
6963 writePSFmt("%ALDImageColor: {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", c, m, y, k);
6964 writePSString(obj2.getString()->toStr());
6965 writePS("\n");
6966 }
6967 }
6968
6969 obj1 = dict->lookup("ColorType");
6970 if (obj1.isName()) {
6971 writePSFmt("%ALDImageColorType: {0:s}\n", obj1.getName());
6972 }
6973
6974 //~ ignores 'Comments' entry
6975 //~ need to handle multiple lines
6976
6977 obj1 = dict->lookup("CropFixed");
6978 if (obj1.isArray()) {
6979 obj2 = obj1.arrayGet(0);
6980 ulx = obj2.getNum();
6981 obj2 = obj1.arrayGet(1);
6982 uly = obj2.getNum();
6983 obj2 = obj1.arrayGet(2);
6984 lrx = obj2.getNum();
6985 obj2 = obj1.arrayGet(3);
6986 lry = obj2.getNum();
6987 writePSFmt("%ALDImageCropFixed: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", ulx, uly, lrx, lry);
6988 }
6989
6990 obj1 = dict->lookup("GrayMap");
6991 if (obj1.isArray()) {
6992 writePS("%ALDImageGrayMap:");
6993 for (i = 0; i < obj1.arrayGetLength(); i += 16) {
6994 if (i > 0) {
6995 writePS("\n%%+");
6996 }
6997 for (j = 0; j < 16 && i + j < obj1.arrayGetLength(); ++j) {
6998 obj2 = obj1.arrayGet(i + j);
6999 writePSFmt(" {0:d}", obj2.getInt());
7000 }
7001 }
7002 writePS("\n");
7003 }
7004
7005 obj1 = dict->lookup("ID");
7006 if (obj1.isString()) {
7007 writePSFmt("%ALDImageID: {0:t}\n", obj1.getString());
7008 }
7009
7010 obj1 = dict->lookup("ImageType");
7011 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
7012 obj2 = obj1.arrayGet(0);
7013 samples = obj2.getInt();
7014 obj2 = obj1.arrayGet(1);
7015 bits = obj2.getInt();
7016 writePSFmt("%ALDImageType: {0:d} {1:d}\n", samples, bits);
7017 }
7018
7019 dict->lookup("Overprint");
7020 if (obj1.isBool()) {
7021 writePSFmt("%ALDImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false");
7022 }
7023
7024 obj1 = dict->lookup("Position");
7025 if (obj1.isArray() && obj1.arrayGetLength() == 8) {
7026 obj2 = obj1.arrayGet(0);
7027 llx = obj2.getNum();
7028 obj2 = obj1.arrayGet(1);
7029 lly = obj2.getNum();
7030 obj2 = obj1.arrayGet(2);
7031 ulx = obj2.getNum();
7032 obj2 = obj1.arrayGet(3);
7033 uly = obj2.getNum();
7034 obj2 = obj1.arrayGet(4);
7035 urx = obj2.getNum();
7036 obj2 = obj1.arrayGet(5);
7037 ury = obj2.getNum();
7038 obj2 = obj1.arrayGet(6);
7039 lrx = obj2.getNum();
7040 obj2 = obj1.arrayGet(7);
7041 lry = obj2.getNum();
7042 opiTransform(state, llx, lly, &tllx, &tlly);
7043 opiTransform(state, ulx, uly, &tulx, &tuly);
7044 opiTransform(state, urx, ury, &turx, &tury);
7045 opiTransform(state, lrx, lry, &tlrx, &tlry);
7046 writePSFmt("%ALDImagePosition: {0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} {6:.6g} {7:.6g}\n", tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
7047 }
7048
7049 obj1 = dict->lookup("Resolution");
7050 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
7051 obj2 = obj1.arrayGet(0);
7052 horiz = obj2.getNum();
7053 obj2 = obj1.arrayGet(1);
7054 vert = obj2.getNum();
7055 writePSFmt("%ALDImageResoution: {0:.6g} {1:.6g}\n", horiz, vert);
7056 }
7057
7058 obj1 = dict->lookup("Size");
7059 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
7060 obj2 = obj1.arrayGet(0);
7061 width = obj2.getInt();
7062 obj2 = obj1.arrayGet(1);
7063 height = obj2.getInt();
7064 writePSFmt("%ALDImageDimensions: {0:d} {1:d}\n", width, height);
7065 }
7066
7067 //~ ignoring 'Tags' entry
7068 //~ need to use writePSString() and deal with >255-char lines
7069
7070 obj1 = dict->lookup("Tint");
7071 if (obj1.isNum()) {
7072 writePSFmt("%ALDImageTint: {0:.6g}\n", obj1.getNum());
7073 }
7074
7075 obj1 = dict->lookup("Transparency");
7076 if (obj1.isBool()) {
7077 writePSFmt("%ALDImageTransparency: {0:s}\n", obj1.getBool() ? "true" : "false");
7078 }
7079
7080 writePS("%%BeginObject: image\n");
7081 writePS("opiMatrix2 setmatrix\n");
7082 ++opi13Nest;
7083 }
7084
7085 // Convert PDF user space coordinates to PostScript default user space
7086 // coordinates. This has to account for both the PDF CTM and the
7087 // PSOutputDev page-fitting transform.
opiTransform(GfxState * state,double x0,double y0,double * x1,double * y1)7088 void PSOutputDev::opiTransform(GfxState *state, double x0, double y0, double *x1, double *y1)
7089 {
7090 double t;
7091
7092 state->transform(x0, y0, x1, y1);
7093 *x1 += tx;
7094 *y1 += ty;
7095 if (rotate == 90) {
7096 t = *x1;
7097 *x1 = -*y1;
7098 *y1 = t;
7099 } else if (rotate == 180) {
7100 *x1 = -*x1;
7101 *y1 = -*y1;
7102 } else if (rotate == 270) {
7103 t = *x1;
7104 *x1 = *y1;
7105 *y1 = -t;
7106 }
7107 *x1 *= xScale;
7108 *y1 *= yScale;
7109 }
7110
opiEnd(GfxState * state,Dict * opiDict)7111 void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict)
7112 {
7113 if (generateOPI) {
7114 Object dict = opiDict->lookup("2.0");
7115 if (dict.isDict()) {
7116 writePS("%%EndIncludedImage\n");
7117 writePS("%%EndOPI\n");
7118 writePS("grestore\n");
7119 --opi20Nest;
7120 } else {
7121 dict = opiDict->lookup("1.3");
7122 if (dict.isDict()) {
7123 writePS("%%EndObject\n");
7124 writePS("restore\n");
7125 --opi13Nest;
7126 }
7127 }
7128 }
7129 }
7130 #endif // OPI_SUPPORT
7131
type3D0(GfxState * state,double wx,double wy)7132 void PSOutputDev::type3D0(GfxState *state, double wx, double wy)
7133 {
7134 writePSFmt("{0:.6g} {1:.6g} setcharwidth\n", wx, wy);
7135 writePS("q\n");
7136 t3NeedsRestore = true;
7137 }
7138
type3D1(GfxState * state,double wx,double wy,double llx,double lly,double urx,double ury)7139 void PSOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury)
7140 {
7141 t3WX = wx;
7142 t3WY = wy;
7143 t3LLX = llx;
7144 t3LLY = lly;
7145 t3URX = urx;
7146 t3URY = ury;
7147 delete t3String;
7148 t3String = new GooString();
7149 writePS("q\n");
7150 t3FillColorOnly = true;
7151 t3Cacheable = true;
7152 t3NeedsRestore = true;
7153 }
7154
drawForm(Ref ref)7155 void PSOutputDev::drawForm(Ref ref)
7156 {
7157 writePSFmt("f_{0:d}_{1:d}\n", ref.num, ref.gen);
7158 }
7159
psXObject(Stream * psStream,Stream * level1Stream)7160 void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream)
7161 {
7162 Stream *str;
7163 int c;
7164
7165 if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
7166 str = level1Stream;
7167 } else {
7168 str = psStream;
7169 }
7170 str->reset();
7171 while ((c = str->getChar()) != EOF) {
7172 writePSChar(c);
7173 }
7174 str->close();
7175 }
7176
7177 //~ can nextFunc be reset to 0 -- maybe at the start of each page?
7178 //~ or maybe at the start of each color space / pattern?
cvtFunction(const Function * func,bool invertPSFunction)7179 void PSOutputDev::cvtFunction(const Function *func, bool invertPSFunction)
7180 {
7181 const SampledFunction *func0;
7182 const ExponentialFunction *func2;
7183 const StitchingFunction *func3;
7184 const PostScriptFunction *func4;
7185 int thisFunc, m, n, nSamples, i, j, k;
7186
7187 switch (func->getType()) {
7188
7189 case -1: // identity
7190 writePS("{}\n");
7191 break;
7192
7193 case 0: // sampled
7194 func0 = (const SampledFunction *)func;
7195 thisFunc = nextFunc++;
7196 m = func0->getInputSize();
7197 n = func0->getOutputSize();
7198 nSamples = n;
7199 for (i = 0; i < m; ++i) {
7200 nSamples *= func0->getSampleSize(i);
7201 }
7202 writePSFmt("/xpdfSamples{0:d} [\n", thisFunc);
7203 for (i = 0; i < nSamples; ++i) {
7204 writePSFmt("{0:.6g}\n", func0->getSamples()[i]);
7205 }
7206 writePS("] def\n");
7207 writePSFmt("{{ {0:d} array {1:d} array {2:d} 2 roll\n", 2 * m, m, m + 2);
7208 // [e01] [efrac] x0 x1 ... xm-1
7209 for (i = m - 1; i >= 0; --i) {
7210 // [e01] [efrac] x0 x1 ... xi
7211 writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add\n", func0->getDomainMin(i), (func0->getEncodeMax(i) - func0->getEncodeMin(i)) / (func0->getDomainMax(i) - func0->getDomainMin(i)), func0->getEncodeMin(i));
7212 // [e01] [efrac] x0 x1 ... xi-1 xi'
7213 writePSFmt("dup 0 lt {{ pop 0 }} {{ dup {0:d} gt {{ pop {1:d} }} if }} ifelse\n", func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1);
7214 // [e01] [efrac] x0 x1 ... xi-1 xi'
7215 writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n");
7216 // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi')
7217 writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i + 3, i);
7218 // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi')
7219 writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i + 3, 2 * i + 1);
7220 // [e01] [efrac] x0 x1 ... xi-1 floor(xi')
7221 writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i + 2, 2 * i);
7222 // [e01] [efrac] x0 x1 ... xi-1
7223 }
7224 // [e01] [efrac]
7225 for (i = 0; i < n; ++i) {
7226 // [e01] [efrac] y(0) ... y(i-1)
7227 for (j = 0; j < (1 << m); ++j) {
7228 // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(j-1)
7229 writePSFmt("xpdfSamples{0:d}\n", thisFunc);
7230 k = m - 1;
7231 writePSFmt("{0:d} index {1:d} get\n", i + j + 2, 2 * k + ((j >> k) & 1));
7232 for (k = m - 2; k >= 0; --k) {
7233 writePSFmt("{0:d} mul {1:d} index {2:d} get add\n", func0->getSampleSize(k), i + j + 3, 2 * k + ((j >> k) & 1));
7234 }
7235 if (n > 1) {
7236 writePSFmt("{0:d} mul {1:d} add ", n, i);
7237 }
7238 writePS("get\n");
7239 }
7240 // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1)
7241 for (j = 0; j < m; ++j) {
7242 // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1)
7243 for (k = 0; k < (1 << (m - j)); k += 2) {
7244 // [e01] [efrac] y(0) ... y(i-1) <k/2 s' values> <2^(m-j)-k s values>
7245 writePSFmt("{0:d} index {1:d} get dup\n", i + k / 2 + (1 << (m - j)) - k, j);
7246 writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n");
7247 writePSFmt("{0:d} 1 roll\n", k / 2 + (1 << (m - j)) - k - 1);
7248 }
7249 // [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1)
7250 }
7251 // [e01] [efrac] y(0) ... y(i-1) s
7252 writePSFmt("{0:.6g} mul {1:.6g} add\n", func0->getDecodeMax(i) - func0->getDecodeMin(i), func0->getDecodeMin(i));
7253 writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func0->getRangeMin(i), func0->getRangeMin(i), func0->getRangeMax(i), func0->getRangeMax(i));
7254 // [e01] [efrac] y(0) ... y(i-1) y(i)
7255 }
7256 // [e01] [efrac] y(0) ... y(n-1)
7257 writePSFmt("{0:d} {1:d} roll pop pop \n", n + 2, n);
7258 if (invertPSFunction) {
7259 for (i = 0; i < n; ++i) {
7260 writePSFmt("{0:d} -1 roll ", n);
7261 writePSFmt("{0:.6g} sub {1:.6g} div ", func0->getRangeMin(i), func0->getRangeMax(i) - func0->getRangeMin(i));
7262 }
7263 }
7264 writePS("}\n");
7265 break;
7266
7267 case 2: // exponential
7268 func2 = (const ExponentialFunction *)func;
7269 n = func2->getOutputSize();
7270 writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func2->getDomainMin(0), func2->getDomainMin(0), func2->getDomainMax(0), func2->getDomainMax(0));
7271 // x
7272 for (i = 0; i < n; ++i) {
7273 // x y(0) .. y(i-1)
7274 writePSFmt("{0:d} index {1:.6g} exp {2:.6g} mul {3:.6g} add\n", i, func2->getE(), func2->getC1()[i] - func2->getC0()[i], func2->getC0()[i]);
7275 if (func2->getHasRange()) {
7276 writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func2->getRangeMin(i), func2->getRangeMin(i), func2->getRangeMax(i), func2->getRangeMax(i));
7277 }
7278 }
7279 // x y(0) .. y(n-1)
7280 writePSFmt("{0:d} {1:d} roll pop \n", n + 1, n);
7281 if (invertPSFunction && func2->getHasRange()) {
7282 for (i = 0; i < n; ++i) {
7283 writePSFmt("{0:d} -1 roll ", n);
7284 writePSFmt("{0:.6g} sub {1:.6g} div ", func2->getRangeMin(i), func2->getRangeMax(i) - func2->getRangeMin(i));
7285 }
7286 }
7287 writePS("}\n");
7288 break;
7289
7290 case 3: // stitching
7291 func3 = (const StitchingFunction *)func;
7292 thisFunc = nextFunc++;
7293 for (i = 0; i < func3->getNumFuncs(); ++i) {
7294 cvtFunction(func3->getFunc(i));
7295 writePSFmt("/xpdfFunc{0:d}_{1:d} exch def\n", thisFunc, i);
7296 }
7297 writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func3->getDomainMin(0), func3->getDomainMin(0), func3->getDomainMax(0), func3->getDomainMax(0));
7298 for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
7299 writePSFmt("dup {0:.6g} lt {{ {1:.6g} sub {2:.6g} mul {3:.6g} add xpdfFunc{4:d}_{5:d} }} {{\n", func3->getBounds()[i + 1], func3->getBounds()[i], func3->getScale()[i], func3->getEncode()[2 * i], thisFunc, i);
7300 }
7301 writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add xpdfFunc{3:d}_{4:d}\n", func3->getBounds()[i], func3->getScale()[i], func3->getEncode()[2 * i], thisFunc, i);
7302 for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
7303 writePS("} ifelse\n");
7304 }
7305 if (invertPSFunction && func3->getHasRange()) {
7306 n = func3->getOutputSize();
7307 for (i = 0; i < n; ++i) {
7308 writePSFmt("{0:d} -1 roll ", n);
7309 writePSFmt("{0:.6g} sub {1:.6g} div ", func3->getRangeMin(i), func3->getRangeMax(i) - func3->getRangeMin(i));
7310 }
7311 }
7312 writePS("}\n");
7313 break;
7314
7315 case 4: // PostScript
7316 func4 = (const PostScriptFunction *)func;
7317 if (invertPSFunction) {
7318 GooString *codeString = new GooString(func4->getCodeString());
7319 for (i = codeString->getLength() - 1; i > 0; i--) {
7320 if (codeString->getChar(i) == '}') {
7321 codeString->del(i);
7322 break;
7323 }
7324 }
7325 writePS(codeString->c_str());
7326 writePS("\n");
7327 delete codeString;
7328 n = func4->getOutputSize();
7329 for (i = 0; i < n; ++i) {
7330 writePSFmt("{0:d} -1 roll ", n);
7331 writePSFmt("{0:.6g} sub {1:.6g} div ", func4->getRangeMin(i), func4->getRangeMax(i) - func4->getRangeMin(i));
7332 }
7333 writePS("}\n");
7334 } else {
7335 writePS(func4->getCodeString()->c_str());
7336 writePS("\n");
7337 }
7338 break;
7339 }
7340 }
7341
writePSChar(char c)7342 void PSOutputDev::writePSChar(char c)
7343 {
7344 if (t3String) {
7345 t3String->append(c);
7346 } else {
7347 (*outputFunc)(outputStream, &c, 1);
7348 }
7349 }
7350
writePS(const char * s)7351 void PSOutputDev::writePS(const char *s)
7352 {
7353 if (t3String) {
7354 t3String->append(s);
7355 } else {
7356 (*outputFunc)(outputStream, s, strlen(s));
7357 }
7358 }
7359
writePSBuf(const char * s,int len)7360 void PSOutputDev::writePSBuf(const char *s, int len)
7361 {
7362 if (t3String) {
7363 for (int i = 0; i < len; i++) {
7364 t3String->append(s[i]);
7365 }
7366 } else {
7367 (*outputFunc)(outputStream, s, len);
7368 }
7369 }
7370
writePSFmt(const char * fmt,...)7371 void PSOutputDev::writePSFmt(const char *fmt, ...)
7372 {
7373 va_list args;
7374 GooString *buf;
7375
7376 va_start(args, fmt);
7377 if (t3String) {
7378 t3String->appendfv((char *)fmt, args);
7379 } else {
7380 buf = GooString::formatv((char *)fmt, args);
7381 (*outputFunc)(outputStream, buf->c_str(), buf->getLength());
7382 delete buf;
7383 }
7384 va_end(args);
7385 }
7386
writePSString(const std::string & s)7387 void PSOutputDev::writePSString(const std::string &s)
7388 {
7389 unsigned char *p;
7390 int n, line;
7391 char buf[8];
7392
7393 writePSChar('(');
7394 line = 1;
7395 for (p = (unsigned char *)s.c_str(), n = s.size(); n; ++p, --n) {
7396 if (line >= 64) {
7397 writePSChar('\\');
7398 writePSChar('\n');
7399 line = 0;
7400 }
7401 if (*p == '(' || *p == ')' || *p == '\\') {
7402 writePSChar('\\');
7403 writePSChar((char)*p);
7404 line += 2;
7405 } else if (*p < 0x20 || *p >= 0x80) {
7406 sprintf(buf, "\\%03o", *p);
7407 writePS(buf);
7408 line += 4;
7409 } else {
7410 writePSChar((char)*p);
7411 ++line;
7412 }
7413 }
7414 writePSChar(')');
7415 }
7416
writePSName(const char * s)7417 void PSOutputDev::writePSName(const char *s)
7418 {
7419 const char *p;
7420 char c;
7421
7422 p = s;
7423 while ((c = *p++)) {
7424 if (c <= (char)0x20 || c >= (char)0x7f || c == '(' || c == ')' || c == '<' || c == '>' || c == '[' || c == ']' || c == '{' || c == '}' || c == '/' || c == '%' || c == '\\') {
7425 writePSFmt("#{0:02x}", c & 0xff);
7426 } else {
7427 writePSChar(c);
7428 }
7429 }
7430 }
7431
filterPSName(const GooString * name)7432 GooString *PSOutputDev::filterPSName(const GooString *name)
7433 {
7434 GooString *name2;
7435 char buf[8];
7436 int i;
7437 char c;
7438
7439 name2 = new GooString();
7440
7441 // ghostscript chokes on names that begin with out-of-limits
7442 // numbers, e.g., 1e4foo is handled correctly (as a name), but
7443 // 1e999foo generates a limitcheck error
7444 c = name->getChar(0);
7445 if (c >= '0' && c <= '9') {
7446 name2->append('f');
7447 }
7448
7449 for (i = 0; i < name->getLength(); ++i) {
7450 c = name->getChar(i);
7451 if (c <= (char)0x20 || c >= (char)0x7f || c == '(' || c == ')' || c == '<' || c == '>' || c == '[' || c == ']' || c == '{' || c == '}' || c == '/' || c == '%') {
7452 sprintf(buf, "#%02x", c & 0xff);
7453 name2->append(buf);
7454 } else {
7455 name2->append(c);
7456 }
7457 }
7458 return name2;
7459 }
7460
7461 // Convert GooString to GooString, with appropriate escaping
7462 // of things that can't appear in a label
7463 // This is heavily based on the writePSTextLine() method
filterPSLabel(GooString * label,bool * needParens)7464 GooString *PSOutputDev::filterPSLabel(GooString *label, bool *needParens)
7465 {
7466 int i, step;
7467 bool isNumeric;
7468
7469 // - DSC comments must be printable ASCII; control chars and
7470 // backslashes have to be escaped (we do cheap UCS2-to-ASCII
7471 // conversion by simply ignoring the high byte)
7472 // - parentheses are escaped. this isn't strictly necessary for matched
7473 // parentheses, but shouldn't be a problem
7474 // - lines are limited to 255 chars (we limit to 200 here to allow
7475 // for the keyword, which was emitted by the caller)
7476
7477 GooString *label2 = new GooString();
7478 int labelLength = label->getLength();
7479
7480 if (labelLength == 0) {
7481 isNumeric = false;
7482 } else {
7483 // this gets changed later if we find a non-numeric character
7484 isNumeric = true;
7485 }
7486
7487 if ((labelLength >= 2) && ((label->getChar(0) & 0xff) == 0xfe) && ((label->getChar(1) & 0xff) == 0xff)) {
7488 // UCS2 mode
7489 i = 3;
7490 step = 2;
7491 if ((label->getChar(labelLength - 1) == 0)) {
7492 // prune the trailing null (0x000 for UCS2)
7493 labelLength -= 2;
7494 }
7495 } else {
7496 i = 0;
7497 step = 1;
7498 }
7499 for (int j = 0; i < labelLength && j < 200; i += step) {
7500 char c = label->getChar(i);
7501 if ((c < '0') || (c > '9')) {
7502 isNumeric = false;
7503 }
7504 if (c == '\\') {
7505 label2->append("\\\\");
7506 j += 2;
7507 } else if (c == ')') {
7508 label2->append("\\)");
7509 } else if (c == '(') {
7510 label2->append("\\(");
7511 } else if (c < 0x20 || c > 0x7e) {
7512 GooString *aux = GooString::format("\\{0:03o}", c);
7513 label2->append(aux);
7514 j += 4;
7515 delete aux;
7516 } else {
7517 label2->append(c);
7518 ++j;
7519 }
7520 }
7521 if (needParens) {
7522 *needParens = !(isNumeric);
7523 }
7524 return label2;
7525 }
7526
7527 // Write a DSC-compliant <textline>.
writePSTextLine(const GooString * s)7528 void PSOutputDev::writePSTextLine(const GooString *s)
7529 {
7530 int i, j, step;
7531 int c;
7532
7533 // - DSC comments must be printable ASCII; control chars and
7534 // backslashes have to be escaped (we do cheap Unicode-to-ASCII
7535 // conversion by simply ignoring the high byte)
7536 // - lines are limited to 255 chars (we limit to 200 here to allow
7537 // for the keyword, which was emitted by the caller)
7538 // - lines that start with a left paren are treated as <text>
7539 // instead of <textline>, so we escape a leading paren
7540 if (s->getLength() >= 2 && (s->getChar(0) & 0xff) == 0xfe && (s->getChar(1) & 0xff) == 0xff) {
7541 i = 3;
7542 step = 2;
7543 } else {
7544 i = 0;
7545 step = 1;
7546 }
7547 for (j = 0; i < s->getLength() && j < 200; i += step) {
7548 c = s->getChar(i) & 0xff;
7549 if (c == '\\') {
7550 writePS("\\\\");
7551 j += 2;
7552 } else if (c < 0x20 || c > 0x7e || (j == 0 && c == '(')) {
7553 writePSFmt("\\{0:03o}", c);
7554 j += 4;
7555 } else {
7556 writePSChar(c);
7557 ++j;
7558 }
7559 }
7560 writePS("\n");
7561 }
7562