]> git.ipfire.org Git - thirdparty/cups.git/blob - pdftops/PSOutputDev.cxx
Merge changes from CUPS 1.4svn-r7199.
[thirdparty/cups.git] / pdftops / PSOutputDev.cxx
1 //========================================================================
2 //
3 // PSOutputDev.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <config.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <ctype.h>
18 #include <stddef.h>
19 #include <stdarg.h>
20 #include <signal.h>
21 #include <math.h>
22 #include "GString.h"
23 #include "GList.h"
24 #include "config.h"
25 #include "GlobalParams.h"
26 #include "Object.h"
27 #include "Error.h"
28 #include "Function.h"
29 #include "Gfx.h"
30 #include "GfxState.h"
31 #include "GfxFont.h"
32 #include "UnicodeMap.h"
33 #include "FoFiType1C.h"
34 #include "FoFiTrueType.h"
35 #include "Catalog.h"
36 #include "Page.h"
37 #include "Stream.h"
38 #include "Annot.h"
39 #include "XRef.h"
40 #include "PSOutputDev.h"
41
42 #ifdef MACOS
43 // needed for setting type/creator of MacOS files
44 #include "ICSupport.h"
45 #endif
46
47 //------------------------------------------------------------------------
48 // PostScript prolog and setup
49 //------------------------------------------------------------------------
50
51 // The '~' escapes mark prolog code that is emitted only in certain
52 // levels:
53 //
54 // ~[123][sn]
55 // ^ ^----- s=psLevel*Sep, n=psLevel*
56 // +----- 1=psLevel1*, 2=psLevel2*, 3=psLevel3*
57
58 static char *prolog[] = {
59 "/xpdf 75 dict def xpdf begin",
60 "% PDF special state",
61 "/pdfDictSize 15 def",
62 "~1sn",
63 "/pdfStates 64 array def",
64 " 0 1 63 {",
65 " pdfStates exch pdfDictSize dict",
66 " dup /pdfStateIdx 3 index put",
67 " put",
68 " } for",
69 "~123sn",
70 "/pdfSetup {",
71 " 3 1 roll 2 array astore",
72 " /setpagedevice where {",
73 " pop 3 dict begin",
74 " /PageSize exch def",
75 " /ImagingBBox null def",
76 " /Policies 1 dict dup begin /PageSize 3 def end def",
77 " { /Duplex true def } if",
78 " currentdict end setpagedevice",
79 " } {",
80 " pop pop",
81 " } ifelse",
82 "} def",
83 "~1sn",
84 "/pdfOpNames [",
85 " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke",
86 " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender",
87 " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath",
88 "] def",
89 "~123sn",
90 "/pdfStartPage {",
91 "~1sn",
92 " pdfStates 0 get begin",
93 "~23sn",
94 " pdfDictSize dict begin",
95 "~23n",
96 " /pdfFillCS [] def",
97 " /pdfFillXform {} def",
98 " /pdfStrokeCS [] def",
99 " /pdfStrokeXform {} def",
100 "~1n",
101 " /pdfFill 0 def",
102 " /pdfStroke 0 def",
103 "~1s",
104 " /pdfFill [0 0 0 1] def",
105 " /pdfStroke [0 0 0 1] def",
106 "~23sn",
107 " /pdfFill [0] def",
108 " /pdfStroke [0] def",
109 " /pdfFillOP false def",
110 " /pdfStrokeOP false def",
111 "~123sn",
112 " /pdfLastFill false def",
113 " /pdfLastStroke false def",
114 " /pdfTextMat [1 0 0 1 0 0] def",
115 " /pdfFontSize 0 def",
116 " /pdfCharSpacing 0 def",
117 " /pdfTextRender 0 def",
118 " /pdfTextRise 0 def",
119 " /pdfWordSpacing 0 def",
120 " /pdfHorizScaling 1 def",
121 " /pdfTextClipPath [] def",
122 "} def",
123 "/pdfEndPage { end } def",
124 "~23s",
125 "% separation convention operators",
126 "/findcmykcustomcolor where {",
127 " pop",
128 "}{",
129 " /findcmykcustomcolor { 5 array astore } def",
130 "} ifelse",
131 "/setcustomcolor where {",
132 " pop",
133 "}{",
134 " /setcustomcolor {",
135 " exch",
136 " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
137 " 0 4 getinterval cvx",
138 " [ exch /dup load exch { mul exch dup } /forall load",
139 " /pop load dup ] cvx",
140 " ] setcolorspace setcolor",
141 " } def",
142 "} ifelse",
143 "/customcolorimage where {",
144 " pop",
145 "}{",
146 " /customcolorimage {",
147 " gsave",
148 " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
149 " 0 4 getinterval",
150 " [ exch /dup load exch { mul exch dup } /forall load",
151 " /pop load dup ] cvx",
152 " ] setcolorspace",
153 " 10 dict begin",
154 " /ImageType 1 def",
155 " /DataSource exch def",
156 " /ImageMatrix exch def",
157 " /BitsPerComponent exch def",
158 " /Height exch def",
159 " /Width exch def",
160 " /Decode [1 0] def",
161 " currentdict end",
162 " image",
163 " grestore",
164 " } def",
165 "} ifelse",
166 "~123sn",
167 "% PDF color state",
168 "~1n",
169 "/g { dup /pdfFill exch def setgray",
170 " /pdfLastFill true def /pdfLastStroke false def } def",
171 "/G { dup /pdfStroke exch def setgray",
172 " /pdfLastStroke true def /pdfLastFill false def } def",
173 "/fCol {",
174 " pdfLastFill not {",
175 " pdfFill setgray",
176 " /pdfLastFill true def /pdfLastStroke false def",
177 " } if",
178 "} def",
179 "/sCol {",
180 " pdfLastStroke not {",
181 " pdfStroke setgray",
182 " /pdfLastStroke true def /pdfLastFill false def",
183 " } if",
184 "} def",
185 "~1s",
186 "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
187 " /pdfLastFill true def /pdfLastStroke false def } def",
188 "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
189 " /pdfLastStroke true def /pdfLastFill false def } def",
190 "/fCol {",
191 " pdfLastFill not {",
192 " pdfFill aload pop setcmykcolor",
193 " /pdfLastFill true def /pdfLastStroke false def",
194 " } if",
195 "} def",
196 "/sCol {",
197 " pdfLastStroke not {",
198 " pdfStroke aload pop setcmykcolor",
199 " /pdfLastStroke true def /pdfLastFill false def",
200 " } if",
201 "} def",
202 "~23n",
203 "/cs { /pdfFillXform exch def dup /pdfFillCS exch def",
204 " setcolorspace } def",
205 "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def",
206 " setcolorspace } def",
207 "/sc { pdfLastFill not { pdfFillCS setcolorspace } if",
208 " dup /pdfFill exch def aload pop pdfFillXform setcolor",
209 " /pdfLastFill true def /pdfLastStroke false def } def",
210 "/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } if",
211 " dup /pdfStroke exch def aload pop pdfStrokeXform setcolor",
212 " /pdfLastStroke true def /pdfLastFill false def } def",
213 "/op { /pdfFillOP exch def",
214 " pdfLastFill { pdfFillOP setoverprint } if } def",
215 "/OP { /pdfStrokeOP exch def",
216 " pdfLastStroke { pdfStrokeOP setoverprint } if } def",
217 "/fCol {",
218 " pdfLastFill not {",
219 " pdfFillCS setcolorspace",
220 " pdfFill aload pop pdfFillXform setcolor",
221 " pdfFillOP setoverprint",
222 " /pdfLastFill true def /pdfLastStroke false def",
223 " } if",
224 "} def",
225 "/sCol {",
226 " pdfLastStroke not {",
227 " pdfStrokeCS setcolorspace",
228 " pdfStroke aload pop pdfStrokeXform setcolor",
229 " pdfStrokeOP setoverprint",
230 " /pdfLastStroke true def /pdfLastFill false def",
231 " } if",
232 "} def",
233 "~23s",
234 "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
235 " /pdfLastFill true def /pdfLastStroke false def } def",
236 "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
237 " /pdfLastStroke true def /pdfLastFill false def } def",
238 "/ck { 6 copy 6 array astore /pdfFill exch def",
239 " findcmykcustomcolor exch setcustomcolor",
240 " /pdfLastFill true def /pdfLastStroke false def } def",
241 "/CK { 6 copy 6 array astore /pdfStroke exch def",
242 " findcmykcustomcolor exch setcustomcolor",
243 " /pdfLastStroke true def /pdfLastFill false def } def",
244 "/op { /pdfFillOP exch def",
245 " pdfLastFill { pdfFillOP setoverprint } if } def",
246 "/OP { /pdfStrokeOP exch def",
247 " pdfLastStroke { pdfStrokeOP setoverprint } if } def",
248 "/fCol {",
249 " pdfLastFill not {",
250 " pdfFill aload length 4 eq {",
251 " setcmykcolor",
252 " }{",
253 " findcmykcustomcolor exch setcustomcolor",
254 " } ifelse",
255 " pdfFillOP setoverprint",
256 " /pdfLastFill true def /pdfLastStroke false def",
257 " } if",
258 "} def",
259 "/sCol {",
260 " pdfLastStroke not {",
261 " pdfStroke aload length 4 eq {",
262 " setcmykcolor",
263 " }{",
264 " findcmykcustomcolor exch setcustomcolor",
265 " } ifelse",
266 " pdfStrokeOP setoverprint",
267 " /pdfLastStroke true def /pdfLastFill false def",
268 " } if",
269 "} def",
270 "~123sn",
271 "% build a font",
272 "/pdfMakeFont {",
273 " 4 3 roll findfont",
274 " 4 2 roll matrix scale makefont",
275 " dup length dict begin",
276 " { 1 index /FID ne { def } { pop pop } ifelse } forall",
277 " /Encoding exch def",
278 " currentdict",
279 " end",
280 " definefont pop",
281 "} def",
282 "/pdfMakeFont16 {",
283 " exch findfont",
284 " dup length dict begin",
285 " { 1 index /FID ne { def } { pop pop } ifelse } forall",
286 " /WMode exch def",
287 " currentdict",
288 " end",
289 " definefont pop",
290 "} def",
291 "~3sn",
292 "/pdfMakeFont16L3 {",
293 " 1 index /CIDFont resourcestatus {",
294 " pop pop 1 index /CIDFont findresource /CIDFontType known",
295 " } {",
296 " false",
297 " } ifelse",
298 " {",
299 " 0 eq { /Identity-H } { /Identity-V } ifelse",
300 " exch 1 array astore composefont pop",
301 " } {",
302 " pdfMakeFont16",
303 " } ifelse",
304 "} def",
305 "~123sn",
306 "% graphics state operators",
307 "~1sn",
308 "/q {",
309 " gsave",
310 " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for",
311 " pdfStates pdfStateIdx 1 add get begin",
312 " pdfOpNames { exch def } forall",
313 "} def",
314 "/Q { end grestore } def",
315 "~23sn",
316 "/q { gsave pdfDictSize dict begin } def",
317 "/Q {",
318 " end grestore",
319 " /pdfLastFill where {",
320 " pop",
321 " pdfLastFill {",
322 " pdfFillOP setoverprint",
323 " } {",
324 " pdfStrokeOP setoverprint",
325 " } ifelse",
326 " } if",
327 "} def",
328 "~123sn",
329 "/cm { concat } def",
330 "/d { setdash } def",
331 "/i { setflat } def",
332 "/j { setlinejoin } def",
333 "/J { setlinecap } def",
334 "/M { setmiterlimit } def",
335 "/w { setlinewidth } def",
336 "% path segment operators",
337 "/m { moveto } def",
338 "/l { lineto } def",
339 "/c { curveto } def",
340 "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
341 " neg 0 rlineto closepath } def",
342 "/h { closepath } def",
343 "% path painting operators",
344 "/S { sCol stroke } def",
345 "/Sf { fCol stroke } def",
346 "/f { fCol fill } def",
347 "/f* { fCol eofill } def",
348 "% clipping operators",
349 "/W { clip newpath } def",
350 "/W* { eoclip newpath } def",
351 "% text state operators",
352 "/Tc { /pdfCharSpacing exch def } def",
353 "/Tf { dup /pdfFontSize exch def",
354 " dup pdfHorizScaling mul exch matrix scale",
355 " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
356 " exch findfont exch makefont setfont } def",
357 "/Tr { /pdfTextRender exch def } def",
358 "/Ts { /pdfTextRise exch def } def",
359 "/Tw { /pdfWordSpacing exch def } def",
360 "/Tz { /pdfHorizScaling exch def } def",
361 "% text positioning operators",
362 "/Td { pdfTextMat transform moveto } def",
363 "/Tm { /pdfTextMat exch def } def",
364 "% text string operators",
365 "/cshow where {",
366 " pop",
367 " /cshow2 {",
368 " dup {",
369 " pop pop",
370 " 1 string dup 0 3 index put 3 index exec",
371 " } exch cshow",
372 " pop pop",
373 " } def",
374 "}{",
375 " /cshow2 {",
376 " currentfont /FontType get 0 eq {",
377 " 0 2 2 index length 1 sub {",
378 " 2 copy get exch 1 add 2 index exch get",
379 " 2 copy exch 256 mul add",
380 " 2 string dup 0 6 5 roll put dup 1 5 4 roll put",
381 " 3 index exec",
382 " } for",
383 " } {",
384 " dup {",
385 " 1 string dup 0 3 index put 3 index exec",
386 " } forall",
387 " } ifelse",
388 " pop pop",
389 " } def",
390 "} ifelse",
391 "/awcp {", // awidthcharpath
392 " exch {",
393 " false charpath",
394 " 5 index 5 index rmoveto",
395 " 6 index eq { 7 index 7 index rmoveto } if",
396 " } exch cshow2",
397 " 6 {pop} repeat",
398 "} def",
399 "/Tj {",
400 " fCol", // because stringwidth has to draw Type 3 chars
401 " 1 index stringwidth pdfTextMat idtransform pop",
402 " sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",
403 " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
404 " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
405 " pdfTextMat dtransform",
406 " 6 5 roll Tj1",
407 "} def",
408 "/Tj16 {",
409 " fCol", // because stringwidth has to draw Type 3 chars
410 " 2 index stringwidth pdfTextMat idtransform pop",
411 " sub exch div",
412 " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
413 " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
414 " pdfTextMat dtransform",
415 " 6 5 roll Tj1",
416 "} def",
417 "/Tj16V {",
418 " fCol", // because stringwidth has to draw Type 3 chars
419 " 2 index stringwidth pdfTextMat idtransform exch pop",
420 " sub exch div",
421 " 0 pdfWordSpacing pdfTextMat dtransform 32",
422 " 4 3 roll pdfCharSpacing add 0 exch",
423 " pdfTextMat dtransform",
424 " 6 5 roll Tj1",
425 "} def",
426 "/Tj1 {",
427 " 0 pdfTextRise pdfTextMat dtransform rmoveto",
428 " currentpoint 8 2 roll",
429 " pdfTextRender 1 and 0 eq {",
430 " 6 copy awidthshow",
431 " } if",
432 " pdfTextRender 3 and dup 1 eq exch 2 eq or {",
433 " 7 index 7 index moveto",
434 " 6 copy",
435 " currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
436 " false awcp currentpoint stroke moveto",
437 " } if",
438 " pdfTextRender 4 and 0 ne {",
439 " 8 6 roll moveto",
440 " false awcp",
441 " /pdfTextClipPath [ pdfTextClipPath aload pop",
442 " {/moveto cvx}",
443 " {/lineto cvx}",
444 " {/curveto cvx}",
445 " {/closepath cvx}",
446 " pathforall ] def",
447 " currentpoint newpath moveto",
448 " } {",
449 " 8 {pop} repeat",
450 " } ifelse",
451 " 0 pdfTextRise neg pdfTextMat dtransform rmoveto",
452 "} def",
453 "/TJm { pdfFontSize 0.001 mul mul neg 0",
454 " pdfTextMat dtransform rmoveto } def",
455 "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch",
456 " pdfTextMat dtransform rmoveto } def",
457 "/Tclip { pdfTextClipPath cvx exec clip newpath",
458 " /pdfTextClipPath [] def } def",
459 "~1ns",
460 "% Level 1 image operators",
461 "~1n",
462 "/pdfIm1 {",
463 " /pdfImBuf1 4 index string def",
464 " { currentfile pdfImBuf1 readhexstring pop } image",
465 "} def",
466 "~1s",
467 "/pdfIm1Sep {",
468 " /pdfImBuf1 4 index string def",
469 " /pdfImBuf2 4 index string def",
470 " /pdfImBuf3 4 index string def",
471 " /pdfImBuf4 4 index string def",
472 " { currentfile pdfImBuf1 readhexstring pop }",
473 " { currentfile pdfImBuf2 readhexstring pop }",
474 " { currentfile pdfImBuf3 readhexstring pop }",
475 " { currentfile pdfImBuf4 readhexstring pop }",
476 " true 4 colorimage",
477 "} def",
478 "~1ns",
479 "/pdfImM1 {",
480 " fCol /pdfImBuf1 4 index 7 add 8 idiv string def",
481 " { currentfile pdfImBuf1 readhexstring pop } imagemask",
482 "} def",
483 "/pdfImM1a {",
484 " { 2 copy get exch 1 add exch } imagemask",
485 " pop pop",
486 "} def",
487 "~23sn",
488 "% Level 2 image operators",
489 "/pdfImBuf 100 string def",
490 "/pdfIm {",
491 " image",
492 " { currentfile pdfImBuf readline",
493 " not { pop exit } if",
494 " (%-EOD-) eq { exit } if } loop",
495 "} def",
496 "~23s",
497 "/pdfImSep {",
498 " findcmykcustomcolor exch",
499 " dup /Width get /pdfImBuf1 exch string def",
500 " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def",
501 " /pdfImDecodeLow exch def",
502 " begin Width Height BitsPerComponent ImageMatrix DataSource end",
503 " /pdfImData exch def",
504 " { pdfImData pdfImBuf1 readstring pop",
505 " 0 1 2 index length 1 sub {",
506 " 1 index exch 2 copy get",
507 " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi",
508 " 255 exch sub put",
509 " } for }",
510 " 6 5 roll customcolorimage",
511 " { currentfile pdfImBuf readline",
512 " not { pop exit } if",
513 " (%-EOD-) eq { exit } if } loop",
514 "} def",
515 "~23sn",
516 "/pdfImM {",
517 " fCol imagemask",
518 " { currentfile pdfImBuf readline",
519 " not { pop exit } if",
520 " (%-EOD-) eq { exit } if } loop",
521 "} def",
522 "/pdfImClip {",
523 " gsave",
524 " 0 2 4 index length 1 sub {",
525 " dup 4 index exch 2 copy",
526 " get 5 index div put",
527 " 1 add 3 index exch 2 copy",
528 " get 3 index div put",
529 " } for",
530 " pop pop rectclip",
531 "} def",
532 "/pdfImClipEnd { grestore } def",
533 "~23n",
534 "% shading operators",
535 "/colordelta {",
536 " false 0 1 3 index length 1 sub {",
537 " dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {",
538 " pop true",
539 " } if",
540 " } for",
541 " exch pop exch pop",
542 "} def",
543 "/funcCol { func n array astore } def",
544 "/funcSH {",
545 " dup 0 eq {",
546 " true",
547 " } {",
548 " dup 6 eq {",
549 " false",
550 " } {",
551 " 4 index 4 index funcCol dup",
552 " 6 index 4 index funcCol dup",
553 " 3 1 roll colordelta 3 1 roll",
554 " 5 index 5 index funcCol dup",
555 " 3 1 roll colordelta 3 1 roll",
556 " 6 index 8 index funcCol dup",
557 " 3 1 roll colordelta 3 1 roll",
558 " colordelta or or or",
559 " } ifelse",
560 " } ifelse",
561 " {",
562 " 1 add",
563 " 4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch",
564 " 6 index 6 index 4 index 4 index 4 index funcSH",
565 " 2 index 6 index 6 index 4 index 4 index funcSH",
566 " 6 index 2 index 4 index 6 index 4 index funcSH",
567 " 5 3 roll 3 2 roll funcSH pop pop",
568 " } {",
569 " pop 3 index 2 index add 0.5 mul 3 index 2 index add 0.5 mul",
570 " funcCol sc",
571 " dup 4 index exch mat transform m",
572 " 3 index 3 index mat transform l",
573 " 1 index 3 index mat transform l",
574 " mat transform l pop pop h f*",
575 " } ifelse",
576 "} def",
577 "/axialCol {",
578 " dup 0 lt {",
579 " pop t0",
580 " } {",
581 " dup 1 gt {",
582 " pop t1",
583 " } {",
584 " dt mul t0 add",
585 " } ifelse",
586 " } ifelse",
587 " func n array astore",
588 "} def",
589 "/axialSH {",
590 " dup 0 eq {",
591 " true",
592 " } {",
593 " dup 8 eq {",
594 " false",
595 " } {",
596 " 2 index axialCol 2 index axialCol colordelta",
597 " } ifelse",
598 " } ifelse",
599 " {",
600 " 1 add 3 1 roll 2 copy add 0.5 mul",
601 " dup 4 3 roll exch 4 index axialSH",
602 " exch 3 2 roll axialSH",
603 " } {",
604 " pop 2 copy add 0.5 mul axialCol sc",
605 " exch dup dx mul x0 add exch dy mul y0 add",
606 " 3 2 roll dup dx mul x0 add exch dy mul y0 add",
607 " dx abs dy abs ge {",
608 " 2 copy yMin sub dy mul dx div add yMin m",
609 " yMax sub dy mul dx div add yMax l",
610 " 2 copy yMax sub dy mul dx div add yMax l",
611 " yMin sub dy mul dx div add yMin l",
612 " h f*",
613 " } {",
614 " exch 2 copy xMin sub dx mul dy div add xMin exch m",
615 " xMax sub dx mul dy div add xMax exch l",
616 " exch 2 copy xMax sub dx mul dy div add xMax exch l",
617 " xMin sub dx mul dy div add xMin exch l",
618 " h f*",
619 " } ifelse",
620 " } ifelse",
621 "} def",
622 "/radialCol {",
623 " dup t0 lt {",
624 " pop t0",
625 " } {",
626 " dup t1 gt {",
627 " pop t1",
628 " } if",
629 " } ifelse",
630 " func n array astore",
631 "} def",
632 "/radialSH {",
633 " dup 0 eq {",
634 " true",
635 " } {",
636 " dup 8 eq {",
637 " false",
638 " } {",
639 " 2 index dt mul t0 add radialCol",
640 " 2 index dt mul t0 add radialCol colordelta",
641 " } ifelse",
642 " } ifelse",
643 " {",
644 " 1 add 3 1 roll 2 copy add 0.5 mul",
645 " dup 4 3 roll exch 4 index radialSH",
646 " exch 3 2 roll radialSH",
647 " } {",
648 " pop 2 copy add 0.5 mul dt mul t0 add axialCol sc",
649 " exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
650 " 0 360 arc h",
651 " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
652 " 0 360 arc h f*",
653 " } ifelse",
654 "} def",
655 "~123sn",
656 "end",
657 NULL
658 };
659
660 static char *cmapProlog[] = {
661 "/CIDInit /ProcSet findresource begin",
662 "10 dict begin",
663 " begincmap",
664 " /CMapType 1 def",
665 " /CMapName /Identity-H def",
666 " /CIDSystemInfo 3 dict dup begin",
667 " /Registry (Adobe) def",
668 " /Ordering (Identity) def",
669 " /Supplement 0 def",
670 " end def",
671 " 1 begincodespacerange",
672 " <0000> <ffff>",
673 " endcodespacerange",
674 " 0 usefont",
675 " 1 begincidrange",
676 " <0000> <ffff> 0",
677 " endcidrange",
678 " endcmap",
679 " currentdict CMapName exch /CMap defineresource pop",
680 "end",
681 "10 dict begin",
682 " begincmap",
683 " /CMapType 1 def",
684 " /CMapName /Identity-V def",
685 " /CIDSystemInfo 3 dict dup begin",
686 " /Registry (Adobe) def",
687 " /Ordering (Identity) def",
688 " /Supplement 0 def",
689 " end def",
690 " /WMode 1 def",
691 " 1 begincodespacerange",
692 " <0000> <ffff>",
693 " endcodespacerange",
694 " 0 usefont",
695 " 1 begincidrange",
696 " <0000> <ffff> 0",
697 " endcidrange",
698 " endcmap",
699 " currentdict CMapName exch /CMap defineresource pop",
700 "end",
701 "end",
702 NULL
703 };
704
705 //------------------------------------------------------------------------
706 // Fonts
707 //------------------------------------------------------------------------
708
709 struct PSSubstFont {
710 char *psName; // PostScript name
711 double mWidth; // width of 'm' character
712 };
713
714 static char *psFonts[] = {
715 "Courier",
716 "Courier-Bold",
717 "Courier-Oblique",
718 "Courier-BoldOblique",
719 "Helvetica",
720 "Helvetica-Bold",
721 "Helvetica-Oblique",
722 "Helvetica-BoldOblique",
723 "Symbol",
724 "Times-Roman",
725 "Times-Bold",
726 "Times-Italic",
727 "Times-BoldItalic",
728 "ZapfDingbats",
729 NULL
730 };
731
732 static PSSubstFont psSubstFonts[] = {
733 {"Helvetica", 0.833},
734 {"Helvetica-Oblique", 0.833},
735 {"Helvetica-Bold", 0.889},
736 {"Helvetica-BoldOblique", 0.889},
737 {"Times-Roman", 0.788},
738 {"Times-Italic", 0.722},
739 {"Times-Bold", 0.833},
740 {"Times-BoldItalic", 0.778},
741 {"Courier", 0.600},
742 {"Courier-Oblique", 0.600},
743 {"Courier-Bold", 0.600},
744 {"Courier-BoldOblique", 0.600}
745 };
746
747 // Encoding info for substitute 16-bit font
748 struct PSFont16Enc {
749 Ref fontID;
750 GString *enc;
751 };
752
753 //------------------------------------------------------------------------
754 // process colors
755 //------------------------------------------------------------------------
756
757 #define psProcessCyan 1
758 #define psProcessMagenta 2
759 #define psProcessYellow 4
760 #define psProcessBlack 8
761 #define psProcessCMYK 15
762
763 //------------------------------------------------------------------------
764 // PSOutCustomColor
765 //------------------------------------------------------------------------
766
767 class PSOutCustomColor {
768 public:
769
770 PSOutCustomColor(double cA, double mA,
771 double yA, double kA, GString *nameA);
772 ~PSOutCustomColor();
773
774 double c, m, y, k;
775 GString *name;
776 PSOutCustomColor *next;
777 };
778
779 PSOutCustomColor::PSOutCustomColor(double cA, double mA,
780 double yA, double kA, GString *nameA) {
781 c = cA;
782 m = mA;
783 y = yA;
784 k = kA;
785 name = nameA;
786 next = NULL;
787 }
788
789 PSOutCustomColor::~PSOutCustomColor() {
790 delete name;
791 }
792
793 //------------------------------------------------------------------------
794 // DeviceNRecoder
795 //------------------------------------------------------------------------
796
797 class DeviceNRecoder: public FilterStream {
798 public:
799
800 DeviceNRecoder(Stream *strA, int widthA, int heightA,
801 GfxImageColorMap *colorMapA);
802 virtual ~DeviceNRecoder();
803 virtual StreamKind getKind() { return strWeird; }
804 virtual void reset();
805 virtual int getChar()
806 { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; }
807 virtual int lookChar()
808 { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; }
809 virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
810 virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
811 virtual GBool isEncoder() { return gTrue; }
812
813 private:
814
815 GBool fillBuf();
816
817 int width, height;
818 GfxImageColorMap *colorMap;
819 Function *func;
820 ImageStream *imgStr;
821 int buf[gfxColorMaxComps];
822 int pixelIdx;
823 int bufIdx;
824 int bufSize;
825 };
826
827 DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA,
828 GfxImageColorMap *colorMapA):
829 FilterStream(strA) {
830 width = widthA;
831 height = heightA;
832 colorMap = colorMapA;
833 imgStr = NULL;
834 pixelIdx = 0;
835 bufIdx = gfxColorMaxComps;
836 bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
837 getAlt()->getNComps();
838 func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
839 getTintTransformFunc();
840 }
841
842 DeviceNRecoder::~DeviceNRecoder() {
843 if (imgStr) {
844 delete imgStr;
845 }
846 }
847
848 void DeviceNRecoder::reset() {
849 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
850 colorMap->getBits());
851 imgStr->reset();
852 }
853
854 GBool DeviceNRecoder::fillBuf() {
855 Guchar pixBuf[gfxColorMaxComps];
856 GfxColor color;
857 double x[gfxColorMaxComps], y[gfxColorMaxComps];
858 int i;
859
860 if (pixelIdx >= width * height) {
861 return gFalse;
862 }
863 imgStr->getPixel(pixBuf);
864 colorMap->getColor(pixBuf, &color);
865 for (i = 0;
866 i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps();
867 ++i) {
868 x[i] = colToDbl(color.c[i]);
869 }
870 func->transform(x, y);
871 for (i = 0; i < bufSize; ++i) {
872 buf[i] = (int)(y[i] * 255 + 0.5);
873 }
874 bufIdx = 0;
875 ++pixelIdx;
876 return gTrue;
877 }
878
879 //------------------------------------------------------------------------
880 // PSOutputDev
881 //------------------------------------------------------------------------
882
883 extern "C" {
884 typedef void (*SignalFunc)(int);
885 }
886
887 static void outputToFile(void *stream, char *data, int len) {
888 fwrite(data, 1, len, (FILE *)stream);
889 }
890
891 PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
892 int firstPage, int lastPage, PSOutMode modeA,
893 int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
894 GBool manualCtrlA, const char *pageRangesA) {
895 FILE *f;
896 PSFileType fileTypeA;
897
898 underlayCbk = NULL;
899 underlayCbkData = NULL;
900 overlayCbk = NULL;
901 overlayCbkData = NULL;
902
903 fontIDs = NULL;
904 fontFileIDs = NULL;
905 fontFileNames = NULL;
906 font16Enc = NULL;
907 xobjStack = NULL;
908 embFontList = NULL;
909 customColors = NULL;
910 haveTextClip = gFalse;
911 t3String = NULL;
912
913 // open file or pipe
914 if (!strcmp(fileName, "-")) {
915 fileTypeA = psStdout;
916 f = stdout;
917 } else if (fileName[0] == '|') {
918 fileTypeA = psPipe;
919 #ifdef HAVE_POPEN
920 #ifndef WIN32
921 signal(SIGPIPE, (SignalFunc)SIG_IGN);
922 #endif
923 if (!(f = popen(fileName + 1, "w"))) {
924 error(-1, "Couldn't run print command '%s'", fileName);
925 ok = gFalse;
926 return;
927 }
928 #else
929 error(-1, "Print commands are not supported ('%s')", fileName);
930 ok = gFalse;
931 return;
932 #endif
933 } else {
934 fileTypeA = psFile;
935 if (!(f = fopen(fileName, "w"))) {
936 error(-1, "Couldn't open PostScript file '%s'", fileName);
937 ok = gFalse;
938 return;
939 }
940 }
941
942 init(outputToFile, f, fileTypeA,
943 xrefA, catalog, firstPage, lastPage, modeA,
944 imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, pageRangesA);
945 }
946
947 PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
948 XRef *xrefA, Catalog *catalog,
949 int firstPage, int lastPage, PSOutMode modeA,
950 int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
951 GBool manualCtrlA, const char *pageRangesA) {
952 underlayCbk = NULL;
953 underlayCbkData = NULL;
954 overlayCbk = NULL;
955 overlayCbkData = NULL;
956
957 fontIDs = NULL;
958 fontFileIDs = NULL;
959 fontFileNames = NULL;
960 font16Enc = NULL;
961 xobjStack = NULL;
962 embFontList = NULL;
963 customColors = NULL;
964 haveTextClip = gFalse;
965 t3String = NULL;
966
967 init(outputFuncA, outputStreamA, psGeneric,
968 xrefA, catalog, firstPage, lastPage, modeA,
969 imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, pageRangesA);
970 }
971
972 void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
973 PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,
974 int firstPage, int lastPage, PSOutMode modeA,
975 int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
976 GBool manualCtrlA, const char *pageRangesA) {
977 Page *page;
978 PDFRectangle *box;
979
980 // initialize
981 ok = gTrue;
982 outputFunc = outputFuncA;
983 outputStream = outputStreamA;
984 fileType = fileTypeA;
985 xref = xrefA;
986 level = globalParams->getPSLevel();
987 mode = modeA;
988 paperWidth = globalParams->getPSPaperWidth();
989 paperHeight = globalParams->getPSPaperHeight();
990 imgLLX = imgLLXA;
991 imgLLY = imgLLYA;
992 imgURX = imgURXA;
993 imgURY = imgURYA;
994 pageRanges = pageRangesA;
995 if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) {
996 globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY);
997 }
998 if (paperWidth < 0 || paperHeight < 0) {
999 // this check is needed in case the document has zero pages
1000 if (firstPage > 0 && firstPage <= catalog->getNumPages()) {
1001 page = catalog->getPage(firstPage);
1002 paperWidth = (int)ceil(page->getMediaWidth());
1003 paperHeight = (int)ceil(page->getMediaHeight());
1004 } else {
1005 paperWidth = 1;
1006 paperHeight = 1;
1007 }
1008 imgLLX = imgLLY = 0;
1009 imgURX = paperWidth;
1010 imgURY = paperHeight;
1011 }
1012 manualCtrl = manualCtrlA;
1013 if (mode == psModeForm) {
1014 lastPage = firstPage;
1015 }
1016 processColors = 0;
1017 inType3Char = gFalse;
1018
1019 #if OPI_SUPPORT
1020 // initialize OPI nesting levels
1021 opi13Nest = 0;
1022 opi20Nest = 0;
1023 #endif
1024
1025 tx0 = ty0 = 0;
1026 xScale0 = yScale0 = 0;
1027 rotate0 = -1;
1028 clipLLX0 = clipLLY0 = 0;
1029 clipURX0 = clipURY0 = -1;
1030
1031 // initialize fontIDs, fontFileIDs, and fontFileNames lists
1032 fontIDSize = 64;
1033 fontIDLen = 0;
1034 fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref));
1035 fontFileIDSize = 64;
1036 fontFileIDLen = 0;
1037 fontFileIDs = (Ref *)gmallocn(fontFileIDSize, sizeof(Ref));
1038 fontFileNameSize = 64;
1039 fontFileNameLen = 0;
1040 fontFileNames = (GString **)gmallocn(fontFileNameSize, sizeof(GString *));
1041 nextTrueTypeNum = 0;
1042 font16EncLen = 0;
1043 font16EncSize = 0;
1044
1045 xobjStack = new GList();
1046 numSaves = 0;
1047 numTilingPatterns = 0;
1048 nextFunc = 0;
1049
1050 // initialize embedded font resource comment list
1051 embFontList = new GString();
1052
1053 if (!manualCtrl) {
1054 // this check is needed in case the document has zero pages
1055 if (firstPage > 0 && firstPage <= catalog->getNumPages()) {
1056 writeHeader(firstPage, lastPage,
1057 catalog->getPage(firstPage)->getMediaBox(),
1058 catalog->getPage(firstPage)->getCropBox(),
1059 catalog->getPage(firstPage)->getRotate());
1060 } else {
1061 box = new PDFRectangle(0, 0, 1, 1);
1062 writeHeader(firstPage, lastPage, box, box, 0);
1063 delete box;
1064 }
1065 if (mode != psModeForm) {
1066 writePS("%%BeginProlog\n");
1067 }
1068 writeXpdfProcset();
1069 if (mode != psModeForm) {
1070 writePS("%%EndProlog\n");
1071 writePS("%%BeginSetup\n");
1072 }
1073 writeDocSetup(catalog, firstPage, lastPage);
1074 if (mode != psModeForm) {
1075 writePS("%%EndSetup\n");
1076 }
1077 }
1078
1079 // initialize sequential page number
1080 seqPage = 1;
1081 }
1082
1083 PSOutputDev::~PSOutputDev() {
1084 PSOutCustomColor *cc;
1085 int i;
1086
1087 if (ok) {
1088 if (!manualCtrl) {
1089 writePS("%%Trailer\n");
1090 writeTrailer();
1091 if (mode != psModeForm) {
1092 writePS("%%EOF\n");
1093 }
1094 }
1095 if (fileType == psFile) {
1096 #ifdef MACOS
1097 ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
1098 #endif
1099 fclose((FILE *)outputStream);
1100 }
1101 #ifdef HAVE_POPEN
1102 else if (fileType == psPipe) {
1103 pclose((FILE *)outputStream);
1104 #ifndef WIN32
1105 signal(SIGPIPE, (SignalFunc)SIG_DFL);
1106 #endif
1107 }
1108 #endif
1109 }
1110 if (embFontList) {
1111 delete embFontList;
1112 }
1113 if (fontIDs) {
1114 gfree(fontIDs);
1115 }
1116 if (fontFileIDs) {
1117 gfree(fontFileIDs);
1118 }
1119 if (fontFileNames) {
1120 for (i = 0; i < fontFileNameLen; ++i) {
1121 delete fontFileNames[i];
1122 }
1123 gfree(fontFileNames);
1124 }
1125 if (font16Enc) {
1126 for (i = 0; i < font16EncLen; ++i) {
1127 delete font16Enc[i].enc;
1128 }
1129 gfree(font16Enc);
1130 }
1131 if (xobjStack) {
1132 delete xobjStack;
1133 }
1134 while (customColors) {
1135 cc = customColors;
1136 customColors = cc->next;
1137 delete cc;
1138 }
1139 }
1140
1141 void PSOutputDev::writeHeader(int firstPage, int lastPage,
1142 PDFRectangle *mediaBox, PDFRectangle *cropBox,
1143 int pageRotate) {
1144 Object info, obj1;
1145 double x1, y1, x2, y2;
1146 GString *s;
1147 int i;
1148
1149 switch (mode) {
1150 case psModePS:
1151 writePS("%!PS-Adobe-3.0\n");
1152 break;
1153 case psModeEPS:
1154 writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
1155 break;
1156 case psModeForm:
1157 writePS("%!PS-Adobe-3.0 Resource-Form\n");
1158 break;
1159 }
1160
1161 // Tell CUPS pstops filter not to do its own rotation...
1162 int cups_rotate = 0;
1163 int width = (int)ceil(mediaBox->x2 - mediaBox->x1);
1164 int height = (int)ceil(mediaBox->y2 - mediaBox->y1);
1165 int imgWidth = (int)ceil(cropBox->x2 - cropBox->x1);
1166
1167 if (width > height && width > imgWidth) cups_rotate = 90;
1168
1169 writePSFmt("%%cupsRotation: %d\n", cups_rotate);
1170
1171 writePSFmt("%%Producer: xpdf/pdftops %s\n", xpdfVersion);
1172 xref->getDocInfo(&info);
1173 if (info.isDict() && info.dictLookup("Creator", &obj1)->isString()) {
1174 writePS("%%Creator: ");
1175 s = obj1.getString();
1176 if ((s->getChar(0) & 0xff) == 0xfe &&
1177 (s->getChar(1) & 0xff) == 0xff) {
1178 // Convert UTF-16 to UTF-8...
1179 for (i = 2; i < s->getLength() && i < 400; i += 2) {
1180 int ch = ((s->getChar(i) & 255) << 8) | (s->getChar(i + 1) & 255);
1181
1182 if (ch >= 0xd800 && ch <= 0xdbff) {
1183 // Multi-word UTF-16 char...
1184 i += 2;
1185 int lch = ((s->getChar(i) & 255) << 8) | (s->getChar(i + 1) & 255);
1186
1187 if (lch < 0xdc00 || lch >= 0xdfff) continue;
1188
1189 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
1190 }
1191
1192 if (ch < 0x80)
1193 {
1194 /*
1195 * Single byte ASCII...
1196 */
1197
1198 writePSChar(ch);
1199 }
1200 else if (ch < 0x800)
1201 {
1202 /*
1203 * Two-byte UTF-8...
1204 */
1205
1206 writePSChar(0xc0 | (ch >> 6));
1207 writePSChar(0x80 | (ch & 0x3f));
1208 }
1209 else if (ch < 0x10000)
1210 {
1211 /*
1212 * Three-byte UTF-8...
1213 */
1214
1215 writePSChar(0xe0 | (ch >> 12));
1216 writePSChar(0x80 | ((ch >> 6) & 0x3f));
1217 writePSChar(0x80 | (ch & 0x3f));
1218 }
1219 else
1220 {
1221 /*
1222 * Four-byte UTF-8...
1223 */
1224
1225 writePSChar(0xf0 | (ch >> 18));
1226 writePSChar(0x80 | ((ch >> 12) & 0x3f));
1227 writePSChar(0x80 | ((ch >> 6) & 0x3f));
1228 writePSChar(0x80 | (ch & 0x3f));
1229 }
1230 }
1231 } else {
1232 for (i = 0; i < s->getLength() && i < 200; ++i) {
1233 writePSChar(s->getChar(i));
1234 }
1235 }
1236 writePS("\n");
1237 }
1238 obj1.free();
1239 if (info.isDict() && info.dictLookup("Title", &obj1)->isString()) {
1240 writePS("%%Title: ");
1241 s = obj1.getString();
1242 if ((s->getChar(0) & 0xff) == 0xfe &&
1243 (s->getChar(1) & 0xff) == 0xff) {
1244 // Convert UTF-16 to UTF-8...
1245 for (i = 2; i < s->getLength() && i < 400; i += 2) {
1246 int ch = ((s->getChar(i) & 255) << 8) | (s->getChar(i + 1) & 255);
1247
1248 if (ch >= 0xd800 && ch <= 0xdbff) {
1249 // Multi-word UTF-16 char...
1250 i += 2;
1251 int lch = ((s->getChar(i) & 255) << 8) | (s->getChar(i + 1) & 255);
1252
1253 if (lch < 0xdc00 || lch >= 0xdfff) continue;
1254
1255 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
1256 }
1257
1258 if (ch < 0x80)
1259 {
1260 /*
1261 * Single byte ASCII...
1262 */
1263
1264 writePSChar(ch);
1265 }
1266 else if (ch < 0x800)
1267 {
1268 /*
1269 * Two-byte UTF-8...
1270 */
1271
1272 writePSChar(0xc0 | (ch >> 6));
1273 writePSChar(0x80 | (ch & 0x3f));
1274 }
1275 else if (ch < 0x10000)
1276 {
1277 /*
1278 * Three-byte UTF-8...
1279 */
1280
1281 writePSChar(0xe0 | (ch >> 12));
1282 writePSChar(0x80 | ((ch >> 6) & 0x3f));
1283 writePSChar(0x80 | (ch & 0x3f));
1284 }
1285 else
1286 {
1287 /*
1288 * Four-byte UTF-8...
1289 */
1290
1291 writePSChar(0xf0 | (ch >> 18));
1292 writePSChar(0x80 | ((ch >> 12) & 0x3f));
1293 writePSChar(0x80 | ((ch >> 6) & 0x3f));
1294 writePSChar(0x80 | (ch & 0x3f));
1295 }
1296 }
1297 } else {
1298 for (i = 0; i < s->getLength() && i < 200; ++i) {
1299 writePSChar(s->getChar(i));
1300 }
1301 }
1302 writePS("\n");
1303 }
1304 obj1.free();
1305 info.free();
1306 writePSFmt("%%%%LanguageLevel: %d\n",
1307 (level == psLevel1 || level == psLevel1Sep) ? 1 :
1308 (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
1309 if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
1310 writePS("%%DocumentProcessColors: (atend)\n");
1311 writePS("%%DocumentCustomColors: (atend)\n");
1312 }
1313 writePS("%%DocumentSuppliedResources: (atend)\n");
1314
1315 switch (mode) {
1316 case psModePS:
1317 writePSFmt("%%%%DocumentMedia: plain %d %d 0 () ()\n",
1318 paperWidth, paperHeight);
1319 writePSFmt("%%%%BoundingBox: 0 0 %d %d\n", paperWidth, paperHeight);
1320 writePSFmt("%%%%Pages: %d\n", lastPage - firstPage + 1);
1321 writePS("%%EndComments\n");
1322 writePS("%%BeginDefaults\n");
1323 writePS("%%PageMedia: plain\n");
1324 writePS("%%EndDefaults\n");
1325 break;
1326 case psModeEPS:
1327 epsX1 = cropBox->x1;
1328 epsY1 = cropBox->y1;
1329 epsX2 = cropBox->x2;
1330 epsY2 = cropBox->y2;
1331 if (pageRotate == 0 || pageRotate == 180) {
1332 x1 = epsX1;
1333 y1 = epsY1;
1334 x2 = epsX2;
1335 y2 = epsY2;
1336 } else { // pageRotate == 90 || pageRotate == 270
1337 x1 = 0;
1338 y1 = 0;
1339 x2 = epsY2 - epsY1;
1340 y2 = epsX2 - epsX1;
1341 }
1342 writePSFmt("%%%%BoundingBox: %d %d %d %d\n",
1343 (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2));
1344 if (floor(x1) != ceil(x1) || floor(y1) != ceil(y1) ||
1345 floor(x2) != ceil(x2) || floor(y2) != ceil(y2)) {
1346 writePSFmt("%%%%HiResBoundingBox: %g %g %g %g\n", x1, y1, x2, y2);
1347 }
1348 writePS("%%EndComments\n");
1349 break;
1350 case psModeForm:
1351 writePS("%%EndComments\n");
1352 writePS("32 dict dup begin\n");
1353 writePSFmt("/BBox [%d %d %d %d] def\n",
1354 (int)floor(mediaBox->x1), (int)floor(mediaBox->y1),
1355 (int)ceil(mediaBox->x2), (int)ceil(mediaBox->y2));
1356 writePS("/FormType 1 def\n");
1357 writePS("/Matrix [1 0 0 1 0 0] def\n");
1358 break;
1359 }
1360 }
1361
1362 void PSOutputDev::writeXpdfProcset() {
1363 GBool lev1, lev2, lev3, sep, nonSep;
1364 char **p;
1365 char *q;
1366
1367 writePSFmt("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion);
1368 lev1 = lev2 = lev3 = sep = nonSep = gTrue;
1369 for (p = prolog; *p; ++p) {
1370 if ((*p)[0] == '~') {
1371 lev1 = lev2 = lev3 = sep = nonSep = gFalse;
1372 for (q = *p + 1; *q; ++q) {
1373 switch (*q) {
1374 case '1': lev1 = gTrue; break;
1375 case '2': lev2 = gTrue; break;
1376 case '3': lev3 = gTrue; break;
1377 case 's': sep = gTrue; break;
1378 case 'n': nonSep = gTrue; break;
1379 }
1380 }
1381 } else if ((level == psLevel1 && lev1 && nonSep) ||
1382 (level == psLevel1Sep && lev1 && sep) ||
1383 (level == psLevel2 && lev2 && nonSep) ||
1384 (level == psLevel2Sep && lev2 && sep) ||
1385 (level == psLevel3 && lev3 && nonSep) ||
1386 (level == psLevel3Sep && lev3 && sep)) {
1387 writePSFmt("%s\n", *p);
1388 }
1389 }
1390 writePS("%%EndResource\n");
1391
1392 if (level >= psLevel3) {
1393 for (p = cmapProlog; *p; ++p) {
1394 writePSFmt("%s\n", *p);
1395 }
1396 }
1397 }
1398
1399 void PSOutputDev::writeDocSetup(Catalog *catalog,
1400 int firstPage, int lastPage) {
1401 Page *page;
1402 Dict *resDict;
1403 Annots *annots;
1404 Object obj1, obj2;
1405 int pg, i;
1406
1407 if (mode == psModeForm) {
1408 // swap the form and xpdf dicts
1409 writePS("xpdf end begin dup begin\n");
1410 } else {
1411 writePS("xpdf begin\n");
1412 }
1413 for (pg = firstPage; pg <= lastPage; ++pg) {
1414 page = catalog->getPage(pg);
1415 if ((resDict = page->getResourceDict())) {
1416 setupResources(resDict);
1417 }
1418 annots = new Annots(xref, catalog, page->getAnnots(&obj1));
1419 obj1.free();
1420 for (i = 0; i < annots->getNumAnnots(); ++i) {
1421 if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
1422 obj1.streamGetDict()->lookup("Resources", &obj2);
1423 if (obj2.isDict()) {
1424 setupResources(obj2.getDict());
1425 }
1426 obj2.free();
1427 }
1428 obj1.free();
1429 }
1430 delete annots;
1431 }
1432 if (mode != psModeForm) {
1433 if (mode != psModeEPS && !manualCtrl) {
1434 writePSFmt("%d %d %s pdfSetup\n",
1435 paperWidth, paperHeight,
1436 globalParams->getPSDuplex() ? "true" : "false");
1437 }
1438 #if OPI_SUPPORT
1439 if (globalParams->getPSOPI()) {
1440 writePS("/opiMatrix matrix currentmatrix def\n");
1441 }
1442 #endif
1443 }
1444 }
1445
1446 void PSOutputDev::writePageTrailer() {
1447 if (mode != psModeForm) {
1448 writePS("pdfEndPage\n");
1449 }
1450 }
1451
1452 void PSOutputDev::writeTrailer() {
1453 PSOutCustomColor *cc;
1454
1455 if (mode == psModeForm) {
1456 writePS("/Foo exch /Form defineresource pop\n");
1457 } else {
1458 writePS("end\n");
1459 writePS("%%DocumentSuppliedResources:\n");
1460 writePS(embFontList->getCString());
1461 if (level == psLevel1Sep || level == psLevel2Sep ||
1462 level == psLevel3Sep) {
1463 writePS("%%DocumentProcessColors:");
1464 if (processColors & psProcessCyan) {
1465 writePS(" Cyan");
1466 }
1467 if (processColors & psProcessMagenta) {
1468 writePS(" Magenta");
1469 }
1470 if (processColors & psProcessYellow) {
1471 writePS(" Yellow");
1472 }
1473 if (processColors & psProcessBlack) {
1474 writePS(" Black");
1475 }
1476 writePS("\n");
1477 writePS("%%DocumentCustomColors:");
1478 for (cc = customColors; cc; cc = cc->next) {
1479 writePSFmt(" (%s)", cc->name->getCString());
1480 }
1481 writePS("\n");
1482 writePS("%%CMYKCustomColor:\n");
1483 for (cc = customColors; cc; cc = cc->next) {
1484 writePSFmt("%%%%+ %g %g %g %g (%s)\n",
1485 cc->c, cc->m, cc->y, cc->k, cc->name->getCString());
1486 }
1487 }
1488 }
1489 }
1490
1491 void PSOutputDev::setupResources(Dict *resDict) {
1492 Object xObjDict, xObjRef, xObj, patDict, patRef, pat, resObj;
1493 Ref ref0, ref1;
1494 GBool skip;
1495 int i, j;
1496
1497 setupFonts(resDict);
1498 setupImages(resDict);
1499
1500 //----- recursively scan XObjects
1501 resDict->lookup("XObject", &xObjDict);
1502 if (xObjDict.isDict()) {
1503 for (i = 0; i < xObjDict.dictGetLength(); ++i) {
1504
1505 // avoid infinite recursion on XObjects
1506 skip = gFalse;
1507 if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) {
1508 ref0 = xObjRef.getRef();
1509 for (j = 0; j < xobjStack->getLength(); ++j) {
1510 ref1 = *(Ref *)xobjStack->get(j);
1511 if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
1512 skip = gTrue;
1513 break;
1514 }
1515 }
1516 if (!skip) {
1517 xobjStack->append(&ref0);
1518 }
1519 }
1520 if (!skip) {
1521
1522 // process the XObject's resource dictionary
1523 xObjDict.dictGetVal(i, &xObj);
1524 if (xObj.isStream()) {
1525 xObj.streamGetDict()->lookup("Resources", &resObj);
1526 if (resObj.isDict()) {
1527 setupResources(resObj.getDict());
1528 }
1529 resObj.free();
1530 }
1531 xObj.free();
1532 }
1533
1534 if (xObjRef.isRef() && !skip) {
1535 xobjStack->del(xobjStack->getLength() - 1);
1536 }
1537 xObjRef.free();
1538 }
1539 }
1540 xObjDict.free();
1541
1542 //----- recursively scan Patterns
1543 resDict->lookup("Pattern", &patDict);
1544 if (patDict.isDict()) {
1545 inType3Char = gTrue;
1546 for (i = 0; i < patDict.dictGetLength(); ++i) {
1547
1548 // avoid infinite recursion on Patterns
1549 skip = gFalse;
1550 if ((patDict.dictGetValNF(i, &patRef)->isRef())) {
1551 ref0 = patRef.getRef();
1552 for (j = 0; j < xobjStack->getLength(); ++j) {
1553 ref1 = *(Ref *)xobjStack->get(j);
1554 if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
1555 skip = gTrue;
1556 break;
1557 }
1558 }
1559 if (!skip) {
1560 xobjStack->append(&ref0);
1561 }
1562 }
1563 if (!skip) {
1564
1565 // process the Pattern's resource dictionary
1566 patDict.dictGetVal(i, &pat);
1567 if (pat.isStream()) {
1568 pat.streamGetDict()->lookup("Resources", &resObj);
1569 if (resObj.isDict()) {
1570 setupResources(resObj.getDict());
1571 }
1572 resObj.free();
1573 }
1574 pat.free();
1575 }
1576
1577 if (patRef.isRef() && !skip) {
1578 xobjStack->del(xobjStack->getLength() - 1);
1579 }
1580 patRef.free();
1581 }
1582 inType3Char = gFalse;
1583 }
1584 patDict.free();
1585 }
1586
1587 void PSOutputDev::setupFonts(Dict *resDict) {
1588 Object obj1, obj2;
1589 Ref r;
1590 GfxFontDict *gfxFontDict;
1591 GfxFont *font;
1592 int i;
1593
1594 gfxFontDict = NULL;
1595 resDict->lookupNF("Font", &obj1);
1596 if (obj1.isRef()) {
1597 obj1.fetch(xref, &obj2);
1598 if (obj2.isDict()) {
1599 r = obj1.getRef();
1600 gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict());
1601 }
1602 obj2.free();
1603 } else if (obj1.isDict()) {
1604 gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict());
1605 }
1606 if (gfxFontDict) {
1607 for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
1608 if ((font = gfxFontDict->getFont(i))) {
1609 setupFont(font, resDict);
1610 }
1611 }
1612 delete gfxFontDict;
1613 }
1614 obj1.free();
1615 }
1616
1617 void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
1618 Ref fontFileID;
1619 GString *name;
1620 PSFontParam *fontParam;
1621 GString *psName;
1622 char type3Name[64], buf[16];
1623 GBool subst;
1624 UnicodeMap *uMap;
1625 char *charName;
1626 double xs, ys;
1627 int code;
1628 double w1, w2;
1629 double *fm;
1630 int i, j;
1631
1632 // check if font is already set up
1633 for (i = 0; i < fontIDLen; ++i) {
1634 if (fontIDs[i].num == font->getID()->num &&
1635 fontIDs[i].gen == font->getID()->gen) {
1636 return;
1637 }
1638 }
1639
1640 // add entry to fontIDs list
1641 if (fontIDLen >= fontIDSize) {
1642 fontIDSize += 64;
1643 fontIDs = (Ref *)greallocn(fontIDs, fontIDSize, sizeof(Ref));
1644 }
1645 fontIDs[fontIDLen++] = *font->getID();
1646
1647 xs = ys = 1;
1648 subst = gFalse;
1649
1650 // check for resident 8-bit font
1651 if (font->getName() &&
1652 (fontParam = globalParams->getPSFont(font->getName()))) {
1653 psName = new GString(fontParam->psFontName->getCString());
1654
1655 // check for embedded Type 1 font
1656 } else if (globalParams->getPSEmbedType1() &&
1657 font->getType() == fontType1 &&
1658 font->getEmbeddedFontID(&fontFileID)) {
1659 psName = filterPSName(font->getEmbeddedFontName());
1660 setupEmbeddedType1Font(&fontFileID, psName);
1661
1662 // check for embedded Type 1C font
1663 } else if (globalParams->getPSEmbedType1() &&
1664 font->getType() == fontType1C &&
1665 font->getEmbeddedFontID(&fontFileID)) {
1666 psName = filterPSName(font->getEmbeddedFontName());
1667 setupEmbeddedType1CFont(font, &fontFileID, psName);
1668
1669 // check for external Type 1 font file
1670 } else if (globalParams->getPSEmbedType1() &&
1671 font->getType() == fontType1 &&
1672 font->getExtFontFile()) {
1673 // this assumes that the PS font name matches the PDF font name
1674 psName = font->getName()->copy();
1675 setupExternalType1Font(font->getExtFontFile(), psName);
1676
1677 // check for embedded TrueType font
1678 } else if (globalParams->getPSEmbedTrueType() &&
1679 font->getType() == fontTrueType &&
1680 font->getEmbeddedFontID(&fontFileID)) {
1681 psName = filterPSName(font->getEmbeddedFontName());
1682 setupEmbeddedTrueTypeFont(font, &fontFileID, psName);
1683
1684 // check for external TrueType font file
1685 } else if (globalParams->getPSEmbedTrueType() &&
1686 font->getType() == fontTrueType &&
1687 font->getExtFontFile()) {
1688 psName = filterPSName(font->getName());
1689 setupExternalTrueTypeFont(font, psName);
1690
1691 // check for embedded CID PostScript font
1692 } else if (globalParams->getPSEmbedCIDPostScript() &&
1693 font->getType() == fontCIDType0C &&
1694 font->getEmbeddedFontID(&fontFileID)) {
1695 psName = filterPSName(font->getEmbeddedFontName());
1696 setupEmbeddedCIDType0Font(font, &fontFileID, psName);
1697
1698 // check for embedded CID TrueType font
1699 } else if (globalParams->getPSEmbedCIDTrueType() &&
1700 font->getType() == fontCIDType2 &&
1701 font->getEmbeddedFontID(&fontFileID)) {
1702 psName = filterPSName(font->getEmbeddedFontName());
1703 //~ should check to see if font actually uses vertical mode
1704 setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName, gTrue);
1705
1706 } else if (font->getType() == fontType3) {
1707 sprintf(type3Name, "T3_%d_%d",
1708 font->getID()->num, font->getID()->gen);
1709 psName = new GString(type3Name);
1710 setupType3Font(font, psName, parentResDict);
1711
1712 // do 8-bit font substitution
1713 } else if (!font->isCIDFont()) {
1714 subst = gTrue;
1715 name = font->getName();
1716 psName = NULL;
1717 if (name) {
1718 for (i = 0; psFonts[i]; ++i) {
1719 if (name->cmp(psFonts[i]) == 0) {
1720 psName = new GString(psFonts[i]);
1721 break;
1722 }
1723 }
1724 }
1725 if (!psName) {
1726 if (font->isFixedWidth()) {
1727 i = 8;
1728 } else if (font->isSerif()) {
1729 i = 4;
1730 } else {
1731 i = 0;
1732 }
1733 if (font->isBold()) {
1734 i += 2;
1735 }
1736 if (font->isItalic()) {
1737 i += 1;
1738 }
1739 psName = new GString(psSubstFonts[i].psName);
1740 for (code = 0; code < 256; ++code) {
1741 if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) &&
1742 charName[0] == 'm' && charName[1] == '\0') {
1743 break;
1744 }
1745 }
1746 if (code < 256) {
1747 w1 = ((Gfx8BitFont *)font)->getWidth(code);
1748 } else {
1749 w1 = 0;
1750 }
1751 w2 = psSubstFonts[i].mWidth;
1752 xs = w1 / w2;
1753 if (xs < 0.1) {
1754 xs = 1;
1755 }
1756 if (font->getType() == fontType3) {
1757 // This is a hack which makes it possible to substitute for some
1758 // Type 3 fonts. The problem is that it's impossible to know what
1759 // the base coordinate system used in the font is without actually
1760 // rendering the font.
1761 ys = xs;
1762 fm = font->getFontMatrix();
1763 if (fm[0] != 0) {
1764 ys *= fm[3] / fm[0];
1765 }
1766 } else {
1767 ys = 1;
1768 }
1769 }
1770
1771 // do 16-bit font substitution
1772 } else if ((fontParam = globalParams->
1773 getPSFont16(font->getName(),
1774 ((GfxCIDFont *)font)->getCollection(),
1775 font->getWMode()))) {
1776 subst = gTrue;
1777 psName = fontParam->psFontName->copy();
1778 if (font16EncLen >= font16EncSize) {
1779 font16EncSize += 16;
1780 font16Enc = (PSFont16Enc *)greallocn(font16Enc,
1781 font16EncSize, sizeof(PSFont16Enc));
1782 }
1783 font16Enc[font16EncLen].fontID = *font->getID();
1784 font16Enc[font16EncLen].enc = fontParam->encoding->copy();
1785 if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) {
1786 uMap->decRefCnt();
1787 ++font16EncLen;
1788 } else {
1789 error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'",
1790 font16Enc[font16EncLen].enc->getCString());
1791 }
1792
1793 // give up - can't do anything with this font
1794 } else {
1795 error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)",
1796 font->getName() ? font->getName()->getCString() : "(unnamed)",
1797 ((GfxCIDFont *)font)->getCollection()
1798 ? ((GfxCIDFont *)font)->getCollection()->getCString()
1799 : "(unknown)");
1800 return;
1801 }
1802
1803 // generate PostScript code to set up the font
1804 if (font->isCIDFont()) {
1805 if (level == psLevel3 || level == psLevel3Sep) {
1806 writePSFmt("/F%d_%d /%s %d pdfMakeFont16L3\n",
1807 font->getID()->num, font->getID()->gen, psName->getCString(),
1808 font->getWMode());
1809 } else {
1810 writePSFmt("/F%d_%d /%s %d pdfMakeFont16\n",
1811 font->getID()->num, font->getID()->gen, psName->getCString(),
1812 font->getWMode());
1813 }
1814 } else {
1815 writePSFmt("/F%d_%d /%s %g %g\n",
1816 font->getID()->num, font->getID()->gen, psName->getCString(),
1817 xs, ys);
1818 for (i = 0; i < 256; i += 8) {
1819 writePSFmt((i == 0) ? "[ " : " ");
1820 for (j = 0; j < 8; ++j) {
1821 if (font->getType() == fontTrueType &&
1822 !subst &&
1823 !((Gfx8BitFont *)font)->getHasEncoding()) {
1824 sprintf(buf, "c%02x", i+j);
1825 charName = buf;
1826 } else {
1827 charName = ((Gfx8BitFont *)font)->getCharName(i+j);
1828 // this is a kludge for broken PDF files that encode char 32
1829 // as .notdef
1830 if (i+j == 32 && charName && !strcmp(charName, ".notdef")) {
1831 charName = "space";
1832 }
1833 }
1834 writePS("/");
1835 writePSName(charName ? charName : (char *)".notdef");
1836 // the empty name is legal in PDF and PostScript, but PostScript
1837 // uses a double-slash (//...) for "immediately evaluated names",
1838 // so we need to add a space character here
1839 if (charName && !charName[0]) {
1840 writePS(" ");
1841 }
1842 }
1843 writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n");
1844 }
1845 writePS("pdfMakeFont\n");
1846 }
1847
1848 delete psName;
1849 }
1850
1851 void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
1852 static char hexChar[17] = "0123456789abcdef";
1853 Object refObj, strObj, obj1, obj2, obj3;
1854 Dict *dict;
1855 int length1, length2, length3;
1856 int c;
1857 int start[4];
1858 GBool binMode;
1859 int i;
1860
1861 // check if font is already embedded
1862 for (i = 0; i < fontFileIDLen; ++i) {
1863 if (fontFileIDs[i].num == id->num &&
1864 fontFileIDs[i].gen == id->gen)
1865 return;
1866 }
1867
1868 // add entry to fontFileIDs list
1869 if (fontFileIDLen >= fontFileIDSize) {
1870 fontFileIDSize += 64;
1871 fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
1872 }
1873 fontFileIDs[fontFileIDLen++] = *id;
1874
1875 // get the font stream and info
1876 refObj.initRef(id->num, id->gen);
1877 refObj.fetch(xref, &strObj);
1878 refObj.free();
1879 if (!strObj.isStream()) {
1880 error(-1, "Embedded font file object is not a stream");
1881 goto err1;
1882 }
1883 if (!(dict = strObj.streamGetDict())) {
1884 error(-1, "Embedded font stream is missing its dictionary");
1885 goto err1;
1886 }
1887 dict->lookup("Length1", &obj1);
1888 dict->lookup("Length2", &obj2);
1889 dict->lookup("Length3", &obj3);
1890 if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {
1891 error(-1, "Missing length fields in embedded font stream dictionary");
1892 obj1.free();
1893 obj2.free();
1894 obj3.free();
1895 goto err1;
1896 }
1897 length1 = obj1.getInt();
1898 length2 = obj2.getInt();
1899 length3 = obj3.getInt();
1900 obj1.free();
1901 obj2.free();
1902 obj3.free();
1903
1904 // beginning comment
1905 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1906 embFontList->append("%%+ font ");
1907 embFontList->append(psName->getCString());
1908 embFontList->append("\n");
1909
1910 // copy ASCII portion of font
1911 strObj.streamReset();
1912 for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
1913 writePSChar(c);
1914 }
1915
1916 // figure out if encrypted portion is binary or ASCII
1917 binMode = gFalse;
1918 for (i = 0; i < 4; ++i) {
1919 start[i] = strObj.streamGetChar();
1920 if (start[i] == EOF) {
1921 error(-1, "Unexpected end of file in embedded font stream");
1922 goto err1;
1923 }
1924 if (!((start[i] >= '0' && start[i] <= '9') ||
1925 (start[i] >= 'A' && start[i] <= 'F') ||
1926 (start[i] >= 'a' && start[i] <= 'f')))
1927 binMode = gTrue;
1928 }
1929
1930 // convert binary data to ASCII
1931 if (binMode) {
1932 for (i = 0; i < 4; ++i) {
1933 writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
1934 writePSChar(hexChar[start[i] & 0x0f]);
1935 }
1936 // if Length2 is incorrect (too small), font data gets chopped, so
1937 // we take a few extra characters from the trailer just in case
1938 // length2 += length3 >= 8 ? 8 : length3;
1939 while (i < length2) {
1940 if ((c = strObj.streamGetChar()) == EOF) {
1941 break;
1942 }
1943 writePSChar(hexChar[(c >> 4) & 0x0f]);
1944 writePSChar(hexChar[c & 0x0f]);
1945 if (++i % 32 == 0) {
1946 writePSChar('\n');
1947 }
1948 }
1949 if (i % 32 > 0) {
1950 writePSChar('\n');
1951 }
1952
1953 // already in ASCII format -- just copy it
1954 } else {
1955 for (i = 0; i < 4; ++i) {
1956 writePSChar(start[i]);
1957 }
1958 for (i = 4; i < length2; ++i) {
1959 if ((c = strObj.streamGetChar()) == EOF) {
1960 break;
1961 }
1962 writePSChar(c);
1963 }
1964 }
1965
1966 // write padding and "cleartomark"
1967 for (i = 0; i < 8; ++i) {
1968 writePS("00000000000000000000000000000000"
1969 "00000000000000000000000000000000\n");
1970 }
1971 writePS("cleartomark\n");
1972
1973 // ending comment
1974 writePS("%%EndResource\n");
1975
1976 err1:
1977 strObj.streamClose();
1978 strObj.free();
1979 }
1980
1981 //~ This doesn't handle .pfb files or binary eexec data (which only
1982 //~ happens in pfb files?).
1983 void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) {
1984 FILE *fontFile;
1985 int c;
1986 int i;
1987
1988 // check if font is already embedded
1989 for (i = 0; i < fontFileNameLen; ++i) {
1990 if (!fontFileNames[i]->cmp(fileName)) {
1991 return;
1992 }
1993 }
1994
1995 // add entry to fontFileNames list
1996 if (fontFileNameLen >= fontFileNameSize) {
1997 fontFileNameSize += 64;
1998 fontFileNames = (GString **)greallocn(fontFileNames,
1999 fontFileNameSize, sizeof(GString *));
2000 }
2001 fontFileNames[fontFileNameLen++] = fileName->copy();
2002
2003 // beginning comment
2004 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2005 embFontList->append("%%+ font ");
2006 embFontList->append(psName->getCString());
2007 embFontList->append("\n");
2008
2009 // copy the font file
2010 if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
2011 error(-1, "Couldn't open external font file");
2012 return;
2013 }
2014 while ((c = fgetc(fontFile)) != EOF) {
2015 writePSChar(c);
2016 }
2017 fclose(fontFile);
2018
2019 // ending comment
2020 writePS("%%EndResource\n");
2021 }
2022
2023 void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
2024 GString *psName) {
2025 char *fontBuf;
2026 int fontLen;
2027 FoFiType1C *ffT1C;
2028 int i;
2029
2030 // check if font is already embedded
2031 for (i = 0; i < fontFileIDLen; ++i) {
2032 if (fontFileIDs[i].num == id->num &&
2033 fontFileIDs[i].gen == id->gen)
2034 return;
2035 }
2036
2037 // add entry to fontFileIDs list
2038 if (fontFileIDLen >= fontFileIDSize) {
2039 fontFileIDSize += 64;
2040 fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
2041 }
2042 fontFileIDs[fontFileIDLen++] = *id;
2043
2044 // beginning comment
2045 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2046 embFontList->append("%%+ font ");
2047 embFontList->append(psName->getCString());
2048 embFontList->append("\n");
2049
2050 // convert it to a Type 1 font
2051 fontBuf = font->readEmbFontFile(xref, &fontLen);
2052 if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
2053 ffT1C->convertToType1(NULL, gTrue, outputFunc, outputStream);
2054 delete ffT1C;
2055 }
2056 gfree(fontBuf);
2057
2058 // ending comment
2059 writePS("%%EndResource\n");
2060 }
2061
2062 void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
2063 GString *psName) {
2064 char unique[32];
2065 char *fontBuf;
2066 int fontLen;
2067 FoFiTrueType *ffTT;
2068 Gushort *codeToGID;
2069 int i;
2070
2071 // check if font is already embedded
2072 for (i = 0; i < fontFileIDLen; ++i) {
2073 if (fontFileIDs[i].num == id->num &&
2074 fontFileIDs[i].gen == id->gen) {
2075 sprintf(unique, "_%d", nextTrueTypeNum++);
2076 psName->append(unique);
2077 break;
2078 }
2079 }
2080
2081 // add entry to fontFileIDs list
2082 if (i == fontFileIDLen) {
2083 if (fontFileIDLen >= fontFileIDSize) {
2084 fontFileIDSize += 64;
2085 fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
2086 }
2087 fontFileIDs[fontFileIDLen++] = *id;
2088 }
2089
2090 // beginning comment
2091 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2092 embFontList->append("%%+ font ");
2093 embFontList->append(psName->getCString());
2094 embFontList->append("\n");
2095
2096 // convert it to a Type 42 font
2097 fontBuf = font->readEmbFontFile(xref, &fontLen);
2098 if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2099 codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
2100 ffTT->convertToType42(psName->getCString(),
2101 ((Gfx8BitFont *)font)->getHasEncoding()
2102 ? ((Gfx8BitFont *)font)->getEncoding()
2103 : (char **)NULL,
2104 codeToGID, outputFunc, outputStream);
2105 gfree(codeToGID);
2106 delete ffTT;
2107 }
2108 gfree(fontBuf);
2109
2110 // ending comment
2111 writePS("%%EndResource\n");
2112 }
2113
2114 void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *psName) {
2115 char unique[32];
2116 GString *fileName;
2117 char *fontBuf;
2118 int fontLen;
2119 FoFiTrueType *ffTT;
2120 Gushort *codeToGID;
2121 int i;
2122
2123 // check if font is already embedded
2124 fileName = font->getExtFontFile();
2125 for (i = 0; i < fontFileNameLen; ++i) {
2126 if (!fontFileNames[i]->cmp(fileName)) {
2127 sprintf(unique, "_%d", nextTrueTypeNum++);
2128 psName->append(unique);
2129 break;
2130 }
2131 }
2132
2133 // add entry to fontFileNames list
2134 if (i == fontFileNameLen) {
2135 if (fontFileNameLen >= fontFileNameSize) {
2136 fontFileNameSize += 64;
2137 fontFileNames =
2138 (GString **)greallocn(fontFileNames,
2139 fontFileNameSize, sizeof(GString *));
2140 }
2141 fontFileNames[fontFileNameLen++] = fileName->copy();
2142 }
2143
2144 // beginning comment
2145 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2146 embFontList->append("%%+ font ");
2147 embFontList->append(psName->getCString());
2148 embFontList->append("\n");
2149
2150 // convert it to a Type 42 font
2151 fontBuf = font->readExtFontFile(&fontLen);
2152 if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2153 codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
2154 ffTT->convertToType42(psName->getCString(),
2155 ((Gfx8BitFont *)font)->getHasEncoding()
2156 ? ((Gfx8BitFont *)font)->getEncoding()
2157 : (char **)NULL,
2158 codeToGID, outputFunc, outputStream);
2159 delete ffTT;
2160 }
2161 gfree(fontBuf);
2162
2163 // ending comment
2164 writePS("%%EndResource\n");
2165 }
2166
2167 void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
2168 GString *psName) {
2169 char *fontBuf;
2170 int fontLen;
2171 FoFiType1C *ffT1C;
2172 int i;
2173
2174 // check if font is already embedded
2175 for (i = 0; i < fontFileIDLen; ++i) {
2176 if (fontFileIDs[i].num == id->num &&
2177 fontFileIDs[i].gen == id->gen)
2178 return;
2179 }
2180
2181 // add entry to fontFileIDs list
2182 if (fontFileIDLen >= fontFileIDSize) {
2183 fontFileIDSize += 64;
2184 fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
2185 }
2186 fontFileIDs[fontFileIDLen++] = *id;
2187
2188 // beginning comment
2189 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2190 embFontList->append("%%+ font ");
2191 embFontList->append(psName->getCString());
2192 embFontList->append("\n");
2193
2194 // convert it to a Type 0 font
2195 fontBuf = font->readEmbFontFile(xref, &fontLen);
2196 if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
2197 if (globalParams->getPSLevel() >= psLevel3) {
2198 // Level 3: use a CID font
2199 ffT1C->convertToCIDType0(psName->getCString(), outputFunc, outputStream);
2200 } else {
2201 // otherwise: use a non-CID composite font
2202 ffT1C->convertToType0(psName->getCString(), outputFunc, outputStream);
2203 }
2204 delete ffT1C;
2205 }
2206 gfree(fontBuf);
2207
2208 // ending comment
2209 writePS("%%EndResource\n");
2210 }
2211
2212 void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
2213 GString *psName,
2214 GBool needVerticalMetrics) {
2215 char unique[32];
2216 char *fontBuf;
2217 int fontLen;
2218 FoFiTrueType *ffTT;
2219 int i;
2220
2221 // check if font is already embedded
2222 for (i = 0; i < fontFileIDLen; ++i) {
2223 if (fontFileIDs[i].num == id->num &&
2224 fontFileIDs[i].gen == id->gen) {
2225 sprintf(unique, "_%d", nextTrueTypeNum++);
2226 psName->append(unique);
2227 break;
2228 }
2229 }
2230
2231 // add entry to fontFileIDs list
2232 if (fontFileIDLen >= fontFileIDSize) {
2233 fontFileIDSize += 64;
2234 fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
2235 }
2236 fontFileIDs[fontFileIDLen++] = *id;
2237
2238 // beginning comment
2239 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2240 embFontList->append("%%+ font ");
2241 embFontList->append(psName->getCString());
2242 embFontList->append("\n");
2243
2244 // convert it to a Type 0 font
2245 fontBuf = font->readEmbFontFile(xref, &fontLen);
2246 if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2247 if (globalParams->getPSLevel() >= psLevel3) {
2248 // Level 3: use a CID font
2249 ffTT->convertToCIDType2(psName->getCString(),
2250 ((GfxCIDFont *)font)->getCIDToGID(),
2251 ((GfxCIDFont *)font)->getCIDToGIDLen(),
2252 needVerticalMetrics,
2253 outputFunc, outputStream);
2254 } else {
2255 // otherwise: use a non-CID composite font
2256 ffTT->convertToType0(psName->getCString(),
2257 ((GfxCIDFont *)font)->getCIDToGID(),
2258 ((GfxCIDFont *)font)->getCIDToGIDLen(),
2259 needVerticalMetrics,
2260 outputFunc, outputStream);
2261 }
2262 delete ffTT;
2263 }
2264 gfree(fontBuf);
2265
2266 // ending comment
2267 writePS("%%EndResource\n");
2268 }
2269
2270 void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
2271 Dict *parentResDict) {
2272 Dict *resDict;
2273 Dict *charProcs;
2274 Object charProc;
2275 Gfx *gfx;
2276 PDFRectangle box;
2277 double *m;
2278 char buf[256];
2279 int i;
2280
2281 // set up resources used by font
2282 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2283 inType3Char = gTrue;
2284 setupResources(resDict);
2285 inType3Char = gFalse;
2286 } else {
2287 resDict = parentResDict;
2288 }
2289
2290 // beginning comment
2291 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2292 embFontList->append("%%+ font ");
2293 embFontList->append(psName->getCString());
2294 embFontList->append("\n");
2295
2296 // font dictionary
2297 writePS("8 dict begin\n");
2298 writePS("/FontType 3 def\n");
2299 m = font->getFontMatrix();
2300 writePSFmt("/FontMatrix [%g %g %g %g %g %g] def\n",
2301 m[0], m[1], m[2], m[3], m[4], m[5]);
2302 m = font->getFontBBox();
2303 writePSFmt("/FontBBox [%g %g %g %g] def\n",
2304 m[0], m[1], m[2], m[3]);
2305 writePS("/Encoding 256 array def\n");
2306 writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
2307 writePS("/BuildGlyph {\n");
2308 writePS(" exch /CharProcs get exch\n");
2309 writePS(" 2 copy known not { pop /.notdef } if\n");
2310 writePS(" get exec\n");
2311 writePS("} bind def\n");
2312 writePS("/BuildChar {\n");
2313 writePS(" 1 index /Encoding get exch get\n");
2314 writePS(" 1 index /BuildGlyph get exec\n");
2315 writePS("} bind def\n");
2316 if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
2317 writePSFmt("/CharProcs %d dict def\n", charProcs->getLength());
2318 writePS("CharProcs begin\n");
2319 box.x1 = m[0];
2320 box.y1 = m[1];
2321 box.x2 = m[2];
2322 box.y2 = m[3];
2323 gfx = new Gfx(xref, this, resDict, &box, NULL);
2324 inType3Char = gTrue;
2325 t3Cacheable = gFalse;
2326 for (i = 0; i < charProcs->getLength(); ++i) {
2327 writePS("/");
2328 writePSName(charProcs->getKey(i));
2329 writePS(" {\n");
2330 gfx->display(charProcs->getVal(i, &charProc));
2331 charProc.free();
2332 if (t3String) {
2333 if (t3Cacheable) {
2334 sprintf(buf, "%g %g %g %g %g %g setcachedevice\n",
2335 t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
2336 } else {
2337 sprintf(buf, "%g %g setcharwidth\n", t3WX, t3WY);
2338 }
2339 (*outputFunc)(outputStream, buf, strlen(buf));
2340 (*outputFunc)(outputStream, t3String->getCString(),
2341 t3String->getLength());
2342 delete t3String;
2343 t3String = NULL;
2344 }
2345 (*outputFunc)(outputStream, "Q\n", 2);
2346 writePS("} def\n");
2347 }
2348 inType3Char = gFalse;
2349 delete gfx;
2350 writePS("end\n");
2351 }
2352 writePS("currentdict end\n");
2353 writePSFmt("/%s exch definefont pop\n", psName->getCString());
2354
2355 // ending comment
2356 writePS("%%EndResource\n");
2357 }
2358
2359 void PSOutputDev::setupImages(Dict *resDict) {
2360 Object xObjDict, xObj, xObjRef, subtypeObj;
2361 int i;
2362
2363 if (!(mode == psModeForm || inType3Char)) {
2364 return;
2365 }
2366
2367 resDict->lookup("XObject", &xObjDict);
2368 if (xObjDict.isDict()) {
2369 for (i = 0; i < xObjDict.dictGetLength(); ++i) {
2370 xObjDict.dictGetValNF(i, &xObjRef);
2371 xObjDict.dictGetVal(i, &xObj);
2372 if (xObj.isStream()) {
2373 xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
2374 if (subtypeObj.isName("Image")) {
2375 if (xObjRef.isRef()) {
2376 setupImage(xObjRef.getRef(), xObj.getStream());
2377 } else {
2378 error(-1, "Image in resource dict is not an indirect reference");
2379 }
2380 }
2381 subtypeObj.free();
2382 }
2383 xObj.free();
2384 xObjRef.free();
2385 }
2386 }
2387 xObjDict.free();
2388 }
2389
2390 void PSOutputDev::setupImage(Ref id, Stream *str) {
2391 GBool useASCIIHex;
2392 int c;
2393 int size, line, col, i;
2394
2395 // construct an encoder stream
2396 useASCIIHex = level == psLevel1 || level == psLevel1Sep ||
2397 globalParams->getPSASCIIHex();
2398 if (useASCIIHex) {
2399 str = new ASCIIHexEncoder(str);
2400 } else {
2401 str = new ASCII85Encoder(str);
2402 }
2403
2404 // compute image data size
2405 str->reset();
2406 col = size = 0;
2407 do {
2408 do {
2409 c = str->getChar();
2410 } while (c == '\n' || c == '\r');
2411 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
2412 break;
2413 }
2414 if (c == 'z') {
2415 ++col;
2416 } else {
2417 ++col;
2418 for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
2419 do {
2420 c = str->getChar();
2421 } while (c == '\n' || c == '\r');
2422 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
2423 break;
2424 }
2425 ++col;
2426 }
2427 }
2428 if (col > 225) {
2429 ++size;
2430 col = 0;
2431 }
2432 } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
2433 ++size;
2434 writePSFmt("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen);
2435 str->close();
2436
2437 // write the data into the array
2438 str->reset();
2439 line = col = 0;
2440 writePS((char *)(useASCIIHex ? "dup 0 <" : "dup 0 <~"));
2441 do {
2442 do {
2443 c = str->getChar();
2444 } while (c == '\n' || c == '\r');
2445 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
2446 break;
2447 }
2448 if (c == 'z') {
2449 writePSChar(c);
2450 ++col;
2451 } else {
2452 writePSChar(c);
2453 ++col;
2454 for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
2455 do {
2456 c = str->getChar();
2457 } while (c == '\n' || c == '\r');
2458 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
2459 break;
2460 }
2461 writePSChar(c);
2462 ++col;
2463 }
2464 }
2465 // each line is: "dup nnnnn <~...data...~> put<eol>"
2466 // so max data length = 255 - 20 = 235
2467 // chunks are 1 or 4 bytes each, so we have to stop at 232
2468 // but make it 225 just to be safe
2469 if (col > 225) {
2470 writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
2471 ++line;
2472 writePSFmt((char *)(useASCIIHex ? "dup %d <" : "dup %d <~"), line);
2473 col = 0;
2474 }
2475 } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
2476 writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
2477 writePS("pop\n");
2478 str->close();
2479
2480 delete str;
2481 }
2482
2483 GBool PSOutputDev::startPage(int pageNum, GfxState *state) {
2484 int x1, y1, x2, y2, width, height;
2485 int imgWidth, imgHeight, imgWidth2, imgHeight2;
2486 GBool landscape;
2487
2488
2489 if (mode == psModePS) {
2490 writePSFmt("%%%%Page: %d %d\n", pageNum, seqPage);
2491 writePS("%%BeginPageSetup\n");
2492 }
2493
2494 // underlays
2495 if (underlayCbk) {
2496 (*underlayCbk)(this, underlayCbkData);
2497 }
2498 if (overlayCbk) {
2499 saveState(NULL);
2500 }
2501
2502 switch (mode) {
2503
2504 case psModePS:
2505 // rotate, translate, and scale page
2506 imgWidth = imgURX - imgLLX;
2507 imgHeight = imgURY - imgLLY;
2508 x1 = (int)floor(state->getX1());
2509 y1 = (int)floor(state->getY1());
2510 x2 = (int)ceil(state->getX2());
2511 y2 = (int)ceil(state->getY2());
2512 width = x2 - x1;
2513 height = y2 - y1;
2514 tx = ty = 0;
2515 // rotation and portrait/landscape mode
2516 if (rotate0 >= 0) {
2517 rotate = (360 - rotate0) % 360;
2518 landscape = gFalse;
2519 } else {
2520 rotate = (360 - state->getRotate()) % 360;
2521
2522 fprintf(stderr, "DEBUG: Page rotate=%d, width=%d, height=%d, imgWidth=%d, imgHeight=%d\n",
2523 state->getRotate(), width, height, imgWidth, imgHeight);
2524
2525 if (rotate == 0 || rotate == 180) {
2526 if (width > height && width > imgWidth) {
2527 rotate += 90;
2528 landscape = gTrue;
2529 } else {
2530 landscape = gFalse;
2531 }
2532 } else { // rotate == 90 || rotate == 270
2533 if (height > width && height > imgWidth) {
2534 rotate = 270 - rotate;
2535 landscape = gTrue;
2536 } else {
2537 landscape = gFalse;
2538 }
2539 }
2540 }
2541
2542 writePSFmt("%%%%PageOrientation: %s\n",
2543 landscape ? "Landscape" : "Portrait");
2544 writePS("pdfStartPage\n");
2545 if (rotate == 0) {
2546 imgWidth2 = imgWidth;
2547 imgHeight2 = imgHeight;
2548 } else if (rotate == 90) {
2549 writePS("90 rotate\n");
2550 ty = -imgWidth;
2551 imgWidth2 = imgHeight;
2552 imgHeight2 = imgWidth;
2553 } else if (rotate == 180) {
2554 writePS("180 rotate\n");
2555 imgWidth2 = imgWidth;
2556 imgHeight2 = imgHeight;
2557 tx = -imgWidth;
2558 ty = -imgHeight;
2559 } else { // rotate == 270
2560 writePS("270 rotate\n");
2561 tx = -imgHeight;
2562 imgWidth2 = imgHeight;
2563 imgHeight2 = imgWidth;
2564 }
2565 // shrink or expand
2566 if (xScale0 > 0 && yScale0 > 0) {
2567 xScale = xScale0;
2568 yScale = yScale0;
2569 } else if ((globalParams->getPSShrinkLarger() &&
2570 (width > imgWidth2 || height > imgHeight2)) ||
2571 (globalParams->getPSExpandSmaller() &&
2572 (width < imgWidth2 && height < imgHeight2))) {
2573 xScale = (double)imgWidth2 / (double)width;
2574 yScale = (double)imgHeight2 / (double)height;
2575 if (yScale < xScale) {
2576 xScale = yScale;
2577 } else {
2578 yScale = xScale;
2579 }
2580 } else {
2581 xScale = yScale = 1;
2582 paperWidth = width;
2583 paperHeight = height;
2584 }
2585
2586 // deal with odd bounding boxes or clipping
2587 if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
2588 tx -= xScale * clipLLX0;
2589 ty -= yScale * clipLLY0;
2590 } else {
2591 tx -= xScale * x1;
2592 ty -= yScale * y1;
2593 }
2594 // center
2595 if (globalParams->getPSCenter()) {
2596 if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
2597 tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2;
2598 ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2;
2599 } else {
2600 tx += (imgWidth2 - xScale * width) / 2;
2601 ty += (imgHeight2 - yScale * height) / 2;
2602 }
2603 }
2604 tx += rotate == 0 ? imgLLX + tx0 : imgLLY + ty0;
2605 ty += rotate == 0 ? imgLLY + ty0 : -(imgLLX + tx0);
2606 if (tx != 0 || ty != 0) {
2607 writePSFmt("%g %g translate\n", tx, ty);
2608 }
2609 if (xScale != 1 || yScale != 1) {
2610 writePSFmt("%0.4f %0.4f scale\n", xScale, xScale);
2611 }
2612 if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
2613 writePSFmt("%g %g %g %g re W\n",
2614 clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0);
2615 } else {
2616 writePSFmt("%d %d %d %d re W\n", x1, y1, x2 - x1, y2 - y1);
2617 }
2618
2619 writePS("%%EndPageSetup\n");
2620 ++seqPage;
2621 break;
2622
2623 case psModeEPS:
2624 writePS("pdfStartPage\n");
2625 tx = ty = 0;
2626 rotate = (360 - state->getRotate()) % 360;
2627 if (rotate == 0) {
2628 } else if (rotate == 90) {
2629 writePS("90 rotate\n");
2630 tx = -epsX1;
2631 ty = -epsY2;
2632 } else if (rotate == 180) {
2633 writePS("180 rotate\n");
2634 tx = -(epsX1 + epsX2);
2635 ty = -(epsY1 + epsY2);
2636 } else { // rotate == 270
2637 writePS("270 rotate\n");
2638 tx = -epsX2;
2639 ty = -epsY1;
2640 }
2641 if (tx != 0 || ty != 0) {
2642 writePSFmt("%g %g translate\n", tx, ty);
2643 }
2644 xScale = yScale = 1;
2645 break;
2646
2647 case psModeForm:
2648 writePS("/PaintProc {\n");
2649 writePS("begin xpdf begin\n");
2650 writePS("pdfStartPage\n");
2651 tx = ty = 0;
2652 xScale = yScale = 1;
2653 rotate = 0;
2654 break;
2655 }
2656
2657 if (!checkRange(pageNum))
2658 return (gFalse);
2659 else
2660 return (gTrue);
2661 }
2662
2663 void PSOutputDev::endPage() {
2664 if (overlayCbk) {
2665 restoreState(NULL);
2666 (*overlayCbk)(this, overlayCbkData);
2667 }
2668
2669 if (mode == psModeForm) {
2670 writePS("pdfEndPage\n");
2671 writePS("end end\n");
2672 writePS("} def\n");
2673 writePS("end end\n");
2674 } else {
2675 if (!manualCtrl) {
2676 writePS("showpage\n");
2677 }
2678 writePS("%%PageTrailer\n");
2679 writePageTrailer();
2680 }
2681 }
2682
2683 void PSOutputDev::saveState(GfxState *state) {
2684 writePS("q\n");
2685 ++numSaves;
2686 }
2687
2688 void PSOutputDev::restoreState(GfxState *state) {
2689 writePS("Q\n");
2690 --numSaves;
2691 }
2692
2693 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
2694 double m21, double m22, double m31, double m32) {
2695 writePSFmt("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32);
2696 }
2697
2698 void PSOutputDev::updateLineDash(GfxState *state) {
2699 double *dash;
2700 double start;
2701 int length, i;
2702
2703 state->getLineDash(&dash, &length, &start);
2704 writePS("[");
2705 for (i = 0; i < length; ++i) {
2706 writePSFmt("%g%s",
2707 dash[i] == 0 ? 1 : dash[i],
2708 (i == length-1) ? "" : " ");
2709 }
2710 writePSFmt("] %g d\n", start);
2711 }
2712
2713 void PSOutputDev::updateFlatness(GfxState *state) {
2714 writePSFmt("%d i\n", state->getFlatness());
2715 }
2716
2717 void PSOutputDev::updateLineJoin(GfxState *state) {
2718 writePSFmt("%d j\n", state->getLineJoin());
2719 }
2720
2721 void PSOutputDev::updateLineCap(GfxState *state) {
2722 writePSFmt("%d J\n", state->getLineCap());
2723 }
2724
2725 void PSOutputDev::updateMiterLimit(GfxState *state) {
2726 writePSFmt("%g M\n", state->getMiterLimit());
2727 }
2728
2729 void PSOutputDev::updateLineWidth(GfxState *state) {
2730 writePSFmt("%g w\n", state->getLineWidth());
2731 }
2732
2733 void PSOutputDev::updateFillColorSpace(GfxState *state) {
2734 switch (level) {
2735 case psLevel1:
2736 case psLevel1Sep:
2737 break;
2738 case psLevel2:
2739 case psLevel3:
2740 if (state->getFillColorSpace()->getMode() != csPattern) {
2741 dumpColorSpaceL2(state->getFillColorSpace(), gTrue, gFalse);
2742 writePS(" cs\n");
2743 }
2744 break;
2745 case psLevel2Sep:
2746 case psLevel3Sep:
2747 break;
2748 }
2749 }
2750
2751 void PSOutputDev::updateStrokeColorSpace(GfxState *state) {
2752 switch (level) {
2753 case psLevel1:
2754 case psLevel1Sep:
2755 break;
2756 case psLevel2:
2757 case psLevel3:
2758 if (state->getStrokeColorSpace()->getMode() != csPattern) {
2759 dumpColorSpaceL2(state->getStrokeColorSpace(), gTrue, gFalse);
2760 writePS(" CS\n");
2761 }
2762 break;
2763 case psLevel2Sep:
2764 case psLevel3Sep:
2765 break;
2766 }
2767 }
2768
2769 void PSOutputDev::updateFillColor(GfxState *state) {
2770 GfxColor color;
2771 GfxColor *colorPtr;
2772 GfxGray gray;
2773 GfxCMYK cmyk;
2774 GfxSeparationColorSpace *sepCS;
2775 double c, m, y, k;
2776 int i;
2777
2778 switch (level) {
2779 case psLevel1:
2780 state->getFillGray(&gray);
2781 writePSFmt("%g g\n", colToDbl(gray));
2782 break;
2783 case psLevel1Sep:
2784 state->getFillCMYK(&cmyk);
2785 c = colToDbl(cmyk.c);
2786 m = colToDbl(cmyk.m);
2787 y = colToDbl(cmyk.y);
2788 k = colToDbl(cmyk.k);
2789 writePSFmt("%g %g %g %g k\n", c, m, y, k);
2790 addProcessColor(c, m, y, k);
2791 break;
2792 case psLevel2:
2793 case psLevel3:
2794 if (state->getFillColorSpace()->getMode() != csPattern) {
2795 colorPtr = state->getFillColor();
2796 writePS("[");
2797 for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) {
2798 if (i > 0) {
2799 writePS(" ");
2800 }
2801 writePSFmt("%g", colToDbl(colorPtr->c[i]));
2802 }
2803 writePS("] sc\n");
2804 }
2805 break;
2806 case psLevel2Sep:
2807 case psLevel3Sep:
2808 if (state->getFillColorSpace()->getMode() == csSeparation) {
2809 sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
2810 color.c[0] = gfxColorComp1;
2811 sepCS->getCMYK(&color, &cmyk);
2812 writePSFmt("%g %g %g %g %g (%s) ck\n",
2813 colToDbl(state->getFillColor()->c[0]),
2814 colToDbl(cmyk.c), colToDbl(cmyk.m),
2815 colToDbl(cmyk.y), colToDbl(cmyk.k),
2816 sepCS->getName()->getCString());
2817 addCustomColor(sepCS);
2818 } else {
2819 state->getFillCMYK(&cmyk);
2820 c = colToDbl(cmyk.c);
2821 m = colToDbl(cmyk.m);
2822 y = colToDbl(cmyk.y);
2823 k = colToDbl(cmyk.k);
2824 writePSFmt("%g %g %g %g k\n", c, m, y, k);
2825 addProcessColor(c, m, y, k);
2826 }
2827 break;
2828 }
2829 t3Cacheable = gFalse;
2830 }
2831
2832 void PSOutputDev::updateStrokeColor(GfxState *state) {
2833 GfxColor color;
2834 GfxColor *colorPtr;
2835 GfxGray gray;
2836 GfxCMYK cmyk;
2837 GfxSeparationColorSpace *sepCS;
2838 double c, m, y, k;
2839 int i;
2840
2841 switch (level) {
2842 case psLevel1:
2843 state->getStrokeGray(&gray);
2844 writePSFmt("%g G\n", colToDbl(gray));
2845 break;
2846 case psLevel1Sep:
2847 state->getStrokeCMYK(&cmyk);
2848 c = colToDbl(cmyk.c);
2849 m = colToDbl(cmyk.m);
2850 y = colToDbl(cmyk.y);
2851 k = colToDbl(cmyk.k);
2852 writePSFmt("%g %g %g %g K\n", c, m, y, k);
2853 addProcessColor(c, m, y, k);
2854 break;
2855 case psLevel2:
2856 case psLevel3:
2857 if (state->getStrokeColorSpace()->getMode() != csPattern) {
2858 colorPtr = state->getStrokeColor();
2859 writePS("[");
2860 for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) {
2861 if (i > 0) {
2862 writePS(" ");
2863 }
2864 writePSFmt("%g", colToDbl(colorPtr->c[i]));
2865 }
2866 writePS("] SC\n");
2867 }
2868 break;
2869 case psLevel2Sep:
2870 case psLevel3Sep:
2871 if (state->getStrokeColorSpace()->getMode() == csSeparation) {
2872 sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
2873 color.c[0] = gfxColorComp1;
2874 sepCS->getCMYK(&color, &cmyk);
2875 writePSFmt("%g %g %g %g %g (%s) CK\n",
2876 colToDbl(state->getStrokeColor()->c[0]),
2877 colToDbl(cmyk.c), colToDbl(cmyk.m),
2878 colToDbl(cmyk.y), colToDbl(cmyk.k),
2879 sepCS->getName()->getCString());
2880 addCustomColor(sepCS);
2881 } else {
2882 state->getStrokeCMYK(&cmyk);
2883 c = colToDbl(cmyk.c);
2884 m = colToDbl(cmyk.m);
2885 y = colToDbl(cmyk.y);
2886 k = colToDbl(cmyk.k);
2887 writePSFmt("%g %g %g %g K\n", c, m, y, k);
2888 addProcessColor(c, m, y, k);
2889 }
2890 break;
2891 }
2892 t3Cacheable = gFalse;
2893 }
2894
2895 void PSOutputDev::addProcessColor(double c, double m, double y, double k) {
2896 if (c > 0) {
2897 processColors |= psProcessCyan;
2898 }
2899 if (m > 0) {
2900 processColors |= psProcessMagenta;
2901 }
2902 if (y > 0) {
2903 processColors |= psProcessYellow;
2904 }
2905 if (k > 0) {
2906 processColors |= psProcessBlack;
2907 }
2908 }
2909
2910 void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) {
2911 PSOutCustomColor *cc;
2912 GfxColor color;
2913 GfxCMYK cmyk;
2914
2915 for (cc = customColors; cc; cc = cc->next) {
2916 if (!cc->name->cmp(sepCS->getName())) {
2917 return;
2918 }
2919 }
2920 color.c[0] = gfxColorComp1;
2921 sepCS->getCMYK(&color, &cmyk);
2922 cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
2923 colToDbl(cmyk.y), colToDbl(cmyk.k),
2924 sepCS->getName()->copy());
2925 cc->next = customColors;
2926 customColors = cc;
2927 }
2928
2929 void PSOutputDev::updateFillOverprint(GfxState *state) {
2930 if (level >= psLevel2) {
2931 writePSFmt("%s op\n", state->getFillOverprint() ? "true" : "false");
2932 }
2933 }
2934
2935 void PSOutputDev::updateStrokeOverprint(GfxState *state) {
2936 if (level >= psLevel2) {
2937 writePSFmt("%s OP\n", state->getStrokeOverprint() ? "true" : "false");
2938 }
2939 }
2940
2941 void PSOutputDev::updateFont(GfxState *state) {
2942 if (state->getFont()) {
2943 writePSFmt("/F%d_%d %g Tf\n",
2944 state->getFont()->getID()->num, state->getFont()->getID()->gen,
2945 fabs(state->getFontSize()) < 0.00001 ? 0.00001
2946 : state->getFontSize());
2947 }
2948 }
2949
2950 void PSOutputDev::updateTextMat(GfxState *state) {
2951 double *mat;
2952
2953 mat = state->getTextMat();
2954 if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.00001) {
2955 // avoid a singular (or close-to-singular) matrix
2956 writePSFmt("[0.00001 0 0 0.00001 %g %g] Tm\n", mat[4], mat[5]);
2957 } else {
2958 writePSFmt("[%g %g %g %g %g %g] Tm\n",
2959 mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
2960 }
2961 }
2962
2963 void PSOutputDev::updateCharSpace(GfxState *state) {
2964 writePSFmt("%g Tc\n", state->getCharSpace());
2965 }
2966
2967 void PSOutputDev::updateRender(GfxState *state) {
2968 int rm;
2969
2970 rm = state->getRender();
2971 writePSFmt("%d Tr\n", rm);
2972 rm &= 3;
2973 if (rm != 0 && rm != 3) {
2974 t3Cacheable = gFalse;
2975 }
2976 }
2977
2978 void PSOutputDev::updateRise(GfxState *state) {
2979 writePSFmt("%g Ts\n", state->getRise());
2980 }
2981
2982 void PSOutputDev::updateWordSpace(GfxState *state) {
2983 writePSFmt("%g Tw\n", state->getWordSpace());
2984 }
2985
2986 void PSOutputDev::updateHorizScaling(GfxState *state) {
2987 double h;
2988
2989 h = state->getHorizScaling();
2990 if (fabs(h) < 0.01) {
2991 h = 0.01;
2992 }
2993 writePSFmt("%g Tz\n", h);
2994 }
2995
2996 void PSOutputDev::updateTextPos(GfxState *state) {
2997 writePSFmt("%g %g Td\n", state->getLineX(), state->getLineY());
2998 }
2999
3000 void PSOutputDev::updateTextShift(GfxState *state, double shift) {
3001 if (state->getFont()->getWMode()) {
3002 writePSFmt("%g TJmV\n", shift);
3003 } else {
3004 writePSFmt("%g TJm\n", shift);
3005 }
3006 }
3007
3008 void PSOutputDev::stroke(GfxState *state) {
3009 doPath(state->getPath());
3010 if (t3String) {
3011 // if we're construct a cacheable Type 3 glyph, we need to do
3012 // everything in the fill color
3013 writePS("Sf\n");
3014 } else {
3015 writePS("S\n");
3016 }
3017 }
3018
3019 void PSOutputDev::fill(GfxState *state) {
3020 doPath(state->getPath());
3021 writePS("f\n");
3022 }
3023
3024 void PSOutputDev::eoFill(GfxState *state) {
3025 doPath(state->getPath());
3026 writePS("f*\n");
3027 }
3028
3029 void PSOutputDev::tilingPatternFill(GfxState *state, Object *str,
3030 int paintType, Dict *resDict,
3031 double *mat, double *bbox,
3032 int x0, int y0, int x1, int y1,
3033 double xStep, double yStep) {
3034 PDFRectangle box;
3035 Gfx *gfx;
3036
3037 // define a Type 3 font
3038 writePS("8 dict begin\n");
3039 writePS("/FontType 3 def\n");
3040 writePS("/FontMatrix [1 0 0 1 0 0] def\n");
3041 writePSFmt("/FontBBox [%g %g %g %g] def\n",
3042 bbox[0], bbox[1], bbox[2], bbox[3]);
3043 writePS("/Encoding 256 array def\n");
3044 writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
3045 writePS(" Encoding 120 /x put\n");
3046 writePS("/BuildGlyph {\n");
3047 writePS(" exch /CharProcs get exch\n");
3048 writePS(" 2 copy known not { pop /.notdef } if\n");
3049 writePS(" get exec\n");
3050 writePS("} bind def\n");
3051 writePS("/BuildChar {\n");
3052 writePS(" 1 index /Encoding get exch get\n");
3053 writePS(" 1 index /BuildGlyph get exec\n");
3054 writePS("} bind def\n");
3055 writePS("/CharProcs 1 dict def\n");
3056 writePS("CharProcs begin\n");
3057 box.x1 = bbox[0];
3058 box.y1 = bbox[1];
3059 box.x2 = bbox[2];
3060 box.y2 = bbox[3];
3061 gfx = new Gfx(xref, this, resDict, &box, NULL);
3062 writePS("/x {\n");
3063 if (paintType == 2) {
3064 writePSFmt("%g 0 %g %g %g %g setcachedevice\n",
3065 xStep, bbox[0], bbox[1], bbox[2], bbox[3]);
3066 } else {
3067 writePSFmt("%g 0 setcharwidth\n", xStep);
3068 }
3069 inType3Char = gTrue;
3070 ++numTilingPatterns;
3071 gfx->display(str);
3072 --numTilingPatterns;
3073 inType3Char = gFalse;
3074 writePS("} def\n");
3075 delete gfx;
3076 writePS("end\n");
3077 writePS("currentdict end\n");
3078 writePSFmt("/xpdfTile%d exch definefont pop\n", numTilingPatterns);
3079
3080 // draw the tiles
3081 writePSFmt("/xpdfTile%d findfont setfont\n", numTilingPatterns);
3082 writePSFmt("gsave [%g %g %g %g %g %g] concat\n",
3083 mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
3084 writePSFmt("%d 1 %d { %g exch %g mul m %d 1 %d { pop (x) show } for } for\n",
3085 y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1);
3086 writePS("grestore\n");
3087 }
3088
3089 void PSOutputDev::functionShadedFill(GfxState *state,
3090 GfxFunctionShading *shading) {
3091 double x0, y0, x1, y1;
3092 double *mat;
3093 int i;
3094
3095 shading->getDomain(&x0, &y0, &x1, &y1);
3096 mat = shading->getMatrix();
3097 writePSFmt("/mat [%g %g %g %g %g %g] def\n",
3098 mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
3099 writePSFmt("/n %d def\n", shading->getColorSpace()->getNComps());
3100 if (shading->getNFuncs() == 1) {
3101 writePS("/func ");
3102 cvtFunction(shading->getFunc(0));
3103 writePS("def\n");
3104 } else {
3105 writePS("/func {\n");
3106 for (i = 0; i < shading->getNFuncs(); ++i) {
3107 if (i < shading->getNFuncs() - 1) {
3108 writePS("2 copy\n");
3109 }
3110 cvtFunction(shading->getFunc(i));
3111 writePS("exec\n");
3112 if (i < shading->getNFuncs() - 1) {
3113 writePS("3 1 roll\n");
3114 }
3115 }
3116 writePS("} def\n");
3117 }
3118 writePSFmt("%g %g %g %g 0 funcSH\n", x0, y0, x1, y1);
3119 }
3120
3121 void PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading) {
3122 double xMin, yMin, xMax, yMax;
3123 double x0, y0, x1, y1, dx, dy, mul;
3124 double tMin, tMax, t, t0, t1;
3125 int i;
3126
3127 // get the clip region bbox
3128 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
3129
3130 // compute min and max t values, based on the four corners of the
3131 // clip region bbox
3132 shading->getCoords(&x0, &y0, &x1, &y1);
3133 dx = x1 - x0;
3134 dy = y1 - y0;
3135 mul = 1 / (dx * dx + dy * dy);
3136 tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
3137 t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
3138 if (t < tMin) {
3139 tMin = t;
3140 } else if (t > tMax) {
3141 tMax = t;
3142 }
3143 t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
3144 if (t < tMin) {
3145 tMin = t;
3146 } else if (t > tMax) {
3147 tMax = t;
3148 }
3149 t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
3150 if (t < tMin) {
3151 tMin = t;
3152 } else if (t > tMax) {
3153 tMax = t;
3154 }
3155 if (tMin < 0 && !shading->getExtend0()) {
3156 tMin = 0;
3157 }
3158 if (tMax > 1 && !shading->getExtend1()) {
3159 tMax = 1;
3160 }
3161
3162 // get the function domain
3163 t0 = shading->getDomain0();
3164 t1 = shading->getDomain1();
3165
3166 // generate the PS code
3167 writePSFmt("/t0 %g def\n", t0);
3168 writePSFmt("/t1 %g def\n", t1);
3169 writePSFmt("/dt %g def\n", t1 - t0);
3170 writePSFmt("/x0 %g def\n", x0);
3171 writePSFmt("/y0 %g def\n", y0);
3172 writePSFmt("/dx %g def\n", x1 - x0);
3173 writePSFmt("/x1 %g def\n", x1);
3174 writePSFmt("/y1 %g def\n", y1);
3175 writePSFmt("/dy %g def\n", y1 - y0);
3176 writePSFmt("/xMin %g def\n", xMin);
3177 writePSFmt("/yMin %g def\n", yMin);
3178 writePSFmt("/xMax %g def\n", xMax);
3179 writePSFmt("/yMax %g def\n", yMax);
3180 writePSFmt("/n %d def\n", shading->getColorSpace()->getNComps());
3181 if (shading->getNFuncs() == 1) {
3182 writePS("/func ");
3183 cvtFunction(shading->getFunc(0));
3184 writePS("def\n");
3185 } else {
3186 writePS("/func {\n");
3187 for (i = 0; i < shading->getNFuncs(); ++i) {
3188 if (i < shading->getNFuncs() - 1) {
3189 writePS("dup\n");
3190 }
3191 cvtFunction(shading->getFunc(i));
3192 writePS("exec\n");
3193 if (i < shading->getNFuncs() - 1) {
3194 writePS("exch\n");
3195 }
3196 }
3197 writePS("} def\n");
3198 }
3199 writePSFmt("%g %g 0 axialSH\n", tMin, tMax);
3200 }
3201
3202 void PSOutputDev::radialShadedFill(GfxState *state,
3203 GfxRadialShading *shading) {
3204 double x0, y0, r0, x1, y1, r1, t0, t1, sMin, sMax;
3205 double xMin, yMin, xMax, yMax;
3206 double d0, d1;
3207 int i;
3208
3209 // get the shading info
3210 shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
3211 t0 = shading->getDomain0();
3212 t1 = shading->getDomain1();
3213
3214 // compute the (possibly extended) s range
3215 sMin = 0;
3216 sMax = 1;
3217 if (shading->getExtend0()) {
3218 if (r0 < r1) {
3219 // extend the smaller end
3220 sMin = -r0 / (r1 - r0);
3221 } else {
3222 // extend the larger end
3223 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
3224 d0 = (x0 - xMin) * (x0 - xMin);
3225 d1 = (x0 - xMax) * (x0 - xMax);
3226 sMin = d0 > d1 ? d0 : d1;
3227 d0 = (y0 - yMin) * (y0 - yMin);
3228 d1 = (y0 - yMax) * (y0 - yMax);
3229 sMin += d0 > d1 ? d0 : d1;
3230 sMin = (sqrt(sMin) - r0) / (r1 - r0);
3231 if (sMin > 0) {
3232 sMin = 0;
3233 } else if (sMin < -20) {
3234 // sanity check
3235 sMin = -20;
3236 }
3237 }
3238 }
3239 if (shading->getExtend1()) {
3240 if (r1 < r0) {
3241 // extend the smaller end
3242 sMax = -r0 / (r1 - r0);
3243 } else if (r1 > r0) {
3244 // extend the larger end
3245 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
3246 d0 = (x1 - xMin) * (x1 - xMin);
3247 d1 = (x1 - xMax) * (x1 - xMax);
3248 sMax = d0 > d1 ? d0 : d1;
3249 d0 = (y1 - yMin) * (y1 - yMin);
3250 d1 = (y1 - yMax) * (y1 - yMax);
3251 sMax += d0 > d1 ? d0 : d1;
3252 sMax = (sqrt(sMax) - r0) / (r1 - r0);
3253 if (sMax < 1) {
3254 sMax = 1;
3255 } else if (sMax > 20) {
3256 // sanity check
3257 sMax = 20;
3258 }
3259 }
3260 }
3261
3262 // generate the PS code
3263 writePSFmt("/x0 %g def\n", x0);
3264 writePSFmt("/x1 %g def\n", x1);
3265 writePSFmt("/dx %g def\n", x1 - x0);
3266 writePSFmt("/y0 %g def\n", y0);
3267 writePSFmt("/y1 %g def\n", y1);
3268 writePSFmt("/dy %g def\n", y1 - y0);
3269 writePSFmt("/r0 %g def\n", r0);
3270 writePSFmt("/r1 %g def\n", r1);
3271 writePSFmt("/dr %g def\n", r1 - r0);
3272 writePSFmt("/t0 %g def\n", t0);
3273 writePSFmt("/t1 %g def\n", t1);
3274 writePSFmt("/dt %g def\n", t1 - t0);
3275 writePSFmt("/n %d def\n", shading->getColorSpace()->getNComps());
3276 if (shading->getNFuncs() == 1) {
3277 writePS("/func ");
3278 cvtFunction(shading->getFunc(0));
3279 writePS("def\n");
3280 } else {
3281 writePS("/func {\n");
3282 for (i = 0; i < shading->getNFuncs(); ++i) {
3283 if (i < shading->getNFuncs() - 1) {
3284 writePS("dup\n");
3285 }
3286 cvtFunction(shading->getFunc(i));
3287 writePS("exec\n");
3288 if (i < shading->getNFuncs() - 1) {
3289 writePS("exch\n");
3290 }
3291 }
3292 writePS("} def\n");
3293 }
3294 writePSFmt("%g %g 0 radialSH\n", sMin, sMax);
3295 }
3296
3297 void PSOutputDev::clip(GfxState *state) {
3298 doPath(state->getPath());
3299 writePS("W\n");
3300 }
3301
3302 void PSOutputDev::eoClip(GfxState *state) {
3303 doPath(state->getPath());
3304 writePS("W*\n");
3305 }
3306
3307 void PSOutputDev::doPath(GfxPath *path) {
3308 GfxSubpath *subpath;
3309 double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
3310 int n, m, i, j;
3311
3312 n = path->getNumSubpaths();
3313
3314 if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
3315 subpath = path->getSubpath(0);
3316 x0 = subpath->getX(0);
3317 y0 = subpath->getY(0);
3318 x4 = subpath->getX(4);
3319 y4 = subpath->getY(4);
3320 if (x4 == x0 && y4 == y0) {
3321 x1 = subpath->getX(1);
3322 y1 = subpath->getY(1);
3323 x2 = subpath->getX(2);
3324 y2 = subpath->getY(2);
3325 x3 = subpath->getX(3);
3326 y3 = subpath->getY(3);
3327 if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
3328 writePSFmt("%g %g %g %g re\n",
3329 x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
3330 fabs(x2 - x0), fabs(y1 - y0));
3331 return;
3332 } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
3333 writePSFmt("%g %g %g %g re\n",
3334 x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
3335 fabs(x1 - x0), fabs(y2 - y0));
3336 return;
3337 }
3338 }
3339 }
3340
3341 for (i = 0; i < n; ++i) {
3342 subpath = path->getSubpath(i);
3343 m = subpath->getNumPoints();
3344 writePSFmt("%g %g m\n", subpath->getX(0), subpath->getY(0));
3345 j = 1;
3346 while (j < m) {
3347 if (subpath->getCurve(j)) {
3348 writePSFmt("%g %g %g %g %g %g c\n", subpath->getX(j), subpath->getY(j),
3349 subpath->getX(j+1), subpath->getY(j+1),
3350 subpath->getX(j+2), subpath->getY(j+2));
3351 j += 3;
3352 } else {
3353 writePSFmt("%g %g l\n", subpath->getX(j), subpath->getY(j));
3354 ++j;
3355 }
3356 }
3357 if (subpath->isClosed()) {
3358 writePS("h\n");
3359 }
3360 }
3361 }
3362
3363 void PSOutputDev::drawString(GfxState *state, GString *s) {
3364 GfxFont *font;
3365 int wMode;
3366 GString *s2;
3367 double dx, dy, dx2, dy2, originX, originY;
3368 char *p;
3369 UnicodeMap *uMap;
3370 CharCode code;
3371 Unicode u[8];
3372 char buf[8];
3373 int len, nChars, uLen, n, m, i, j;
3374
3375 // check for invisible text -- this is used by Acrobat Capture
3376 if (state->getRender() == 3) {
3377 return;
3378 }
3379
3380 // ignore empty strings
3381 if (s->getLength() == 0) {
3382 return;
3383 }
3384
3385 // get the font
3386 if (!(font = state->getFont())) {
3387 return;
3388 }
3389 wMode = font->getWMode();
3390
3391 // check for a subtitute 16-bit font
3392 uMap = NULL;
3393 if (font->isCIDFont()) {
3394 for (i = 0; i < font16EncLen; ++i) {
3395 if (font->getID()->num == font16Enc[i].fontID.num &&
3396 font->getID()->gen == font16Enc[i].fontID.gen) {
3397 uMap = globalParams->getUnicodeMap(font16Enc[i].enc);
3398 break;
3399 }
3400 }
3401 }
3402
3403 // compute width of chars in string, ignoring char spacing and word
3404 // spacing -- the Tj operator will adjust for the metrics of the
3405 // font that's actually used
3406 dx = dy = 0;
3407 nChars = 0;
3408 p = s->getCString();
3409 len = s->getLength();
3410 if (font->isCIDFont()) {
3411 s2 = new GString();
3412 } else {
3413 s2 = s;
3414 }
3415 while (len > 0) {
3416 n = font->getNextChar(p, len, &code,
3417 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
3418 &dx2, &dy2, &originX, &originY);
3419 if (font->isCIDFont()) {
3420 if (uMap) {
3421 for (i = 0; i < uLen; ++i) {
3422 m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf));
3423 for (j = 0; j < m; ++j) {
3424 s2->append(buf[j]);
3425 }
3426 }
3427 //~ this really needs to get the number of chars in the target
3428 //~ encoding - which may be more than the number of Unicode
3429 //~ chars
3430 nChars += uLen;
3431 } else {
3432 s2->append((char)((code >> 8) & 0xff));
3433 s2->append((char)(code & 0xff));
3434 ++nChars;
3435 }
3436 }
3437 dx += dx2;
3438 dy += dy2;
3439 p += n;
3440 len -= n;
3441 }
3442 dx *= state->getFontSize() * state->getHorizScaling();
3443 dy *= state->getFontSize();
3444 if (uMap) {
3445 uMap->decRefCnt();
3446 }
3447
3448 if (s2->getLength() > 0) {
3449 writePSString(s2);
3450 if (font->isCIDFont()) {
3451 if (wMode) {
3452 writePSFmt(" %d %g Tj16V\n", nChars, dy);
3453 } else {
3454 writePSFmt(" %d %g Tj16\n", nChars, dx);
3455 }
3456 } else {
3457 writePSFmt(" %g Tj\n", dx);
3458 }
3459 }
3460 if (font->isCIDFont()) {
3461 delete s2;
3462 }
3463
3464 if (state->getRender() & 4) {
3465 haveTextClip = gTrue;
3466 }
3467 }
3468
3469 void PSOutputDev::endTextObject(GfxState *state) {
3470 if (haveTextClip) {
3471 writePS("Tclip\n");
3472 haveTextClip = gFalse;
3473 }
3474 }
3475
3476 void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
3477 int width, int height, GBool invert,
3478 GBool inlineImg) {
3479 int len;
3480
3481 len = height * ((width + 7) / 8);
3482 if (level == psLevel1 || level == psLevel1Sep) {
3483 doImageL1(ref, NULL, invert, inlineImg, str, width, height, len);
3484 } else {
3485 doImageL2(ref, NULL, invert, inlineImg, str, width, height, len,
3486 NULL, NULL, 0, 0, gFalse);
3487 }
3488 }
3489
3490 void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
3491 int width, int height, GfxImageColorMap *colorMap,
3492 int *maskColors, GBool inlineImg) {
3493 int len;
3494
3495 len = height * ((width * colorMap->getNumPixelComps() *
3496 colorMap->getBits() + 7) / 8);
3497 switch (level) {
3498 case psLevel1:
3499 doImageL1(ref, colorMap, gFalse, inlineImg, str, width, height, len);
3500 break;
3501 case psLevel1Sep:
3502 //~ handle indexed, separation, ... color spaces
3503 doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len);
3504 break;
3505 case psLevel2:
3506 case psLevel2Sep:
3507 case psLevel3:
3508 case psLevel3Sep:
3509 doImageL2(ref, colorMap, gFalse, inlineImg, str,
3510 width, height, len, maskColors, NULL, 0, 0, gFalse);
3511 break;
3512 }
3513 t3Cacheable = gFalse;
3514 }
3515
3516 void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
3517 int width, int height,
3518 GfxImageColorMap *colorMap,
3519 Stream *maskStr,
3520 int maskWidth, int maskHeight,
3521 GBool maskInvert) {
3522 int len;
3523
3524 len = height * ((width * colorMap->getNumPixelComps() *
3525 colorMap->getBits() + 7) / 8);
3526 switch (level) {
3527 case psLevel1:
3528 doImageL1(ref, colorMap, gFalse, gFalse, str, width, height, len);
3529 break;
3530 case psLevel1Sep:
3531 //~ handle indexed, separation, ... color spaces
3532 doImageL1Sep(colorMap, gFalse, gFalse, str, width, height, len);
3533 break;
3534 case psLevel2:
3535 case psLevel2Sep:
3536 case psLevel3:
3537 case psLevel3Sep:
3538 doImageL2(ref, colorMap, gFalse, gFalse, str, width, height, len,
3539 NULL, maskStr, maskWidth, maskHeight, maskInvert);
3540 break;
3541 }
3542 t3Cacheable = gFalse;
3543 }
3544
3545 void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap,
3546 GBool invert, GBool inlineImg,
3547 Stream *str, int width, int height, int len) {
3548 ImageStream *imgStr;
3549 Guchar pixBuf[gfxColorMaxComps];
3550 GfxGray gray;
3551 int col, x, y, c, i;
3552
3553 if (inType3Char && !colorMap) {
3554 if (inlineImg) {
3555 // create an array
3556 str = new FixedLengthEncoder(str, len);
3557 str = new ASCIIHexEncoder(str);
3558 str->reset();
3559 col = 0;
3560 writePS("[<");
3561 do {
3562 do {
3563 c = str->getChar();
3564 } while (c == '\n' || c == '\r');
3565 if (c == '>' || c == EOF) {
3566 break;
3567 }
3568 writePSChar(c);
3569 ++col;
3570 // each line is: "<...data...><eol>"
3571 // so max data length = 255 - 4 = 251
3572 // but make it 240 just to be safe
3573 // chunks are 2 bytes each, so we need to stop on an even col number
3574 if (col == 240) {
3575 writePS(">\n<");
3576 col = 0;
3577 }
3578 } while (c != '>' && c != EOF);
3579 writePS(">]\n");
3580 writePS("0\n");
3581 str->close();
3582 delete str;
3583 } else {
3584 // set up to use the array already created by setupImages()
3585 writePSFmt("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
3586 }
3587 }
3588
3589 // image/imagemask command
3590 if (inType3Char && !colorMap) {
3591 writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1a\n",
3592 width, height, invert ? "true" : "false",
3593 width, -height, height);
3594 } else if (colorMap) {
3595 writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n",
3596 width, height,
3597 width, -height, height);
3598 } else {
3599 writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n",
3600 width, height, invert ? "true" : "false",
3601 width, -height, height);
3602 }
3603
3604 // image data
3605 if (!(inType3Char && !colorMap)) {
3606
3607 if (colorMap) {
3608
3609 // set up to process the data stream
3610 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
3611 colorMap->getBits());
3612 imgStr->reset();
3613
3614 // process the data stream
3615 i = 0;
3616 for (y = 0; y < height; ++y) {
3617
3618 // write the line
3619 for (x = 0; x < width; ++x) {
3620 imgStr->getPixel(pixBuf);
3621 colorMap->getGray(pixBuf, &gray);
3622 writePSFmt("%02x", colToByte(gray));
3623 if (++i == 32) {
3624 writePSChar('\n');
3625 i = 0;
3626 }
3627 }
3628 }
3629 if (i != 0) {
3630 writePSChar('\n');
3631 }
3632 delete imgStr;
3633
3634 // imagemask
3635 } else {
3636 str->reset();
3637 i = 0;
3638 for (y = 0; y < height; ++y) {
3639 for (x = 0; x < width; x += 8) {
3640 writePSFmt("%02x", str->getChar() & 0xff);
3641 if (++i == 32) {
3642 writePSChar('\n');
3643 i = 0;
3644 }
3645 }
3646 }
3647 if (i != 0) {
3648 writePSChar('\n');
3649 }
3650 str->close();
3651 }
3652 }
3653 }
3654
3655 void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap,
3656 GBool invert, GBool inlineImg,
3657 Stream *str, int width, int height, int len) {
3658 ImageStream *imgStr;
3659 Guchar *lineBuf;
3660 Guchar pixBuf[gfxColorMaxComps];
3661 GfxCMYK cmyk;
3662 int x, y, i, comp;
3663
3664 // width, height, matrix, bits per component
3665 writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1Sep\n",
3666 width, height,
3667 width, -height, height);
3668
3669 // allocate a line buffer
3670 lineBuf = (Guchar *)gmalloc(4 * width);
3671
3672 // set up to process the data stream
3673 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
3674 colorMap->getBits());
3675 imgStr->reset();
3676
3677 // process the data stream
3678 i = 0;
3679 for (y = 0; y < height; ++y) {
3680
3681 // read the line
3682 for (x = 0; x < width; ++x) {
3683 imgStr->getPixel(pixBuf);
3684 colorMap->getCMYK(pixBuf, &cmyk);
3685 lineBuf[4*x+0] = colToByte(cmyk.c);
3686 lineBuf[4*x+1] = colToByte(cmyk.m);
3687 lineBuf[4*x+2] = colToByte(cmyk.y);
3688 lineBuf[4*x+3] = colToByte(cmyk.k);
3689 addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
3690 colToDbl(cmyk.y), colToDbl(cmyk.k));
3691 }
3692
3693 // write one line of each color component
3694 for (comp = 0; comp < 4; ++comp) {
3695 for (x = 0; x < width; ++x) {
3696 writePSFmt("%02x", lineBuf[4*x + comp]);
3697 if (++i == 32) {
3698 writePSChar('\n');
3699 i = 0;
3700 }
3701 }
3702 }
3703 }
3704
3705 if (i != 0) {
3706 writePSChar('\n');
3707 }
3708
3709 delete imgStr;
3710 gfree(lineBuf);
3711 }
3712
3713 void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
3714 GBool invert, GBool inlineImg,
3715 Stream *str, int width, int height, int len,
3716 int *maskColors, Stream *maskStr,
3717 int maskWidth, int maskHeight, GBool maskInvert) {
3718 ImageStream *imgStr;
3719 Guchar *line, *pix;
3720 GString *s;
3721 int n, numComps;
3722 GBool useRLE, useASCII, useASCIIHex, useCompressed;
3723 GfxSeparationColorSpace *sepCS;
3724 GfxColor color;
3725 GfxCMYK cmyk;
3726 int c;
3727 int col, i, x, x0, y, y0, maskXor;
3728
3729 // color key masking
3730 if (maskColors && colorMap && !inlineImg) {
3731 // can't read the stream twice for inline images -- but masking
3732 // isn't allowed with inline images anyway
3733 writePS("[\n");
3734 numComps = colorMap->getNumPixelComps();
3735 imgStr = new ImageStream(str, width, numComps, colorMap->getBits());
3736 imgStr->reset();
3737 for (y = 0, y0 = 0; y < height; ++y) {
3738 if (!(line = imgStr->getLine())) {
3739 break;
3740 }
3741 for (x = 0, x0 = 0, pix = line; x < width; ++x, pix += numComps) {
3742 for (i = 0; i < numComps; ++i) {
3743 if (pix[i] < maskColors[2*i] ||
3744 pix[i] > maskColors[2*i+1]) {
3745 break;
3746 }
3747 }
3748 if (i == numComps) {
3749 if (y0 < y) {
3750 writePSFmt("0 %d %d %d\n", height - y, width, y - y0);
3751 }
3752 if (x0 < x) {
3753 writePSFmt("%d %d %d 1\n", x0, height - y - 1, x - x0);
3754 }
3755 x0 = x + 1;
3756 y0 = y + 1;
3757 }
3758 }
3759 if (x0 > 0 && x0 < width) {
3760 writePSFmt("%d %d %d 1\n", x0, height - y - 1, width - x0);
3761 }
3762 }
3763 if (y0 < height) {
3764 writePSFmt("0 0 %d %d\n", width, height - y0);
3765 }
3766 delete imgStr;
3767 str->close();
3768 writePSFmt("] %d %d pdfImClip\n", width, height);
3769
3770 // explicit masking
3771 } else if (maskStr) {
3772 writePS("[\n");
3773 imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
3774 imgStr->reset();
3775 maskXor = maskInvert ? 1 : 0;
3776 for (y = 0, y0 = 0; y < maskHeight; ++y) {
3777 if (!(line = imgStr->getLine())) {
3778 break;
3779 }
3780 for (x = 0, x0 = 0, pix = line; x < maskWidth; ++x, ++pix) {
3781 if (*pix ^ maskXor) {
3782 if (y0 < y) {
3783 writePSFmt("0 %d %d %d\n", maskHeight - y, maskWidth, y - y0);
3784 }
3785 if (x0 < x) {
3786 writePSFmt("%d %d %d 1\n", x0, maskHeight - y - 1, x - x0);
3787 }
3788 x0 = x + 1;
3789 y0 = y + 1;
3790 }
3791 }
3792 if (x0 > 0 && x0 < maskWidth) {
3793 writePSFmt("%d %d %d 1\n", x0, maskHeight - y - 1, maskWidth - x0);
3794 }
3795 }
3796 if (y0 < maskHeight) {
3797 writePSFmt("0 0 %d %d\n", maskWidth, maskHeight - y0);
3798 }
3799 delete imgStr;
3800 maskStr->close();
3801 writePSFmt("] %d %d pdfImClip\n", maskWidth, maskHeight);
3802 }
3803
3804 // color space
3805 if (colorMap) {
3806 dumpColorSpaceL2(colorMap->getColorSpace(), gFalse, gTrue);
3807 writePS(" setcolorspace\n");
3808 }
3809
3810 useASCIIHex = globalParams->getPSASCIIHex();
3811
3812 // set up the image data
3813 if (mode == psModeForm || inType3Char) {
3814 if (inlineImg) {
3815 // create an array
3816 str = new FixedLengthEncoder(str, len);
3817 if (useASCIIHex) {
3818 str = new ASCIIHexEncoder(str);
3819 } else {
3820 str = new ASCII85Encoder(str);
3821 }
3822 str->reset();
3823 col = 0;
3824 writePS((char *)(useASCIIHex ? "[<" : "[<~"));
3825 do {
3826 do {
3827 c = str->getChar();
3828 } while (c == '\n' || c == '\r');
3829 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
3830 break;
3831 }
3832 if (c == 'z') {
3833 writePSChar(c);
3834 ++col;
3835 } else {
3836 writePSChar(c);
3837 ++col;
3838 for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
3839 do {
3840 c = str->getChar();
3841 } while (c == '\n' || c == '\r');
3842 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
3843 break;
3844 }
3845 writePSChar(c);
3846 ++col;
3847 }
3848 }
3849 // each line is: "<~...data...~><eol>"
3850 // so max data length = 255 - 6 = 249
3851 // chunks are 1 or 5 bytes each, so we have to stop at 245
3852 // but make it 240 just to be safe
3853 if (col > 240) {
3854 writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
3855 col = 0;
3856 }
3857 } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
3858 writePS((char *)(useASCIIHex ? ">]\n" : "~>]\n"));
3859 writePS("0\n");
3860 str->close();
3861 delete str;
3862 } else {
3863 // set up to use the array already created by setupImages()
3864 writePSFmt("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
3865 }
3866 }
3867
3868 // image dictionary
3869 writePS("<<\n /ImageType 1\n");
3870
3871 // width, height, matrix, bits per component
3872 writePSFmt(" /Width %d\n", width);
3873 writePSFmt(" /Height %d\n", height);
3874 writePSFmt(" /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height);
3875 if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
3876 writePSFmt(" /BitsPerComponent 8\n");
3877 } else {
3878 writePSFmt(" /BitsPerComponent %d\n",
3879 colorMap ? colorMap->getBits() : 1);
3880 }
3881
3882 // decode
3883 if (colorMap) {
3884 writePS(" /Decode [");
3885 if ((level == psLevel2Sep || level == psLevel3Sep) &&
3886 colorMap->getColorSpace()->getMode() == csSeparation) {
3887 // this matches up with the code in the pdfImSep operator
3888 n = (1 << colorMap->getBits()) - 1;
3889 writePSFmt("%g %g", colorMap->getDecodeLow(0) * n,
3890 colorMap->getDecodeHigh(0) * n);
3891 } else if (colorMap->getColorSpace()->getMode() == csDeviceN) {
3892 numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
3893 getAlt()->getNComps();
3894 for (i = 0; i < numComps; ++i) {
3895 if (i > 0) {
3896 writePS(" ");
3897 }
3898 writePS("0 1");
3899 }
3900 } else {
3901 numComps = colorMap->getNumPixelComps();
3902 for (i = 0; i < numComps; ++i) {
3903 if (i > 0) {
3904 writePS(" ");
3905 }
3906 writePSFmt("%g %g", colorMap->getDecodeLow(i),
3907 colorMap->getDecodeHigh(i));
3908 }
3909 }
3910 writePS("]\n");
3911 } else {
3912 writePSFmt(" /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1);
3913 }
3914
3915 if (mode == psModeForm || inType3Char) {
3916
3917 // data source
3918 writePS(" /DataSource { 2 copy get exch 1 add exch }\n");
3919
3920 // end of image dictionary
3921 writePSFmt(">>\n%s\n", colorMap ? "image" : "imagemask");
3922
3923 // get rid of the array and index
3924 writePS("pop pop\n");
3925
3926 } else {
3927
3928 // data source
3929 writePS(" /DataSource currentfile\n");
3930 s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,
3931 " ");
3932 if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||
3933 inlineImg || !s) {
3934 useRLE = gTrue;
3935 useASCII = gTrue;
3936 useCompressed = gFalse;
3937 } else {
3938 useRLE = gFalse;
3939 useASCII = str->isBinary();
3940 useCompressed = gTrue;
3941 }
3942 if (useASCII) {
3943 writePSFmt(" /ASCII%sDecode filter\n",
3944 useASCIIHex ? "Hex" : "85");
3945 }
3946 if (useRLE) {
3947 writePS(" /RunLengthDecode filter\n");
3948 }
3949 if (useCompressed) {
3950 writePS(s->getCString());
3951 }
3952 if (s) {
3953 delete s;
3954 }
3955
3956 // cut off inline image streams at appropriate length
3957 if (inlineImg) {
3958 str = new FixedLengthEncoder(str, len);
3959 } else if (useCompressed) {
3960 str = str->getBaseStream();
3961 }
3962
3963 // recode DeviceN data
3964 if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
3965 str = new DeviceNRecoder(str, width, height, colorMap);
3966 }
3967
3968 // add RunLengthEncode and ASCIIHex/85 encode filters
3969 if (useRLE) {
3970 str = new RunLengthEncoder(str);
3971 }
3972 if (useASCII) {
3973 if (useASCIIHex) {
3974 str = new ASCIIHexEncoder(str);
3975 } else {
3976 str = new ASCII85Encoder(str);
3977 }
3978 }
3979
3980 // end of image dictionary
3981 writePS(">>\n");
3982 #if OPI_SUPPORT
3983 if (opi13Nest) {
3984 if (inlineImg) {
3985 // this can't happen -- OPI dictionaries are in XObjects
3986 error(-1, "Internal: OPI in inline image");
3987 n = 0;
3988 } else {
3989 // need to read the stream to count characters -- the length
3990 // is data-dependent (because of ASCII and RLE filters)
3991 str->reset();
3992 n = 0;
3993 while ((c = str->getChar()) != EOF) {
3994 ++n;
3995 }
3996 str->close();
3997 }
3998 // +6/7 for "pdfIm\n" / "pdfImM\n"
3999 // +8 for newline + trailer
4000 n += colorMap ? 14 : 15;
4001 writePSFmt("%%%%BeginData: %d Hex Bytes\n", n);
4002 }
4003 #endif
4004 if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
4005 colorMap->getColorSpace()->getMode() == csSeparation) {
4006 color.c[0] = gfxColorComp1;
4007 sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
4008 sepCS->getCMYK(&color, &cmyk);
4009 writePSFmt("%g %g %g %g (%s) pdfImSep\n",
4010 colToDbl(cmyk.c), colToDbl(cmyk.m),
4011 colToDbl(cmyk.y), colToDbl(cmyk.k),
4012 sepCS->getName()->getCString());
4013 } else {
4014 writePSFmt("%s\n", colorMap ? "pdfIm" : "pdfImM");
4015 }
4016
4017 // copy the stream data
4018 str->reset();
4019 while ((c = str->getChar()) != EOF) {
4020 writePSChar(c);
4021 }
4022 str->close();
4023
4024 // add newline and trailer to the end
4025 writePSChar('\n');
4026 writePS("%-EOD-\n");
4027 #if OPI_SUPPORT
4028 if (opi13Nest) {
4029 writePS("%%EndData\n");
4030 }
4031 #endif
4032
4033 // delete encoders
4034 if (useRLE || useASCII || inlineImg) {
4035 delete str;
4036 }
4037 }
4038
4039 if ((maskColors && colorMap && !inlineImg) || maskStr) {
4040 writePS("pdfImClipEnd\n");
4041 }
4042 }
4043
4044 void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace,
4045 GBool genXform, GBool updateColors) {
4046 GfxCalGrayColorSpace *calGrayCS;
4047 GfxCalRGBColorSpace *calRGBCS;
4048 GfxLabColorSpace *labCS;
4049 GfxIndexedColorSpace *indexedCS;
4050 GfxSeparationColorSpace *separationCS;
4051 GfxDeviceNColorSpace *deviceNCS;
4052 GfxColorSpace *baseCS;
4053 Guchar *lookup, *p;
4054 double x[gfxColorMaxComps], y[gfxColorMaxComps];
4055 GfxColor color;
4056 GfxCMYK cmyk;
4057 Function *func;
4058 int n, numComps, numAltComps;
4059 int byte;
4060 int i, j, k;
4061
4062 switch (colorSpace->getMode()) {
4063
4064 case csDeviceGray:
4065 writePS("/DeviceGray");
4066 if (genXform) {
4067 writePS(" {}");
4068 }
4069 if (updateColors) {
4070 processColors |= psProcessBlack;
4071 }
4072 break;
4073
4074 case csCalGray:
4075 calGrayCS = (GfxCalGrayColorSpace *)colorSpace;
4076 writePS("[/CIEBasedA <<\n");
4077 writePSFmt(" /DecodeA {%g exp} bind\n", calGrayCS->getGamma());
4078 writePSFmt(" /MatrixA [%g %g %g]\n",
4079 calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
4080 calGrayCS->getWhiteZ());
4081 writePSFmt(" /WhitePoint [%g %g %g]\n",
4082 calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
4083 calGrayCS->getWhiteZ());
4084 writePSFmt(" /BlackPoint [%g %g %g]\n",
4085 calGrayCS->getBlackX(), calGrayCS->getBlackY(),
4086 calGrayCS->getBlackZ());
4087 writePS(">>]");
4088 if (genXform) {
4089 writePS(" {}");
4090 }
4091 if (updateColors) {
4092 processColors |= psProcessBlack;
4093 }
4094 break;
4095
4096 case csDeviceRGB:
4097 writePS("/DeviceRGB");
4098 if (genXform) {
4099 writePS(" {}");
4100 }
4101 if (updateColors) {
4102 processColors |= psProcessCMYK;
4103 }
4104 break;
4105
4106 case csCalRGB:
4107 calRGBCS = (GfxCalRGBColorSpace *)colorSpace;
4108 writePS("[/CIEBasedABC <<\n");
4109 writePSFmt(" /DecodeABC [{%g exp} bind {%g exp} bind {%g exp} bind]\n",
4110 calRGBCS->getGammaR(), calRGBCS->getGammaG(),
4111 calRGBCS->getGammaB());
4112 writePSFmt(" /MatrixABC [%g %g %g %g %g %g %g %g %g]\n",
4113 calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1],
4114 calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3],
4115 calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5],
4116 calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7],
4117 calRGBCS->getMatrix()[8]);
4118 writePSFmt(" /WhitePoint [%g %g %g]\n",
4119 calRGBCS->getWhiteX(), calRGBCS->getWhiteY(),
4120 calRGBCS->getWhiteZ());
4121 writePSFmt(" /BlackPoint [%g %g %g]\n",
4122 calRGBCS->getBlackX(), calRGBCS->getBlackY(),
4123 calRGBCS->getBlackZ());
4124 writePS(">>]");
4125 if (genXform) {
4126 writePS(" {}");
4127 }
4128 if (updateColors) {
4129 processColors |= psProcessCMYK;
4130 }
4131 break;
4132
4133 case csDeviceCMYK:
4134 writePS("/DeviceCMYK");
4135 if (genXform) {
4136 writePS(" {}");
4137 }
4138 if (updateColors) {
4139 processColors |= psProcessCMYK;
4140 }
4141 break;
4142
4143 case csLab:
4144 labCS = (GfxLabColorSpace *)colorSpace;
4145 writePS("[/CIEBasedABC <<\n");
4146 writePSFmt(" /RangeABC [0 100 %g %g %g %g]\n",
4147 labCS->getAMin(), labCS->getAMax(),
4148 labCS->getBMin(), labCS->getBMax());
4149 writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
4150 writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
4151 writePS(" /DecodeLMN\n");
4152 writePS(" [{dup 6 29 div ge {dup dup mul mul}\n");
4153 writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
4154 labCS->getWhiteX());
4155 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
4156 writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
4157 labCS->getWhiteY());
4158 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
4159 writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n",
4160 labCS->getWhiteZ());
4161 writePSFmt(" /WhitePoint [%g %g %g]\n",
4162 labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());
4163 writePSFmt(" /BlackPoint [%g %g %g]\n",
4164 labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());
4165 writePS(">>]");
4166 if (genXform) {
4167 writePS(" {}");
4168 }
4169 if (updateColors) {
4170 processColors |= psProcessCMYK;
4171 }
4172 break;
4173
4174 case csICCBased:
4175 // there is no transform function to the alternate color space, so
4176 // we can use it directly
4177 dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt(),
4178 genXform, updateColors);
4179 break;
4180
4181 case csIndexed:
4182 indexedCS = (GfxIndexedColorSpace *)colorSpace;
4183 baseCS = indexedCS->getBase();
4184 writePS("[/Indexed ");
4185 dumpColorSpaceL2(baseCS, gFalse, gFalse);
4186 n = indexedCS->getIndexHigh();
4187 numComps = baseCS->getNComps();
4188 lookup = indexedCS->getLookup();
4189 writePSFmt(" %d <\n", n);
4190 if (baseCS->getMode() == csDeviceN) {
4191 func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();
4192 numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps();
4193 p = lookup;
4194 for (i = 0; i <= n; i += 8) {
4195 writePS(" ");
4196 for (j = i; j < i+8 && j <= n; ++j) {
4197 for (k = 0; k < numComps; ++k) {
4198 x[k] = *p++ / 255.0;
4199 }
4200 func->transform(x, y);
4201 for (k = 0; k < numAltComps; ++k) {
4202 byte = (int)(y[k] * 255 + 0.5);
4203 if (byte < 0) {
4204 byte = 0;
4205 } else if (byte > 255) {
4206 byte = 255;
4207 }
4208 writePSFmt("%02x", byte);
4209 }
4210 if (updateColors) {
4211 color.c[0] = dblToCol(j);
4212 indexedCS->getCMYK(&color, &cmyk);
4213 addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
4214 colToDbl(cmyk.y), colToDbl(cmyk.k));
4215 }
4216 }
4217 writePS("\n");
4218 }
4219 } else {
4220 for (i = 0; i <= n; i += 8) {
4221 writePS(" ");
4222 for (j = i; j < i+8 && j <= n; ++j) {
4223 for (k = 0; k < numComps; ++k) {
4224 writePSFmt("%02x", lookup[j * numComps + k]);
4225 }
4226 if (updateColors) {
4227 color.c[0] = dblToCol(j);
4228 indexedCS->getCMYK(&color, &cmyk);
4229 addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
4230 colToDbl(cmyk.y), colToDbl(cmyk.k));
4231 }
4232 }
4233 writePS("\n");
4234 }
4235 }
4236 writePS(">]");
4237 if (genXform) {
4238 writePS(" {}");
4239 }
4240 break;
4241
4242 case csSeparation:
4243 separationCS = (GfxSeparationColorSpace *)colorSpace;
4244 writePS("[/Separation /");
4245 writePSName(separationCS->getName()->getCString());
4246 writePS(" ");
4247 dumpColorSpaceL2(separationCS->getAlt(), gFalse, gFalse);
4248 writePS("\n");
4249 cvtFunction(separationCS->getFunc());
4250 writePS("]");
4251 if (genXform) {
4252 writePS(" {}");
4253 }
4254 if (updateColors) {
4255 addCustomColor(separationCS);
4256 }
4257 break;
4258
4259 case csDeviceN:
4260 // DeviceN color spaces are a Level 3 PostScript feature.
4261 deviceNCS = (GfxDeviceNColorSpace *)colorSpace;
4262 dumpColorSpaceL2(deviceNCS->getAlt(), gFalse, updateColors);
4263 if (genXform) {
4264 writePS(" ");
4265 cvtFunction(deviceNCS->getTintTransformFunc());
4266 }
4267 break;
4268
4269 case csPattern:
4270 //~ unimplemented
4271 break;
4272 }
4273 }
4274
4275 #if OPI_SUPPORT
4276 void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
4277 Object dict;
4278
4279 if (globalParams->getPSOPI()) {
4280 opiDict->lookup("2.0", &dict);
4281 if (dict.isDict()) {
4282 opiBegin20(state, dict.getDict());
4283 dict.free();
4284 } else {
4285 dict.free();
4286 opiDict->lookup("1.3", &dict);
4287 if (dict.isDict()) {
4288 opiBegin13(state, dict.getDict());
4289 }
4290 dict.free();
4291 }
4292 }
4293 }
4294
4295 void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
4296 Object obj1, obj2, obj3, obj4;
4297 double width, height, left, right, top, bottom;
4298 int w, h;
4299 int i;
4300
4301 writePS("%%BeginOPI: 2.0\n");
4302 writePS("%%Distilled\n");
4303
4304 dict->lookup("F", &obj1);
4305 if (getFileSpec(&obj1, &obj2)) {
4306 writePSFmt("%%%%ImageFileName: %s\n",
4307 obj2.getString()->getCString());
4308 obj2.free();
4309 }
4310 obj1.free();
4311
4312 dict->lookup("MainImage", &obj1);
4313 if (obj1.isString()) {
4314 writePSFmt("%%%%MainImage: %s\n", obj1.getString()->getCString());
4315 }
4316 obj1.free();
4317
4318 //~ ignoring 'Tags' entry
4319 //~ need to use writePSString() and deal with >255-char lines
4320
4321 dict->lookup("Size", &obj1);
4322 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4323 obj1.arrayGet(0, &obj2);
4324 width = obj2.getNum();
4325 obj2.free();
4326 obj1.arrayGet(1, &obj2);
4327 height = obj2.getNum();
4328 obj2.free();
4329 writePSFmt("%%%%ImageDimensions: %g %g\n", width, height);
4330 }
4331 obj1.free();
4332
4333 dict->lookup("CropRect", &obj1);
4334 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
4335 obj1.arrayGet(0, &obj2);
4336 left = obj2.getNum();
4337 obj2.free();
4338 obj1.arrayGet(1, &obj2);
4339 top = obj2.getNum();
4340 obj2.free();
4341 obj1.arrayGet(2, &obj2);
4342 right = obj2.getNum();
4343 obj2.free();
4344 obj1.arrayGet(3, &obj2);
4345 bottom = obj2.getNum();
4346 obj2.free();
4347 writePSFmt("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom);
4348 }
4349 obj1.free();
4350
4351 dict->lookup("Overprint", &obj1);
4352 if (obj1.isBool()) {
4353 writePSFmt("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
4354 }
4355 obj1.free();
4356
4357 dict->lookup("Inks", &obj1);
4358 if (obj1.isName()) {
4359 writePSFmt("%%%%ImageInks: %s\n", obj1.getName());
4360 } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
4361 obj1.arrayGet(0, &obj2);
4362 if (obj2.isName()) {
4363 writePSFmt("%%%%ImageInks: %s %d",
4364 obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
4365 for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) {
4366 obj1.arrayGet(i, &obj3);
4367 obj1.arrayGet(i+1, &obj4);
4368 if (obj3.isString() && obj4.isNum()) {
4369 writePS(" ");
4370 writePSString(obj3.getString());
4371 writePSFmt(" %g", obj4.getNum());
4372 }
4373 obj3.free();
4374 obj4.free();
4375 }
4376 writePS("\n");
4377 }
4378 obj2.free();
4379 }
4380 obj1.free();
4381
4382 writePS("gsave\n");
4383
4384 writePS("%%BeginIncludedImage\n");
4385
4386 dict->lookup("IncludedImageDimensions", &obj1);
4387 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4388 obj1.arrayGet(0, &obj2);
4389 w = obj2.getInt();
4390 obj2.free();
4391 obj1.arrayGet(1, &obj2);
4392 h = obj2.getInt();
4393 obj2.free();
4394 writePSFmt("%%%%IncludedImageDimensions: %d %d\n", w, h);
4395 }
4396 obj1.free();
4397
4398 dict->lookup("IncludedImageQuality", &obj1);
4399 if (obj1.isNum()) {
4400 writePSFmt("%%%%IncludedImageQuality: %g\n", obj1.getNum());
4401 }
4402 obj1.free();
4403
4404 ++opi20Nest;
4405 }
4406
4407 void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
4408 Object obj1, obj2;
4409 int left, right, top, bottom, samples, bits, width, height;
4410 double c, m, y, k;
4411 double llx, lly, ulx, uly, urx, ury, lrx, lry;
4412 double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
4413 double horiz, vert;
4414 int i, j;
4415
4416 writePS("save\n");
4417 writePS("/opiMatrix2 matrix currentmatrix def\n");
4418 writePS("opiMatrix setmatrix\n");
4419
4420 dict->lookup("F", &obj1);
4421 if (getFileSpec(&obj1, &obj2)) {
4422 writePSFmt("%%ALDImageFileName: %s\n",
4423 obj2.getString()->getCString());
4424 obj2.free();
4425 }
4426 obj1.free();
4427
4428 dict->lookup("CropRect", &obj1);
4429 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
4430 obj1.arrayGet(0, &obj2);
4431 left = obj2.getInt();
4432 obj2.free();
4433 obj1.arrayGet(1, &obj2);
4434 top = obj2.getInt();
4435 obj2.free();
4436 obj1.arrayGet(2, &obj2);
4437 right = obj2.getInt();
4438 obj2.free();
4439 obj1.arrayGet(3, &obj2);
4440 bottom = obj2.getInt();
4441 obj2.free();
4442 writePSFmt("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom);
4443 }
4444 obj1.free();
4445
4446 dict->lookup("Color", &obj1);
4447 if (obj1.isArray() && obj1.arrayGetLength() == 5) {
4448 obj1.arrayGet(0, &obj2);
4449 c = obj2.getNum();
4450 obj2.free();
4451 obj1.arrayGet(1, &obj2);
4452 m = obj2.getNum();
4453 obj2.free();
4454 obj1.arrayGet(2, &obj2);
4455 y = obj2.getNum();
4456 obj2.free();
4457 obj1.arrayGet(3, &obj2);
4458 k = obj2.getNum();
4459 obj2.free();
4460 obj1.arrayGet(4, &obj2);
4461 if (obj2.isString()) {
4462 writePSFmt("%%ALDImageColor: %g %g %g %g ", c, m, y, k);
4463 writePSString(obj2.getString());
4464 writePS("\n");
4465 }
4466 obj2.free();
4467 }
4468 obj1.free();
4469
4470 dict->lookup("ColorType", &obj1);
4471 if (obj1.isName()) {
4472 writePSFmt("%%ALDImageColorType: %s\n", obj1.getName());
4473 }
4474 obj1.free();
4475
4476 //~ ignores 'Comments' entry
4477 //~ need to handle multiple lines
4478
4479 dict->lookup("CropFixed", &obj1);
4480 if (obj1.isArray()) {
4481 obj1.arrayGet(0, &obj2);
4482 ulx = obj2.getNum();
4483 obj2.free();
4484 obj1.arrayGet(1, &obj2);
4485 uly = obj2.getNum();
4486 obj2.free();
4487 obj1.arrayGet(2, &obj2);
4488 lrx = obj2.getNum();
4489 obj2.free();
4490 obj1.arrayGet(3, &obj2);
4491 lry = obj2.getNum();
4492 obj2.free();
4493 writePSFmt("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry);
4494 }
4495 obj1.free();
4496
4497 dict->lookup("GrayMap", &obj1);
4498 if (obj1.isArray()) {
4499 writePS("%ALDImageGrayMap:");
4500 for (i = 0; i < obj1.arrayGetLength(); i += 16) {
4501 if (i > 0) {
4502 writePS("\n%%+");
4503 }
4504 for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
4505 obj1.arrayGet(i+j, &obj2);
4506 writePSFmt(" %d", obj2.getInt());
4507 obj2.free();
4508 }
4509 }
4510 writePS("\n");
4511 }
4512 obj1.free();
4513
4514 dict->lookup("ID", &obj1);
4515 if (obj1.isString()) {
4516 writePSFmt("%%ALDImageID: %s\n", obj1.getString()->getCString());
4517 }
4518 obj1.free();
4519
4520 dict->lookup("ImageType", &obj1);
4521 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4522 obj1.arrayGet(0, &obj2);
4523 samples = obj2.getInt();
4524 obj2.free();
4525 obj1.arrayGet(1, &obj2);
4526 bits = obj2.getInt();
4527 obj2.free();
4528 writePSFmt("%%ALDImageType: %d %d\n", samples, bits);
4529 }
4530 obj1.free();
4531
4532 dict->lookup("Overprint", &obj1);
4533 if (obj1.isBool()) {
4534 writePSFmt("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
4535 }
4536 obj1.free();
4537
4538 dict->lookup("Position", &obj1);
4539 if (obj1.isArray() && obj1.arrayGetLength() == 8) {
4540 obj1.arrayGet(0, &obj2);
4541 llx = obj2.getNum();
4542 obj2.free();
4543 obj1.arrayGet(1, &obj2);
4544 lly = obj2.getNum();
4545 obj2.free();
4546 obj1.arrayGet(2, &obj2);
4547 ulx = obj2.getNum();
4548 obj2.free();
4549 obj1.arrayGet(3, &obj2);
4550 uly = obj2.getNum();
4551 obj2.free();
4552 obj1.arrayGet(4, &obj2);
4553 urx = obj2.getNum();
4554 obj2.free();
4555 obj1.arrayGet(5, &obj2);
4556 ury = obj2.getNum();
4557 obj2.free();
4558 obj1.arrayGet(6, &obj2);
4559 lrx = obj2.getNum();
4560 obj2.free();
4561 obj1.arrayGet(7, &obj2);
4562 lry = obj2.getNum();
4563 obj2.free();
4564 opiTransform(state, llx, lly, &tllx, &tlly);
4565 opiTransform(state, ulx, uly, &tulx, &tuly);
4566 opiTransform(state, urx, ury, &turx, &tury);
4567 opiTransform(state, lrx, lry, &tlrx, &tlry);
4568 writePSFmt("%%ALDImagePosition: %g %g %g %g %g %g %g %g\n",
4569 tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
4570 obj2.free();
4571 }
4572 obj1.free();
4573
4574 dict->lookup("Resolution", &obj1);
4575 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4576 obj1.arrayGet(0, &obj2);
4577 horiz = obj2.getNum();
4578 obj2.free();
4579 obj1.arrayGet(1, &obj2);
4580 vert = obj2.getNum();
4581 obj2.free();
4582 writePSFmt("%%ALDImageResoution: %g %g\n", horiz, vert);
4583 obj2.free();
4584 }
4585 obj1.free();
4586
4587 dict->lookup("Size", &obj1);
4588 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4589 obj1.arrayGet(0, &obj2);
4590 width = obj2.getInt();
4591 obj2.free();
4592 obj1.arrayGet(1, &obj2);
4593 height = obj2.getInt();
4594 obj2.free();
4595 writePSFmt("%%ALDImageDimensions: %d %d\n", width, height);
4596 }
4597 obj1.free();
4598
4599 //~ ignoring 'Tags' entry
4600 //~ need to use writePSString() and deal with >255-char lines
4601
4602 dict->lookup("Tint", &obj1);
4603 if (obj1.isNum()) {
4604 writePSFmt("%%ALDImageTint: %g\n", obj1.getNum());
4605 }
4606 obj1.free();
4607
4608 dict->lookup("Transparency", &obj1);
4609 if (obj1.isBool()) {
4610 writePSFmt("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false");
4611 }
4612 obj1.free();
4613
4614 writePS("%%BeginObject: image\n");
4615 writePS("opiMatrix2 setmatrix\n");
4616 ++opi13Nest;
4617 }
4618
4619 // Convert PDF user space coordinates to PostScript default user space
4620 // coordinates. This has to account for both the PDF CTM and the
4621 // PSOutputDev page-fitting transform.
4622 void PSOutputDev::opiTransform(GfxState *state, double x0, double y0,
4623 double *x1, double *y1) {
4624 double t;
4625
4626 state->transform(x0, y0, x1, y1);
4627 *x1 += tx;
4628 *y1 += ty;
4629 if (rotate == 90) {
4630 t = *x1;
4631 *x1 = -*y1;
4632 *y1 = t;
4633 } else if (rotate == 180) {
4634 *x1 = -*x1;
4635 *y1 = -*y1;
4636 } else if (rotate == 270) {
4637 t = *x1;
4638 *x1 = *y1;
4639 *y1 = -t;
4640 }
4641 *x1 *= xScale;
4642 *y1 *= yScale;
4643 }
4644
4645 void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
4646 Object dict;
4647
4648 if (globalParams->getPSOPI()) {
4649 opiDict->lookup("2.0", &dict);
4650 if (dict.isDict()) {
4651 writePS("%%EndIncludedImage\n");
4652 writePS("%%EndOPI\n");
4653 writePS("grestore\n");
4654 --opi20Nest;
4655 dict.free();
4656 } else {
4657 dict.free();
4658 opiDict->lookup("1.3", &dict);
4659 if (dict.isDict()) {
4660 writePS("%%EndObject\n");
4661 writePS("restore\n");
4662 --opi13Nest;
4663 }
4664 dict.free();
4665 }
4666 }
4667 }
4668
4669 GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
4670 if (fileSpec->isString()) {
4671 fileSpec->copy(fileName);
4672 return gTrue;
4673 }
4674 if (fileSpec->isDict()) {
4675 fileSpec->dictLookup("DOS", fileName);
4676 if (fileName->isString()) {
4677 return gTrue;
4678 }
4679 fileName->free();
4680 fileSpec->dictLookup("Mac", fileName);
4681 if (fileName->isString()) {
4682 return gTrue;
4683 }
4684 fileName->free();
4685 fileSpec->dictLookup("Unix", fileName);
4686 if (fileName->isString()) {
4687 return gTrue;
4688 }
4689 fileName->free();
4690 fileSpec->dictLookup("F", fileName);
4691 if (fileName->isString()) {
4692 return gTrue;
4693 }
4694 fileName->free();
4695 }
4696 return gFalse;
4697 }
4698 #endif // OPI_SUPPORT
4699
4700 void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
4701 writePSFmt("%g %g setcharwidth\n", wx, wy);
4702 writePS("q\n");
4703 }
4704
4705 void PSOutputDev::type3D1(GfxState *state, double wx, double wy,
4706 double llx, double lly, double urx, double ury) {
4707 t3WX = wx;
4708 t3WY = wy;
4709 t3LLX = llx;
4710 t3LLY = lly;
4711 t3URX = urx;
4712 t3URY = ury;
4713 t3String = new GString();
4714 writePS("q\n");
4715 t3Cacheable = gTrue;
4716 }
4717
4718 void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
4719 Stream *str;
4720 int c;
4721
4722 if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
4723 str = level1Stream;
4724 } else {
4725 str = psStream;
4726 }
4727 str->reset();
4728 while ((c = str->getChar()) != EOF) {
4729 writePSChar(c);
4730 }
4731 str->close();
4732 }
4733
4734 //~ can nextFunc be reset to 0 -- maybe at the start of each page?
4735 //~ or maybe at the start of each color space / pattern?
4736 void PSOutputDev::cvtFunction(Function *func) {
4737 SampledFunction *func0;
4738 ExponentialFunction *func2;
4739 StitchingFunction *func3;
4740 PostScriptFunction *func4;
4741 int thisFunc, m, n, nSamples, i, j, k;
4742
4743 switch (func->getType()) {
4744
4745 case -1: // identity
4746 writePS("{}\n");
4747 break;
4748
4749 case 0: // sampled
4750 func0 = (SampledFunction *)func;
4751 thisFunc = nextFunc++;
4752 m = func0->getInputSize();
4753 n = func0->getOutputSize();
4754 nSamples = n;
4755 for (i = 0; i < m; ++i) {
4756 nSamples *= func0->getSampleSize(i);
4757 }
4758 writePSFmt("/xpdfSamples%d [\n", thisFunc);
4759 for (i = 0; i < nSamples; ++i) {
4760 writePSFmt("%g\n", func0->getSamples()[i]);
4761 }
4762 writePS("] def\n");
4763 writePSFmt("{ %d array %d array %d 2 roll\n", 2*m, m, m+2);
4764 // [e01] [efrac] x0 x1 ... xm-1
4765 for (i = m-1; i >= 0; --i) {
4766 // [e01] [efrac] x0 x1 ... xi
4767 writePSFmt("%g sub %g mul %g add\n",
4768 func0->getDomainMin(i),
4769 (func0->getEncodeMax(i) - func0->getEncodeMin(i)) /
4770 (func0->getDomainMax(i) - func0->getDomainMin(i)),
4771 func0->getEncodeMin(i));
4772 // [e01] [efrac] x0 x1 ... xi-1 xi'
4773 writePSFmt("dup 0 lt { pop 0 } { dup %d gt { pop %d } if } ifelse\n",
4774 func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1);
4775 // [e01] [efrac] x0 x1 ... xi-1 xi'
4776 writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n");
4777 // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi')
4778 writePSFmt("%d index %d 3 2 roll put\n", i+3, i);
4779 // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi')
4780 writePSFmt("%d index %d 3 2 roll put\n", i+3, 2*i+1);
4781 // [e01] [efrac] x0 x1 ... xi-1 floor(xi')
4782 writePSFmt("%d index %d 3 2 roll put\n", i+2, 2*i);
4783 // [e01] [efrac] x0 x1 ... xi-1
4784 }
4785 // [e01] [efrac]
4786 for (i = 0; i < n; ++i) {
4787 // [e01] [efrac] y(0) ... y(i-1)
4788 for (j = 0; j < (1<<m); ++j) {
4789 // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(j-1)
4790 writePSFmt("xpdfSamples%d\n", thisFunc);
4791 k = m - 1;
4792 writePSFmt("%d index %d get\n", i+j+2, 2 * k + ((j >> k) & 1));
4793 for (k = m - 2; k >= 0; --k) {
4794 writePSFmt("%d mul %d index %d get add\n",
4795 func0->getSampleSize(k),
4796 i + j + 3,
4797 2 * k + ((j >> k) & 1));
4798 }
4799 if (n > 1) {
4800 writePSFmt("%d mul %d add ", n, i);
4801 }
4802 writePS("get\n");
4803 }
4804 // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1)
4805 for (j = 0; j < m; ++j) {
4806 // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1)
4807 for (k = 0; k < (1 << (m - j)); k += 2) {
4808 // [e01] [efrac] y(0) ... y(i-1) <k/2 s' values> <2^(m-j)-k s values>
4809 writePSFmt("%d index %d get dup\n", i + k/2 + (1 << (m-j)) - k, j);
4810 writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n");
4811 writePSFmt("%d 1 roll\n", k/2 + (1 << m-j) - k - 1);
4812 }
4813 // [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1)
4814 }
4815 // [e01] [efrac] y(0) ... y(i-1) s
4816 writePSFmt("%g mul %g add\n",
4817 func0->getDecodeMax(i) - func0->getDecodeMin(i),
4818 func0->getDecodeMin(i));
4819 writePSFmt("dup %g lt { pop %g } { dup %g gt { pop %g } if } ifelse\n",
4820 func0->getRangeMin(i), func0->getRangeMin(i),
4821 func0->getRangeMax(i), func0->getRangeMax(i));
4822 // [e01] [efrac] y(0) ... y(i-1) y(i)
4823 }
4824 // [e01] [efrac] y(0) ... y(n-1)
4825 writePSFmt("%d %d roll pop pop }\n", n+2, n);
4826 break;
4827
4828 case 2: // exponential
4829 func2 = (ExponentialFunction *)func;
4830 n = func2->getOutputSize();
4831 writePSFmt("{ dup %g lt { pop %g } { dup %g gt { pop %g } if } ifelse\n",
4832 func2->getDomainMin(0), func2->getDomainMin(0),
4833 func2->getDomainMax(0), func2->getDomainMax(0));
4834 // x
4835 for (i = 0; i < n; ++i) {
4836 // x y(0) .. y(i-1)
4837 writePSFmt("%d index %g exp %g mul %g add\n",
4838 i, func2->getE(), func2->getC1()[i] - func2->getC0()[i],
4839 func2->getC0()[i]);
4840 if (func2->getHasRange()) {
4841 writePSFmt("dup %g lt { pop %g } { dup %g gt { pop %g } if } ifelse\n",
4842 func2->getRangeMin(i), func2->getRangeMin(i),
4843 func2->getRangeMax(i), func2->getRangeMax(i));
4844 }
4845 }
4846 // x y(0) .. y(n-1)
4847 writePSFmt("%d %d roll pop }\n", n+1, n);
4848 break;
4849
4850 case 3: // stitching
4851 func3 = (StitchingFunction *)func;
4852 thisFunc = nextFunc++;
4853 for (i = 0; i < func3->getNumFuncs(); ++i) {
4854 cvtFunction(func3->getFunc(i));
4855 writePSFmt("/xpdfFunc%d_%d exch def\n", thisFunc, i);
4856 }
4857 writePSFmt("{ dup %g lt { pop %g } { dup %g gt { pop %g } if } ifelse\n",
4858 func3->getDomainMin(0), func3->getDomainMin(0),
4859 func3->getDomainMax(0), func3->getDomainMax(0));
4860 for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
4861 writePSFmt("dup %g lt { %g sub %g mul %g add xpdfFunc%d_%d } {\n",
4862 func3->getBounds()[i+1],
4863 func3->getBounds()[i],
4864 (func3->getEncode()[2*i+1] - func3->getEncode()[2*i]) /
4865 (func3->getBounds()[i+1] - func3->getBounds()[i]),
4866 func3->getEncode()[2*i],
4867 thisFunc, i);
4868 }
4869 writePSFmt("%g sub %g mul %g add xpdfFunc%d_%d\n",
4870 func3->getBounds()[i],
4871 (func3->getEncode()[2*i+1] - func3->getEncode()[2*i]) /
4872 (func3->getBounds()[i+1] - func3->getBounds()[i]),
4873 func3->getEncode()[2*i],
4874 thisFunc, i);
4875 for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
4876 writePS("} ifelse\n");
4877 }
4878 writePS("}\n");
4879 break;
4880
4881 case 4: // PostScript
4882 func4 = (PostScriptFunction *)func;
4883 writePS(func4->getCodeString()->getCString());
4884 writePS("\n");
4885 break;
4886 }
4887 }
4888
4889 void PSOutputDev::writePSChar(char c) {
4890 if (t3String) {
4891 t3String->append(c);
4892 } else {
4893 (*outputFunc)(outputStream, &c, 1);
4894 }
4895 }
4896
4897 void PSOutputDev::writePS(char *s) {
4898 if (t3String) {
4899 t3String->append(s);
4900 } else {
4901 (*outputFunc)(outputStream, s, strlen(s));
4902 }
4903 }
4904
4905 void PSOutputDev::writePSFmt(const char *fmt, ...) {
4906 va_list args;
4907 char buf[512];
4908
4909 va_start(args, fmt);
4910 vsprintf(buf, fmt, args);
4911 va_end(args);
4912 if (t3String) {
4913 t3String->append(buf);
4914 } else {
4915 (*outputFunc)(outputStream, buf, strlen(buf));
4916 }
4917 }
4918
4919 void PSOutputDev::writePSString(GString *s) {
4920 Guchar *p;
4921 int n, line;
4922 char buf[8];
4923
4924 writePSChar('(');
4925 line = 1;
4926 for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
4927 if (line >= 64) {
4928 writePSChar('\\');
4929 writePSChar('\n');
4930 line = 0;
4931 }
4932 if (*p == '(' || *p == ')' || *p == '\\') {
4933 writePSChar('\\');
4934 writePSChar((char)*p);
4935 line += 2;
4936 } else if (*p < 0x20 || *p >= 0x80) {
4937 sprintf(buf, "\\%03o", *p);
4938 writePS(buf);
4939 line += 4;
4940 } else {
4941 writePSChar((char)*p);
4942 ++line;
4943 }
4944 }
4945 writePSChar(')');
4946 }
4947
4948 void PSOutputDev::writePSName(char *s) {
4949 char *p;
4950 char c;
4951
4952 p = s;
4953 while ((c = *p++)) {
4954 if (c <= (char)0x20 || c >= (char)0x7f ||
4955 c == '(' || c == ')' || c == '<' || c == '>' ||
4956 c == '[' || c == ']' || c == '{' || c == '}' ||
4957 c == '/' || c == '%') {
4958 writePSFmt("#%02x", c & 0xff);
4959 } else {
4960 writePSChar(c);
4961 }
4962 }
4963 }
4964
4965 GString *PSOutputDev::filterPSName(GString *name) {
4966 GString *name2;
4967 char buf[8];
4968 int i;
4969 char c;
4970
4971 name2 = new GString();
4972
4973 // ghostscript chokes on names that begin with out-of-limits
4974 // numbers, e.g., 1e4foo is handled correctly (as a name), but
4975 // 1e999foo generates a limitcheck error
4976 c = name->getChar(0);
4977 if (c >= '0' && c <= '9') {
4978 name2->append('f');
4979 }
4980
4981 for (i = 0; i < name->getLength(); ++i) {
4982 c = name->getChar(i);
4983 if (c <= (char)0x20 || c >= (char)0x7f ||
4984 c == '(' || c == ')' || c == '<' || c == '>' ||
4985 c == '[' || c == ']' || c == '{' || c == '}' ||
4986 c == '/' || c == '%') {
4987 sprintf(buf, "#%02x", c & 0xff);
4988 name2->append(buf);
4989 } else {
4990 name2->append(c);
4991 }
4992 }
4993 return name2;
4994 }
4995
4996 GBool /* O - gTrue if selected, gFalse otherwise */
4997 PSOutputDev::checkRange(int page) /* I - Page number */
4998 {
4999 const char *range; /* Pointer into range string */
5000 int lower, upper; /* Lower and upper page numbers */
5001
5002
5003 if (pageRanges == NULL)
5004 return (gTrue); /* No range, print all pages... */
5005
5006 for (range = pageRanges; *range != '\0';)
5007 {
5008 if (*range == '-')
5009 {
5010 lower = 1;
5011 range ++;
5012 upper = strtol(range, (char **)&range, 10);
5013 }
5014 else
5015 {
5016 lower = strtol(range, (char **)&range, 10);
5017
5018 if (*range == '-')
5019 {
5020 range ++;
5021 if (!isdigit(*range & 255))
5022 upper = 65535;
5023 else
5024 upper = strtol(range, (char **)&range, 10);
5025 }
5026 else
5027 upper = lower;
5028 }
5029
5030 if (page >= lower && page <= upper)
5031 return (gTrue);
5032
5033 if (*range == ',')
5034 range ++;
5035 else
5036 break;
5037 }
5038
5039 return (gFalse);
5040 }