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