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