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