Coverage Report

Created: 2026-10-14 08:19

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