/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 | } |