w32tex
About: TeX Live provides a comprehensive TeX system including all the major TeX-related programs, macro packages, and fonts that are free software. Windows sources.
  Fossies Dox: w32tex-src.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

PSOutputDev.cc
Go to the documentation of this file.
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 Christian Persch <chpe@src.gnome.org>
38 // Copyright (C) 2019 Oliver Sander <oliver.sander@tu-dresden.de>
39 // Copyright (C) 2020 Philipp Knechtges <philipp-dev@knechtges.com>
40 //
41 // To see a description of the changes please see the Changelog file that
42 // came with your tarball or type make ChangeLog if you are building from git
43 //
44 //========================================================================
45 
46 #include <config.h>
47 
48 #include <cstdio>
49 #include <cstddef>
50 #include <cstdarg>
51 #include <csignal>
52 #include <cmath>
53 #include <climits>
54 #include <algorithm>
55 #include <array>
56 #include "goo/GooString.h"
57 #include "poppler-config.h"
58 #include "GlobalParams.h"
59 #include "Object.h"
60 #include "Error.h"
61 #include "Function.h"
62 #include "Gfx.h"
63 #include "GfxState.h"
64 #include "GfxFont.h"
65 #include "UnicodeMap.h"
66 #include <fofi/FoFiType1C.h>
67 #include <fofi/FoFiTrueType.h>
68 #include "Catalog.h"
69 #include "Page.h"
70 #include "Stream.h"
71 #ifdef ENABLE_ZLIB
72 # include "FlateEncoder.h"
73 #endif
74 #ifdef ENABLE_ZLIB_UNCOMPRESS
75 # include "FlateStream.h"
76 #endif
77 #include "Annot.h"
78 #include "XRef.h"
79 #include "PreScanOutputDev.h"
80 #include "FileSpec.h"
81 #include "CharCodeToUnicode.h"
82 #ifdef HAVE_SPLASH
83 # include "splash/Splash.h"
84 # include "splash/SplashBitmap.h"
85 # include "SplashOutputDev.h"
86 #endif
87 #include "PSOutputDev.h"
88 #include "PDFDoc.h"
89 
90 #ifdef USE_CMS
91 # include <lcms2.h>
92 #endif
93 
94 // the MSVC math.h doesn't define this
95 #ifndef M_PI
96 # define M_PI 3.14159265358979323846
97 #endif
98 
99 //------------------------------------------------------------------------
100 
101 // Max size of a slice when rasterizing pages, in pixels.
102 #define rasterizationSliceSize 20000000
103 
104 //------------------------------------------------------------------------
105 // PostScript prolog and setup
106 //------------------------------------------------------------------------
107 
108 // The '~' escapes mark prolog code that is emitted only in certain
109 // levels:
110 //
111 // ~[123][sn]
112 // ^ ^----- s=psLevel*Sep, n=psLevel*
113 // +----- 1=psLevel1*, 2=psLevel2*, 3=psLevel3*
114 
115 static const char *prolog[] = { "/xpdf 75 dict def xpdf begin",
116  "% PDF special state",
117  "/pdfDictSize 15 def",
118  "~1sn",
119  "/pdfStates 64 array def",
120  " 0 1 63 {",
121  " pdfStates exch pdfDictSize dict",
122  " dup /pdfStateIdx 3 index put",
123  " put",
124  " } for",
125  "~123sn",
126  "/pdfSetup {",
127  " /setpagedevice where {",
128  " pop 2 dict begin",
129  " /Policies 1 dict dup begin /PageSize 6 def end def",
130  " { /Duplex true def } if",
131  " currentdict end setpagedevice",
132  " } {",
133  " pop",
134  " } ifelse",
135  "} def",
136  "/pdfSetupPaper {",
137  " % Change paper size, but only if different from previous paper size otherwise",
138  " % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size",
139  " % so we use the same when checking if the size changes.",
140  " /setpagedevice where {",
141  " pop currentpagedevice",
142  " /PageSize known {",
143  " 2 copy",
144  " currentpagedevice /PageSize get aload pop",
145  " exch 4 1 roll",
146  " sub abs 5 gt",
147  " 3 1 roll",
148  " sub abs 5 gt",
149  " or",
150  " } {",
151  " true",
152  " } ifelse",
153  " {",
154  " 2 array astore",
155  " 2 dict begin",
156  " /PageSize exch def",
157  " /ImagingBBox null def",
158  " currentdict end",
159  " setpagedevice",
160  " } {",
161  " pop pop",
162  " } ifelse",
163  " } {",
164  " pop",
165  " } ifelse",
166  "} def",
167  "~1sn",
168  "/pdfOpNames [",
169  " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke",
170  " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender /pdfPatternCS",
171  " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath",
172  "] def",
173  "~123sn",
174  "/pdfStartPage {",
175  "~1sn",
176  " pdfStates 0 get begin",
177  "~23sn",
178  " pdfDictSize dict begin",
179  "~23n",
180  " /pdfFillCS [] def",
181  " /pdfFillXform {} def",
182  " /pdfStrokeCS [] def",
183  " /pdfStrokeXform {} def",
184  "~1n",
185  " /pdfFill 0 def",
186  " /pdfStroke 0 def",
187  "~1s",
188  " /pdfFill [0 0 0 1] def",
189  " /pdfStroke [0 0 0 1] def",
190  "~23sn",
191  " /pdfFill [0] def",
192  " /pdfStroke [0] def",
193  " /pdfFillOP false def",
194  " /pdfStrokeOP false def",
195  "~3sn",
196  " /pdfOPM false def",
197  "~123sn",
198  " /pdfLastFill false def",
199  " /pdfLastStroke false def",
200  " /pdfTextMat [1 0 0 1 0 0] def",
201  " /pdfFontSize 0 def",
202  " /pdfCharSpacing 0 def",
203  " /pdfTextRender 0 def",
204  " /pdfPatternCS false def",
205  " /pdfTextRise 0 def",
206  " /pdfWordSpacing 0 def",
207  " /pdfHorizScaling 1 def",
208  " /pdfTextClipPath [] def",
209  "} def",
210  "/pdfEndPage { end } def",
211  "~23s",
212  "% separation convention operators",
213  "/findcmykcustomcolor where {",
214  " pop",
215  "}{",
216  " /findcmykcustomcolor { 5 array astore } def",
217  "} ifelse",
218  "/setcustomcolor where {",
219  " pop",
220  "}{",
221  " /setcustomcolor {",
222  " exch",
223  " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
224  " 0 4 getinterval cvx",
225  " [ exch /dup load exch { mul exch dup } /forall load",
226  " /pop load dup ] cvx",
227  " ] setcolorspace setcolor",
228  " } def",
229  "} ifelse",
230  "/customcolorimage where {",
231  " pop",
232  "}{",
233  " /customcolorimage {",
234  " gsave",
235  " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
236  " 0 4 getinterval",
237  " [ exch /dup load exch { mul exch dup } /forall load",
238  " /pop load dup ] cvx",
239  " ] setcolorspace",
240  " 10 dict begin",
241  " /ImageType 1 def",
242  " /DataSource exch def",
243  " /ImageMatrix exch def",
244  " /BitsPerComponent exch def",
245  " /Height exch def",
246  " /Width exch def",
247  " /Decode [1 0] def",
248  " currentdict end",
249  " image",
250  " grestore",
251  " } def",
252  "} ifelse",
253  "~123sn",
254  "% PDF color state",
255  "~1n",
256  "/g { dup /pdfFill exch def setgray",
257  " /pdfLastFill true def /pdfLastStroke false def } def",
258  "/G { dup /pdfStroke exch def setgray",
259  " /pdfLastStroke true def /pdfLastFill false def } def",
260  "/fCol {",
261  " pdfLastFill not {",
262  " pdfFill setgray",
263  " /pdfLastFill true def /pdfLastStroke false def",
264  " } if",
265  "} def",
266  "/sCol {",
267  " pdfLastStroke not {",
268  " pdfStroke setgray",
269  " /pdfLastStroke true def /pdfLastFill false def",
270  " } if",
271  "} def",
272  "~1s",
273  "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
274  " /pdfLastFill true def /pdfLastStroke false def } def",
275  "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
276  " /pdfLastStroke true def /pdfLastFill false def } def",
277  "/fCol {",
278  " pdfLastFill not {",
279  " pdfFill aload pop setcmykcolor",
280  " /pdfLastFill true def /pdfLastStroke false def",
281  " } if",
282  "} def",
283  "/sCol {",
284  " pdfLastStroke not {",
285  " pdfStroke aload pop setcmykcolor",
286  " /pdfLastStroke true def /pdfLastFill false def",
287  " } if",
288  "} def",
289  "~3n",
290  "/opm { dup /pdfOPM exch def",
291  " /setoverprintmode where{pop setoverprintmode}{pop}ifelse } def",
292  "~23n",
293  "/cs { /pdfFillXform exch def dup /pdfFillCS exch def",
294  " setcolorspace } def",
295  "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def",
296  " setcolorspace } def",
297  "/sc { pdfLastFill not { pdfFillCS setcolorspace } if",
298  " dup /pdfFill exch def aload pop pdfFillXform setcolor",
299  " /pdfLastFill true def /pdfLastStroke false def } def",
300  "/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } if",
301  " dup /pdfStroke exch def aload pop pdfStrokeXform setcolor",
302  " /pdfLastStroke true def /pdfLastFill false def } def",
303  "/op { /pdfFillOP exch def",
304  " pdfLastFill { pdfFillOP setoverprint } if } def",
305  "/OP { /pdfStrokeOP exch def",
306  " pdfLastStroke { pdfStrokeOP setoverprint } if } def",
307  "/fCol {",
308  " pdfLastFill not {",
309  " pdfFillCS setcolorspace",
310  " pdfFill aload pop pdfFillXform setcolor",
311  " pdfFillOP setoverprint",
312  " /pdfLastFill true def /pdfLastStroke false def",
313  " } if",
314  "} def",
315  "/sCol {",
316  " pdfLastStroke not {",
317  " pdfStrokeCS setcolorspace",
318  " pdfStroke aload pop pdfStrokeXform setcolor",
319  " pdfStrokeOP setoverprint",
320  " /pdfLastStroke true def /pdfLastFill false def",
321  " } if",
322  "} def",
323  "~3s",
324  "/opm { dup /pdfOPM exch def",
325  " /setoverprintmode where{pop setoverprintmode}{pop}ifelse } def",
326  "~23s",
327  "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
328  " /pdfLastFill true def /pdfLastStroke false def } def",
329  "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
330  " /pdfLastStroke true def /pdfLastFill false def } def",
331  "/ck { 6 copy 6 array astore /pdfFill exch def",
332  " findcmykcustomcolor exch setcustomcolor",
333  " /pdfLastFill true def /pdfLastStroke false def } def",
334  "/CK { 6 copy 6 array astore /pdfStroke exch def",
335  " findcmykcustomcolor exch setcustomcolor",
336  " /pdfLastStroke true def /pdfLastFill false def } def",
337  "/op { /pdfFillOP exch def",
338  " pdfLastFill { pdfFillOP setoverprint } if } def",
339  "/OP { /pdfStrokeOP exch def",
340  " pdfLastStroke { pdfStrokeOP setoverprint } if } def",
341  "/fCol {",
342  " pdfLastFill not {",
343  " pdfFill aload length 4 eq {",
344  " setcmykcolor",
345  " }{",
346  " findcmykcustomcolor exch setcustomcolor",
347  " } ifelse",
348  " pdfFillOP setoverprint",
349  " /pdfLastFill true def /pdfLastStroke false def",
350  " } if",
351  "} def",
352  "/sCol {",
353  " pdfLastStroke not {",
354  " pdfStroke aload length 4 eq {",
355  " setcmykcolor",
356  " }{",
357  " findcmykcustomcolor exch setcustomcolor",
358  " } ifelse",
359  " pdfStrokeOP setoverprint",
360  " /pdfLastStroke true def /pdfLastFill false def",
361  " } if",
362  "} def",
363  "~123sn",
364  "% build a font",
365  "/pdfMakeFont {",
366  " 4 3 roll findfont",
367  " 4 2 roll matrix scale makefont",
368  " dup length dict begin",
369  " { 1 index /FID ne { def } { pop pop } ifelse } forall",
370  " /Encoding exch def",
371  " currentdict",
372  " end",
373  " definefont pop",
374  "} def",
375  "/pdfMakeFont16 {",
376  " exch findfont",
377  " dup length dict begin",
378  " { 1 index /FID ne { def } { pop pop } ifelse } forall",
379  " /WMode exch def",
380  " currentdict",
381  " end",
382  " definefont pop",
383  "} def",
384  "~3sn",
385  "/pdfMakeFont16L3 {",
386  " 1 index /CIDFont resourcestatus {",
387  " pop pop 1 index /CIDFont findresource /CIDFontType known",
388  " } {",
389  " false",
390  " } ifelse",
391  " {",
392  " 0 eq { /Identity-H } { /Identity-V } ifelse",
393  " exch 1 array astore composefont pop",
394  " } {",
395  " pdfMakeFont16",
396  " } ifelse",
397  "} def",
398  "~123sn",
399  "% graphics state operators",
400  "~1sn",
401  "/q {",
402  " gsave",
403  " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for",
404  " pdfStates pdfStateIdx 1 add get begin",
405  " pdfOpNames { exch def } forall",
406  "} def",
407  "/Q { end grestore } def",
408  "~23sn",
409  "/q { gsave pdfDictSize dict begin } def",
410  "/Q {",
411  " end grestore",
412  " /pdfLastFill where {",
413  " pop",
414  " pdfLastFill {",
415  " pdfFillOP setoverprint",
416  " } {",
417  " pdfStrokeOP setoverprint",
418  " } ifelse",
419  " } if",
420  "~3sn",
421  " /pdfOPM where {",
422  " pop",
423  " pdfOPM /setoverprintmode where{pop setoverprintmode}{pop}ifelse ",
424  " } if",
425  "~23sn",
426  "} def",
427  "~123sn",
428  "/cm { concat } def",
429  "/d { setdash } def",
430  "/i { setflat } def",
431  "/j { setlinejoin } def",
432  "/J { setlinecap } def",
433  "/M { setmiterlimit } def",
434  "/w { setlinewidth } def",
435  "% path segment operators",
436  "/m { moveto } def",
437  "/l { lineto } def",
438  "/c { curveto } def",
439  "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
440  " neg 0 rlineto closepath } def",
441  "/h { closepath } def",
442  "% path painting operators",
443  "/S { sCol stroke } def",
444  "/Sf { fCol stroke } def",
445  "/f { fCol fill } def",
446  "/f* { fCol eofill } def",
447  "% clipping operators",
448  "/W { clip newpath } def",
449  "/W* { eoclip newpath } def",
450  "/Ws { strokepath clip newpath } def",
451  "% text state operators",
452  "/Tc { /pdfCharSpacing exch def } def",
453  "/Tf { dup /pdfFontSize exch def",
454  " dup pdfHorizScaling mul exch matrix scale",
455  " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
456  " exch findfont exch makefont setfont } def",
457  "/Tr { /pdfTextRender exch def } def",
458  "/Tp { /pdfPatternCS exch def } def",
459  "/Ts { /pdfTextRise exch def } def",
460  "/Tw { /pdfWordSpacing exch def } def",
461  "/Tz { /pdfHorizScaling exch def } def",
462  "% text positioning operators",
463  "/Td { pdfTextMat transform moveto } def",
464  "/Tm { /pdfTextMat exch def } def",
465  "% text string operators",
466  "/xyshow where {",
467  " pop",
468  " /xyshow2 {",
469  " dup length array",
470  " 0 2 2 index length 1 sub {",
471  " 2 index 1 index 2 copy get 3 1 roll 1 add get",
472  " pdfTextMat dtransform",
473  " 4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put",
474  " } for",
475  " exch pop",
476  " xyshow",
477  " } def",
478  "}{",
479  " /xyshow2 {",
480  " currentfont /FontType get 0 eq {",
481  " 0 2 3 index length 1 sub {",
482  " currentpoint 4 index 3 index 2 getinterval show moveto",
483  " 2 copy get 2 index 3 2 roll 1 add get",
484  " pdfTextMat dtransform rmoveto",
485  " } for",
486  " } {",
487  " 0 1 3 index length 1 sub {",
488  " currentpoint 4 index 3 index 1 getinterval show moveto",
489  " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get",
490  " pdfTextMat dtransform rmoveto",
491  " } for",
492  " } ifelse",
493  " pop pop",
494  " } def",
495  "} ifelse",
496  "/cshow where {",
497  " pop",
498  " /xycp {", // xycharpath
499  " 0 3 2 roll",
500  " {",
501  " pop pop currentpoint 3 2 roll",
502  " 1 string dup 0 4 3 roll put false charpath moveto",
503  " 2 copy get 2 index 2 index 1 add get",
504  " pdfTextMat dtransform rmoveto",
505  " 2 add",
506  " } exch cshow",
507  " pop pop",
508  " } def",
509  "}{",
510  " /xycp {", // xycharpath
511  " currentfont /FontType get 0 eq {",
512  " 0 2 3 index length 1 sub {",
513  " currentpoint 4 index 3 index 2 getinterval false charpath moveto",
514  " 2 copy get 2 index 3 2 roll 1 add get",
515  " pdfTextMat dtransform rmoveto",
516  " } for",
517  " } {",
518  " 0 1 3 index length 1 sub {",
519  " currentpoint 4 index 3 index 1 getinterval false charpath moveto",
520  " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get",
521  " pdfTextMat dtransform rmoveto",
522  " } for",
523  " } ifelse",
524  " pop pop",
525  " } def",
526  "} ifelse",
527  "/Tj {",
528  " fCol", // because stringwidth has to draw Type 3 chars
529  " 0 pdfTextRise pdfTextMat dtransform rmoveto",
530  " currentpoint 4 2 roll",
531  " pdfTextRender 1 and 0 eq {",
532  " 2 copy xyshow2",
533  " } if",
534  " pdfTextRender 3 and dup 1 eq exch 2 eq or {",
535  " 3 index 3 index moveto",
536  " 2 copy",
537  " currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
538  " xycp currentpoint stroke moveto",
539  " } if",
540  " pdfTextRender 4 and 0 ne {",
541  " 4 2 roll moveto xycp",
542  " /pdfTextClipPath [ pdfTextClipPath aload pop",
543  " {/moveto cvx}",
544  " {/lineto cvx}",
545  " {/curveto cvx}",
546  " {/closepath cvx}",
547  " pathforall ] def",
548  " currentpoint newpath moveto",
549  " } {",
550  " pop pop pop pop",
551  " } ifelse",
552  " 0 pdfTextRise neg pdfTextMat dtransform rmoveto",
553  "} def",
554  "/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0",
555  " pdfTextMat dtransform rmoveto } def",
556  "/TJmV { 0.001 mul pdfFontSize mul neg 0 exch",
557  " pdfTextMat dtransform rmoveto } def",
558  "/Tclip { pdfTextClipPath cvx exec clip newpath",
559  " /pdfTextClipPath [] def } def",
560  "/Tclip* { pdfTextClipPath cvx exec eoclip newpath",
561  " /pdfTextClipPath [] def } def",
562  "~1ns",
563  "% Level 1 image operators",
564  "/pdfIm1 {",
565  " /pdfImBuf1 4 index string def",
566  " { currentfile pdfImBuf1 readhexstring pop } image",
567  "} def",
568  "/pdfIm1Bin {",
569  " /pdfImBuf1 4 index string def",
570  " { currentfile pdfImBuf1 readstring pop } image",
571  "} def",
572  "~1s",
573  "/pdfIm1Sep {",
574  " /pdfImBuf1 4 index string def",
575  " /pdfImBuf2 4 index string def",
576  " /pdfImBuf3 4 index string def",
577  " /pdfImBuf4 4 index string def",
578  " { currentfile pdfImBuf1 readhexstring pop }",
579  " { currentfile pdfImBuf2 readhexstring pop }",
580  " { currentfile pdfImBuf3 readhexstring pop }",
581  " { currentfile pdfImBuf4 readhexstring pop }",
582  " true 4 colorimage",
583  "} def",
584  "/pdfIm1SepBin {",
585  " /pdfImBuf1 4 index string def",
586  " /pdfImBuf2 4 index string def",
587  " /pdfImBuf3 4 index string def",
588  " /pdfImBuf4 4 index string def",
589  " { currentfile pdfImBuf1 readstring pop }",
590  " { currentfile pdfImBuf2 readstring pop }",
591  " { currentfile pdfImBuf3 readstring pop }",
592  " { currentfile pdfImBuf4 readstring pop }",
593  " true 4 colorimage",
594  "} def",
595  "~1ns",
596  "/pdfImM1 {",
597  " fCol /pdfImBuf1 4 index 7 add 8 idiv string def",
598  " { currentfile pdfImBuf1 readhexstring pop } imagemask",
599  "} def",
600  "/pdfImM1Bin {",
601  " fCol /pdfImBuf1 4 index 7 add 8 idiv string def",
602  " { currentfile pdfImBuf1 readstring pop } imagemask",
603  "} def",
604  "/pdfImStr {",
605  " 2 copy exch length lt {",
606  " 2 copy get exch 1 add exch",
607  " } {",
608  " ()",
609  " } ifelse",
610  "} def",
611  "/pdfImM1a {",
612  " { pdfImStr } imagemask",
613  " pop pop",
614  "} def",
615  "~23sn",
616  "% Level 2/3 image operators",
617  "/pdfImBuf 100 string def",
618  "/pdfImStr {",
619  " 2 copy exch length lt {",
620  " 2 copy get exch 1 add exch",
621  " } {",
622  " ()",
623  " } ifelse",
624  "} def",
625  "/skipEOD {",
626  " { currentfile pdfImBuf readline",
627  " not { pop exit } if",
628  " (%-EOD-) eq { exit } if } loop",
629  "} def",
630  "/pdfIm { image skipEOD } def",
631  "~3sn",
632  "/pdfMask {",
633  " /ReusableStreamDecode filter",
634  " skipEOD",
635  " /maskStream exch def",
636  "} def",
637  "/pdfMaskEnd { maskStream closefile } def",
638  "/pdfMaskInit {",
639  " /maskArray exch def",
640  " /maskIdx 0 def",
641  "} def",
642  "/pdfMaskSrc {",
643  " maskIdx maskArray length lt {",
644  " maskArray maskIdx get",
645  " /maskIdx maskIdx 1 add def",
646  " } {",
647  " ()",
648  " } ifelse",
649  "} def",
650  "~23s",
651  "/pdfImSep {",
652  " findcmykcustomcolor exch",
653  " dup /Width get /pdfImBuf1 exch string def",
654  " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def",
655  " /pdfImDecodeLow exch def",
656  " begin Width Height BitsPerComponent ImageMatrix DataSource end",
657  " /pdfImData exch def",
658  " { pdfImData pdfImBuf1 readstring pop",
659  " 0 1 2 index length 1 sub {",
660  " 1 index exch 2 copy get",
661  " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi",
662  " 255 exch sub put",
663  " } for }",
664  " 6 5 roll customcolorimage",
665  " skipEOD",
666  "} def",
667  "~23sn",
668  "/pdfImM { fCol imagemask skipEOD } def",
669  "~123sn",
670  "/pr { 2 index 2 index 3 2 roll putinterval 4 add } def",
671  "/pdfImClip {",
672  " gsave",
673  " 0 2 4 index length 1 sub {",
674  " dup 4 index exch 2 copy",
675  " get 5 index div put",
676  " 1 add 3 index exch 2 copy",
677  " get 3 index div put",
678  " } for",
679  " pop pop rectclip",
680  "} def",
681  "/pdfImClipEnd { grestore } def",
682  "~23sn",
683  "% shading operators",
684  "/colordelta {",
685  " false 0 1 3 index length 1 sub {",
686  " dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {",
687  " pop true",
688  " } if",
689  " } for",
690  " exch pop exch pop",
691  "} def",
692  "/funcCol { func n array astore } def",
693  "/funcSH {",
694  " dup 0 eq {",
695  " true",
696  " } {",
697  " dup 6 eq {",
698  " false",
699  " } {",
700  " 4 index 4 index funcCol dup",
701  " 6 index 4 index funcCol dup",
702  " 3 1 roll colordelta 3 1 roll",
703  " 5 index 5 index funcCol dup",
704  " 3 1 roll colordelta 3 1 roll",
705  " 6 index 8 index funcCol dup",
706  " 3 1 roll colordelta 3 1 roll",
707  " colordelta or or or",
708  " } ifelse",
709  " } ifelse",
710  " {",
711  " 1 add",
712  " 4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch",
713  " 6 index 6 index 4 index 4 index 4 index funcSH",
714  " 2 index 6 index 6 index 4 index 4 index funcSH",
715  " 6 index 2 index 4 index 6 index 4 index funcSH",
716  " 5 3 roll 3 2 roll funcSH pop pop",
717  " } {",
718  " pop 3 index 2 index add 0.5 mul 3 index 2 index add 0.5 mul",
719  "~23n",
720  " funcCol sc",
721  "~23s",
722  " funcCol aload pop k",
723  "~23sn",
724  " dup 4 index exch mat transform m",
725  " 3 index 3 index mat transform l",
726  " 1 index 3 index mat transform l",
727  " mat transform l pop pop h f*",
728  " } ifelse",
729  "} def",
730  "/axialCol {",
731  " dup 0 lt {",
732  " pop t0",
733  " } {",
734  " dup 1 gt {",
735  " pop t1",
736  " } {",
737  " dt mul t0 add",
738  " } ifelse",
739  " } ifelse",
740  " func n array astore",
741  "} def",
742  "/axialSH {",
743  " dup 0 eq {",
744  " true",
745  " } {",
746  " dup 8 eq {",
747  " false",
748  " } {",
749  " 2 index axialCol 2 index axialCol colordelta",
750  " } ifelse",
751  " } ifelse",
752  " {",
753  " 1 add 3 1 roll 2 copy add 0.5 mul",
754  " dup 4 3 roll exch 4 index axialSH",
755  " exch 3 2 roll axialSH",
756  " } {",
757  " pop 2 copy add 0.5 mul",
758  "~23n",
759  " axialCol sc",
760  "~23s",
761  " axialCol aload pop k",
762  "~23sn",
763  " exch dup dx mul x0 add exch dy mul y0 add",
764  " 3 2 roll dup dx mul x0 add exch dy mul y0 add",
765  " dx abs dy abs ge {",
766  " 2 copy yMin sub dy mul dx div add yMin m",
767  " yMax sub dy mul dx div add yMax l",
768  " 2 copy yMax sub dy mul dx div add yMax l",
769  " yMin sub dy mul dx div add yMin l",
770  " h f*",
771  " } {",
772  " exch 2 copy xMin sub dx mul dy div add xMin exch m",
773  " xMax sub dx mul dy div add xMax exch l",
774  " exch 2 copy xMax sub dx mul dy div add xMax exch l",
775  " xMin sub dx mul dy div add xMin exch l",
776  " h f*",
777  " } ifelse",
778  " } ifelse",
779  "} def",
780  "/radialCol {",
781  " dup t0 lt {",
782  " pop t0",
783  " } {",
784  " dup t1 gt {",
785  " pop t1",
786  " } if",
787  " } ifelse",
788  " func n array astore",
789  "} def",
790  "/radialSH {",
791  " dup 0 eq {",
792  " true",
793  " } {",
794  " dup 8 eq {",
795  " false",
796  " } {",
797  " 2 index dt mul t0 add radialCol",
798  " 2 index dt mul t0 add radialCol colordelta",
799  " } ifelse",
800  " } ifelse",
801  " {",
802  " 1 add 3 1 roll 2 copy add 0.5 mul",
803  " dup 4 3 roll exch 4 index radialSH",
804  " exch 3 2 roll radialSH",
805  " } {",
806  " pop 2 copy add 0.5 mul dt mul t0 add",
807  "~23n",
808  " radialCol sc",
809  "~23s",
810  " radialCol aload pop k",
811  "~23sn",
812  " encl {",
813  " exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
814  " 0 360 arc h",
815  " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
816  " 360 0 arcn h f",
817  " } {",
818  " 2 copy",
819  " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
820  " a1 a2 arcn",
821  " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
822  " a2 a1 arcn h",
823  " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
824  " a1 a2 arc",
825  " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
826  " a2 a1 arc h f",
827  " } ifelse",
828  " } ifelse",
829  "} def",
830  "~123sn",
831  "end",
832  nullptr };
833 
834 static const char *cmapProlog[] = { "/CIDInit /ProcSet findresource begin",
835  "10 dict begin",
836  " begincmap",
837  " /CMapType 1 def",
838  " /CMapName /Identity-H def",
839  " /CIDSystemInfo 3 dict dup begin",
840  " /Registry (Adobe) def",
841  " /Ordering (Identity) def",
842  " /Supplement 0 def",
843  " end def",
844  " 1 begincodespacerange",
845  " <0000> <ffff>",
846  " endcodespacerange",
847  " 0 usefont",
848  " 1 begincidrange",
849  " <0000> <ffff> 0",
850  " endcidrange",
851  " endcmap",
852  " currentdict CMapName exch /CMap defineresource pop",
853  "end",
854  "10 dict begin",
855  " begincmap",
856  " /CMapType 1 def",
857  " /CMapName /Identity-V def",
858  " /CIDSystemInfo 3 dict dup begin",
859  " /Registry (Adobe) def",
860  " /Ordering (Identity) def",
861  " /Supplement 0 def",
862  " end def",
863  " /WMode 1 def",
864  " 1 begincodespacerange",
865  " <0000> <ffff>",
866  " endcodespacerange",
867  " 0 usefont",
868  " 1 begincidrange",
869  " <0000> <ffff> 0",
870  " endcidrange",
871  " endcmap",
872  " currentdict CMapName exch /CMap defineresource pop",
873  "end",
874  "end",
875  nullptr };
876 
877 //------------------------------------------------------------------------
878 // Fonts
879 //------------------------------------------------------------------------
880 
881 struct PSSubstFont
882 {
883  const char *psName; // PostScript name
884  double mWidth; // width of 'm' character
885 };
886 
887 // NB: must be in same order as base14SubstFonts in GfxFont.cc
888 static const PSSubstFont psBase14SubstFonts[14] = { { "Courier", 0.600 },
889  { "Courier-Oblique", 0.600 },
890  { "Courier-Bold", 0.600 },
891  { "Courier-BoldOblique", 0.600 },
892  { "Helvetica", 0.833 },
893  { "Helvetica-Oblique", 0.833 },
894  { "Helvetica-Bold", 0.889 },
895  { "Helvetica-BoldOblique", 0.889 },
896  { "Times-Roman", 0.788 },
897  { "Times-Italic", 0.722 },
898  { "Times-Bold", 0.833 },
899  { "Times-BoldItalic", 0.778 },
900  // the last two are never used for substitution
901  { "Symbol", 0 },
902  { "ZapfDingbats", 0 } };
903 
904 // Mapping from Type 1/1C font file to PS font name.
906 {
908  GooString *psName; // PostScript font name used for this
909  // embedded font file
910 };
911 
912 // Info for 8-bit fonts
914 {
916  int *codeToGID; // code-to-GID mapping for TrueType fonts
917 };
918 
919 // Encoding info for substitute 16-bit font
921 {
924 };
925 
926 //------------------------------------------------------------------------
927 // process colors
928 //------------------------------------------------------------------------
929 
930 #define psProcessCyan 1
931 #define psProcessMagenta 2
932 #define psProcessYellow 4
933 #define psProcessBlack 8
934 #define psProcessCMYK 15
935 
936 //------------------------------------------------------------------------
937 // PSOutCustomColor
938 //------------------------------------------------------------------------
939 
940 class PSOutCustomColor
941 {
942 public:
943  PSOutCustomColor(double cA, double mA, double yA, double kA, GooString *nameA);
945 
948 
949  double c, m, y, k;
952 };
953 
954 PSOutCustomColor::PSOutCustomColor(double cA, double mA, double yA, double kA, GooString *nameA)
955 {
956  c = cA;
957  m = mA;
958  y = yA;
959  k = kA;
960  name = nameA;
961  next = nullptr;
962 }
963 
965 {
966  delete name;
967 }
968 
969 //------------------------------------------------------------------------
970 
971 struct PSOutImgClipRect
972 {
973  int x0, x1, y0, y1;
974 };
975 
976 //------------------------------------------------------------------------
977 
978 struct PSOutPaperSize
979 {
980  PSOutPaperSize(GooString *nameA, int wA, int hA)
981  {
982  name = nameA;
983  w = wA;
984  h = hA;
985  }
986  ~PSOutPaperSize() { delete name; }
987  PSOutPaperSize(const PSOutPaperSize &) = delete;
990  int w, h;
991 };
992 
993 //------------------------------------------------------------------------
994 // DeviceNRecoder
995 //------------------------------------------------------------------------
996 
997 class DeviceNRecoder : public FilterStream
998 {
999 public:
1000  DeviceNRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA);
1001  ~DeviceNRecoder() override;
1002  StreamKind getKind() const override { return strWeird; }
1003  void reset() override;
1004  int getChar() override { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; }
1005  int lookChar() override { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; }
1006  GooString *getPSFilter(int psLevel, const char *indent) override { return nullptr; }
1007  bool isBinary(bool last = true) override { return true; }
1008  bool isEncoder() override { return true; }
1009 
1010 private:
1011  bool fillBuf();
1012 
1013  int width, height;
1015  const Function *func;
1017  int buf[gfxColorMaxComps];
1018  int pixelIdx;
1019  int bufIdx;
1020  int bufSize;
1021 };
1022 
1023 DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA) : FilterStream(strA)
1024 {
1025  width = widthA;
1026  height = heightA;
1027  colorMap = colorMapA;
1028  imgStr = nullptr;
1029  pixelIdx = 0;
1031  bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getAlt()->getNComps();
1032  func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getTintTransformFunc();
1033 }
1034 
1036 {
1037  if (imgStr) {
1038  delete imgStr;
1039  }
1040  if (str->isEncoder()) {
1041  delete str;
1042  }
1043 }
1044 
1045 void DeviceNRecoder::reset()
1046 {
1048  imgStr->reset();
1049 }
1050 
1052 {
1053  unsigned char pixBuf[gfxColorMaxComps];
1054  GfxColor color;
1056  int i;
1057 
1058  if (pixelIdx >= width * height) {
1059  return false;
1060  }
1061  imgStr->getPixel(pixBuf);
1062  colorMap->getColor(pixBuf, &color);
1063  for (i = 0; i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps(); ++i) {
1064  x[i] = colToDbl(color.c[i]);
1065  }
1066  func->transform(x, y);
1067  for (i = 0; i < bufSize; ++i) {
1068  buf[i] = (int)(y[i] * 255 + 0.5);
1069  }
1070  bufIdx = 0;
1071  ++pixelIdx;
1072  return true;
1073 }
1074 
1075 //------------------------------------------------------------------------
1076 // PSOutputDev
1077 //------------------------------------------------------------------------
1078 
1079 extern "C" {
1080 typedef void (*SignalFunc)(int);
1081 }
1082 
1083 static void outputToFile(void *stream, const char *data, int len)
1084 {
1085  fwrite(data, 1, len, (FILE *)stream);
1086 }
1087 
1088 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,
1089  PSForceRasterize forceRasterizeA, bool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA, PSLevel levelA)
1090 {
1091  FILE *f;
1092  PSFileType fileTypeA;
1093 
1094  underlayCbk = nullptr;
1095  underlayCbkData = nullptr;
1096  overlayCbk = nullptr;
1097  overlayCbkData = nullptr;
1098  customCodeCbk = customCodeCbkA;
1099  customCodeCbkData = customCodeCbkDataA;
1100 
1101  fontIDs = nullptr;
1102  t1FontNames = nullptr;
1103  font8Info = nullptr;
1104  font16Enc = nullptr;
1105  imgIDs = nullptr;
1106  formIDs = nullptr;
1107  paperSizes = nullptr;
1108  embFontList = nullptr;
1109  customColors = nullptr;
1110  haveTextClip = false;
1111  t3String = nullptr;
1112  forceRasterize = forceRasterizeA;
1113  psTitle = nullptr;
1114 
1115  // open file or pipe
1116  if (!strcmp(fileName, "-")) {
1117  fileTypeA = psStdout;
1118  f = stdout;
1119  } else if (fileName[0] == '|') {
1120  fileTypeA = psPipe;
1121 #ifdef HAVE_POPEN
1122 # ifndef _WIN32
1123  signal(SIGPIPE, (SignalFunc)SIG_IGN);
1124 # endif
1125  if (!(f = popen(fileName + 1, "w"))) {
1126  error(errIO, -1, "Couldn't run print command '{0:s}'", fileName);
1127  ok = false;
1128  return;
1129  }
1130 #else
1131  error(errIO, -1, "Print commands are not supported ('{0:s}')", fileName);
1132  ok = false;
1133  return;
1134 #endif
1135  } else {
1136  fileTypeA = psFile;
1137  if (!(f = openFile(fileName, "w"))) {
1138  error(errIO, -1, "Couldn't open PostScript file '{0:s}'", fileName);
1139  ok = false;
1140  return;
1141  }
1142  }
1143 
1144  init(outputToFile, f, fileTypeA, psTitleA, docA, pagesA, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, paperWidthA, paperHeightA, noCropA, duplexA, levelA);
1145 }
1146 
1147 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,
1148  int imgURXA, int imgURYA, PSForceRasterize forceRasterizeA, bool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA, PSLevel levelA)
1149 {
1150  underlayCbk = nullptr;
1151  underlayCbkData = nullptr;
1152  overlayCbk = nullptr;
1153  overlayCbkData = nullptr;
1154  customCodeCbk = customCodeCbkA;
1155  customCodeCbkData = customCodeCbkDataA;
1156 
1157  fontIDs = nullptr;
1158  t1FontNames = nullptr;
1159  font8Info = nullptr;
1160  font16Enc = nullptr;
1161  imgIDs = nullptr;
1162  formIDs = nullptr;
1163  paperSizes = nullptr;
1164  embFontList = nullptr;
1165  customColors = nullptr;
1166  haveTextClip = false;
1167  t3String = nullptr;
1168  forceRasterize = forceRasterizeA;
1169  psTitle = nullptr;
1170 
1171  init(outputFuncA, outputStreamA, psGeneric, psTitleA, docA, pagesA, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, paperWidthA, paperHeightA, noCropA, duplexA, levelA);
1172 }
1173 
1175 {
1176  const char *name;
1177  int width;
1178  int height;
1179 };
1180 
1181 static const StandardMedia standardMedia[] = { { "A0", 2384, 3371 }, { "A1", 1685, 2384 }, { "A2", 1190, 1684 }, { "A3", 842, 1190 }, { "A4", 595, 842 }, { "A5", 420, 595 },
1182  { "B4", 729, 1032 }, { "B5", 516, 729 }, { "Letter", 612, 792 }, { "Tabloid", 792, 1224 }, { "Ledger", 1224, 792 }, { "Legal", 612, 1008 },
1183  { "Statement", 396, 612 }, { "Executive", 540, 720 }, { "Folio", 612, 936 }, { "Quarto", 610, 780 }, { "10x14", 720, 1008 }, { nullptr, 0, 0 } };
1184 
1185 /* PLRM specifies a tolerance of 5 points when matching page sizes */
1186 static bool pageDimensionEqual(int a, int b)
1187 {
1188  int aux;
1189  if (unlikely(checkedSubtraction(a, b, &aux))) {
1190  return false;
1191  }
1192  return (abs(aux) < 5);
1193 }
1194 
1195 // Shared initialization of PSOutputDev members.
1196 // Store the values but do not process them so the function that
1197 // created the PSOutputDev can use the various setters to change defaults.
1198 
1199 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,
1200  int paperWidthA, int paperHeightA, bool noCropA, bool duplexA, PSLevel levelA)
1201 {
1202 
1203  if (pagesA.empty()) {
1204  ok = false;
1205  return;
1206  }
1207 
1208  // initialize
1209  postInitDone = false;
1210  embedType1 = true;
1211  embedTrueType = true;
1212  embedCIDPostScript = true;
1213  embedCIDTrueType = true;
1214  fontPassthrough = false;
1215  optimizeColorSpace = false;
1216  passLevel1CustomColor = false;
1217  preloadImagesForms = false;
1218  generateOPI = false;
1219  useASCIIHex = false;
1220  useBinary = false;
1221  enableLZW = true;
1222  enableFlate = true;
1223  rasterResolution = 300;
1224  uncompressPreloadedImages = false;
1225  psCenter = true;
1226  rasterAntialias = false;
1227  displayText = true;
1228  ok = true;
1229  outputFunc = outputFuncA;
1230  outputStream = outputStreamA;
1231  fileType = fileTypeA;
1232  psTitle = (psTitleA ? strdup(psTitleA) : nullptr);
1233  doc = docA;
1234  level = levelA;
1235  pages = pagesA;
1236  mode = modeA;
1237  paperWidth = paperWidthA;
1238  paperHeight = paperHeightA;
1239  noCrop = noCropA;
1240  duplex = duplexA;
1241  imgLLX = imgLLXA;
1242  imgLLY = imgLLYA;
1243  imgURX = imgURXA;
1244  imgURY = imgURYA;
1245  manualCtrl = manualCtrlA;
1246 
1247  xref = nullptr;
1248 
1249  processColors = 0;
1250  inType3Char = false;
1251  inUncoloredPattern = false;
1252  t3FillColorOnly = false;
1253 
1254 #ifdef OPI_SUPPORT
1255  // initialize OPI nesting levels
1256  opi13Nest = 0;
1257  opi20Nest = 0;
1258 #endif
1259 
1260  tx0 = ty0 = -1;
1261  xScale0 = yScale0 = 0;
1262  rotate0 = -1;
1263  clipLLX0 = clipLLY0 = 0;
1264  clipURX0 = clipURY0 = -1;
1265 
1266 #ifdef HAVE_SPLASH
1267  processColorFormatSpecified = false;
1268 #endif
1269 
1270  // initialize sequential page number
1271  seqPage = 1;
1272 }
1273 
1274 // Complete the initialization after the function that created the PSOutputDev
1275 // has had a chance to modify default values with the various setters.
1276 
1278 {
1279  Catalog *catalog;
1280  PDFRectangle *box;
1282  int w, h, i;
1283 
1284  if (postInitDone || !ok) {
1285  return;
1286  }
1287 
1288  postInitDone = true;
1289 
1290  xref = doc->getXRef();
1291  catalog = doc->getCatalog();
1292 
1293  if (paperWidth < 0 || paperHeight < 0) {
1294  paperMatch = true;
1295  } else {
1296  paperMatch = false;
1297  }
1298 
1299  paperSizes = new std::vector<PSOutPaperSize *>();
1300  for (const int pg : pages) {
1301  Page *page = catalog->getPage(pg);
1302  if (page == nullptr)
1303  paperMatch = false;
1304  if (!paperMatch) {
1305  w = paperWidth;
1306  h = paperHeight;
1307  if (w < 0 || h < 0) {
1308  // Unable to obtain a paper size from the document and no page size
1309  // specified. In this case use A4 as the page size to ensure the PS output is
1310  // valid. This will only occur if the PDF is very broken.
1311  w = 595;
1312  h = 842;
1313  }
1314  } else if (noCrop) {
1315  w = (int)ceil(page->getMediaWidth());
1316  h = (int)ceil(page->getMediaHeight());
1317  } else {
1318  w = (int)ceil(page->getCropWidth());
1319  h = (int)ceil(page->getCropHeight());
1320  }
1321  if (paperMatch) {
1322  const int pageRotate = page->getRotate();
1323  if (pageRotate == 90 || pageRotate == 270)
1324  std::swap(w, h);
1325  }
1326  if (w > paperWidth)
1327  paperWidth = w;
1328  if (h > paperHeight)
1329  paperHeight = h;
1330  for (i = 0; i < (int)paperSizes->size(); ++i) {
1331  size = (*paperSizes)[i];
1332  if (pageDimensionEqual(w, size->w) && pageDimensionEqual(h, size->h))
1333  break;
1334  }
1335  if (i == (int)paperSizes->size()) {
1336  const StandardMedia *media = standardMedia;
1337  GooString *name = nullptr;
1338  while (media->name) {
1339  if (pageDimensionEqual(w, media->width) && pageDimensionEqual(h, media->height)) {
1340  name = new GooString(media->name);
1341  w = media->width;
1342  h = media->height;
1343  break;
1344  }
1345  media++;
1346  }
1347  if (!name)
1348  name = GooString::format("{0:d}x{1:d}mm", int(w * 25.4 / 72), int(h * 25.4 / 72));
1349  paperSizes->push_back(new PSOutPaperSize(name, w, h));
1350  }
1351  pagePaperSize.insert(std::pair<int, int>(pg, i));
1352  if (!paperMatch)
1353  break; // we only need one entry when all pages are the same size
1354  }
1355  if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) {
1356  imgLLX = imgLLY = 0;
1357  imgURX = paperWidth;
1358  imgURY = paperHeight;
1359  }
1360  std::vector<int> pageList;
1361  if (mode == psModeForm) {
1362  pageList.push_back(pages[0]);
1363  } else {
1364  pageList = pages;
1365  }
1366 
1367  // initialize fontIDs, fontFileIDs, and fontFileNames lists
1368  fontIDSize = 64;
1369  fontIDLen = 0;
1370  fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref));
1371  for (i = 0; i < 14; ++i) {
1372  fontNames.emplace(psBase14SubstFonts[i].psName);
1373  }
1374  t1FontNameSize = 64;
1375  t1FontNameLen = 0;
1377  font8InfoLen = 0;
1378  font8InfoSize = 0;
1379  font16EncLen = 0;
1380  font16EncSize = 0;
1381  imgIDLen = 0;
1382  imgIDSize = 0;
1383  formIDLen = 0;
1384  formIDSize = 0;
1385 
1386  numSaves = 0;
1387  numTilingPatterns = 0;
1388  nextFunc = 0;
1389 
1390 #ifdef HAVE_SPLASH
1391  // set some default process color format if none is set
1392  if (!processColorFormatSpecified) {
1393  if (level == psLevel1) {
1394  processColorFormat = splashModeMono8;
1396  processColorFormat = splashModeCMYK8;
1397  }
1398 # ifdef USE_CMS
1399  else if (getDisplayProfile()) {
1400  auto processcolorspace = cmsGetColorSpace(getDisplayProfile().get());
1401  if (processcolorspace == cmsSigCmykData) {
1402  processColorFormat = splashModeCMYK8;
1403  } else if (processcolorspace == cmsSigGrayData) {
1404  processColorFormat = splashModeMono8;
1405  } else {
1406  processColorFormat = splashModeRGB8;
1407  }
1408  }
1409 # endif
1410  else {
1411  processColorFormat = splashModeRGB8;
1412  }
1413  }
1414 
1415  // check for consistency between the processColorFormat the LanguageLevel and other settings
1416  if (level == psLevel1 && processColorFormat != splashModeMono8) {
1417  error(errConfig, -1,
1418  "Conflicting settings between LanguageLevel=psLevel1 and processColorFormat."
1419  " Resetting processColorFormat to MONO8.");
1420  processColorFormat = splashModeMono8;
1421  } else if ((level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep || globalParams->getOverprintPreview()) && processColorFormat != splashModeCMYK8) {
1422  error(errConfig, -1,
1423  "Conflicting settings between LanguageLevel and/or overprint simulation, and processColorFormat."
1424  " Resetting processColorFormat to CMYK8.");
1425  processColorFormat = splashModeCMYK8;
1426  }
1427 # ifdef USE_CMS
1428  if (getDisplayProfile()) {
1429  auto processcolorspace = cmsGetColorSpace(getDisplayProfile().get());
1430  if (processColorFormat == splashModeCMYK8) {
1431  if (processcolorspace != cmsSigCmykData) {
1432  error(errConfig, -1, "Mismatch between processColorFormat=CMYK8 and ICC profile color format.");
1433  }
1434  } else if (processColorFormat == splashModeMono8) {
1435  if (processcolorspace != cmsSigGrayData) {
1436  error(errConfig, -1, "Mismatch between processColorFormat=MONO8 and ICC profile color format.");
1437  }
1438  } else if (processColorFormat == splashModeRGB8) {
1439  if (processcolorspace != cmsSigRgbData) {
1440  error(errConfig, -1, "Mismatch between processColorFormat=RGB8 and ICC profile color format.");
1441  }
1442  }
1443  }
1444 # endif
1445 #endif
1446 
1447  // initialize embedded font resource comment list
1448  embFontList = new GooString();
1449 
1450  if (!manualCtrl) {
1451  Page *page;
1452  // this check is needed in case the document has zero pages
1453  if ((page = doc->getPage(pageList[0]))) {
1454  writeHeader(pageList.size(), page->getMediaBox(), page->getCropBox(), page->getRotate(), psTitle);
1455  } else {
1456  error(errSyntaxError, -1, "Invalid page {0:d}", pageList[0]);
1457  box = new PDFRectangle(0, 0, 1, 1);
1458  writeHeader(pageList.size(), box, box, 0, psTitle);
1459  delete box;
1460  }
1461  if (mode != psModeForm) {
1462  writePS("%%BeginProlog\n");
1463  }
1464  writeXpdfProcset();
1465  if (mode != psModeForm) {
1466  writePS("%%EndProlog\n");
1467  writePS("%%BeginSetup\n");
1468  }
1469  writeDocSetup(catalog, pageList, duplex);
1470  if (mode != psModeForm) {
1471  writePS("%%EndSetup\n");
1472  }
1473  }
1474 }
1475 
1477 {
1478  PSOutCustomColor *cc;
1479  int i;
1480 
1481  if (ok) {
1482  if (!postInitDone) {
1483  postInit();
1484  }
1485  if (!manualCtrl) {
1486  writePS("%%Trailer\n");
1487  writeTrailer();
1488  if (mode != psModeForm) {
1489  writePS("%%EOF\n");
1490  }
1491  }
1492  if (fileType == psFile) {
1493  fclose((FILE *)outputStream);
1494  }
1495 #ifdef HAVE_POPEN
1496  else if (fileType == psPipe) {
1497  pclose((FILE *)outputStream);
1498 # ifndef _WIN32
1499  signal(SIGPIPE, (SignalFunc)SIG_DFL);
1500 # endif
1501  }
1502 #endif
1503  }
1504  if (paperSizes) {
1505  for (auto entry : *paperSizes) {
1506  delete entry;
1507  }
1508  delete paperSizes;
1509  }
1510  if (embFontList) {
1511  delete embFontList;
1512  }
1513  if (fontIDs) {
1514  gfree(fontIDs);
1515  }
1516  if (t1FontNames) {
1517  for (i = 0; i < t1FontNameLen; ++i) {
1518  delete t1FontNames[i].psName;
1519  }
1520  gfree(t1FontNames);
1521  }
1522  if (font8Info) {
1523  for (i = 0; i < font8InfoLen; ++i) {
1524  gfree(font8Info[i].codeToGID);
1525  }
1526  gfree(font8Info);
1527  }
1528  if (font16Enc) {
1529  for (i = 0; i < font16EncLen; ++i) {
1530  if (font16Enc[i].enc) {
1531  delete font16Enc[i].enc;
1532  }
1533  }
1534  gfree(font16Enc);
1535  }
1536  gfree(imgIDs);
1537  gfree(formIDs);
1538  while (customColors) {
1539  cc = customColors;
1540  customColors = cc->next;
1541  delete cc;
1542  }
1543  gfree(psTitle);
1544  delete t3String;
1545 }
1546 
1547 void PSOutputDev::writeHeader(int nPages, const PDFRectangle *mediaBox, const PDFRectangle *cropBox, int pageRotate, const char *title)
1548 {
1550  double x1, y1, x2, y2;
1551 
1552  switch (mode) {
1553  case psModePS:
1554  writePS("%!PS-Adobe-3.0\n");
1555  break;
1556  case psModeEPS:
1557  writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
1558  break;
1559  case psModeForm:
1560  writePS("%!PS-Adobe-3.0 Resource-Form\n");
1561  break;
1562  }
1563  writePSFmt("%Produced by poppler pdftops version: {0:s} (http://poppler.freedesktop.org)\n", PACKAGE_VERSION);
1564  Object info = xref->getDocInfo();
1565  if (info.isDict()) {
1566  Object obj1 = info.dictLookup("Creator");
1567  if (obj1.isString()) {
1568  writePS("%%Creator: ");
1569  writePSTextLine(obj1.getString());
1570  }
1571  }
1572  if (title) {
1573  char *sanitizedTitle = strdup(title);
1574  for (size_t i = 0; i < strlen(sanitizedTitle); ++i) {
1575  if (sanitizedTitle[i] == '\n' || sanitizedTitle[i] == '\r') {
1576  sanitizedTitle[i] = ' ';
1577  }
1578  }
1579  writePSFmt("%%Title: {0:s}\n", sanitizedTitle);
1580  free(sanitizedTitle);
1581  }
1582  writePSFmt("%%LanguageLevel: {0:d}\n", (level == psLevel1 || level == psLevel1Sep) ? 1 : (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
1583  if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
1584  writePS("%%DocumentProcessColors: (atend)\n");
1585  writePS("%%DocumentCustomColors: (atend)\n");
1586  }
1587  writePS("%%DocumentSuppliedResources: (atend)\n");
1588  if ((level == psLevel1 || level == psLevel1Sep) && useBinary) {
1589  writePS("%%DocumentData: Binary\n");
1590  }
1591 
1592  switch (mode) {
1593  case psModePS:
1594  for (std::size_t i = 0; i < paperSizes->size(); ++i) {
1595  size = (*paperSizes)[i];
1596  writePSFmt("%%{0:s} {1:t} {2:d} {3:d} 0 () ()\n", i == 0 ? "DocumentMedia:" : "+", size->name, size->w, size->h);
1597  }
1598  writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight);
1599  writePSFmt("%%Pages: {0:d}\n", nPages);
1600  writePS("%%EndComments\n");
1601  if (!paperMatch) {
1602  size = (*paperSizes)[0];
1603  writePS("%%BeginDefaults\n");
1604  writePSFmt("%%PageMedia: {0:t}\n", size->name);
1605  writePS("%%EndDefaults\n");
1606  }
1607  break;
1608  case psModeEPS:
1609  epsX1 = cropBox->x1;
1610  epsY1 = cropBox->y1;
1611  epsX2 = cropBox->x2;
1612  epsY2 = cropBox->y2;
1613  if (pageRotate == 0 || pageRotate == 180) {
1614  x1 = epsX1;
1615  y1 = epsY1;
1616  x2 = epsX2;
1617  y2 = epsY2;
1618  } else { // pageRotate == 90 || pageRotate == 270
1619  x1 = 0;
1620  y1 = 0;
1621  x2 = epsY2 - epsY1;
1622  y2 = epsX2 - epsX1;
1623  }
1624  writePSFmt("%%BoundingBox: {0:d} {1:d} {2:d} {3:d}\n", (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2));
1625  writePSFmt("%%HiResBoundingBox: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", x1, y1, x2, y2);
1626  writePS("%%DocumentSuppliedResources: (atend)\n");
1627  writePS("%%EndComments\n");
1628  break;
1629  case psModeForm:
1630  writePS("%%EndComments\n");
1631  writePS("32 dict dup begin\n");
1632  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));
1633  writePS("/FormType 1 def\n");
1634  writePS("/Matrix [1 0 0 1 0 0] def\n");
1635  break;
1636  }
1637 }
1638 
1640 {
1641  bool lev1, lev2, lev3, sep, nonSep;
1642  const char **p;
1643  const char *q;
1644 
1645  writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", "3.00");
1646  writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright);
1647  lev1 = lev2 = lev3 = sep = nonSep = true;
1648  for (p = prolog; *p; ++p) {
1649  if ((*p)[0] == '~') {
1650  lev1 = lev2 = lev3 = sep = nonSep = false;
1651  for (q = *p + 1; *q; ++q) {
1652  switch (*q) {
1653  case '1':
1654  lev1 = true;
1655  break;
1656  case '2':
1657  lev2 = true;
1658  break;
1659  case '3':
1660  lev3 = true;
1661  break;
1662  case 's':
1663  sep = true;
1664  break;
1665  case 'n':
1666  nonSep = true;
1667  break;
1668  }
1669  }
1670  } else if ((level == psLevel1 && lev1 && nonSep) || (level == psLevel1Sep && lev1 && sep) || (level == psLevel1Sep && lev2 && sep && getPassLevel1CustomColor()) || (level == psLevel2 && lev2 && nonSep)
1671  || (level == psLevel2Sep && lev2 && sep) || (level == psLevel3 && lev3 && nonSep) || (level == psLevel3Sep && lev3 && sep)) {
1672  writePSFmt("{0:s}\n", *p);
1673  }
1674  }
1675  writePS("%%EndResource\n");
1676 
1677  if (level >= psLevel3) {
1678  for (p = cmapProlog; *p; ++p) {
1679  writePSFmt("{0:s}\n", *p);
1680  }
1681  }
1682 }
1683 
1684 void PSOutputDev::writeDocSetup(Catalog *catalog, const std::vector<int> &pageList, bool duplexA)
1685 {
1686  Page *page;
1687  Dict *resDict;
1688  Annots *annots;
1689  Object *acroForm;
1690  GooString *s;
1691 
1692  if (mode == psModeForm) {
1693  // swap the form and xpdf dicts
1694  writePS("xpdf end begin dup begin\n");
1695  } else {
1696  writePS("xpdf begin\n");
1697  }
1698  for (const int pg : pageList) {
1699  page = doc->getPage(pg);
1700  if (!page) {
1701  error(errSyntaxError, -1, "Failed writing resources for page {0:d}", pg);
1702  continue;
1703  }
1704  if ((resDict = page->getResourceDict())) {
1705  setupResources(resDict);
1706  }
1707  annots = page->getAnnots();
1708  for (int i = 0; i < annots->getNumAnnots(); ++i) {
1709  Object obj1 = annots->getAnnot(i)->getAppearanceResDict();
1710  if (obj1.isDict()) {
1711  setupResources(obj1.getDict());
1712  }
1713  }
1714  }
1715  if ((acroForm = catalog->getAcroForm()) && acroForm->isDict()) {
1716  Object obj1 = acroForm->dictLookup("DR");
1717  if (obj1.isDict()) {
1718  setupResources(obj1.getDict());
1719  }
1720  obj1 = acroForm->dictLookup("Fields");
1721  if (obj1.isArray()) {
1722  for (int i = 0; i < obj1.arrayGetLength(); ++i) {
1723  Object obj2 = obj1.arrayGet(i);
1724  if (obj2.isDict()) {
1725  Object obj3 = obj2.dictLookup("DR");
1726  if (obj3.isDict()) {
1727  setupResources(obj3.getDict());
1728  }
1729  }
1730  }
1731  }
1732  }
1733  if (mode != psModeForm) {
1734  if (mode != psModeEPS && !manualCtrl) {
1735  writePSFmt("{0:s} pdfSetup\n", duplexA ? "true" : "false");
1736  if (!paperMatch) {
1737  writePSFmt("{0:d} {1:d} pdfSetupPaper\n", paperWidth, paperHeight);
1738  }
1739  }
1740 #ifdef OPI_SUPPORT
1741  if (generateOPI) {
1742  writePS("/opiMatrix matrix currentmatrix def\n");
1743  }
1744 #endif
1745  }
1746  if (customCodeCbk) {
1747  if ((s = (*customCodeCbk)(this, psOutCustomDocSetup, 0, customCodeCbkData))) {
1748  writePS(s->c_str());
1749  delete s;
1750  }
1751  }
1752 }
1753 
1755 {
1756  if (mode != psModeForm) {
1757  writePS("pdfEndPage\n");
1758  }
1759 }
1760 
1762 {
1763  PSOutCustomColor *cc;
1764 
1765  if (mode == psModeForm) {
1766  writePS("/Foo exch /Form defineresource pop\n");
1767  } else {
1768  writePS("end\n");
1769  writePS("%%DocumentSuppliedResources:\n");
1770  writePS(embFontList->c_str());
1771  if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
1772  writePS("%%DocumentProcessColors:");
1773  if (processColors & psProcessCyan) {
1774  writePS(" Cyan");
1775  }
1777  writePS(" Magenta");
1778  }
1780  writePS(" Yellow");
1781  }
1782  if (processColors & psProcessBlack) {
1783  writePS(" Black");
1784  }
1785  writePS("\n");
1786  writePS("%%DocumentCustomColors:");
1787  for (cc = customColors; cc; cc = cc->next) {
1788  writePS(" ");
1789  writePSString(cc->name->toStr());
1790  }
1791  writePS("\n");
1792  writePS("%%CMYKCustomColor:\n");
1793  for (cc = customColors; cc; cc = cc->next) {
1794  writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", cc->c, cc->m, cc->y, cc->k);
1795  writePSString(cc->name->toStr());
1796  writePS("\n");
1797  }
1798  }
1799  }
1800 }
1801 
1802 void PSOutputDev::setupResources(Dict *resDict)
1803 {
1804  bool skip;
1805 
1806  setupFonts(resDict);
1807  setupImages(resDict);
1808  setupForms(resDict);
1809 
1810  //----- recursively scan XObjects
1811  Object xObjDict = resDict->lookup("XObject");
1812  if (xObjDict.isDict()) {
1813  for (int i = 0; i < xObjDict.dictGetLength(); ++i) {
1814 
1815  // avoid infinite recursion on XObjects
1816  skip = false;
1817  const Object &xObjRef = xObjDict.dictGetValNF(i);
1818  if (xObjRef.isRef()) {
1819  Ref ref0 = xObjRef.getRef();
1820  if (resourceIDs.find(ref0.num) != resourceIDs.end()) {
1821  skip = true;
1822  } else {
1823  resourceIDs.insert(ref0.num);
1824  }
1825  }
1826  if (!skip) {
1827 
1828  // process the XObject's resource dictionary
1829  Object xObj = xObjDict.dictGetVal(i);
1830  if (xObj.isStream()) {
1831  Object resObj = xObj.streamGetDict()->lookup("Resources");
1832  if (resObj.isDict()) {
1833  setupResources(resObj.getDict());
1834  }
1835  }
1836  }
1837  }
1838  }
1839 
1840  //----- recursively scan Patterns
1841  Object patDict = resDict->lookup("Pattern");
1842  if (patDict.isDict()) {
1843  inType3Char = true;
1844  for (int i = 0; i < patDict.dictGetLength(); ++i) {
1845 
1846  // avoid infinite recursion on Patterns
1847  skip = false;
1848  const Object &patRef = patDict.dictGetValNF(i);
1849  if (patRef.isRef()) {
1850  Ref ref0 = patRef.getRef();
1851  if (resourceIDs.find(ref0.num) != resourceIDs.end()) {
1852  skip = true;
1853  } else {
1854  resourceIDs.insert(ref0.num);
1855  }
1856  }
1857  if (!skip) {
1858 
1859  // process the Pattern's resource dictionary
1860  Object pat = patDict.dictGetVal(i);
1861  if (pat.isStream()) {
1862  Object resObj = pat.streamGetDict()->lookup("Resources");
1863  if (resObj.isDict()) {
1864  setupResources(resObj.getDict());
1865  }
1866  }
1867  }
1868  }
1869  inType3Char = false;
1870  }
1871 }
1872 
1873 void PSOutputDev::setupFonts(Dict *resDict)
1874 {
1875  Ref r;
1876  GfxFontDict *gfxFontDict;
1877  GfxFont *font;
1878  int i;
1879 
1880  gfxFontDict = nullptr;
1881  const Object &obj1 = resDict->lookupNF("Font");
1882  if (obj1.isRef()) {
1883  Object obj2 = obj1.fetch(xref);
1884  if (obj2.isDict()) {
1885  r = obj1.getRef();
1886  gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict());
1887  }
1888  } else if (obj1.isDict()) {
1889  gfxFontDict = new GfxFontDict(xref, nullptr, obj1.getDict());
1890  }
1891  if (gfxFontDict) {
1892  for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
1893  if ((font = gfxFontDict->getFont(i))) {
1894  setupFont(font, resDict);
1895  }
1896  }
1897  delete gfxFontDict;
1898  }
1899 }
1900 
1901 void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict)
1902 {
1903  GfxFontLoc *fontLoc;
1904  GooString *psName;
1905  char buf[16];
1906  bool subst;
1907  const UnicodeMap *uMap;
1908  const char *charName;
1909  double xs, ys;
1910  int code;
1911  double w1, w2;
1912  int i, j;
1913 
1914  // check if font is already set up
1915  for (i = 0; i < fontIDLen; ++i) {
1916  if (fontIDs[i] == *font->getID()) {
1917  return;
1918  }
1919  }
1920 
1921  // add entry to fontIDs list
1922  if (fontIDLen >= fontIDSize) {
1923  fontIDSize += 64;
1924  fontIDs = (Ref *)greallocn(fontIDs, fontIDSize, sizeof(Ref));
1925  }
1926  fontIDs[fontIDLen++] = *font->getID();
1927 
1928  psName = nullptr;
1929  xs = ys = 1;
1930  subst = false;
1931 
1932  if (font->getType() == fontType3) {
1933  psName = GooString::format("T3_{0:d}_{1:d}", font->getID()->num, font->getID()->gen);
1934  setupType3Font(font, psName, parentResDict);
1935  } else {
1936  fontLoc = font->locateFont(xref, this);
1937  if (fontLoc != nullptr) {
1938  switch (fontLoc->locType) {
1939  case gfxFontLocEmbedded:
1940  switch (fontLoc->fontType) {
1941  case fontType1:
1942  // this assumes that the PS font name matches the PDF font name
1943  psName = font->getEmbeddedFontName() ? font->getEmbeddedFontName()->copy() : new GooString();
1944  setupEmbeddedType1Font(&fontLoc->embFontID, psName);
1945  break;
1946  case fontType1C:
1947  psName = makePSFontName(font, &fontLoc->embFontID);
1948  setupEmbeddedType1CFont(font, &fontLoc->embFontID, psName);
1949  break;
1950  case fontType1COT:
1951  psName = makePSFontName(font, &fontLoc->embFontID);
1952  setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID, psName);
1953  break;
1954  case fontTrueType:
1955  case fontTrueTypeOT:
1956  psName = makePSFontName(font, font->getID());
1957  setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID, psName);
1958  break;
1959  case fontCIDType0C:
1960  psName = makePSFontName(font, &fontLoc->embFontID);
1961  setupEmbeddedCIDType0Font(font, &fontLoc->embFontID, psName);
1962  break;
1963  case fontCIDType2:
1964  case fontCIDType2OT:
1965  psName = makePSFontName(font, font->getID());
1966  //~ should check to see if font actually uses vertical mode
1967  setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID, psName, true);
1968  break;
1969  case fontCIDType0COT:
1970  psName = makePSFontName(font, &fontLoc->embFontID);
1971  setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID, psName);
1972  break;
1973  default:
1974  break;
1975  }
1976  break;
1977  case gfxFontLocExternal:
1978  //~ add cases for external 16-bit fonts
1979  switch (fontLoc->fontType) {
1980  case fontType1:
1981  if (font->getEmbeddedFontName()) {
1982  // this assumes that the PS font name matches the PDF font name
1983  psName = font->getEmbeddedFontName()->copy();
1984  } else {
1985  //~ this won't work -- the PS font name won't match
1986  psName = makePSFontName(font, font->getID());
1987  }
1988  setupExternalType1Font(fontLoc->path, psName);
1989  break;
1990  case fontTrueType:
1991  case fontTrueTypeOT:
1992  psName = makePSFontName(font, font->getID());
1993  setupExternalTrueTypeFont(font, fontLoc->path, psName);
1994  break;
1995  case fontCIDType2:
1996  case fontCIDType2OT:
1997  psName = makePSFontName(font, font->getID());
1998  //~ should check to see if font actually uses vertical mode
1999  setupExternalCIDTrueTypeFont(font, fontLoc->path, psName, true);
2000  break;
2001  default:
2002  break;
2003  }
2004  break;
2005  case gfxFontLocResident:
2006  psName = fontLoc->path->copy();
2007  break;
2008  }
2009  }
2010 
2011  if (!psName) {
2012  if (font->isCIDFont()) {
2013  error(errSyntaxError, -1, "Couldn't find a font to substitute for '{0:s}' ('{1:s}' character collection)", font->getName() ? font->getName()->c_str() : "(unnamed)",
2014  ((GfxCIDFont *)font)->getCollection() ? ((GfxCIDFont *)font)->getCollection()->c_str() : "(unknown)");
2015  if (font16EncLen >= font16EncSize) {
2016  font16EncSize += 16;
2018  }
2019  font16Enc[font16EncLen].fontID = *font->getID();
2020  font16Enc[font16EncLen].enc = nullptr;
2021  ++font16EncLen;
2022  } else {
2023  error(errSyntaxError, -1, "Couldn't find a font to substitute for '{0:s}'", font->getName() ? font->getName()->c_str() : "(unnamed)");
2024  }
2025  delete fontLoc;
2026  return;
2027  }
2028 
2029  // scale substituted 8-bit fonts
2030  if (fontLoc->locType == gfxFontLocResident && fontLoc->substIdx >= 0) {
2031  subst = true;
2032  for (code = 0; code < 256; ++code) {
2033  if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) && charName[0] == 'm' && charName[1] == '\0') {
2034  break;
2035  }
2036  }
2037  if (code < 256) {
2038  w1 = ((Gfx8BitFont *)font)->getWidth(code);
2039  } else {
2040  w1 = 0;
2041  }
2042  w2 = psBase14SubstFonts[fontLoc->substIdx].mWidth;
2043  xs = w1 / w2;
2044  if (xs < 0.1) {
2045  xs = 1;
2046  }
2047  }
2048 
2049  // handle encodings for substituted CID fonts
2050  if (fontLoc->locType == gfxFontLocResident && fontLoc->fontType >= fontCIDType0) {
2051  subst = true;
2052  if (font16EncLen >= font16EncSize) {
2053  font16EncSize += 16;
2055  }
2056  font16Enc[font16EncLen].fontID = *font->getID();
2057  if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding->toStr()))) {
2058  font16Enc[font16EncLen].enc = fontLoc->encoding->copy();
2059  } else {
2060  error(errSyntaxError, -1, "Couldn't find Unicode map for 16-bit font encoding '{0:t}'", fontLoc->encoding);
2061  font16Enc[font16EncLen].enc = nullptr;
2062  }
2063  ++font16EncLen;
2064  }
2065 
2066  delete fontLoc;
2067  }
2068 
2069  // generate PostScript code to set up the font
2070  if (font->isCIDFont()) {
2071  if (level == psLevel3 || level == psLevel3Sep) {
2072  writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n", font->getID()->num, font->getID()->gen, psName, font->getWMode());
2073  } else {
2074  writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n", font->getID()->num, font->getID()->gen, psName, font->getWMode());
2075  }
2076  } else {
2077  writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n", font->getID()->num, font->getID()->gen, psName, xs, ys);
2078  for (i = 0; i < 256; i += 8) {
2079  writePS((char *)((i == 0) ? "[ " : " "));
2080  for (j = 0; j < 8; ++j) {
2081  if (font->getType() == fontTrueType && !subst && !((Gfx8BitFont *)font)->getHasEncoding()) {
2082  sprintf(buf, "c%02x", i + j);
2083  charName = buf;
2084  } else {
2085  charName = ((Gfx8BitFont *)font)->getCharName(i + j);
2086  }
2087  writePS("/");
2088  writePSName(charName ? charName : (char *)".notdef");
2089  // the empty name is legal in PDF and PostScript, but PostScript
2090  // uses a double-slash (//...) for "immediately evaluated names",
2091  // so we need to add a space character here
2092  if (charName && !charName[0]) {
2093  writePS(" ");
2094  }
2095  }
2096  writePS((i == 256 - 8) ? (char *)"]\n" : (char *)"\n");
2097  }
2098  writePS("pdfMakeFont\n");
2099  }
2100 
2101  delete psName;
2102 }
2103 
2105 {
2106  static const char hexChar[17] = "0123456789abcdef";
2107  Dict *dict;
2108  long length1, length2, length3, i;
2109  int c;
2110  int start[4];
2111  bool binMode;
2112  bool writePadding = true;
2113 
2114  // check if font is already embedded
2115  if (!fontNames.emplace(psName->toStr()).second) {
2116  return;
2117  }
2118 
2119  // get the font stream and info
2120  Object obj1, obj2, obj3;
2121  Object refObj(*id);
2122  Object strObj = refObj.fetch(xref);
2123  if (!strObj.isStream()) {
2124  error(errSyntaxError, -1, "Embedded font file object is not a stream");
2125  goto err1;
2126  }
2127  if (!(dict = strObj.streamGetDict())) {
2128  error(errSyntaxError, -1, "Embedded font stream is missing its dictionary");
2129  goto err1;
2130  }
2131  obj1 = dict->lookup("Length1");
2132  obj2 = dict->lookup("Length2");
2133  obj3 = dict->lookup("Length3");
2134  if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {
2135  error(errSyntaxError, -1, "Missing length fields in embedded font stream dictionary");
2136  goto err1;
2137  }
2138  length1 = obj1.getInt();
2139  length2 = obj2.getInt();
2140  length3 = obj3.getInt();
2141 
2142  // beginning comment
2143  writePSFmt("%%BeginResource: font {0:t}\n", psName);
2144  embFontList->append("%%+ font ");
2145  embFontList->append(psName->c_str());
2146  embFontList->append("\n");
2147 
2148  strObj.streamReset();
2149  if (strObj.streamGetChar() == 0x80 && strObj.streamGetChar() == 1) {
2150  // PFB format
2151  length1 = strObj.streamGetChar() | (strObj.streamGetChar() << 8) | (strObj.streamGetChar() << 16) | (strObj.streamGetChar() << 24);
2152  } else {
2153  strObj.streamReset();
2154  }
2155  // copy ASCII portion of font
2156  for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
2157  writePSChar(c);
2158  }
2159 
2160  // figure out if encrypted portion is binary or ASCII
2161  binMode = false;
2162  for (i = 0; i < 4; ++i) {
2163  start[i] = strObj.streamGetChar();
2164  if (start[i] == EOF) {
2165  error(errSyntaxError, -1, "Unexpected end of file in embedded font stream");
2166  goto err1;
2167  }
2168  if (!((start[i] >= '0' && start[i] <= '9') || (start[i] >= 'A' && start[i] <= 'F') || (start[i] >= 'a' && start[i] <= 'f')))
2169  binMode = true;
2170  }
2171 
2172  if (length2 == 0) {
2173  // length2 == 0 is an error
2174  // trying to solve it by just piping all
2175  // the stream data
2176  error(errSyntaxWarning, -1, "Font has length2 as 0, trying to overcome the problem reading the stream until the end");
2177  length2 = INT_MAX;
2178  writePadding = false;
2179  }
2180 
2181  // convert binary data to ASCII
2182  if (binMode) {
2183  if (start[0] == 0x80 && start[1] == 2) {
2184  length2 = start[2] | (start[3] << 8) | (strObj.streamGetChar() << 16) | (strObj.streamGetChar() << 24);
2185  i = 0;
2186  } else {
2187  for (i = 0; i < 4; ++i) {
2188  writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
2189  writePSChar(hexChar[start[i] & 0x0f]);
2190  }
2191  }
2192 #if 0 // this causes trouble for various PostScript printers
2193  // if Length2 is incorrect (too small), font data gets chopped, so
2194  // we take a few extra characters from the trailer just in case
2195  length2 += length3 >= 8 ? 8 : length3;
2196 #endif
2197  while (i < length2) {
2198  if ((c = strObj.streamGetChar()) == EOF) {
2199  break;
2200  }
2201  writePSChar(hexChar[(c >> 4) & 0x0f]);
2202  writePSChar(hexChar[c & 0x0f]);
2203  if (++i % 32 == 0) {
2204  writePSChar('\n');
2205  }
2206  }
2207  if (i % 32 > 0) {
2208  writePSChar('\n');
2209  }
2210 
2211  // already in ASCII format -- just copy it
2212  } else {
2213  for (i = 0; i < 4; ++i) {
2214  writePSChar(start[i]);
2215  }
2216  for (i = 4; i < length2; ++i) {
2217  if ((c = strObj.streamGetChar()) == EOF) {
2218  break;
2219  }
2220  writePSChar(c);
2221  }
2222  }
2223 
2224  if (writePadding) {
2225  if (length3 > 0) {
2226  // write fixed-content portion
2227  c = strObj.streamGetChar();
2228  if (c == 0x80) {
2229  c = strObj.streamGetChar();
2230  if (c == 1) {
2231  length3 = strObj.streamGetChar() | (strObj.streamGetChar() << 8) | (strObj.streamGetChar() << 16) | (strObj.streamGetChar() << 24);
2232 
2233  i = 0;
2234  while (i < length3) {
2235  if ((c = strObj.streamGetChar()) == EOF) {
2236  break;
2237  }
2238  writePSChar(c);
2239  ++i;
2240  }
2241  }
2242  } else {
2243  if (c != EOF) {
2244  writePSChar(c);
2245 
2246  while ((c = strObj.streamGetChar()) != EOF) {
2247  writePSChar(c);
2248  }
2249  }
2250  }
2251  } else {
2252  // write padding and "cleartomark"
2253  for (i = 0; i < 8; ++i) {
2254  writePS("00000000000000000000000000000000"
2255  "00000000000000000000000000000000\n");
2256  }
2257  writePS("cleartomark\n");
2258  }
2259  }
2260 
2261  // ending comment
2262  writePS("%%EndResource\n");
2263 
2264 err1:
2265  if (strObj.isStream())
2266  strObj.streamClose();
2267 }
2268 
2270 {
2271  static const char hexChar[17] = "0123456789abcdef";
2272  FILE *fontFile;
2273  int c;
2274 
2275  if (!fontNames.emplace(psName->toStr()).second) {
2276  return;
2277  }
2278 
2279  // beginning comment
2280  writePSFmt("%%BeginResource: font {0:t}\n", psName);
2281  embFontList->append("%%+ font ");
2282  embFontList->append(psName->c_str());
2283  embFontList->append("\n");
2284 
2285  // copy the font file
2286  if (!(fontFile = openFile(fileName->c_str(), "rb"))) {
2287  error(errIO, -1, "Couldn't open external font file");
2288  return;
2289  }
2290 
2291  c = fgetc(fontFile);
2292  if (c == 0x80) {
2293  // PFB file
2294  ungetc(c, fontFile);
2295  while (!feof(fontFile)) {
2296  fgetc(fontFile); // skip start of segment byte (0x80)
2297  int segType = fgetc(fontFile);
2298  long segLen = fgetc(fontFile) | (fgetc(fontFile) << 8) | (fgetc(fontFile) << 16) | (fgetc(fontFile) << 24);
2299  if (feof(fontFile))
2300  break;
2301 
2302  if (segType == 1) {
2303  // ASCII segment
2304  for (long i = 0; i < segLen; i++) {
2305  c = fgetc(fontFile);
2306  if (c == EOF)
2307  break;
2308  writePSChar(c);
2309  }
2310  } else if (segType == 2) {
2311  // binary segment
2312  for (long i = 0; i < segLen; i++) {
2313  c = fgetc(fontFile);
2314  if (c == EOF)
2315  break;
2316  writePSChar(hexChar[(c >> 4) & 0x0f]);
2317  writePSChar(hexChar[c & 0x0f]);
2318  if (i % 36 == 35)
2319  writePSChar('\n');
2320  }
2321  } else {
2322  // end of file
2323  break;
2324  }
2325  }
2326  } else if (c != EOF) {
2327  writePSChar(c);
2328  while ((c = fgetc(fontFile)) != EOF)
2329  writePSChar(c);
2330  }
2331  fclose(fontFile);
2332 
2333  // ending comment
2334  writePS("%%EndResource\n");
2335 }
2336 
2338 {
2339  char *fontBuf;
2340  int fontLen;
2341  FoFiType1C *ffT1C;
2342  int i;
2343 
2344  // check if font is already embedded
2345  for (i = 0; i < t1FontNameLen; ++i) {
2346  if (t1FontNames[i].fontFileID == *id) {
2347  psName->clear();
2348  psName->insert(0, t1FontNames[i].psName);
2349  return;
2350  }
2351  }
2352  if (t1FontNameLen == t1FontNameSize) {
2353  t1FontNameSize *= 2;
2355  }
2357  t1FontNames[t1FontNameLen].psName = psName->copy();
2358  ++t1FontNameLen;
2359 
2360  // beginning comment
2361  writePSFmt("%%BeginResource: font {0:t}\n", psName);
2362  embFontList->append("%%+ font ");
2363  embFontList->append(psName->c_str());
2364  embFontList->append("\n");
2365 
2366  // convert it to a Type 1 font
2367  if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2368  if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
2369  ffT1C->convertToType1(psName->c_str(), nullptr, true, outputFunc, outputStream);
2370  delete ffT1C;
2371  }
2372  gfree(fontBuf);
2373  }
2374 
2375  // ending comment
2376  writePS("%%EndResource\n");
2377 }
2378 
2380 {
2381  char *fontBuf;
2382  int fontLen;
2383  FoFiTrueType *ffTT;
2384  int i;
2385 
2386  // check if font is already embedded
2387  for (i = 0; i < t1FontNameLen; ++i) {
2388  if (t1FontNames[i].fontFileID == *id) {
2389  psName->clear();
2390  psName->insert(0, t1FontNames[i].psName);
2391  return;
2392  }
2393  }
2394  if (t1FontNameLen == t1FontNameSize) {
2395  t1FontNameSize *= 2;
2397  }
2399  t1FontNames[t1FontNameLen].psName = psName->copy();
2400  ++t1FontNameLen;
2401 
2402  // beginning comment
2403  writePSFmt("%%BeginResource: font {0:t}\n", psName);
2404  embFontList->append("%%+ font ");
2405  embFontList->append(psName->c_str());
2406  embFontList->append("\n");
2407 
2408  // convert it to a Type 1 font
2409  if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2410  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2411  if (ffTT->isOpenTypeCFF()) {
2412  ffTT->convertToType1(psName->c_str(), nullptr, true, outputFunc, outputStream);
2413  }
2414  delete ffTT;
2415  }
2416  gfree(fontBuf);
2417  }
2418 
2419  // ending comment
2420  writePS("%%EndResource\n");
2421 }
2422 
2424 {
2425  char *fontBuf;
2426  int fontLen;
2427  FoFiTrueType *ffTT;
2428  int *codeToGID;
2429 
2430  // beginning comment
2431  writePSFmt("%%BeginResource: font {0:t}\n", psName);
2432  embFontList->append("%%+ font ");
2433  embFontList->append(psName->c_str());
2434  embFontList->append("\n");
2435 
2436  // convert it to a Type 42 font
2437  if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2438  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2439  codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
2440  ffTT->convertToType42(psName->c_str(), ((Gfx8BitFont *)font)->getHasEncoding() ? ((Gfx8BitFont *)font)->getEncoding() : nullptr, codeToGID, outputFunc, outputStream);
2441  if (codeToGID) {
2442  if (font8InfoLen >= font8InfoSize) {
2443  font8InfoSize += 16;
2445  }
2446  font8Info[font8InfoLen].fontID = *font->getID();
2447  font8Info[font8InfoLen].codeToGID = codeToGID;
2448  ++font8InfoLen;
2449  }
2450  delete ffTT;
2451  }
2452  gfree(fontBuf);
2453  }
2454 
2455  // ending comment
2456  writePS("%%EndResource\n");
2457 }
2458 
2460 {
2461  FoFiTrueType *ffTT;
2462  int *codeToGID;
2463 
2464  // beginning comment
2465  writePSFmt("%%BeginResource: font {0:t}\n", psName);
2466  embFontList->append("%%+ font ");
2467  embFontList->append(psName->c_str());
2468  embFontList->append("\n");
2469 
2470  // convert it to a Type 42 font
2471  if ((ffTT = FoFiTrueType::load(fileName->c_str()))) {
2472  codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
2473  ffTT->convertToType42(psName->c_str(), ((Gfx8BitFont *)font)->getHasEncoding() ? ((Gfx8BitFont *)font)->getEncoding() : nullptr, codeToGID, outputFunc, outputStream);
2474  if (codeToGID) {
2475  if (font8InfoLen >= font8InfoSize) {
2476  font8InfoSize += 16;
2478  }
2479  font8Info[font8InfoLen].fontID = *font->getID();
2480  font8Info[font8InfoLen].codeToGID = codeToGID;
2481  ++font8InfoLen;
2482  }
2483  delete ffTT;
2484  }
2485 
2486  // ending comment
2487  writePS("%%EndResource\n");
2488 }
2489 
2491 {
2492  if (maxValidGlyph >= 0 && font->getName()) {
2493  auto &fontMaxValidGlyph = perFontMaxValidGlyph[font->getName()->toStr()];
2494  if (fontMaxValidGlyph < maxValidGlyph) {
2495  fontMaxValidGlyph = maxValidGlyph;
2496  }
2497  }
2498 }
2499 
2501 {
2502  FoFiTrueType *ffTT;
2503  int *codeToGID;
2504  int codeToGIDLen;
2505 
2506  // beginning comment
2507  writePSFmt("%%BeginResource: font {0:t}\n", psName);
2508  embFontList->append("%%+ font ");
2509  embFontList->append(psName->c_str());
2510  embFontList->append("\n");
2511 
2512  // convert it to a Type 0 font
2513  //~ this should use fontNum to load the correct font
2514  if ((ffTT = FoFiTrueType::load(fileName->c_str()))) {
2515 
2516  // check for embedding permission
2517  if (ffTT->getEmbeddingRights() >= 1) {
2518  codeToGID = nullptr;
2519  codeToGIDLen = 0;
2520  if (((GfxCIDFont *)font)->getCIDToGID()) {
2521  codeToGIDLen = ((GfxCIDFont *)font)->getCIDToGIDLen();
2522  if (codeToGIDLen) {
2523  codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
2524  memcpy(codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), codeToGIDLen * sizeof(int));
2525  }
2526  } else {
2527  codeToGID = ((GfxCIDFont *)font)->getCodeToGIDMap(ffTT, &codeToGIDLen);
2528  }
2529  if (ffTT->isOpenTypeCFF()) {
2530  ffTT->convertToCIDType0(psName->c_str(), codeToGID, codeToGIDLen, outputFunc, outputStream);
2531  } else if (level >= psLevel3) {
2532  // Level 3: use a CID font
2533  ffTT->convertToCIDType2(psName->c_str(), codeToGID, codeToGIDLen, needVerticalMetrics, outputFunc, outputStream);
2534  } else {
2535  // otherwise: use a non-CID composite font
2536  int maxValidGlyph = -1;
2537  ffTT->convertToType0(psName->c_str(), codeToGID, codeToGIDLen, needVerticalMetrics, &maxValidGlyph, outputFunc, outputStream);
2538  updateFontMaxValidGlyph(font, maxValidGlyph);
2539  }
2540  gfree(codeToGID);
2541  } else {
2542  error(errSyntaxError, -1, "TrueType font '{0:s}' does not allow embedding", font->getName() ? font->getName()->c_str() : "(unnamed)");
2543  }
2544  delete ffTT;
2545  }
2546 
2547  // ending comment
2548  writePS("%%EndResource\n");
2549 }
2550 
2552 {
2553  char *fontBuf;
2554  int fontLen;
2555  FoFiType1C *ffT1C;
2556  int i;
2557 
2558  // check if font is already embedded
2559  for (i = 0; i < t1FontNameLen; ++i) {
2560  if (t1FontNames[i].fontFileID == *id) {
2561  psName->clear();
2562  psName->insert(0, t1FontNames[i].psName);
2563  return;
2564  }
2565  }
2566  if (t1FontNameLen == t1FontNameSize) {
2567  t1FontNameSize *= 2;
2569  }
2571  t1FontNames[t1FontNameLen].psName = psName->copy();
2572  ++t1FontNameLen;
2573 
2574  // beginning comment
2575  writePSFmt("%%BeginResource: font {0:t}\n", psName);
2576  embFontList->append("%%+ font ");
2577  embFontList->append(psName->c_str());
2578  embFontList->append("\n");
2579 
2580  // convert it to a Type 0 font
2581  if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2582  if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
2583  if (level >= psLevel3) {
2584  // Level 3: use a CID font
2585  ffT1C->convertToCIDType0(psName->c_str(), nullptr, 0, outputFunc, outputStream);
2586  } else {
2587  // otherwise: use a non-CID composite font
2588  ffT1C->convertToType0(psName->c_str(), nullptr, 0, outputFunc, outputStream);
2589  }
2590  delete ffT1C;
2591  }
2592  gfree(fontBuf);
2593  }
2594 
2595  // ending comment
2596  writePS("%%EndResource\n");
2597 }
2598 
2599 void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GooString *psName, bool needVerticalMetrics)
2600 {
2601  char *fontBuf;
2602  int fontLen;
2603  FoFiTrueType *ffTT;
2604 
2605  // beginning comment
2606  writePSFmt("%%BeginResource: font {0:t}\n", psName);
2607  embFontList->append("%%+ font ");
2608  embFontList->append(psName->c_str());
2609  embFontList->append("\n");
2610 
2611  // convert it to a Type 0 font
2612  if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2613  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2614  if (level >= psLevel3) {
2615  // Level 3: use a CID font
2616  ffTT->convertToCIDType2(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), needVerticalMetrics, outputFunc, outputStream);
2617  } else {
2618  // otherwise: use a non-CID composite font
2619  int maxValidGlyph = -1;
2620  ffTT->convertToType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), needVerticalMetrics, &maxValidGlyph, outputFunc, outputStream);
2621  updateFontMaxValidGlyph(font, maxValidGlyph);
2622  }
2623  delete ffTT;
2624  }
2625  gfree(fontBuf);
2626  }
2627 
2628  // ending comment
2629  writePS("%%EndResource\n");
2630 }
2631 
2633 {
2634  char *fontBuf;
2635  int fontLen;
2636  FoFiTrueType *ffTT;
2637  int i;
2638 
2639  // check if font is already embedded
2640  for (i = 0; i < t1FontNameLen; ++i) {
2641  if (t1FontNames[i].fontFileID == *id) {
2642  psName->clear();
2643  psName->insert(0, t1FontNames[i].psName);
2644  return;
2645  }
2646  }
2647  if (t1FontNameLen == t1FontNameSize) {
2648  t1FontNameSize *= 2;
2650  }
2652  t1FontNames[t1FontNameLen].psName = psName->copy();
2653  ++t1FontNameLen;
2654 
2655  // beginning comment
2656  writePSFmt("%%BeginResource: font {0:t}\n", psName);
2657  embFontList->append("%%+ font ");
2658  embFontList->append(psName->c_str());
2659  embFontList->append("\n");
2660 
2661  // convert it to a Type 0 font
2662  if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2663  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2664  if (ffTT->isOpenTypeCFF()) {
2665  if (level >= psLevel3) {
2666  // Level 3: use a CID font
2667  ffTT->convertToCIDType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream);
2668  } else {
2669  // otherwise: use a non-CID composite font
2670  ffTT->convertToType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream);
2671  }
2672  }
2673  delete ffTT;
2674  }
2675  gfree(fontBuf);
2676  }
2677 
2678  // ending comment
2679  writePS("%%EndResource\n");
2680 }
2681 
2682 void PSOutputDev::setupType3Font(GfxFont *font, GooString *psName, Dict *parentResDict)
2683 {
2684  Dict *resDict;
2685  Dict *charProcs;
2686  Gfx *gfx;
2687  PDFRectangle box;
2688  const double *m;
2689  GooString *buf;
2690  int i;
2691 
2692  // set up resources used by font
2693  if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2694  inType3Char = true;
2695  setupResources(resDict);
2696  inType3Char = false;
2697  } else {
2698  resDict = parentResDict;
2699  }
2700 
2701  // beginning comment
2702  writePSFmt("%%BeginResource: font {0:t}\n", psName);
2703  embFontList->append("%%+ font ");
2704  embFontList->append(psName->c_str());
2705  embFontList->append("\n");
2706 
2707  // font dictionary
2708  writePS("8 dict begin\n");
2709  writePS("/FontType 3 def\n");
2710  m = font->getFontMatrix();
2711  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]);
2712  m = font->getFontBBox();
2713  writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", m[0], m[1], m[2], m[3]);
2714  writePS("/Encoding 256 array def\n");
2715  writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
2716  writePS("/BuildGlyph {\n");
2717  writePS(" exch /CharProcs get exch\n");
2718  writePS(" 2 copy known not { pop /.notdef } if\n");
2719  writePS(" get exec\n");
2720  writePS("} bind def\n");
2721  writePS("/BuildChar {\n");
2722  writePS(" 1 index /Encoding get exch get\n");
2723  writePS(" 1 index /BuildGlyph get exec\n");
2724  writePS("} bind def\n");
2725  if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
2726  writePSFmt("/CharProcs {0:d} dict def\n", charProcs->getLength());
2727  writePS("CharProcs begin\n");
2728  box.x1 = m[0];
2729  box.y1 = m[1];
2730  box.x2 = m[2];
2731  box.y2 = m[3];
2732  gfx = new Gfx(doc, this, resDict, &box, nullptr);
2733  inType3Char = true;
2734  for (i = 0; i < charProcs->getLength(); ++i) {
2735  t3FillColorOnly = false;
2736  t3Cacheable = false;
2737  t3NeedsRestore = false;
2738  writePS("/");
2739  writePSName(charProcs->getKey(i));
2740  writePS(" {\n");
2741  Object charProc = charProcs->getVal(i);
2742  gfx->display(&charProc);
2743  if (t3String) {
2744  if (t3Cacheable) {
2745  buf = GooString::format("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} setcachedevice\n", t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
2746  } else {
2747  buf = GooString::format("{0:.6g} {1:.6g} setcharwidth\n", t3WX, t3WY);
2748  }
2749  (*outputFunc)(outputStream, buf->c_str(), buf->getLength());
2750  delete buf;
2751  (*outputFunc)(outputStream, t3String->c_str(), t3String->getLength());
2752  delete t3String;
2753  t3String = nullptr;
2754  }
2755  if (t3NeedsRestore) {
2756  (*outputFunc)(outputStream, "Q\n", 2);
2757  }
2758  writePS("} def\n");
2759  }
2760  inType3Char = false;
2761  delete gfx;
2762  writePS("end\n");
2763  }
2764  writePS("currentdict end\n");
2765  writePSFmt("/{0:t} exch definefont pop\n", psName);
2766 
2767  // ending comment
2768  writePS("%%EndResource\n");
2769 }
2770 
2771 // Make a unique PS font name, based on the names given in the PDF
2772 // font object, and an object ID (font file object for
2774 {
2775  GooString *psName;
2776  const GooString *s;
2777 
2778  if ((s = font->getEmbeddedFontName())) {
2779  psName = filterPSName(s);
2780  if (fontNames.emplace(psName->toStr()).second) {
2781  return psName;
2782  }
2783  delete psName;
2784  }
2785  if ((s = font->getName())) {
2786  psName = filterPSName(s);
2787  if (fontNames.emplace(psName->toStr()).second) {
2788  return psName;
2789  }
2790  delete psName;
2791  }
2792  psName = GooString::format("FF{0:d}_{1:d}", id->num, id->gen);
2793  if ((s = font->getEmbeddedFontName())) {
2794  s = filterPSName(s);
2795  psName->append('_')->append(s);
2796  delete s;
2797  } else if ((s = font->getName())) {
2798  s = filterPSName(s);
2799  psName->append('_')->append(s);
2800  delete s;
2801  }
2802  fontNames.emplace(psName->toStr());
2803  return psName;
2804 }
2805 
2806 void PSOutputDev::setupImages(Dict *resDict)
2807 {
2808  Ref imgID;
2809 
2810  if (!(mode == psModeForm || inType3Char || preloadImagesForms)) {
2811  return;
2812  }
2813 
2814  //----- recursively scan XObjects
2815  Object xObjDict = resDict->lookup("XObject");
2816  if (xObjDict.isDict()) {
2817  for (int i = 0; i < xObjDict.dictGetLength(); ++i) {
2818  const Object &xObjRef = xObjDict.dictGetValNF(i);
2819  Object xObj = xObjDict.dictGetVal(i);
2820  if (xObj.isStream()) {
2821  Object subtypeObj = xObj.streamGetDict()->lookup("Subtype");
2822  if (subtypeObj.isName("Image")) {
2823  if (xObjRef.isRef()) {
2824  imgID = xObjRef.getRef();
2825  int j;
2826  for (j = 0; j < imgIDLen; ++j) {
2827  if (imgIDs[j] == imgID) {
2828  break;
2829  }
2830  }
2831  if (j == imgIDLen) {
2832  if (imgIDLen >= imgIDSize) {
2833  if (imgIDSize == 0) {
2834  imgIDSize = 64;
2835  } else {
2836  imgIDSize *= 2;
2837  }
2838  imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref));
2839  }
2840  imgIDs[imgIDLen++] = imgID;
2841  setupImage(imgID, xObj.getStream(), false);
2842  if (level >= psLevel3) {
2843  Object maskObj = xObj.streamGetDict()->lookup("Mask");
2844  if (maskObj.isStream()) {
2845  setupImage(imgID, maskObj.getStream(), true);
2846  }
2847  }
2848  }
2849  } else {
2850  error(errSyntaxError, -1, "Image in resource dict is not an indirect reference");
2851  }
2852  }
2853  }
2854  }
2855  }
2856 }
2857 
2859 {
2860  bool useFlate, useLZW, useRLE, useCompressed, doUseASCIIHex;
2861  GooString *s;
2862  int c;
2863  int size, line, col, i;
2864  int outerSize, outer;
2865 
2866  // filters
2867  //~ this does not correctly handle the DeviceN color space
2868  //~ -- need to use DeviceNRecoder
2869 
2870  useFlate = useLZW = useRLE = false;
2871  useCompressed = false;
2872  doUseASCIIHex = false;
2873 
2874  if (level < psLevel2) {
2875  doUseASCIIHex = true;
2876  } else {
2878  /* nothing to do */;
2879  } else {
2880  s = str->getPSFilter(level < psLevel3 ? 2 : 3, "");
2881  if (s) {
2882  useCompressed = true;
2883  delete s;
2884  } else {
2885  if (level >= psLevel3 && getEnableFlate()) {
2886  useFlate = true;
2887  } else if (getEnableLZW()) {
2888  useLZW = true;
2889  } else {
2890  useRLE = true;
2891  }
2892  }
2893  }
2894  doUseASCIIHex = useASCIIHex;
2895  }
2896  if (useCompressed) {
2897  str = str->getUndecodedStream();
2898  }
2899 #ifdef ENABLE_ZLIB
2900  if (useFlate) {
2901  str = new FlateEncoder(str);
2902  } else
2903 #endif
2904  if (useLZW) {
2905  str = new LZWEncoder(str);
2906  } else if (useRLE) {
2907  str = new RunLengthEncoder(str);
2908  }
2909  if (doUseASCIIHex) {
2910  str = new ASCIIHexEncoder(str);
2911  } else {
2912  str = new ASCII85Encoder(str);
2913  }
2914 
2915  // compute image data size
2916  str->reset();
2917  col = size = 0;
2918  do {
2919  do {
2920  c = str->getChar();
2921  } while (c == '\n' || c == '\r');
2922  if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
2923  break;
2924  }
2925  if (c == 'z') {
2926  ++col;
2927  } else {
2928  ++col;
2929  for (i = 1; i <= (doUseASCIIHex ? 1 : 4); ++i) {
2930  do {
2931  c = str->getChar();
2932  } while (c == '\n' || c == '\r');
2933  if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
2934  break;
2935  }
2936  ++col;
2937  }
2938  if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
2939  break;
2940  }
2941  }
2942  if (col > 225) {
2943  ++size;
2944  col = 0;
2945  }
2946  } while (c != (doUseASCIIHex ? '>' : '~') && c != EOF);
2947  // add one entry for the final line of data; add another entry
2948  // because the LZWDecode/RunLengthDecode filter may read past the end
2949  ++size;
2950  if (useLZW || useRLE) {
2951  ++size;
2952  }
2953  outerSize = size / 65535 + 1;
2954 
2955  writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n", outerSize, mask ? "Mask" : "Im", id.num, id.gen);
2956  str->close();
2957 
2958  // write the data into the array
2959  str->reset();
2960  for (outer = 0; outer < outerSize; outer++) {
2961  int innerSize = size > 65535 ? 65535 : size;
2962 
2963  // put the inner array into the outer array
2964  writePSFmt("{0:d} array 1 index {1:d} 2 index put\n", innerSize, outer);
2965  line = col = 0;
2966  writePS((char *)(doUseASCIIHex ? "dup 0 <" : "dup 0 <~"));
2967  for (;;) {
2968  do {
2969  c = str->getChar();
2970  } while (c == '\n' || c == '\r');
2971  if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
2972  break;
2973  }
2974  if (c == 'z') {
2975  writePSChar(c);
2976  ++col;
2977  } else {
2978  writePSChar(c);
2979  ++col;
2980  for (i = 1; i <= (doUseASCIIHex ? 1 : 4); ++i) {
2981  do {
2982  c = str->getChar();
2983  } while (c == '\n' || c == '\r');
2984  if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
2985  break;
2986  }
2987  writePSChar(c);
2988  ++col;
2989  }
2990  }
2991  if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
2992  break;
2993  }
2994  // each line is: "dup nnnnn <~...data...~> put<eol>"
2995  // so max data length = 255 - 20 = 235
2996  // chunks are 1 or 4 bytes each, so we have to stop at 232
2997  // but make it 225 just to be safe
2998  if (col > 225) {
2999  writePS((char *)(doUseASCIIHex ? "> put\n" : "~> put\n"));
3000  ++line;
3001  if (line >= innerSize)
3002  break;
3003  writePSFmt((char *)(doUseASCIIHex ? "dup {0:d} <" : "dup {0:d} <~"), line);
3004  col = 0;
3005  }
3006  }
3007  if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) {
3008  writePS((char *)(doUseASCIIHex ? "> put\n" : "~> put\n"));
3009  if (useLZW || useRLE) {
3010  ++line;
3011  writePSFmt("{0:d} <> put\n", line);
3012  } else {
3013  writePS("pop\n");
3014  }
3015  break;
3016  }
3017  writePS("pop\n");
3018  size -= innerSize;
3019  }
3020  writePS("pop\n");
3021  str->close();
3022 
3023  delete str;
3024 }
3025 
3026 void PSOutputDev::setupForms(Dict *resDict)
3027 {
3028  if (!preloadImagesForms) {
3029  return;
3030  }
3031 
3032  Object xObjDict = resDict->lookup("XObject");
3033  if (xObjDict.isDict()) {
3034  for (int i = 0; i < xObjDict.dictGetLength(); ++i) {
3035  const Object &xObjRef = xObjDict.dictGetValNF(i);
3036  Object xObj = xObjDict.dictGetVal(i);
3037  if (xObj.isStream()) {
3038  Object subtypeObj = xObj.streamGetDict()->lookup("Subtype");
3039  if (subtypeObj.isName("Form")) {
3040  if (xObjRef.isRef()) {
3041  setupForm(xObjRef.getRef(), &xObj);
3042  } else {
3043  error(errSyntaxError, -1, "Form in resource dict is not an indirect reference");
3044  }
3045  }
3046  }
3047  }
3048  }
3049 }
3050 
3052 {
3053  Dict *dict, *resDict;
3054  double m[6], bbox[4];
3055  PDFRectangle box;
3056  Gfx *gfx;
3057 
3058  // check if form is already defined
3059  for (int i = 0; i < formIDLen; ++i) {
3060  if (formIDs[i] == id) {
3061  return;
3062  }
3063  }
3064 
3065  // add entry to formIDs list
3066  if (formIDLen >= formIDSize) {
3067  if (formIDSize == 0) {
3068  formIDSize = 64;
3069  } else {
3070  formIDSize *= 2;
3071  }
3072  formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref));
3073  }
3074  formIDs[formIDLen++] = id;
3075 
3076  dict = strObj->streamGetDict();
3077 
3078  // get bounding box
3079  Object bboxObj = dict->lookup("BBox");
3080  if (!bboxObj.isArray()) {
3081  error(errSyntaxError, -1, "Bad form bounding box");
3082  return;
3083  }
3084  for (int i = 0; i < 4; ++i) {
3085  Object obj1 = bboxObj.arrayGet(i);
3086  bbox[i] = obj1.getNum();
3087  }
3088 
3089  // get matrix
3090  Object matrixObj = dict->lookup("Matrix");
3091  if (matrixObj.isArray()) {
3092  for (int i = 0; i < 6; ++i) {
3093  Object obj1 = matrixObj.arrayGet(i);
3094  m[i] = obj1.getNum();
3095  }
3096  } else {
3097  m[0] = 1;
3098  m[1] = 0;
3099  m[2] = 0;
3100  m[3] = 1;
3101  m[4] = 0;
3102  m[5] = 0;
3103  }
3104 
3105  // get resources
3106  Object resObj = dict->lookup("Resources");
3107  resDict = resObj.isDict() ? resObj.getDict() : nullptr;
3108 
3109  writePSFmt("/f_{0:d}_{1:d} {{\n", id.num, id.gen);
3110  writePS("q\n");
3111  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]);
3112 
3113  box.x1 = bbox[0];
3114  box.y1 = bbox[1];
3115  box.x2 = bbox[2];
3116  box.y2 = bbox[3];
3117  gfx = new Gfx(doc, this, resDict, &box, &box);
3118  gfx->display(strObj);
3119  delete gfx;
3120 
3121  writePS("Q\n");
3122  writePS("} def\n");
3123 }
3124 
3125 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),
3126  void *abortCheckCbkData, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), void *annotDisplayDecideCbkData)
3127 {
3129  bool rasterize;
3130 #ifdef HAVE_SPLASH
3131  bool useFlate, useLZW;
3132  SplashOutputDev *splashOut;
3133  SplashColor paperColor;
3134  PDFRectangle box;
3135  GfxState *state;
3137  Stream *str0, *str;
3138  unsigned char *p;
3139  unsigned char col[4];
3140  double hDPI2, vDPI2;
3141  double m0, m1, m2, m3, m4, m5;
3142  int nStripes, stripeH, stripeY;
3143  int c, w, h, x, y, comp, i;
3144  int numComps, initialNumComps;
3145  char hexBuf[32 * 2 + 2]; // 32 values X 2 chars/value + line ending + null
3146  unsigned char digit;
3147  bool isOptimizedGray;
3148  bool overprint;
3149  SplashColorMode internalColorFormat;
3150 #endif
3151 
3152  if (!postInitDone) {
3153  postInit();
3154  }
3156  rasterize = true;
3157  } else if (forceRasterize == psNeverRasterize) {
3158  rasterize = false;
3159  } else {
3160  scan = new PreScanOutputDev(level);
3161  page->displaySlice(scan, 72, 72, rotateA, useMediaBox, crop, sliceX, sliceY, sliceW, sliceH, printing, abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData);
3162  rasterize = scan->usesTransparency() || scan->usesPatternImageMask();
3163  delete scan;
3164  }
3165  if (!rasterize) {
3166  return true;
3167  }
3168 
3169 #ifdef HAVE_SPLASH
3170  // get the rasterization parameters
3171  useFlate = getEnableFlate() && level >= psLevel3;
3172  useLZW = getEnableLZW();
3173  // start the PS page
3174  page->makeBox(rasterResolution, rasterResolution, rotateA, useMediaBox, false, sliceX, sliceY, sliceW, sliceH, &box, &crop);
3175  rotateA += page->getRotate();
3176  if (rotateA >= 360) {
3177  rotateA -= 360;
3178  } else if (rotateA < 0) {
3179  rotateA += 360;
3180  }
3181  state = new GfxState(rasterResolution, rasterResolution, &box, rotateA, false);
3182  startPage(page->getNum(), state, xref);
3183  delete state;
3184 
3185  // If we would not rasterize this page, we would emit the overprint code anyway for language level 2 and upwards.
3186  // As such it is safe to assume for a CMYK printer that it would respect the overprint operands.
3187  overprint = globalParams->getOverprintPreview() || (processColorFormat == splashModeCMYK8 && level >= psLevel2);
3188 
3189  // set up the SplashOutputDev
3190  internalColorFormat = processColorFormat;
3191  if (processColorFormat == splashModeMono8) {
3192  numComps = 1;
3193  paperColor[0] = 0xff;
3194  } else if (processColorFormat == splashModeCMYK8) {
3195  numComps = 4;
3196  splashClearColor(paperColor);
3197 
3198  // If overprinting is emulated, it is not sufficient to just store the CMYK values in a bitmap.
3199  // All separation channels need to be stored and collapsed at the end.
3200  // Cf. PDF32000_2008 Section 11.7.4.5 and Tables 148, 149
3201  if (overprint) {
3202  internalColorFormat = splashModeDeviceN8;
3203  }
3204  } else if (processColorFormat == splashModeRGB8) {
3205  numComps = 3;
3206  paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
3207  } else {
3208  error(errUnimplemented, -1, "Unsupported processColorMode. Falling back to RGB8.");
3209  processColorFormat = splashModeRGB8;
3210  internalColorFormat = processColorFormat;
3211  numComps = 3;
3212  paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
3213  }
3214  splashOut = new SplashOutputDev(internalColorFormat, 1, false, paperColor, false, splashThinLineDefault, overprint);
3215  splashOut->setFontAntialias(rasterAntialias);
3216  splashOut->setVectorAntialias(rasterAntialias);
3217 # ifdef USE_CMS
3218  splashOut->setDisplayProfile(getDisplayProfile());
3222 # endif
3223  splashOut->startDoc(doc);
3224 
3225  // break the page into stripes
3226  hDPI2 = xScale * rasterResolution;
3227  vDPI2 = yScale * rasterResolution;
3228  if (sliceW < 0 || sliceH < 0) {
3229  if (useMediaBox) {
3230  box = *page->getMediaBox();
3231  } else {
3232  box = *page->getCropBox();
3233  }
3234  sliceX = sliceY = 0;
3235  sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0);
3236  sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0);
3237  }
3238  nStripes = (int)ceil((double)(sliceW * sliceH) / (double)rasterizationSliceSize);
3239  if (unlikely(nStripes == 0)) {
3240  delete splashOut;
3241  return false;
3242  }
3243  stripeH = (sliceH + nStripes - 1) / nStripes;
3244 
3245  // render the stripes
3246  initialNumComps = numComps;
3247  for (stripeY = sliceY; stripeY < sliceH; stripeY += stripeH) {
3248 
3249  // rasterize a stripe
3250  page->makeBox(hDPI2, vDPI2, 0, useMediaBox, false, sliceX, stripeY, sliceW, stripeH, &box, &crop);
3251  m0 = box.x2 - box.x1;
3252  m1 = 0;
3253  m2 = 0;
3254  m3 = box.y2 - box.y1;
3255  m4 = box.x1;
3256  m5 = box.y1;
3257  page->displaySlice(splashOut, hDPI2, vDPI2, (360 - page->getRotate()) % 360, useMediaBox, crop, sliceX, stripeY, sliceW, stripeH, printing, abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData);
3258 
3259  // draw the rasterized image
3260  bitmap = splashOut->getBitmap();
3261  numComps = initialNumComps;
3262  w = bitmap->getWidth();
3263  h = bitmap->getHeight();
3264  writePS("gsave\n");
3265  writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", m0, m1, m2, m3, m4, m5);
3266  switch (level) {
3267  case psLevel1:
3268  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" : "");
3269  p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
3270  i = 0;
3271  if (useBinary) {
3272  for (y = 0; y < h; ++y) {
3273  for (x = 0; x < w; ++x) {
3274  hexBuf[i++] = *p++;
3275  if (i >= 64) {
3276  writePSBuf(hexBuf, i);
3277  i = 0;
3278  }
3279  }
3280  }
3281  } else {
3282  for (y = 0; y < h; ++y) {
3283  for (x = 0; x < w; ++x) {
3284  digit = *p / 16;
3285  hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3286  digit = *p++ % 16;
3287  hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3288  if (i >= 64) {
3289  hexBuf[i++] = '\n';
3290  writePSBuf(hexBuf, i);
3291  i = 0;
3292  }
3293  }
3294  }
3295  }
3296  if (i != 0) {
3297  if (!useBinary) {
3298  hexBuf[i++] = '\n';
3299  }
3300  writePSBuf(hexBuf, i);
3301  }
3302  break;
3303  case psLevel1Sep:
3304  p = bitmap->getDataPtr();
3305  // Check for an all gray image
3306  if (getOptimizeColorSpace()) {
3307  isOptimizedGray = true;
3308  for (y = 0; y < h; ++y) {
3309  for (x = 0; x < w; ++x) {
3310  if (p[4 * x] != p[4 * x + 1] || p[4 * x] != p[4 * x + 2]) {
3311  isOptimizedGray = false;
3312  y = h;
3313  break;
3314  }
3315  }
3316  p += bitmap->getRowSize();
3317  }
3318  } else {
3319  isOptimizedGray = false;
3320  }
3321  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" : "");
3322  p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
3323  i = 0;
3324  col[0] = col[1] = col[2] = col[3] = 0;
3325  if (isOptimizedGray) {
3326  int g;
3327  if ((psProcessBlack & processColors) == 0) {
3328  // Check if the image uses black
3329  for (y = 0; y < h; ++y) {
3330  for (x = 0; x < w; ++x) {
3331  if (p[4 * x] > 0 || p[4 * x + 3] > 0) {
3332  col[3] = 1;
3333  y = h;
3334  break;
3335  }
3336  }
3337  p -= bitmap->getRowSize();
3338  }
3339  p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
3340  }
3341  for (y = 0; y < h; ++y) {
3342  if (useBinary) {
3343  // Binary gray image
3344  for (x = 0; x < w; ++x) {
3345  g = p[4 * x] + p[4 * x + 3];
3346  g = 255 - g;
3347  if (g < 0)
3348  g = 0;
3349  hexBuf[i++] = (unsigned char)g;
3350  if (i >= 64) {
3351  writePSBuf(hexBuf, i);
3352  i = 0;
3353  }
3354  }
3355  } else {
3356  // Hex gray image
3357  for (x = 0; x < w; ++x) {
3358  g = p[4 * x] + p[4 * x + 3];
3359  g = 255 - g;
3360  if (g < 0)
3361  g = 0;
3362  digit = g / 16;
3363  hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3364  digit = g % 16;
3365  hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3366  if (i >= 64) {
3367  hexBuf[i++] = '\n';
3368  writePSBuf(hexBuf, i);
3369  i = 0;
3370  }
3371  }
3372  }
3373  p -= bitmap->getRowSize();
3374  }
3376  // Color image, need to check color flags for each dot
3377  for (y = 0; y < h; ++y) {
3378  for (comp = 0; comp < 4; ++comp) {
3379  if (useBinary) {
3380  // Binary color image
3381  for (x = 0; x < w; ++x) {
3382  col[comp] |= p[4 * x + comp];
3383  hexBuf[i++] = p[4 * x + comp];
3384  if (i >= 64) {
3385  writePSBuf(hexBuf, i);
3386  i = 0;
3387  }
3388  }
3389  } else {
3390  // Gray color image
3391  for (x = 0; x < w; ++x) {
3392  col[comp] |= p[4 * x + comp];
3393  digit = p[4 * x + comp] / 16;
3394  hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3395  digit = p[4 * x + comp] % 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  }
3405  p -= bitmap->getRowSize();
3406  }
3407  } else {
3408  // Color image, do not need to check color flags
3409  for (y = 0; y < h; ++y) {
3410  for (comp = 0; comp < 4; ++comp) {
3411  if (useBinary) {
3412  // Binary color image
3413  for (x = 0; x < w; ++x) {
3414  hexBuf[i++] = p[4 * x + comp];
3415  if (i >= 64) {
3416  writePSBuf(hexBuf, i);
3417  i = 0;
3418  }
3419  }
3420  } else {
3421  // Hex color image
3422  for (x = 0; x < w; ++x) {
3423  digit = p[4 * x + comp] / 16;
3424  hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3425  digit = p[4 * x + comp] % 16;
3426  hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0');
3427  if (i >= 64) {
3428  hexBuf[i++] = '\n';
3429  writePSBuf(hexBuf, i);
3430  i = 0;
3431  }
3432  }
3433  }
3434  }
3435  p -= bitmap->getRowSize();
3436  }
3437  }
3438  if (i != 0) {
3439  if (!useBinary) {
3440  hexBuf[i++] = '\n';
3441  }
3442  writePSBuf(hexBuf, i);
3443  }
3444  if (col[0]) {
3446  }
3447  if (col[1]) {
3449  }
3450  if (col[2]) {
3452  }
3453  if (col[3]) {
3455  }
3456  break;
3457  case psLevel2:
3458  case psLevel2Sep:
3459  case psLevel3:
3460  case psLevel3Sep:
3461  p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
3462  if (processColorFormat == splashModeCMYK8 && internalColorFormat != splashModeCMYK8) {
3463  str0 = new SplashBitmapCMYKEncoder(bitmap);
3464  } else {
3465  str0 = new MemStream((char *)p, 0, w * h * numComps, Object(objNull));
3466  }
3467  // Check for a color image that uses only gray
3468  if (!getOptimizeColorSpace()) {
3469  isOptimizedGray = false;
3470  } else if (numComps == 4) {
3471  int compCyan;
3472  isOptimizedGray = true;
3473  while ((compCyan = str0->getChar()) != EOF) {
3474  if (str0->getChar() != compCyan || str0->getChar() != compCyan) {
3475  isOptimizedGray = false;
3476  break;
3477  }
3478  str0->getChar();
3479  }
3480  } else if (numComps == 3) {
3481  int compRed;
3482  isOptimizedGray = true;
3483  while ((compRed = str0->getChar()) != EOF) {
3484  if (str0->getChar() != compRed || str0->getChar() != compRed) {
3485  isOptimizedGray = false;
3486  break;
3487  }
3488  }
3489  } else {
3490  isOptimizedGray = false;
3491  }
3492  str0->reset();
3493 # ifdef ENABLE_ZLIB
3494  if (useFlate) {
3495  if (isOptimizedGray && numComps == 4) {
3496  str = new FlateEncoder(new CMYKGrayEncoder(str0));
3497  numComps = 1;
3498  } else if (isOptimizedGray && numComps == 3) {
3499  str = new FlateEncoder(new RGBGrayEncoder(str0));
3500  numComps = 1;
3501  } else {
3502  str = new FlateEncoder(str0);
3503  }
3504  } else
3505 # endif
3506  if (useLZW) {
3507  if (isOptimizedGray && numComps == 4) {
3508  str = new LZWEncoder(new CMYKGrayEncoder(str0));
3509  numComps = 1;
3510  } else if (isOptimizedGray && numComps == 3) {
3511  str = new LZWEncoder(new RGBGrayEncoder(str0));
3512  numComps = 1;
3513  } else {
3514  str = new LZWEncoder(str0);
3515  }
3516  } else {
3517  if (isOptimizedGray && numComps == 4) {
3518  str = new RunLengthEncoder(new CMYKGrayEncoder(str0));
3519  numComps = 1;
3520  } else if (isOptimizedGray && numComps == 3) {
3521  str = new RunLengthEncoder(new RGBGrayEncoder(str0));
3522  numComps = 1;
3523  } else {
3524  str = new RunLengthEncoder(str0);
3525  }
3526  }
3527  if (numComps == 1) {
3528  writePS("/DeviceGray setcolorspace\n");
3529  } else if (numComps == 3) {
3530  writePS("/DeviceRGB setcolorspace\n");
3531  } else {
3532  writePS("/DeviceCMYK setcolorspace\n");
3533  }
3534  writePS("<<\n /ImageType 1\n");
3535  writePSFmt(" /Width {0:d}\n", bitmap->getWidth());
3536  writePSFmt(" /Height {0:d}\n", bitmap->getHeight());
3537  writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h);
3538  writePS(" /BitsPerComponent 8\n");
3539  if (numComps == 1) {
3540  // the optimized gray variants are implemented as a subtractive color space,
3541  // such that the range is flipped for them
3542  if (isOptimizedGray) {
3543  writePS(" /Decode [1 0]\n");
3544  } else {
3545  writePS(" /Decode [0 1]\n");
3546  }
3547  } else if (numComps == 3) {
3548  writePS(" /Decode [0 1 0 1 0 1]\n");
3549  } else {
3550  writePS(" /Decode [0 1 0 1 0 1 0 1]\n");
3551  }
3552  writePS(" /DataSource currentfile\n");
3553  if (useBinary) {
3554  /* nothing to do */;
3555  } else if (useASCIIHex) {
3556  writePS(" /ASCIIHexDecode filter\n");
3557  } else {
3558  writePS(" /ASCII85Decode filter\n");
3559  }
3560  if (useFlate) {
3561  writePS(" /FlateDecode filter\n");
3562  } else if (useLZW) {
3563  writePS(" /LZWDecode filter\n");
3564  } else {
3565  writePS(" /RunLengthDecode filter\n");
3566  }
3567  writePS(">>\n");
3568  if (useBinary) {
3569  /* nothing to do */;
3570  } else if (useASCIIHex) {
3571  str = new ASCIIHexEncoder(str);
3572  } else {
3573  str = new ASCII85Encoder(str);
3574  }
3575  str->reset();
3576  if (useBinary) {
3577  // Count the bytes to write a document comment
3578  int len = 0;
3579  while (str->getChar() != EOF) {
3580  len++;
3581  }
3582  str->reset();
3583  writePSFmt("%%BeginData: {0:d} Binary Bytes\n", len + 6 + 1);
3584  }
3585  writePS("image\n");
3586  while ((c = str->getChar()) != EOF) {
3587  writePSChar(c);
3588  }
3589  str->close();
3590  delete str;
3591  delete str0;
3592  writePSChar('\n');
3593  if (useBinary) {
3594  writePS("%%EndData\n");
3595  }
3596  processColors |= (numComps == 1) ? psProcessBlack : psProcessCMYK;
3597  break;
3598  }
3599  writePS("grestore\n");
3600  }
3601 
3602  delete splashOut;
3603 
3604  // finish the PS page
3605  endPage();
3606 
3607  return false;
3608 
3609 #else // HAVE_SPLASH
3610 
3611  error(errSyntaxWarning, -1,
3612  "PDF page uses transparency and PSOutputDev was built without"
3613  " the Splash rasterizer - output may not be correct");
3614  return true;
3615 #endif // HAVE_SPLASH
3616 }
3617 
3618 void PSOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA)
3619 {
3620  Page *page;
3621  int x1, y1, x2, y2, width, height, t;
3622  int imgWidth, imgHeight, imgWidth2, imgHeight2;
3623  bool landscape;
3624  GooString *s;
3626 
3627  if (!postInitDone) {
3628  postInit();
3629  }
3630  xref = xrefA;
3631  if (mode == psModePS) {
3632  GooString pageLabel;
3633  const bool gotLabel = doc->getCatalog()->indexToLabel(pageNum - 1, &pageLabel);
3634  if (gotLabel) {
3635  // See bug13338 for why we try to avoid parentheses...
3636  bool needParens;
3637  GooString *filteredString = filterPSLabel(&pageLabel, &needParens);
3638  if (needParens) {
3639  writePSFmt("%%Page: ({0:t}) {1:d}\n", filteredString, seqPage);
3640  } else {
3641  writePSFmt("%%Page: {0:t} {1:d}\n", filteredString, seqPage);
3642  }
3643  delete filteredString;
3644  } else {
3645  writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage);
3646  }
3647  if (paperMatch) {
3648  page = doc->getCatalog()->getPage(pageNum);
3649  imgLLX = imgLLY = 0;
3650  if (noCrop) {
3651  imgURX = (int)ceil(page->getMediaWidth());
3652  imgURY = (int)ceil(page->getMediaHeight());
3653  } else {
3654  imgURX = (int)ceil(page->getCropWidth());
3655  imgURY = (int)ceil(page->getCropHeight());
3656  }
3657  if (state->getRotate() == 90 || state->getRotate() == 270) {
3658  t = imgURX;
3659  imgURX = imgURY;
3660  imgURY = t;
3661  }
3662  }
3663  }
3664 
3665  // underlays
3666  if (underlayCbk) {
3667  (*underlayCbk)(this, underlayCbkData);
3668  }
3669  if (overlayCbk) {
3670  saveState(nullptr);
3671  }
3672 
3673  xScale = yScale = 1;
3674  switch (mode) {
3675 
3676  case psModePS:
3677  // rotate, translate, and scale page
3678  imgWidth = imgURX - imgLLX;
3679  imgHeight = imgURY - imgLLY;
3680  x1 = (int)floor(state->getX1());
3681  y1 = (int)floor(state->getY1());
3682  x2 = (int)ceil(state->getX2());
3683  y2 = (int)ceil(state->getY2());
3684  if (unlikely(checkedSubtraction(x2, x1, &width))) {
3685  error(errSyntaxError, -1, "width too big");
3686  return;
3687  }
3688  height = y2 - y1;
3689  tx = ty = 0;
3690  // rotation and portrait/landscape mode
3691  if (paperMatch) {
3692  rotate = (360 - state->getRotate()) % 360;
3693  landscape = false;
3694  } else if (rotate0 >= 0) {
3695  rotate = (360 - rotate0) % 360;
3696  landscape = false;
3697  } else {
3698  rotate = (360 - state->getRotate()) % 360;
3699  if (rotate == 0 || rotate == 180) {
3700  if ((width < height && imgWidth > imgHeight && height > imgHeight) || (width > height && imgWidth < imgHeight && width > imgWidth)) {
3701  rotate += 90;
3702  landscape = true;
3703  } else {
3704  landscape = false;
3705  }
3706  } else { // rotate == 90 || rotate == 270
3707  if ((height < width && imgWidth > imgHeight && width > imgHeight) || (height > width && imgWidth < imgHeight && height > imgWidth)) {
3708  rotate = 270 - rotate;
3709  landscape = true;
3710  } else {
3711  landscape = false;
3712  }
3713  }
3714  }
3715  if (rotate == 0) {
3716  imgWidth2 = imgWidth;
3717  imgHeight2 = imgHeight;
3718  } else if (rotate == 90) {
3719  ty = -imgWidth;
3720  imgWidth2 = imgHeight;
3721  imgHeight2 = imgWidth;
3722  } else if (rotate == 180) {
3723  imgWidth2 = imgWidth;
3724  imgHeight2 = imgHeight;
3725  tx = -imgWidth;
3726  ty = -imgHeight;
3727  } else { // rotate == 270
3728  tx = -imgHeight;
3729  imgWidth2 = imgHeight;
3730  imgHeight2 = imgWidth;
3731  }
3732  // shrink or expand
3733  if (xScale0 > 0 && yScale0 > 0) {
3734  xScale = xScale0;
3735  yScale = yScale0;
3736  } else if ((globalParams->getPSShrinkLarger() && (width > imgWidth2 || height > imgHeight2)) || (globalParams->getPSExpandSmaller() && (width < imgWidth2 && height < imgHeight2))) {
3737  if (unlikely(width == 0)) {
3738  error(errSyntaxError, -1, "width 0, xScale would be infinite");
3739  return;
3740  }
3741  xScale = (double)imgWidth2 / (double)width;
3742  yScale = (double)imgHeight2 / (double)height;
3743  if (yScale < xScale) {
3744  xScale = yScale;
3745  } else {
3746  yScale = xScale;
3747  }
3748  }
3749  // deal with odd bounding boxes or clipping
3750  if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
3751  tx -= xScale * clipLLX0;
3752  ty -= yScale * clipLLY0;
3753  } else {
3754  tx -= xScale * x1;
3755  ty -= yScale * y1;
3756  }
3757  // center
3758  if (tx0 >= 0 && ty0 >= 0) {
3759  tx += (rotate == 0 || rotate == 180) ? tx0 : ty0;
3760  ty += (rotate == 0 || rotate == 180) ? ty0 : -tx0;
3761  } else if (psCenter) {
3762  if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
3763  tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2;
3764  ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2;
3765  } else {
3766  tx += (imgWidth2 - xScale * width) / 2;
3767  ty += (imgHeight2 - yScale * height) / 2;
3768  }
3769  }
3770  tx += (rotate == 0 || rotate == 180) ? imgLLX : imgLLY;
3771  ty += (rotate == 0 || rotate == 180) ? imgLLY : -imgLLX;
3772 
3773  if (paperMatch) {
3774  paperSize = (*paperSizes)[pagePaperSize[pageNum]];
3775  writePSFmt("%%PageMedia: {0:t}\n", paperSize->name);
3776  }
3777 
3778  // Create a matrix with the same transform that will be output to PS
3779  Matrix m;
3780  switch (rotate) {
3781  default:
3782  case 0:
3783  m.init(1, 0, 0, 1, 0, 0);
3784  break;
3785  case 90:
3786  m.init(0, 1, -1, 0, 0, 0);
3787  break;
3788  case 180:
3789  m.init(-1, 0, 0, -1, 0, 0);
3790  break;
3791  case 270:
3792  m.init(0, -1, 1, 0, 0, 0);
3793  break;
3794  }
3795  m.translate(tx, ty);
3796  m.scale(xScale, yScale);
3797 
3798  double bboxX1, bboxY1, bboxX2, bboxY2;
3799  m.transform(0, 0, &bboxX1, &bboxY1);
3800  m.transform(width, height, &bboxX2, &bboxY2);
3801 
3802  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)));
3803 
3804  writePSFmt("%%PageOrientation: {0:s}\n", landscape ? "Landscape" : "Portrait");
3805  writePS("%%BeginPageSetup\n");
3806  if (paperMatch) {
3807  writePSFmt("{0:d} {1:d} pdfSetupPaper\n", imgURX, imgURY);
3808  }
3809  writePS("pdfStartPage\n");
3810  if (rotate)
3811  writePSFmt("{0:d} rotate\n", rotate);
3812  if (tx != 0 || ty != 0) {
3813  writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty);
3814  }
3815  if (xScale != 1 || yScale != 1) {
3816  writePSFmt("{0:.6f} {1:.6f} scale\n", xScale, yScale);
3817  }
3818  if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
3819  writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re W\n", clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0);
3820  } else {
3821  writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1);
3822  }
3823 
3824  ++seqPage;
3825  break;
3826 
3827  case psModeEPS:
3828  writePS("pdfStartPage\n");
3829  tx = ty = 0;
3830  rotate = (360 - state->getRotate()) % 360;
3831  if (rotate == 0) {
3832  } else if (rotate == 90) {
3833  writePS("90 rotate\n");
3834  tx = -epsX1;
3835  ty = -epsY2;
3836  } else if (rotate == 180) {
3837  writePS("180 rotate\n");
3838  tx = -(epsX1 + epsX2);
3839  ty = -(epsY1 + epsY2);
3840  } else { // rotate == 270
3841  writePS("270 rotate\n");
3842  tx = -epsX2;
3843  ty = -epsY1;
3844  }
3845  if (tx != 0 || ty != 0) {
3846  writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty);
3847  }
3848  break;
3849 
3850  case psModeForm:
3851  writePS("/PaintProc {\n");
3852  writePS("begin xpdf begin\n");
3853  writePS("pdfStartPage\n");
3854  tx = ty = 0;
3855  rotate = 0;
3856  break;
3857  }
3858 
3859  if (customCodeCbk) {
3860  if ((s = (*customCodeCbk)(this, psOutCustomPageSetup, pageNum, customCodeCbkData))) {
3861  writePS(s->c_str());
3862  delete s;
3863  }
3864  }
3865 
3866  writePS("%%EndPageSetup\n");
3867 }
3868 
3869 void PSOutputDev::endPage()
3870 {
3871  if (overlayCbk) {
3872  restoreState(nullptr);
3873  (*overlayCbk)(this, overlayCbkData);
3874  }
3875 
3876  for (const auto &item : iccEmitted) {
3877  writePSFmt("userdict /{0:s} undef\n", item.c_str());
3878  }
3879  iccEmitted.clear();
3880 
3881  if (mode == psModeForm) {
3882  writePS("pdfEndPage\n");
3883  writePS("end end\n");
3884  writePS("} def\n");
3885  writePS("end end\n");
3886  } else {
3887  if (!manualCtrl) {
3888  writePS("showpage\n");
3889  }
3890  writePS("%%PageTrailer\n");
3891  writePageTrailer();
3892  }
3893 }
3894 
3896 {
3897  writePS("q\n");
3898  ++numSaves;
3899 }
3900 
3902 {
3903  writePS("Q\n");
3904  --numSaves;
3905 }
3906 
3907 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32)
3908 {
3909  writePSFmt("[{0:.6gs} {1:.6gs} {2:.6gs} {3:.6gs} {4:.6gs} {5:.6gs}] cm\n", m11, m12, m21, m22, m31, m32);
3910 }
3911 
3913 {
3914  double *dash;
3915  double start;
3916  int length, i;
3917 
3918  state->getLineDash(&dash, &length, &start);
3919  writePS("[");
3920  for (i = 0; i < length; ++i) {
3921  writePSFmt("{0:.6g}{1:w}", dash[i] < 0 ? 0 : dash[i], (i == length - 1) ? 0 : 1);
3922  }
3923  writePSFmt("] {0:.6g} d\n", start);
3924 }
3925 
3927 {
3928  writePSFmt("{0:d} i\n", state->getFlatness());
3929 }
3930 
3932 {
3933  writePSFmt("{0:d} j\n", state->getLineJoin());
3934 }
3935 
3937 {
3938  writePSFmt("{0:d} J\n", state->getLineCap());
3939 }
3940 
3942 {
3943  writePSFmt("{0:.6g} M\n", state->getMiterLimit());
3944 }
3945 
3947 {
3948  writePSFmt("{0:.6g} w\n", state->getLineWidth());
3949 }
3950 
3952 {
3953  if (inUncoloredPattern) {
3954  return;
3955  }
3956  switch (level) {
3957  case psLevel1:
3958  case psLevel1Sep:
3959  break;
3960  case psLevel2:
3961  case psLevel3:
3962  if (state->getFillColorSpace()->getMode() != csPattern) {
3963  dumpColorSpaceL2(state, state->getFillColorSpace(), true, false, false);
3964  writePS(" cs\n");
3965  }
3966  break;
3967  case psLevel2Sep:
3968  case psLevel3Sep:
3969  break;
3970  }
3971 }
3972 
3974 {
3975  if (inUncoloredPattern) {
3976  return;
3977  }
3978  switch (level) {
3979  case psLevel1:
3980  case psLevel1Sep:
3981  break;
3982  case psLevel2:
3983  case psLevel3:
3984  if (state->getStrokeColorSpace()->getMode() != csPattern) {
3985  dumpColorSpaceL2(state, state->getStrokeColorSpace(), true, false, false);
3986  writePS(" CS\n");
3987  }
3988  break;
3989  case psLevel2Sep:
3990  case psLevel3Sep:
3991  break;
3992  }
3993 }
3994 
3996 {
3997  GfxColor color;
3998  GfxGray gray;
3999  GfxCMYK cmyk;
4000  GfxSeparationColorSpace *sepCS;
4001  double c, m, y, k;
4002  int i;
4003 
4004  if (inUncoloredPattern) {
4005  return;
4006  }
4007  switch (level) {
4008  case psLevel1:
4009  state->getFillGray(&gray);
4010  writePSFmt("{0:.4g} g\n", colToDbl(gray));
4011  break;
4012  case psLevel2:
4013  case psLevel3:
4014  if (state->getFillColorSpace()->getMode() != csPattern) {
4015  const GfxColor *colorPtr = state->getFillColor();
4016  writePS("[");
4017  for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) {
4018  if (i > 0) {
4019  writePS(" ");
4020  }
4021  writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i]));
4022  }
4023  writePS("] sc\n");
4024  }
4025  break;
4026  case psLevel1Sep:
4027  case psLevel2Sep:
4028  case psLevel3Sep:
4029  if (state->getFillColorSpace()->getMode() == csSeparation && (level > psLevel1Sep || getPassLevel1CustomColor())) {
4030  sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
4031  color.c[0] = gfxColorComp1;
4032  sepCS->getCMYK(&color, &cmyk);
4033  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());
4034  addCustomColor(sepCS);
4035  } else {
4036  state->getFillCMYK(&cmyk);
4037  c = colToDbl(cmyk.c);
4038  m = colToDbl(cmyk.m);
4039  y = colToDbl(cmyk.y);
4040  k = colToDbl(cmyk.k);
4041  if (getOptimizeColorSpace()) {
4042  double g;
4043  g = 0.299 * c + 0.587 * m + 0.114 * y;
4044  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)) {
4045  c = m = y = 0.0;
4046  k += g;
4047  if (k > 1.0)
4048  k = 1.0;
4049  }
4050  }
4051  writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k);
4052  addProcessColor(c, m, y, k);
4053  }
4054  break;
4055  }
4056  t3Cacheable = false;
4057 }
4058 
4060 {
4061  GfxColor color;
4062  GfxGray gray;
4063  GfxCMYK cmyk;
4064  GfxSeparationColorSpace *sepCS;
4065  double c, m, y, k;
4066  int i;
4067 
4068  if (inUncoloredPattern) {
4069  return;
4070  }
4071  switch (level) {
4072  case psLevel1:
4073  state->getStrokeGray(&gray);
4074  writePSFmt("{0:.4g} G\n", colToDbl(gray));
4075  break;
4076  case psLevel2:
4077  case psLevel3:
4078  if (state->getStrokeColorSpace()->getMode() != csPattern) {
4079  const GfxColor *colorPtr = state->getStrokeColor();
4080  writePS("[");
4081  for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) {
4082  if (i > 0) {
4083  writePS(" ");
4084  }
4085  writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i]));
4086  }
4087  writePS("] SC\n");
4088  }
4089  break;
4090  case psLevel1Sep:
4091  case psLevel2Sep:
4092  case psLevel3Sep:
4093  if (state->getStrokeColorSpace()->getMode() == csSeparation && (level > psLevel1Sep || getPassLevel1CustomColor())) {
4094  sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
4095  color.c[0] = gfxColorComp1;
4096  sepCS->getCMYK(&color, &cmyk);
4097  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());
4098  addCustomColor(sepCS);
4099  } else {
4100  state->getStrokeCMYK(&cmyk);
4101  c = colToDbl(cmyk.c);
4102  m = colToDbl(cmyk.m);
4103  y = colToDbl(cmyk.y);
4104  k = colToDbl(cmyk.k);
4105  if (getOptimizeColorSpace()) {
4106  double g;
4107  g = 0.299 * c + 0.587 * m + 0.114 * y;
4108  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)) {
4109  c = m = y = 0.0;
4110  k += g;
4111  if (k > 1.0)
4112  k = 1.0;
4113  }
4114  }
4115  writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k);
4116  addProcessColor(c, m, y, k);
4117  }
4118  break;
4119  }
4120  t3Cacheable = false;
4121 }
4122 
4123 void PSOutputDev::addProcessColor(double c, double m, double y, double k)
4124 {
4125  if (c > 0) {
4127  }
4128  if (m > 0) {
4130  }
4131  if (y > 0) {
4133  }
4134  if (k > 0) {
4136  }
4137 }
4138 
4140 {
4141  PSOutCustomColor *cc;
4142  GfxColor color;
4143  GfxCMYK cmyk;
4144 
4145  if (!sepCS->getName()->cmp("Black")) {
4147  return;
4148  }
4149  if (!sepCS->getName()->cmp("Cyan")) {
4151  return;
4152  }
4153  if (!sepCS->getName()->cmp("Yellow")) {
4155  return;
4156  }
4157  if (!sepCS->getName()->cmp("Magenta")) {
4159  return;
4160  }
4161  if (!sepCS->getName()->cmp("All"))
4162  return;
4163  if (!sepCS->getName()->cmp("None"))
4164  return;
4165  for (cc = customColors; cc; cc = cc->next) {
4166  if (!cc->name->cmp(sepCS->getName())) {
4167  return;
4168  }
4169  }
4170  color.c[0] = gfxColorComp1;
4171  sepCS->getCMYK(&color, &cmyk);
4172  cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()->copy());
4173  cc->next = customColors;
4174  customColors = cc;
4175 }
4176 
4178 {
4179  if (level >= psLevel2) {
4180  writePSFmt("{0:s} op\n", state->getFillOverprint() ? "true" : "false");
4181  }
4182 }
4183 
4185 {
4186  if (level >= psLevel2) {
4187  writePSFmt("{0:s} OP\n", state->getStrokeOverprint() ? "true" : "false");
4188  }
4189 }
4190 
4192 {
4193  if (level >= psLevel3) {
4194  writePSFmt("{0:s} opm\n", state->getOverprintMode() ? "true" : "false");
4195  }
4196 }
4197 
4199 {
4200  Function **funcs;
4201  int i;
4202 
4203  funcs = state->getTransfer();
4204  if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) {
4205  if (level >= psLevel2) {
4206  for (i = 0; i < 4; ++i) {
4207  cvtFunction(funcs[i]);
4208  }
4209  writePS("setcolortransfer\n");
4210  } else {
4211  cvtFunction(funcs[3]);
4212  writePS("settransfer\n");
4213  }
4214  } else if (funcs[0]) {
4215  cvtFunction(funcs[0]);
4216  writePS("settransfer\n");
4217  } else {
4218  writePS("{} settransfer\n");
4219  }
4220 }
4221 
4223 {
4224  if (state->getFont()) {
4225  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());
4226  }
4227 }
4228 
4230 {
4231  const double *mat = state->getTextMat();
4232  if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.00001) {
4233  // avoid a singular (or close-to-singular) matrix
4234  writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", mat[4], mat[5]);
4235  } else {
4236  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]);
4237  }
4238 }
4239 
4241 {
4242  writePSFmt("{0:.6g} Tc\n", state->getCharSpace());
4243 }
4244 
4246 {
4247  int rm;
4248 
4249  rm = state->getRender();
4250  writePSFmt("{0:d} Tr\n", rm);
4251  rm &= 3;
4252  if (rm != 0 && rm != 3) {
4253  t3Cacheable = false;
4254  }
4255 }
4256 
4258 {
4259  writePSFmt("{0:.6g} Ts\n", state->getRise());
4260 }
4261 
4263 {
4264  writePSFmt("{0:.6g} Tw\n", state->getWordSpace());
4265 }
4266 
4268 {
4269  double h;
4270 
4271  h = state->getHorizScaling();
4272  if (fabs(h) < 0.01) {
4273  h = 0.01;
4274  }
4275  writePSFmt("{0:.6g} Tz\n", h);
4276 }
4277 
4279 {
4280  writePSFmt("{0:.6g} {1:.6g} Td\n", state->getLineX(), state->getLineY());
4281 }
4282 
4284 {
4285  if (state->getFont()->getWMode()) {
4286  writePSFmt("{0:.6g} TJmV\n", shift);
4287  } else {
4288  writePSFmt("{0:.6g} TJm\n", shift);
4289  }
4290 }
4291 
4293 {
4294  writePS("currentpoint\n");
4295 }
4296 
4298 {
4299  writePS("m\n");
4300 }
4301 
4303 {
4304  doPath(state->getPath());
4305  if (inType3Char && t3FillColorOnly) {
4306  // if we're constructing a cacheable Type 3 glyph, we need to do
4307  // everything in the fill color
4308  writePS("Sf\n");
4309  } else {
4310  writePS("S\n");
4311  }
4312 }
4313 
4315 {
4316  doPath(state->getPath());
4317  writePS("f\n");
4318 }
4319 
4321 {
4322  doPath(state->getPath());
4323  writePS("f*\n");
4324 }
4325 
4326 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,
4327  double yStep)
4328 {
4329  PDFRectangle box;
4330  Gfx *gfx;
4331 
4332  // define a Type 3 font
4333  writePS("8 dict begin\n");
4334  writePS("/FontType 3 def\n");
4335  writePS("/FontMatrix [1 0 0 1 0 0] def\n");
4336  writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", bbox[0], bbox[1], bbox[2], bbox[3]);
4337  writePS("/Encoding 256 array def\n");
4338  writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
4339  writePS(" Encoding 120 /x put\n");
4340  writePS("/BuildGlyph {\n");
4341  writePS(" exch /CharProcs get exch\n");
4342  writePS(" 2 copy known not { pop /.notdef } if\n");
4343  writePS(" get exec\n");
4344  writePS("} bind def\n");
4345  writePS("/BuildChar {\n");
4346  writePS(" 1 index /Encoding get exch get\n");
4347  writePS(" 1 index /BuildGlyph get exec\n");
4348  writePS("} bind def\n");
4349  writePS("/CharProcs 1 dict def\n");
4350  writePS("CharProcs begin\n");
4351  box.x1 = bbox[0];
4352  box.y1 = bbox[1];
4353  box.x2 = bbox[2];
4354  box.y2 = bbox[3];
4355  gfx = new Gfx(doc, this, resDict, &box, nullptr);
4356  writePS("/x {\n");
4357  if (paintType == 2) {
4358  writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n", xStep, bbox[0], bbox[1], bbox[2], bbox[3]);
4359  t3FillColorOnly = true;
4360  } else {
4361  if (x1 - 1 <= x0) {
4362  writePS("1 0 setcharwidth\n");
4363  } else {
4364  writePSFmt("{0:.6g} 0 setcharwidth\n", xStep);
4365  }
4366  t3FillColorOnly = false;
4367  }
4368  inType3Char = true;
4369  if (paintType == 2) {
4370  inUncoloredPattern = true;
4371  // ensure any PS procedures that contain sCol or fCol do not change the color
4372  writePS("/pdfLastFill true def\n");
4373  writePS("/pdfLastStroke true def\n");
4374  }
4376  gfx->display(str);
4378  if (paintType == 2) {
4379  inUncoloredPattern = false;
4380  // ensure the next PS procedures that uses sCol or fCol will update the color
4381  writePS("/pdfLastFill false def\n");
4382  writePS("/pdfLastStroke false def\n");
4383  }
4384  inType3Char = false;
4385  writePS("} def\n");
4386  delete gfx;
4387  writePS("end\n");
4388  writePS("currentdict end\n");
4389  writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns);
4390 
4391  // draw the tiles
4392  writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns);
4393  writePS("fCol\n");
4394  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]);
4395  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);
4396  writePS("grestore\n");
4397 
4398  return true;
4399 }
4400 
4401 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,
4402  double yStep)
4403 {
4404  PDFRectangle box;
4405  Gfx *gfx;
4406 
4407  if (paintType == 2) {
4408  // setpattern with PaintType 2 needs the paint color
4409  writePS("currentcolor\n");
4410  }
4411  writePS("<<\n /PatternType 1\n");
4412  writePSFmt(" /PaintType {0:d}\n", paintType);
4413  writePSFmt(" /TilingType {0:d}\n", tilingType);
4414  writePSFmt(" /BBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}]\n", bbox[0], bbox[1], bbox[2], bbox[3]);
4415  writePSFmt(" /XStep {0:.6g}\n", xStep);
4416  writePSFmt(" /YStep {0:.6g}\n", yStep);
4417  writePS(" /PaintProc { \n");
4418  box.x1 = bbox[0];
4419  box.y1 = bbox[1];
4420  box.x2 = bbox[2];
4421  box.y2 = bbox[3];
4422  gfx = new Gfx(doc, this, resDict, &box, nullptr);
4423  inType3Char = true;
4424  if (paintType == 2) {
4425  inUncoloredPattern = true;
4426  // ensure any PS procedures that contain sCol or fCol do not change the color
4427  writePS("/pdfLastFill true def\n");
4428  writePS("/pdfLastStroke true def\n");
4429  }
4430  gfx->display(str);
4431  if (paintType == 2) {
4432  inUncoloredPattern = false;
4433  // ensure the next PS procedures that uses sCol or fCol will update the color
4434  writePS("/pdfLastFill false def\n");
4435  writePS("/pdfLastStroke false def\n");
4436  }
4437  inType3Char = false;
4438  delete gfx;
4439  writePS(" }\n");
4440  writePS(">>\n");
4441  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]);
4442  writePS("makepattern setpattern\n");
4443  writePS("clippath fill\n"); // Gfx sets up a clip before calling out->tilingPatternFill()
4444 
4445  return true;
4446 }
4447 
4448 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)
4449 {
4450  std::set<int>::iterator patternRefIt;
4451  const int patternRefNum = tPat->getPatternRefNum();
4452  if (patternRefNum != -1) {
4453  if (patternsBeingTiled.find(patternRefNum) == patternsBeingTiled.end()) {
4454  patternRefIt = patternsBeingTiled.insert(patternRefNum).first;
4455  } else {
4456  // pretend we drew it anyway
4457  error(errSyntaxError, -1, "Loop in pattern fills");
4458  return true;
4459  }
4460  }
4461 
4462  const double *bbox = tPat->getBBox();
4463  const double *pmat = tPat->getMatrix();
4464  const int paintType = tPat->getPaintType();
4465  const int tilingType = tPat->getTilingType();
4466  Dict *resDict = tPat->getResDict();
4467  Object *str = tPat->getContentStream();
4468 
4469  bool res;
4470  if (x1 - x0 == 1 && y1 - y0 == 1) {
4471  // Don't need to use patterns if only one instance of the pattern is used
4472  PDFRectangle box;
4473  Gfx *gfx;
4474 
4475  const double singleStep_x = x0 * xStep;
4476  const double singleStep_y = y0 * yStep;
4477  const double singleStep_tx = singleStep_x * mat[0] + singleStep_y * mat[2] + mat[4];
4478  const double singleStep_ty = singleStep_x * mat[1] + singleStep_y * mat[3] + mat[5];
4479  box.x1 = bbox[0];
4480  box.y1 = bbox[1];
4481  box.x2 = bbox[2];
4482  box.y2 = bbox[3];
4483  gfx = new Gfx(doc, this, resDict, &box, nullptr, nullptr, nullptr, gfxA);
4484  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);
4485  inType3Char = true;
4486  gfx->display(str);
4487  inType3Char = false;
4488  delete gfx;
4489  res = true;
4490  } else if (level == psLevel1 || level == psLevel1Sep) {
4491  res = tilingPatternFillL1(state, cat, str, pmat, paintType, tilingType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
4492  } else {
4493  res = tilingPatternFillL2(state, cat, str, pmat, paintType, tilingType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
4494  }
4495 
4496  if (patternRefNum != -1) {
4497  patternsBeingTiled.erase(patternRefIt);
4498  }
4499 
4500  return res;
4501 }
4502 
4504 {
4505  double x0, y0, x1, y1;
4506  int i;
4507 
4508  if (level == psLevel2Sep || level == psLevel3Sep) {
4509  if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
4510  return false;
4511  }
4513  }
4514 
4515  shading->getDomain(&x0, &y0, &x1, &y1);
4516  const double *mat = shading->getMatrix();
4517  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]);
4518  writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
4519  if (shading->getNFuncs() == 1) {
4520  writePS("/func ");
4521  cvtFunction(shading->getFunc(0));
4522  writePS("def\n");
4523  } else {
4524  writePS("/func {\n");
4525  for (i = 0; i < shading->getNFuncs(); ++i) {
4526  if (i < shading->getNFuncs() - 1) {
4527  writePS("2 copy\n");
4528  }
4529  cvtFunction(shading->getFunc(i));
4530  writePS("exec\n");
4531  if (i < shading->getNFuncs() - 1) {
4532  writePS("3 1 roll\n");
4533  }
4534  }
4535  writePS("} def\n");
4536  }
4537  writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} 0 funcSH\n", x0, y0, x1, y1);
4538 
4539  return true;
4540 }
4541 
4542 bool PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double /*tMin*/, double /*tMax*/)
4543 {
4544  double xMin, yMin, xMax, yMax;
4545  double x0, y0, x1, y1, dx, dy, mul;
4546  double tMin, tMax, t, t0, t1;
4547  int i;
4548 
4549  if (level == psLevel2Sep || level == psLevel3Sep) {
4550  if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
4551  return false;
4552  }
4554  }
4555 
4556  // get the clip region bbox
4557  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
4558 
4559  // compute min and max t values, based on the four corners of the
4560  // clip region bbox
4561  shading->getCoords(&x0, &y0, &x1, &y1);
4562  dx = x1 - x0;
4563  dy = y1 - y0;
4564  if (fabs(dx) < 0.01 && fabs(dy) < 0.01) {
4565  return true;
4566  } else {
4567  mul = 1 / (dx * dx + dy * dy);
4568  tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
4569  t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
4570  if (t < tMin) {
4571  tMin = t;
4572  } else if (t > tMax) {
4573  tMax =