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