]> git.ipfire.org Git - thirdparty/cups.git/blob - pdftops/PSOutputDev.cxx
Load cups into easysw/current.
[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 writePSFmt("%%Producer: xpdf/pdftops %s\n", xpdfVersion);
1162 xref->getDocInfo(&info);
1163 if (info.dictLookup("Creator", &obj1)->isString()) {
1164 writePS("%%Creator: ");
1165 s = obj1.getString();
1166 if ((s->getChar(0) & 0xff) == 0xfe &&
1167 (s->getChar(1) & 0xff) == 0xff) {
1168 // Convert UTF-16 to UTF-8...
1169 for (i = 2; i < s->getLength() && i < 400; i += 2) {
1170 int ch = ((s->getChar(i) & 255) << 8) | (s->getChar(i + 1) & 255);
1171
1172 if (ch >= 0xd800 && ch <= 0xdbff) {
1173 // Multi-word UTF-16 char...
1174 i += 2;
1175 int lch = ((s->getChar(i) & 255) << 8) | (s->getChar(i + 1) & 255);
1176
1177 if (lch < 0xdc00 || lch >= 0xdfff) continue;
1178
1179 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
1180 }
1181
1182 if (ch < 0x80)
1183 {
1184 /*
1185 * Single byte ASCII...
1186 */
1187
1188 writePSChar(ch);
1189 }
1190 else if (ch < 0x800)
1191 {
1192 /*
1193 * Two-byte UTF-8...
1194 */
1195
1196 writePSChar(0xc0 | (ch >> 6));
1197 writePSChar(0x80 | (ch & 0x3f));
1198 }
1199 else if (ch < 0x10000)
1200 {
1201 /*
1202 * Three-byte UTF-8...
1203 */
1204
1205 writePSChar(0xe0 | (ch >> 12));
1206 writePSChar(0x80 | ((ch >> 6) & 0x3f));
1207 writePSChar(0x80 | (ch & 0x3f));
1208 }
1209 else
1210 {
1211 /*
1212 * Four-byte UTF-8...
1213 */
1214
1215 writePSChar(0xf0 | (ch >> 18));
1216 writePSChar(0x80 | ((ch >> 12) & 0x3f));
1217 writePSChar(0x80 | ((ch >> 6) & 0x3f));
1218 writePSChar(0x80 | (ch & 0x3f));
1219 }
1220 }
1221 } else {
1222 for (i = 0; i < s->getLength() && i < 200; ++i) {
1223 writePSChar(s->getChar(i));
1224 }
1225 }
1226 writePS("\n");
1227 }
1228 obj1.free();
1229 if (info.dictLookup("Title", &obj1)->isString()) {
1230 writePS("%%Title: ");
1231 s = obj1.getString();
1232 if ((s->getChar(0) & 0xff) == 0xfe &&
1233 (s->getChar(1) & 0xff) == 0xff) {
1234 // Convert UTF-16 to UTF-8...
1235 for (i = 2; i < s->getLength() && i < 400; i += 2) {
1236 int ch = ((s->getChar(i) & 255) << 8) | (s->getChar(i + 1) & 255);
1237
1238 if (ch >= 0xd800 && ch <= 0xdbff) {
1239 // Multi-word UTF-16 char...
1240 i += 2;
1241 int lch = ((s->getChar(i) & 255) << 8) | (s->getChar(i + 1) & 255);
1242
1243 if (lch < 0xdc00 || lch >= 0xdfff) continue;
1244
1245 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
1246 }
1247
1248 if (ch < 0x80)
1249 {
1250 /*
1251 * Single byte ASCII...
1252 */
1253
1254 writePSChar(ch);
1255 }
1256 else if (ch < 0x800)
1257 {
1258 /*
1259 * Two-byte UTF-8...
1260 */
1261
1262 writePSChar(0xc0 | (ch >> 6));
1263 writePSChar(0x80 | (ch & 0x3f));
1264 }
1265 else if (ch < 0x10000)
1266 {
1267 /*
1268 * Three-byte UTF-8...
1269 */
1270
1271 writePSChar(0xe0 | (ch >> 12));
1272 writePSChar(0x80 | ((ch >> 6) & 0x3f));
1273 writePSChar(0x80 | (ch & 0x3f));
1274 }
1275 else
1276 {
1277 /*
1278 * Four-byte UTF-8...
1279 */
1280
1281 writePSChar(0xf0 | (ch >> 18));
1282 writePSChar(0x80 | ((ch >> 12) & 0x3f));
1283 writePSChar(0x80 | ((ch >> 6) & 0x3f));
1284 writePSChar(0x80 | (ch & 0x3f));
1285 }
1286 }
1287 } else {
1288 for (i = 0; i < s->getLength() && i < 200; ++i) {
1289 writePSChar(s->getChar(i));
1290 }
1291 }
1292 writePS("\n");
1293 }
1294 obj1.free();
1295 info.free();
1296 writePSFmt("%%%%LanguageLevel: %d\n",
1297 (level == psLevel1 || level == psLevel1Sep) ? 1 :
1298 (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
1299 if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
1300 writePS("%%DocumentProcessColors: (atend)\n");
1301 writePS("%%DocumentCustomColors: (atend)\n");
1302 }
1303 writePS("%%DocumentSuppliedResources: (atend)\n");
1304
1305 switch (mode) {
1306 case psModePS:
1307 writePSFmt("%%%%DocumentMedia: plain %d %d 0 () ()\n",
1308 paperWidth, paperHeight);
1309 writePSFmt("%%%%BoundingBox: 0 0 %d %d\n", paperWidth, paperHeight);
1310 writePSFmt("%%%%Pages: %d\n", lastPage - firstPage + 1);
1311 writePS("%%EndComments\n");
1312 writePS("%%BeginDefaults\n");
1313 writePS("%%PageMedia: plain\n");
1314 writePS("%%EndDefaults\n");
1315 break;
1316 case psModeEPS:
1317 epsX1 = cropBox->x1;
1318 epsY1 = cropBox->y1;
1319 epsX2 = cropBox->x2;
1320 epsY2 = cropBox->y2;
1321 if (pageRotate == 0 || pageRotate == 180) {
1322 x1 = epsX1;
1323 y1 = epsY1;
1324 x2 = epsX2;
1325 y2 = epsY2;
1326 } else { // pageRotate == 90 || pageRotate == 270
1327 x1 = 0;
1328 y1 = 0;
1329 x2 = epsY2 - epsY1;
1330 y2 = epsX2 - epsX1;
1331 }
1332 writePSFmt("%%%%BoundingBox: %d %d %d %d\n",
1333 (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2));
1334 if (floor(x1) != ceil(x1) || floor(y1) != ceil(y1) ||
1335 floor(x2) != ceil(x2) || floor(y2) != ceil(y2)) {
1336 writePSFmt("%%%%HiResBoundingBox: %g %g %g %g\n", x1, y1, x2, y2);
1337 }
1338 writePS("%%EndComments\n");
1339 break;
1340 case psModeForm:
1341 writePS("%%EndComments\n");
1342 writePS("32 dict dup begin\n");
1343 writePSFmt("/BBox [%d %d %d %d] def\n",
1344 (int)floor(mediaBox->x1), (int)floor(mediaBox->y1),
1345 (int)ceil(mediaBox->x2), (int)ceil(mediaBox->y2));
1346 writePS("/FormType 1 def\n");
1347 writePS("/Matrix [1 0 0 1 0 0] def\n");
1348 break;
1349 }
1350 }
1351
1352 void PSOutputDev::writeXpdfProcset() {
1353 GBool lev1, lev2, lev3, sep, nonSep;
1354 char **p;
1355 char *q;
1356
1357 writePSFmt("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion);
1358 lev1 = lev2 = lev3 = sep = nonSep = gTrue;
1359 for (p = prolog; *p; ++p) {
1360 if ((*p)[0] == '~') {
1361 lev1 = lev2 = lev3 = sep = nonSep = gFalse;
1362 for (q = *p + 1; *q; ++q) {
1363 switch (*q) {
1364 case '1': lev1 = gTrue; break;
1365 case '2': lev2 = gTrue; break;
1366 case '3': lev3 = gTrue; break;
1367 case 's': sep = gTrue; break;
1368 case 'n': nonSep = gTrue; break;
1369 }
1370 }
1371 } else if ((level == psLevel1 && lev1 && nonSep) ||
1372 (level == psLevel1Sep && lev1 && sep) ||
1373 (level == psLevel2 && lev2 && nonSep) ||
1374 (level == psLevel2Sep && lev2 && sep) ||
1375 (level == psLevel3 && lev3 && nonSep) ||
1376 (level == psLevel3Sep && lev3 && sep)) {
1377 writePSFmt("%s\n", *p);
1378 }
1379 }
1380 writePS("%%EndResource\n");
1381
1382 if (level >= psLevel3) {
1383 for (p = cmapProlog; *p; ++p) {
1384 writePSFmt("%s\n", *p);
1385 }
1386 }
1387 }
1388
1389 void PSOutputDev::writeDocSetup(Catalog *catalog,
1390 int firstPage, int lastPage) {
1391 Page *page;
1392 Dict *resDict;
1393 Annots *annots;
1394 Object obj1, obj2;
1395 int pg, i;
1396
1397 if (mode == psModeForm) {
1398 // swap the form and xpdf dicts
1399 writePS("xpdf end begin dup begin\n");
1400 } else {
1401 writePS("xpdf begin\n");
1402 }
1403 for (pg = firstPage; pg <= lastPage; ++pg) {
1404 page = catalog->getPage(pg);
1405 if ((resDict = page->getResourceDict())) {
1406 setupResources(resDict);
1407 }
1408 annots = new Annots(xref, catalog, page->getAnnots(&obj1));
1409 obj1.free();
1410 for (i = 0; i < annots->getNumAnnots(); ++i) {
1411 if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
1412 obj1.streamGetDict()->lookup("Resources", &obj2);
1413 if (obj2.isDict()) {
1414 setupResources(obj2.getDict());
1415 }
1416 obj2.free();
1417 }
1418 obj1.free();
1419 }
1420 delete annots;
1421 }
1422 if (mode != psModeForm) {
1423 if (mode != psModeEPS && !manualCtrl) {
1424 writePSFmt("%d %d %s pdfSetup\n",
1425 paperWidth, paperHeight,
1426 globalParams->getPSDuplex() ? "true" : "false");
1427 }
1428 #if OPI_SUPPORT
1429 if (globalParams->getPSOPI()) {
1430 writePS("/opiMatrix matrix currentmatrix def\n");
1431 }
1432 #endif
1433 }
1434 }
1435
1436 void PSOutputDev::writePageTrailer() {
1437 if (mode != psModeForm) {
1438 writePS("pdfEndPage\n");
1439 }
1440 }
1441
1442 void PSOutputDev::writeTrailer() {
1443 PSOutCustomColor *cc;
1444
1445 if (mode == psModeForm) {
1446 writePS("/Foo exch /Form defineresource pop\n");
1447 } else {
1448 writePS("end\n");
1449 writePS("%%DocumentSuppliedResources:\n");
1450 writePS(embFontList->getCString());
1451 if (level == psLevel1Sep || level == psLevel2Sep ||
1452 level == psLevel3Sep) {
1453 writePS("%%DocumentProcessColors:");
1454 if (processColors & psProcessCyan) {
1455 writePS(" Cyan");
1456 }
1457 if (processColors & psProcessMagenta) {
1458 writePS(" Magenta");
1459 }
1460 if (processColors & psProcessYellow) {
1461 writePS(" Yellow");
1462 }
1463 if (processColors & psProcessBlack) {
1464 writePS(" Black");
1465 }
1466 writePS("\n");
1467 writePS("%%DocumentCustomColors:");
1468 for (cc = customColors; cc; cc = cc->next) {
1469 writePSFmt(" (%s)", cc->name->getCString());
1470 }
1471 writePS("\n");
1472 writePS("%%CMYKCustomColor:\n");
1473 for (cc = customColors; cc; cc = cc->next) {
1474 writePSFmt("%%%%+ %g %g %g %g (%s)\n",
1475 cc->c, cc->m, cc->y, cc->k, cc->name->getCString());
1476 }
1477 }
1478 }
1479 }
1480
1481 void PSOutputDev::setupResources(Dict *resDict) {
1482 Object xObjDict, xObjRef, xObj, patDict, patRef, pat, resObj;
1483 Ref ref0, ref1;
1484 GBool skip;
1485 int i, j;
1486
1487 setupFonts(resDict);
1488 setupImages(resDict);
1489
1490 //----- recursively scan XObjects
1491 resDict->lookup("XObject", &xObjDict);
1492 if (xObjDict.isDict()) {
1493 for (i = 0; i < xObjDict.dictGetLength(); ++i) {
1494
1495 // avoid infinite recursion on XObjects
1496 skip = gFalse;
1497 if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) {
1498 ref0 = xObjRef.getRef();
1499 for (j = 0; j < xobjStack->getLength(); ++j) {
1500 ref1 = *(Ref *)xobjStack->get(j);
1501 if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
1502 skip = gTrue;
1503 break;
1504 }
1505 }
1506 if (!skip) {
1507 xobjStack->append(&ref0);
1508 }
1509 }
1510 if (!skip) {
1511
1512 // process the XObject's resource dictionary
1513 xObjDict.dictGetVal(i, &xObj);
1514 if (xObj.isStream()) {
1515 xObj.streamGetDict()->lookup("Resources", &resObj);
1516 if (resObj.isDict()) {
1517 setupResources(resObj.getDict());
1518 }
1519 resObj.free();
1520 }
1521 xObj.free();
1522 }
1523
1524 if (xObjRef.isRef() && !skip) {
1525 xobjStack->del(xobjStack->getLength() - 1);
1526 }
1527 xObjRef.free();
1528 }
1529 }
1530 xObjDict.free();
1531
1532 //----- recursively scan Patterns
1533 resDict->lookup("Pattern", &patDict);
1534 if (patDict.isDict()) {
1535 inType3Char = gTrue;
1536 for (i = 0; i < patDict.dictGetLength(); ++i) {
1537
1538 // avoid infinite recursion on Patterns
1539 skip = gFalse;
1540 if ((patDict.dictGetValNF(i, &patRef)->isRef())) {
1541 ref0 = patRef.getRef();
1542 for (j = 0; j < xobjStack->getLength(); ++j) {
1543 ref1 = *(Ref *)xobjStack->get(j);
1544 if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
1545 skip = gTrue;
1546 break;
1547 }
1548 }
1549 if (!skip) {
1550 xobjStack->append(&ref0);
1551 }
1552 }
1553 if (!skip) {
1554
1555 // process the Pattern's resource dictionary
1556 patDict.dictGetVal(i, &pat);
1557 if (pat.isStream()) {
1558 pat.streamGetDict()->lookup("Resources", &resObj);
1559 if (resObj.isDict()) {
1560 setupResources(resObj.getDict());
1561 }
1562 resObj.free();
1563 }
1564 pat.free();
1565 }
1566
1567 if (patRef.isRef() && !skip) {
1568 xobjStack->del(xobjStack->getLength() - 1);
1569 }
1570 patRef.free();
1571 }
1572 inType3Char = gFalse;
1573 }
1574 patDict.free();
1575 }
1576
1577 void PSOutputDev::setupFonts(Dict *resDict) {
1578 Object obj1, obj2;
1579 Ref r;
1580 GfxFontDict *gfxFontDict;
1581 GfxFont *font;
1582 int i;
1583
1584 gfxFontDict = NULL;
1585 resDict->lookupNF("Font", &obj1);
1586 if (obj1.isRef()) {
1587 obj1.fetch(xref, &obj2);
1588 if (obj2.isDict()) {
1589 r = obj1.getRef();
1590 gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict());
1591 }
1592 obj2.free();
1593 } else if (obj1.isDict()) {
1594 gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict());
1595 }
1596 if (gfxFontDict) {
1597 for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
1598 if ((font = gfxFontDict->getFont(i))) {
1599 setupFont(font, resDict);
1600 }
1601 }
1602 delete gfxFontDict;
1603 }
1604 obj1.free();
1605 }
1606
1607 void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
1608 Ref fontFileID;
1609 GString *name;
1610 PSFontParam *fontParam;
1611 GString *psName;
1612 char type3Name[64], buf[16];
1613 GBool subst;
1614 UnicodeMap *uMap;
1615 char *charName;
1616 double xs, ys;
1617 int code;
1618 double w1, w2;
1619 double *fm;
1620 int i, j;
1621
1622 // check if font is already set up
1623 for (i = 0; i < fontIDLen; ++i) {
1624 if (fontIDs[i].num == font->getID()->num &&
1625 fontIDs[i].gen == font->getID()->gen) {
1626 return;
1627 }
1628 }
1629
1630 // add entry to fontIDs list
1631 if (fontIDLen >= fontIDSize) {
1632 fontIDSize += 64;
1633 fontIDs = (Ref *)greallocn(fontIDs, fontIDSize, sizeof(Ref));
1634 }
1635 fontIDs[fontIDLen++] = *font->getID();
1636
1637 xs = ys = 1;
1638 subst = gFalse;
1639
1640 // check for resident 8-bit font
1641 if (font->getName() &&
1642 (fontParam = globalParams->getPSFont(font->getName()))) {
1643 psName = new GString(fontParam->psFontName->getCString());
1644
1645 // check for embedded Type 1 font
1646 } else if (globalParams->getPSEmbedType1() &&
1647 font->getType() == fontType1 &&
1648 font->getEmbeddedFontID(&fontFileID)) {
1649 psName = filterPSName(font->getEmbeddedFontName());
1650 setupEmbeddedType1Font(&fontFileID, psName);
1651
1652 // check for embedded Type 1C font
1653 } else if (globalParams->getPSEmbedType1() &&
1654 font->getType() == fontType1C &&
1655 font->getEmbeddedFontID(&fontFileID)) {
1656 psName = filterPSName(font->getEmbeddedFontName());
1657 setupEmbeddedType1CFont(font, &fontFileID, psName);
1658
1659 // check for external Type 1 font file
1660 } else if (globalParams->getPSEmbedType1() &&
1661 font->getType() == fontType1 &&
1662 font->getExtFontFile()) {
1663 // this assumes that the PS font name matches the PDF font name
1664 psName = font->getName()->copy();
1665 setupExternalType1Font(font->getExtFontFile(), psName);
1666
1667 // check for embedded TrueType font
1668 } else if (globalParams->getPSEmbedTrueType() &&
1669 font->getType() == fontTrueType &&
1670 font->getEmbeddedFontID(&fontFileID)) {
1671 psName = filterPSName(font->getEmbeddedFontName());
1672 setupEmbeddedTrueTypeFont(font, &fontFileID, psName);
1673
1674 // check for external TrueType font file
1675 } else if (globalParams->getPSEmbedTrueType() &&
1676 font->getType() == fontTrueType &&
1677 font->getExtFontFile()) {
1678 psName = filterPSName(font->getName());
1679 setupExternalTrueTypeFont(font, psName);
1680
1681 // check for embedded CID PostScript font
1682 } else if (globalParams->getPSEmbedCIDPostScript() &&
1683 font->getType() == fontCIDType0C &&
1684 font->getEmbeddedFontID(&fontFileID)) {
1685 psName = filterPSName(font->getEmbeddedFontName());
1686 setupEmbeddedCIDType0Font(font, &fontFileID, psName);
1687
1688 // check for embedded CID TrueType font
1689 } else if (globalParams->getPSEmbedCIDTrueType() &&
1690 font->getType() == fontCIDType2 &&
1691 font->getEmbeddedFontID(&fontFileID)) {
1692 psName = filterPSName(font->getEmbeddedFontName());
1693 //~ should check to see if font actually uses vertical mode
1694 setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName, gTrue);
1695
1696 } else if (font->getType() == fontType3) {
1697 sprintf(type3Name, "T3_%d_%d",
1698 font->getID()->num, font->getID()->gen);
1699 psName = new GString(type3Name);
1700 setupType3Font(font, psName, parentResDict);
1701
1702 // do 8-bit font substitution
1703 } else if (!font->isCIDFont()) {
1704 subst = gTrue;
1705 name = font->getName();
1706 psName = NULL;
1707 if (name) {
1708 for (i = 0; psFonts[i]; ++i) {
1709 if (name->cmp(psFonts[i]) == 0) {
1710 psName = new GString(psFonts[i]);
1711 break;
1712 }
1713 }
1714 }
1715 if (!psName) {
1716 if (font->isFixedWidth()) {
1717 i = 8;
1718 } else if (font->isSerif()) {
1719 i = 4;
1720 } else {
1721 i = 0;
1722 }
1723 if (font->isBold()) {
1724 i += 2;
1725 }
1726 if (font->isItalic()) {
1727 i += 1;
1728 }
1729 psName = new GString(psSubstFonts[i].psName);
1730 for (code = 0; code < 256; ++code) {
1731 if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) &&
1732 charName[0] == 'm' && charName[1] == '\0') {
1733 break;
1734 }
1735 }
1736 if (code < 256) {
1737 w1 = ((Gfx8BitFont *)font)->getWidth(code);
1738 } else {
1739 w1 = 0;
1740 }
1741 w2 = psSubstFonts[i].mWidth;
1742 xs = w1 / w2;
1743 if (xs < 0.1) {
1744 xs = 1;
1745 }
1746 if (font->getType() == fontType3) {
1747 // This is a hack which makes it possible to substitute for some
1748 // Type 3 fonts. The problem is that it's impossible to know what
1749 // the base coordinate system used in the font is without actually
1750 // rendering the font.
1751 ys = xs;
1752 fm = font->getFontMatrix();
1753 if (fm[0] != 0) {
1754 ys *= fm[3] / fm[0];
1755 }
1756 } else {
1757 ys = 1;
1758 }
1759 }
1760
1761 // do 16-bit font substitution
1762 } else if ((fontParam = globalParams->
1763 getPSFont16(font->getName(),
1764 ((GfxCIDFont *)font)->getCollection(),
1765 font->getWMode()))) {
1766 subst = gTrue;
1767 psName = fontParam->psFontName->copy();
1768 if (font16EncLen >= font16EncSize) {
1769 font16EncSize += 16;
1770 font16Enc = (PSFont16Enc *)greallocn(font16Enc,
1771 font16EncSize, sizeof(PSFont16Enc));
1772 }
1773 font16Enc[font16EncLen].fontID = *font->getID();
1774 font16Enc[font16EncLen].enc = fontParam->encoding->copy();
1775 if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) {
1776 uMap->decRefCnt();
1777 ++font16EncLen;
1778 } else {
1779 error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'",
1780 font16Enc[font16EncLen].enc->getCString());
1781 }
1782
1783 // give up - can't do anything with this font
1784 } else {
1785 error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)",
1786 font->getName() ? font->getName()->getCString() : "(unnamed)",
1787 ((GfxCIDFont *)font)->getCollection()
1788 ? ((GfxCIDFont *)font)->getCollection()->getCString()
1789 : "(unknown)");
1790 return;
1791 }
1792
1793 // generate PostScript code to set up the font
1794 if (font->isCIDFont()) {
1795 if (level == psLevel3 || level == psLevel3Sep) {
1796 writePSFmt("/F%d_%d /%s %d pdfMakeFont16L3\n",
1797 font->getID()->num, font->getID()->gen, psName->getCString(),
1798 font->getWMode());
1799 } else {
1800 writePSFmt("/F%d_%d /%s %d pdfMakeFont16\n",
1801 font->getID()->num, font->getID()->gen, psName->getCString(),
1802 font->getWMode());
1803 }
1804 } else {
1805 writePSFmt("/F%d_%d /%s %g %g\n",
1806 font->getID()->num, font->getID()->gen, psName->getCString(),
1807 xs, ys);
1808 for (i = 0; i < 256; i += 8) {
1809 writePSFmt((i == 0) ? "[ " : " ");
1810 for (j = 0; j < 8; ++j) {
1811 if (font->getType() == fontTrueType &&
1812 !subst &&
1813 !((Gfx8BitFont *)font)->getHasEncoding()) {
1814 sprintf(buf, "c%02x", i+j);
1815 charName = buf;
1816 } else {
1817 charName = ((Gfx8BitFont *)font)->getCharName(i+j);
1818 // this is a kludge for broken PDF files that encode char 32
1819 // as .notdef
1820 if (i+j == 32 && charName && !strcmp(charName, ".notdef")) {
1821 charName = "space";
1822 }
1823 }
1824 writePS("/");
1825 writePSName(charName ? charName : (char *)".notdef");
1826 // the empty name is legal in PDF and PostScript, but PostScript
1827 // uses a double-slash (//...) for "immediately evaluated names",
1828 // so we need to add a space character here
1829 if (charName && !charName[0]) {
1830 writePS(" ");
1831 }
1832 }
1833 writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n");
1834 }
1835 writePS("pdfMakeFont\n");
1836 }
1837
1838 delete psName;
1839 }
1840
1841 void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
1842 static char hexChar[17] = "0123456789abcdef";
1843 Object refObj, strObj, obj1, obj2, obj3;
1844 Dict *dict;
1845 int length1, length2, length3;
1846 int c;
1847 int start[4];
1848 GBool binMode;
1849 int i;
1850
1851 // check if font is already embedded
1852 for (i = 0; i < fontFileIDLen; ++i) {
1853 if (fontFileIDs[i].num == id->num &&
1854 fontFileIDs[i].gen == id->gen)
1855 return;
1856 }
1857
1858 // add entry to fontFileIDs list
1859 if (fontFileIDLen >= fontFileIDSize) {
1860 fontFileIDSize += 64;
1861 fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
1862 }
1863 fontFileIDs[fontFileIDLen++] = *id;
1864
1865 // get the font stream and info
1866 refObj.initRef(id->num, id->gen);
1867 refObj.fetch(xref, &strObj);
1868 refObj.free();
1869 if (!strObj.isStream()) {
1870 error(-1, "Embedded font file object is not a stream");
1871 goto err1;
1872 }
1873 if (!(dict = strObj.streamGetDict())) {
1874 error(-1, "Embedded font stream is missing its dictionary");
1875 goto err1;
1876 }
1877 dict->lookup("Length1", &obj1);
1878 dict->lookup("Length2", &obj2);
1879 dict->lookup("Length3", &obj3);
1880 if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {
1881 error(-1, "Missing length fields in embedded font stream dictionary");
1882 obj1.free();
1883 obj2.free();
1884 obj3.free();
1885 goto err1;
1886 }
1887 length1 = obj1.getInt();
1888 length2 = obj2.getInt();
1889 length3 = obj3.getInt();
1890 obj1.free();
1891 obj2.free();
1892 obj3.free();
1893
1894 // beginning comment
1895 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1896 embFontList->append("%%+ font ");
1897 embFontList->append(psName->getCString());
1898 embFontList->append("\n");
1899
1900 // copy ASCII portion of font
1901 strObj.streamReset();
1902 for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
1903 writePSChar(c);
1904 }
1905
1906 // figure out if encrypted portion is binary or ASCII
1907 binMode = gFalse;
1908 for (i = 0; i < 4; ++i) {
1909 start[i] = strObj.streamGetChar();
1910 if (start[i] == EOF) {
1911 error(-1, "Unexpected end of file in embedded font stream");
1912 goto err1;
1913 }
1914 if (!((start[i] >= '0' && start[i] <= '9') ||
1915 (start[i] >= 'A' && start[i] <= 'F') ||
1916 (start[i] >= 'a' && start[i] <= 'f')))
1917 binMode = gTrue;
1918 }
1919
1920 // convert binary data to ASCII
1921 if (binMode) {
1922 for (i = 0; i < 4; ++i) {
1923 writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
1924 writePSChar(hexChar[start[i] & 0x0f]);
1925 }
1926 // if Length2 is incorrect (too small), font data gets chopped, so
1927 // we take a few extra characters from the trailer just in case
1928 // length2 += length3 >= 8 ? 8 : length3;
1929 while (i < length2) {
1930 if ((c = strObj.streamGetChar()) == EOF) {
1931 break;
1932 }
1933 writePSChar(hexChar[(c >> 4) & 0x0f]);
1934 writePSChar(hexChar[c & 0x0f]);
1935 if (++i % 32 == 0) {
1936 writePSChar('\n');
1937 }
1938 }
1939 if (i % 32 > 0) {
1940 writePSChar('\n');
1941 }
1942
1943 // already in ASCII format -- just copy it
1944 } else {
1945 for (i = 0; i < 4; ++i) {
1946 writePSChar(start[i]);
1947 }
1948 for (i = 4; i < length2; ++i) {
1949 if ((c = strObj.streamGetChar()) == EOF) {
1950 break;
1951 }
1952 writePSChar(c);
1953 }
1954 }
1955
1956 // write padding and "cleartomark"
1957 for (i = 0; i < 8; ++i) {
1958 writePS("00000000000000000000000000000000"
1959 "00000000000000000000000000000000\n");
1960 }
1961 writePS("cleartomark\n");
1962
1963 // ending comment
1964 writePS("%%EndResource\n");
1965
1966 err1:
1967 strObj.streamClose();
1968 strObj.free();
1969 }
1970
1971 //~ This doesn't handle .pfb files or binary eexec data (which only
1972 //~ happens in pfb files?).
1973 void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) {
1974 FILE *fontFile;
1975 int c;
1976 int i;
1977
1978 // check if font is already embedded
1979 for (i = 0; i < fontFileNameLen; ++i) {
1980 if (!fontFileNames[i]->cmp(fileName)) {
1981 return;
1982 }
1983 }
1984
1985 // add entry to fontFileNames list
1986 if (fontFileNameLen >= fontFileNameSize) {
1987 fontFileNameSize += 64;
1988 fontFileNames = (GString **)greallocn(fontFileNames,
1989 fontFileNameSize, sizeof(GString *));
1990 }
1991 fontFileNames[fontFileNameLen++] = fileName->copy();
1992
1993 // beginning comment
1994 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1995 embFontList->append("%%+ font ");
1996 embFontList->append(psName->getCString());
1997 embFontList->append("\n");
1998
1999 // copy the font file
2000 if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
2001 error(-1, "Couldn't open external font file");
2002 return;
2003 }
2004 while ((c = fgetc(fontFile)) != EOF) {
2005 writePSChar(c);
2006 }
2007 fclose(fontFile);
2008
2009 // ending comment
2010 writePS("%%EndResource\n");
2011 }
2012
2013 void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
2014 GString *psName) {
2015 char *fontBuf;
2016 int fontLen;
2017 FoFiType1C *ffT1C;
2018 int i;
2019
2020 // check if font is already embedded
2021 for (i = 0; i < fontFileIDLen; ++i) {
2022 if (fontFileIDs[i].num == id->num &&
2023 fontFileIDs[i].gen == id->gen)
2024 return;
2025 }
2026
2027 // add entry to fontFileIDs list
2028 if (fontFileIDLen >= fontFileIDSize) {
2029 fontFileIDSize += 64;
2030 fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
2031 }
2032 fontFileIDs[fontFileIDLen++] = *id;
2033
2034 // beginning comment
2035 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2036 embFontList->append("%%+ font ");
2037 embFontList->append(psName->getCString());
2038 embFontList->append("\n");
2039
2040 // convert it to a Type 1 font
2041 fontBuf = font->readEmbFontFile(xref, &fontLen);
2042 if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
2043 ffT1C->convertToType1(NULL, gTrue, outputFunc, outputStream);
2044 delete ffT1C;
2045 }
2046 gfree(fontBuf);
2047
2048 // ending comment
2049 writePS("%%EndResource\n");
2050 }
2051
2052 void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
2053 GString *psName) {
2054 char unique[32];
2055 char *fontBuf;
2056 int fontLen;
2057 FoFiTrueType *ffTT;
2058 Gushort *codeToGID;
2059 int i;
2060
2061 // check if font is already embedded
2062 for (i = 0; i < fontFileIDLen; ++i) {
2063 if (fontFileIDs[i].num == id->num &&
2064 fontFileIDs[i].gen == id->gen) {
2065 sprintf(unique, "_%d", nextTrueTypeNum++);
2066 psName->append(unique);
2067 break;
2068 }
2069 }
2070
2071 // add entry to fontFileIDs list
2072 if (i == fontFileIDLen) {
2073 if (fontFileIDLen >= fontFileIDSize) {
2074 fontFileIDSize += 64;
2075 fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
2076 }
2077 fontFileIDs[fontFileIDLen++] = *id;
2078 }
2079
2080 // beginning comment
2081 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2082 embFontList->append("%%+ font ");
2083 embFontList->append(psName->getCString());
2084 embFontList->append("\n");
2085
2086 // convert it to a Type 42 font
2087 fontBuf = font->readEmbFontFile(xref, &fontLen);
2088 if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2089 codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
2090 ffTT->convertToType42(psName->getCString(),
2091 ((Gfx8BitFont *)font)->getHasEncoding()
2092 ? ((Gfx8BitFont *)font)->getEncoding()
2093 : (char **)NULL,
2094 codeToGID, outputFunc, outputStream);
2095 gfree(codeToGID);
2096 delete ffTT;
2097 }
2098 gfree(fontBuf);
2099
2100 // ending comment
2101 writePS("%%EndResource\n");
2102 }
2103
2104 void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *psName) {
2105 char unique[32];
2106 GString *fileName;
2107 char *fontBuf;
2108 int fontLen;
2109 FoFiTrueType *ffTT;
2110 Gushort *codeToGID;
2111 int i;
2112
2113 // check if font is already embedded
2114 fileName = font->getExtFontFile();
2115 for (i = 0; i < fontFileNameLen; ++i) {
2116 if (!fontFileNames[i]->cmp(fileName)) {
2117 sprintf(unique, "_%d", nextTrueTypeNum++);
2118 psName->append(unique);
2119 break;
2120 }
2121 }
2122
2123 // add entry to fontFileNames list
2124 if (i == fontFileNameLen) {
2125 if (fontFileNameLen >= fontFileNameSize) {
2126 fontFileNameSize += 64;
2127 fontFileNames =
2128 (GString **)greallocn(fontFileNames,
2129 fontFileNameSize, sizeof(GString *));
2130 }
2131 fontFileNames[fontFileNameLen++] = fileName->copy();
2132 }
2133
2134 // beginning comment
2135 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2136 embFontList->append("%%+ font ");
2137 embFontList->append(psName->getCString());
2138 embFontList->append("\n");
2139
2140 // convert it to a Type 42 font
2141 fontBuf = font->readExtFontFile(&fontLen);
2142 if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2143 codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
2144 ffTT->convertToType42(psName->getCString(),
2145 ((Gfx8BitFont *)font)->getHasEncoding()
2146 ? ((Gfx8BitFont *)font)->getEncoding()
2147 : (char **)NULL,
2148 codeToGID, outputFunc, outputStream);
2149 delete ffTT;
2150 }
2151 gfree(fontBuf);
2152
2153 // ending comment
2154 writePS("%%EndResource\n");
2155 }
2156
2157 void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
2158 GString *psName) {
2159 char *fontBuf;
2160 int fontLen;
2161 FoFiType1C *ffT1C;
2162 int i;
2163
2164 // check if font is already embedded
2165 for (i = 0; i < fontFileIDLen; ++i) {
2166 if (fontFileIDs[i].num == id->num &&
2167 fontFileIDs[i].gen == id->gen)
2168 return;
2169 }
2170
2171 // add entry to fontFileIDs list
2172 if (fontFileIDLen >= fontFileIDSize) {
2173 fontFileIDSize += 64;
2174 fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
2175 }
2176 fontFileIDs[fontFileIDLen++] = *id;
2177
2178 // beginning comment
2179 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2180 embFontList->append("%%+ font ");
2181 embFontList->append(psName->getCString());
2182 embFontList->append("\n");
2183
2184 // convert it to a Type 0 font
2185 fontBuf = font->readEmbFontFile(xref, &fontLen);
2186 if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
2187 if (globalParams->getPSLevel() >= psLevel3) {
2188 // Level 3: use a CID font
2189 ffT1C->convertToCIDType0(psName->getCString(), outputFunc, outputStream);
2190 } else {
2191 // otherwise: use a non-CID composite font
2192 ffT1C->convertToType0(psName->getCString(), outputFunc, outputStream);
2193 }
2194 delete ffT1C;
2195 }
2196 gfree(fontBuf);
2197
2198 // ending comment
2199 writePS("%%EndResource\n");
2200 }
2201
2202 void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
2203 GString *psName,
2204 GBool needVerticalMetrics) {
2205 char unique[32];
2206 char *fontBuf;
2207 int fontLen;
2208 FoFiTrueType *ffTT;
2209 int i;
2210
2211 // check if font is already embedded
2212 for (i = 0; i < fontFileIDLen; ++i) {
2213 if (fontFileIDs[i].num == id->num &&
2214 fontFileIDs[i].gen == id->gen) {
2215 sprintf(unique, "_%d", nextTrueTypeNum++);
2216 psName->append(unique);
2217 break;
2218 }
2219 }
2220
2221 // add entry to fontFileIDs list
2222 if (fontFileIDLen >= fontFileIDSize) {
2223 fontFileIDSize += 64;
2224 fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
2225 }
2226 fontFileIDs[fontFileIDLen++] = *id;
2227
2228 // beginning comment
2229 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2230 embFontList->append("%%+ font ");
2231 embFontList->append(psName->getCString());
2232 embFontList->append("\n");
2233
2234 // convert it to a Type 0 font
2235 fontBuf = font->readEmbFontFile(xref, &fontLen);
2236 if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2237 if (globalParams->getPSLevel() >= psLevel3) {
2238 // Level 3: use a CID font
2239 ffTT->convertToCIDType2(psName->getCString(),
2240 ((GfxCIDFont *)font)->getCIDToGID(),
2241 ((GfxCIDFont *)font)->getCIDToGIDLen(),
2242 needVerticalMetrics,
2243 outputFunc, outputStream);
2244 } else {
2245 // otherwise: use a non-CID composite font
2246 ffTT->convertToType0(psName->getCString(),
2247 ((GfxCIDFont *)font)->getCIDToGID(),
2248 ((GfxCIDFont *)font)->getCIDToGIDLen(),
2249 needVerticalMetrics,
2250 outputFunc, outputStream);
2251 }
2252 delete ffTT;
2253 }
2254 gfree(fontBuf);
2255
2256 // ending comment
2257 writePS("%%EndResource\n");
2258 }
2259
2260 void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
2261 Dict *parentResDict) {
2262 Dict *resDict;
2263 Dict *charProcs;
2264 Object charProc;
2265 Gfx *gfx;
2266 PDFRectangle box;
2267 double *m;
2268 char buf[256];
2269 int i;
2270
2271 // set up resources used by font
2272 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2273 inType3Char = gTrue;
2274 setupResources(resDict);
2275 inType3Char = gFalse;
2276 } else {
2277 resDict = parentResDict;
2278 }
2279
2280 // beginning comment
2281 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2282 embFontList->append("%%+ font ");
2283 embFontList->append(psName->getCString());
2284 embFontList->append("\n");
2285
2286 // font dictionary
2287 writePS("8 dict begin\n");
2288 writePS("/FontType 3 def\n");
2289 m = font->getFontMatrix();
2290 writePSFmt("/FontMatrix [%g %g %g %g %g %g] def\n",
2291 m[0], m[1], m[2], m[3], m[4], m[5]);
2292 m = font->getFontBBox();
2293 writePSFmt("/FontBBox [%g %g %g %g] def\n",
2294 m[0], m[1], m[2], m[3]);
2295 writePS("/Encoding 256 array def\n");
2296 writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
2297 writePS("/BuildGlyph {\n");
2298 writePS(" exch /CharProcs get exch\n");
2299 writePS(" 2 copy known not { pop /.notdef } if\n");
2300 writePS(" get exec\n");
2301 writePS("} bind def\n");
2302 writePS("/BuildChar {\n");
2303 writePS(" 1 index /Encoding get exch get\n");
2304 writePS(" 1 index /BuildGlyph get exec\n");
2305 writePS("} bind def\n");
2306 if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
2307 writePSFmt("/CharProcs %d dict def\n", charProcs->getLength());
2308 writePS("CharProcs begin\n");
2309 box.x1 = m[0];
2310 box.y1 = m[1];
2311 box.x2 = m[2];
2312 box.y2 = m[3];
2313 gfx = new Gfx(xref, this, resDict, &box, NULL);
2314 inType3Char = gTrue;
2315 t3Cacheable = gFalse;
2316 for (i = 0; i < charProcs->getLength(); ++i) {
2317 writePS("/");
2318 writePSName(charProcs->getKey(i));
2319 writePS(" {\n");
2320 gfx->display(charProcs->getVal(i, &charProc));
2321 charProc.free();
2322 if (t3String) {
2323 if (t3Cacheable) {
2324 sprintf(buf, "%g %g %g %g %g %g setcachedevice\n",
2325 t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
2326 } else {
2327 sprintf(buf, "%g %g setcharwidth\n", t3WX, t3WY);
2328 }
2329 (*outputFunc)(outputStream, buf, strlen(buf));
2330 (*outputFunc)(outputStream, t3String->getCString(),
2331 t3String->getLength());
2332 delete t3String;
2333 t3String = NULL;
2334 }
2335 (*outputFunc)(outputStream, "Q\n", 2);
2336 writePS("} def\n");
2337 }
2338 inType3Char = gFalse;
2339 delete gfx;
2340 writePS("end\n");
2341 }
2342 writePS("currentdict end\n");
2343 writePSFmt("/%s exch definefont pop\n", psName->getCString());
2344
2345 // ending comment
2346 writePS("%%EndResource\n");
2347 }
2348
2349 void PSOutputDev::setupImages(Dict *resDict) {
2350 Object xObjDict, xObj, xObjRef, subtypeObj;
2351 int i;
2352
2353 if (!(mode == psModeForm || inType3Char)) {
2354 return;
2355 }
2356
2357 resDict->lookup("XObject", &xObjDict);
2358 if (xObjDict.isDict()) {
2359 for (i = 0; i < xObjDict.dictGetLength(); ++i) {
2360 xObjDict.dictGetValNF(i, &xObjRef);
2361 xObjDict.dictGetVal(i, &xObj);
2362 if (xObj.isStream()) {
2363 xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
2364 if (subtypeObj.isName("Image")) {
2365 if (xObjRef.isRef()) {
2366 setupImage(xObjRef.getRef(), xObj.getStream());
2367 } else {
2368 error(-1, "Image in resource dict is not an indirect reference");
2369 }
2370 }
2371 subtypeObj.free();
2372 }
2373 xObj.free();
2374 xObjRef.free();
2375 }
2376 }
2377 xObjDict.free();
2378 }
2379
2380 void PSOutputDev::setupImage(Ref id, Stream *str) {
2381 GBool useASCIIHex;
2382 int c;
2383 int size, line, col, i;
2384
2385 // construct an encoder stream
2386 useASCIIHex = level == psLevel1 || level == psLevel1Sep ||
2387 globalParams->getPSASCIIHex();
2388 if (useASCIIHex) {
2389 str = new ASCIIHexEncoder(str);
2390 } else {
2391 str = new ASCII85Encoder(str);
2392 }
2393
2394 // compute image data size
2395 str->reset();
2396 col = size = 0;
2397 do {
2398 do {
2399 c = str->getChar();
2400 } while (c == '\n' || c == '\r');
2401 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
2402 break;
2403 }
2404 if (c == 'z') {
2405 ++col;
2406 } else {
2407 ++col;
2408 for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
2409 do {
2410 c = str->getChar();
2411 } while (c == '\n' || c == '\r');
2412 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
2413 break;
2414 }
2415 ++col;
2416 }
2417 }
2418 if (col > 225) {
2419 ++size;
2420 col = 0;
2421 }
2422 } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
2423 ++size;
2424 writePSFmt("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen);
2425 str->close();
2426
2427 // write the data into the array
2428 str->reset();
2429 line = col = 0;
2430 writePS((char *)(useASCIIHex ? "dup 0 <" : "dup 0 <~"));
2431 do {
2432 do {
2433 c = str->getChar();
2434 } while (c == '\n' || c == '\r');
2435 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
2436 break;
2437 }
2438 if (c == 'z') {
2439 writePSChar(c);
2440 ++col;
2441 } else {
2442 writePSChar(c);
2443 ++col;
2444 for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
2445 do {
2446 c = str->getChar();
2447 } while (c == '\n' || c == '\r');
2448 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
2449 break;
2450 }
2451 writePSChar(c);
2452 ++col;
2453 }
2454 }
2455 // each line is: "dup nnnnn <~...data...~> put<eol>"
2456 // so max data length = 255 - 20 = 235
2457 // chunks are 1 or 4 bytes each, so we have to stop at 232
2458 // but make it 225 just to be safe
2459 if (col > 225) {
2460 writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
2461 ++line;
2462 writePSFmt((char *)(useASCIIHex ? "dup %d <" : "dup %d <~"), line);
2463 col = 0;
2464 }
2465 } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
2466 writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
2467 writePS("pop\n");
2468 str->close();
2469
2470 delete str;
2471 }
2472
2473 GBool PSOutputDev::startPage(int pageNum, GfxState *state) {
2474 int x1, y1, x2, y2, width, height;
2475 int imgWidth, imgHeight, imgWidth2, imgHeight2;
2476 GBool landscape;
2477
2478
2479 if (mode == psModePS) {
2480 writePSFmt("%%%%Page: %d %d\n", pageNum, seqPage);
2481 writePS("%%BeginPageSetup\n");
2482 }
2483
2484 // underlays
2485 if (underlayCbk) {
2486 (*underlayCbk)(this, underlayCbkData);
2487 }
2488 if (overlayCbk) {
2489 saveState(NULL);
2490 }
2491
2492 switch (mode) {
2493
2494 case psModePS:
2495 // rotate, translate, and scale page
2496 imgWidth = imgURX - imgLLX;
2497 imgHeight = imgURY - imgLLY;
2498 x1 = (int)floor(state->getX1());
2499 y1 = (int)floor(state->getY1());
2500 x2 = (int)ceil(state->getX2());
2501 y2 = (int)ceil(state->getY2());
2502 width = x2 - x1;
2503 height = y2 - y1;
2504 tx = ty = 0;
2505 // rotation and portrait/landscape mode
2506 if (rotate0 >= 0) {
2507 rotate = (360 - rotate0) % 360;
2508 landscape = gFalse;
2509 } else {
2510 rotate = (360 - state->getRotate()) % 360;
2511 if (rotate == 0 || rotate == 180) {
2512 if (width > height && width > imgWidth) {
2513 rotate += 90;
2514 landscape = gTrue;
2515 } else {
2516 landscape = gFalse;
2517 }
2518 } else { // rotate == 90 || rotate == 270
2519 if (height > width && height > imgWidth) {
2520 rotate = 270 - rotate;
2521 landscape = gTrue;
2522 } else {
2523 landscape = gFalse;
2524 }
2525 }
2526 }
2527 writePSFmt("%%%%PageOrientation: %s\n",
2528 landscape ? "Landscape" : "Portrait");
2529 writePS("pdfStartPage\n");
2530 if (rotate == 0) {
2531 imgWidth2 = imgWidth;
2532 imgHeight2 = imgHeight;
2533 } else if (rotate == 90) {
2534 writePS("90 rotate\n");
2535 ty = -imgWidth;
2536 imgWidth2 = imgHeight;
2537 imgHeight2 = imgWidth;
2538 } else if (rotate == 180) {
2539 writePS("180 rotate\n");
2540 imgWidth2 = imgWidth;
2541 imgHeight2 = imgHeight;
2542 tx = -imgWidth;
2543 ty = -imgHeight;
2544 } else { // rotate == 270
2545 writePS("270 rotate\n");
2546 tx = -imgHeight;
2547 imgWidth2 = imgHeight;
2548 imgHeight2 = imgWidth;
2549 }
2550 // shrink or expand
2551 if (xScale0 > 0 && yScale0 > 0) {
2552 xScale = xScale0;
2553 yScale = yScale0;
2554 } else if ((globalParams->getPSShrinkLarger() &&
2555 (width > imgWidth2 || height > imgHeight2)) ||
2556 (globalParams->getPSExpandSmaller() &&
2557 (width < imgWidth2 && height < imgHeight2))) {
2558 xScale = (double)imgWidth2 / (double)width;
2559 yScale = (double)imgHeight2 / (double)height;
2560 if (yScale < xScale) {
2561 xScale = yScale;
2562 } else {
2563 yScale = xScale;
2564 }
2565 } else {
2566 xScale = yScale = 1;
2567 paperWidth = width;
2568 paperHeight = height;
2569 }
2570
2571 // deal with odd bounding boxes or clipping
2572 if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
2573 tx -= xScale * clipLLX0;
2574 ty -= yScale * clipLLY0;
2575 } else {
2576 tx -= xScale * x1;
2577 ty -= yScale * y1;
2578 }
2579 // center
2580 if (globalParams->getPSCenter()) {
2581 if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
2582 tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2;
2583 ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2;
2584 } else {
2585 tx += (imgWidth2 - xScale * width) / 2;
2586 ty += (imgHeight2 - yScale * height) / 2;
2587 }
2588 }
2589 tx += rotate == 0 ? imgLLX + tx0 : imgLLY + ty0;
2590 ty += rotate == 0 ? imgLLY + ty0 : -(imgLLX + tx0);
2591 if (tx != 0 || ty != 0) {
2592 writePSFmt("%g %g translate\n", tx, ty);
2593 }
2594 if (xScale != 1 || yScale != 1) {
2595 writePSFmt("%0.4f %0.4f scale\n", xScale, xScale);
2596 }
2597 if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
2598 writePSFmt("%g %g %g %g re W\n",
2599 clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0);
2600 } else {
2601 writePSFmt("%d %d %d %d re W\n", x1, y1, x2 - x1, y2 - y1);
2602 }
2603
2604 writePS("%%EndPageSetup\n");
2605 ++seqPage;
2606 break;
2607
2608 case psModeEPS:
2609 writePS("pdfStartPage\n");
2610 tx = ty = 0;
2611 rotate = (360 - state->getRotate()) % 360;
2612 if (rotate == 0) {
2613 } else if (rotate == 90) {
2614 writePS("90 rotate\n");
2615 tx = -epsX1;
2616 ty = -epsY2;
2617 } else if (rotate == 180) {
2618 writePS("180 rotate\n");
2619 tx = -(epsX1 + epsX2);
2620 ty = -(epsY1 + epsY2);
2621 } else { // rotate == 270
2622 writePS("270 rotate\n");
2623 tx = -epsX2;
2624 ty = -epsY1;
2625 }
2626 if (tx != 0 || ty != 0) {
2627 writePSFmt("%g %g translate\n", tx, ty);
2628 }
2629 xScale = yScale = 1;
2630 break;
2631
2632 case psModeForm:
2633 writePS("/PaintProc {\n");
2634 writePS("begin xpdf begin\n");
2635 writePS("pdfStartPage\n");
2636 tx = ty = 0;
2637 xScale = yScale = 1;
2638 rotate = 0;
2639 break;
2640 }
2641
2642 if (!checkRange(pageNum))
2643 return (gFalse);
2644 else
2645 return (gTrue);
2646 }
2647
2648 void PSOutputDev::endPage() {
2649 if (overlayCbk) {
2650 restoreState(NULL);
2651 (*overlayCbk)(this, overlayCbkData);
2652 }
2653
2654 if (mode == psModeForm) {
2655 writePS("pdfEndPage\n");
2656 writePS("end end\n");
2657 writePS("} def\n");
2658 writePS("end end\n");
2659 } else {
2660 if (!manualCtrl) {
2661 writePS("showpage\n");
2662 }
2663 writePS("%%PageTrailer\n");
2664 writePageTrailer();
2665 }
2666 }
2667
2668 void PSOutputDev::saveState(GfxState *state) {
2669 writePS("q\n");
2670 ++numSaves;
2671 }
2672
2673 void PSOutputDev::restoreState(GfxState *state) {
2674 writePS("Q\n");
2675 --numSaves;
2676 }
2677
2678 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
2679 double m21, double m22, double m31, double m32) {
2680 writePSFmt("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32);
2681 }
2682
2683 void PSOutputDev::updateLineDash(GfxState *state) {
2684 double *dash;
2685 double start;
2686 int length, i;
2687
2688 state->getLineDash(&dash, &length, &start);
2689 writePS("[");
2690 for (i = 0; i < length; ++i) {
2691 writePSFmt("%g%s",
2692 dash[i] == 0 ? 1 : dash[i],
2693 (i == length-1) ? "" : " ");
2694 }
2695 writePSFmt("] %g d\n", start);
2696 }
2697
2698 void PSOutputDev::updateFlatness(GfxState *state) {
2699 writePSFmt("%d i\n", state->getFlatness());
2700 }
2701
2702 void PSOutputDev::updateLineJoin(GfxState *state) {
2703 writePSFmt("%d j\n", state->getLineJoin());
2704 }
2705
2706 void PSOutputDev::updateLineCap(GfxState *state) {
2707 writePSFmt("%d J\n", state->getLineCap());
2708 }
2709
2710 void PSOutputDev::updateMiterLimit(GfxState *state) {
2711 writePSFmt("%g M\n", state->getMiterLimit());
2712 }
2713
2714 void PSOutputDev::updateLineWidth(GfxState *state) {
2715 writePSFmt("%g w\n", state->getLineWidth());
2716 }
2717
2718 void PSOutputDev::updateFillColorSpace(GfxState *state) {
2719 switch (level) {
2720 case psLevel1:
2721 case psLevel1Sep:
2722 break;
2723 case psLevel2:
2724 case psLevel3:
2725 if (state->getFillColorSpace()->getMode() != csPattern) {
2726 dumpColorSpaceL2(state->getFillColorSpace(), gTrue, gFalse);
2727 writePS(" cs\n");
2728 }
2729 break;
2730 case psLevel2Sep:
2731 case psLevel3Sep:
2732 break;
2733 }
2734 }
2735
2736 void PSOutputDev::updateStrokeColorSpace(GfxState *state) {
2737 switch (level) {
2738 case psLevel1:
2739 case psLevel1Sep:
2740 break;
2741 case psLevel2:
2742 case psLevel3:
2743 if (state->getStrokeColorSpace()->getMode() != csPattern) {
2744 dumpColorSpaceL2(state->getStrokeColorSpace(), gTrue, gFalse);
2745 writePS(" CS\n");
2746 }
2747 break;
2748 case psLevel2Sep:
2749 case psLevel3Sep:
2750 break;
2751 }
2752 }
2753
2754 void PSOutputDev::updateFillColor(GfxState *state) {
2755 GfxColor color;
2756 GfxColor *colorPtr;
2757 GfxGray gray;
2758 GfxCMYK cmyk;
2759 GfxSeparationColorSpace *sepCS;
2760 double c, m, y, k;
2761 int i;
2762
2763 switch (level) {
2764 case psLevel1:
2765 state->getFillGray(&gray);
2766 writePSFmt("%g g\n", colToDbl(gray));
2767 break;
2768 case psLevel1Sep:
2769 state->getFillCMYK(&cmyk);
2770 c = colToDbl(cmyk.c);
2771 m = colToDbl(cmyk.m);
2772 y = colToDbl(cmyk.y);
2773 k = colToDbl(cmyk.k);
2774 writePSFmt("%g %g %g %g k\n", c, m, y, k);
2775 addProcessColor(c, m, y, k);
2776 break;
2777 case psLevel2:
2778 case psLevel3:
2779 if (state->getFillColorSpace()->getMode() != csPattern) {
2780 colorPtr = state->getFillColor();
2781 writePS("[");
2782 for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) {
2783 if (i > 0) {
2784 writePS(" ");
2785 }
2786 writePSFmt("%g", colToDbl(colorPtr->c[i]));
2787 }
2788 writePS("] sc\n");
2789 }
2790 break;
2791 case psLevel2Sep:
2792 case psLevel3Sep:
2793 if (state->getFillColorSpace()->getMode() == csSeparation) {
2794 sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
2795 color.c[0] = gfxColorComp1;
2796 sepCS->getCMYK(&color, &cmyk);
2797 writePSFmt("%g %g %g %g %g (%s) ck\n",
2798 colToDbl(state->getFillColor()->c[0]),
2799 colToDbl(cmyk.c), colToDbl(cmyk.m),
2800 colToDbl(cmyk.y), colToDbl(cmyk.k),
2801 sepCS->getName()->getCString());
2802 addCustomColor(sepCS);
2803 } else {
2804 state->getFillCMYK(&cmyk);
2805 c = colToDbl(cmyk.c);
2806 m = colToDbl(cmyk.m);
2807 y = colToDbl(cmyk.y);
2808 k = colToDbl(cmyk.k);
2809 writePSFmt("%g %g %g %g k\n", c, m, y, k);
2810 addProcessColor(c, m, y, k);
2811 }
2812 break;
2813 }
2814 t3Cacheable = gFalse;
2815 }
2816
2817 void PSOutputDev::updateStrokeColor(GfxState *state) {
2818 GfxColor color;
2819 GfxColor *colorPtr;
2820 GfxGray gray;
2821 GfxCMYK cmyk;
2822 GfxSeparationColorSpace *sepCS;
2823 double c, m, y, k;
2824 int i;
2825
2826 switch (level) {
2827 case psLevel1:
2828 state->getStrokeGray(&gray);
2829 writePSFmt("%g G\n", colToDbl(gray));
2830 break;
2831 case psLevel1Sep:
2832 state->getStrokeCMYK(&cmyk);
2833 c = colToDbl(cmyk.c);
2834 m = colToDbl(cmyk.m);
2835 y = colToDbl(cmyk.y);
2836 k = colToDbl(cmyk.k);
2837 writePSFmt("%g %g %g %g K\n", c, m, y, k);
2838 addProcessColor(c, m, y, k);
2839 break;
2840 case psLevel2:
2841 case psLevel3:
2842 if (state->getStrokeColorSpace()->getMode() != csPattern) {
2843 colorPtr = state->getStrokeColor();
2844 writePS("[");
2845 for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) {
2846 if (i > 0) {
2847 writePS(" ");
2848 }
2849 writePSFmt("%g", colToDbl(colorPtr->c[i]));
2850 }
2851 writePS("] SC\n");
2852 }
2853 break;
2854 case psLevel2Sep:
2855 case psLevel3Sep:
2856 if (state->getStrokeColorSpace()->getMode() == csSeparation) {
2857 sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
2858 color.c[0] = gfxColorComp1;
2859 sepCS->getCMYK(&color, &cmyk);
2860 writePSFmt("%g %g %g %g %g (%s) CK\n",
2861 colToDbl(state->getStrokeColor()->c[0]),
2862 colToDbl(cmyk.c), colToDbl(cmyk.m),
2863 colToDbl(cmyk.y), colToDbl(cmyk.k),
2864 sepCS->getName()->getCString());
2865 addCustomColor(sepCS);
2866 } else {
2867 state->getStrokeCMYK(&cmyk);
2868 c = colToDbl(cmyk.c);
2869 m = colToDbl(cmyk.m);
2870 y = colToDbl(cmyk.y);
2871 k = colToDbl(cmyk.k);
2872 writePSFmt("%g %g %g %g K\n", c, m, y, k);
2873 addProcessColor(c, m, y, k);
2874 }
2875 break;
2876 }
2877 t3Cacheable = gFalse;
2878 }
2879
2880 void PSOutputDev::addProcessColor(double c, double m, double y, double k) {
2881 if (c > 0) {
2882 processColors |= psProcessCyan;
2883 }
2884 if (m > 0) {
2885 processColors |= psProcessMagenta;
2886 }
2887 if (y > 0) {
2888 processColors |= psProcessYellow;
2889 }
2890 if (k > 0) {
2891 processColors |= psProcessBlack;
2892 }
2893 }
2894
2895 void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) {
2896 PSOutCustomColor *cc;
2897 GfxColor color;
2898 GfxCMYK cmyk;
2899
2900 for (cc = customColors; cc; cc = cc->next) {
2901 if (!cc->name->cmp(sepCS->getName())) {
2902 return;
2903 }
2904 }
2905 color.c[0] = gfxColorComp1;
2906 sepCS->getCMYK(&color, &cmyk);
2907 cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
2908 colToDbl(cmyk.y), colToDbl(cmyk.k),
2909 sepCS->getName()->copy());
2910 cc->next = customColors;
2911 customColors = cc;
2912 }
2913
2914 void PSOutputDev::updateFillOverprint(GfxState *state) {
2915 if (level >= psLevel2) {
2916 writePSFmt("%s op\n", state->getFillOverprint() ? "true" : "false");
2917 }
2918 }
2919
2920 void PSOutputDev::updateStrokeOverprint(GfxState *state) {
2921 if (level >= psLevel2) {
2922 writePSFmt("%s OP\n", state->getStrokeOverprint() ? "true" : "false");
2923 }
2924 }
2925
2926 void PSOutputDev::updateFont(GfxState *state) {
2927 if (state->getFont()) {
2928 writePSFmt("/F%d_%d %g Tf\n",
2929 state->getFont()->getID()->num, state->getFont()->getID()->gen,
2930 fabs(state->getFontSize()) < 0.00001 ? 0.00001
2931 : state->getFontSize());
2932 }
2933 }
2934
2935 void PSOutputDev::updateTextMat(GfxState *state) {
2936 double *mat;
2937
2938 mat = state->getTextMat();
2939 if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.00001) {
2940 // avoid a singular (or close-to-singular) matrix
2941 writePSFmt("[0.00001 0 0 0.00001 %g %g] Tm\n", mat[4], mat[5]);
2942 } else {
2943 writePSFmt("[%g %g %g %g %g %g] Tm\n",
2944 mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
2945 }
2946 }
2947
2948 void PSOutputDev::updateCharSpace(GfxState *state) {
2949 writePSFmt("%g Tc\n", state->getCharSpace());
2950 }
2951
2952 void PSOutputDev::updateRender(GfxState *state) {
2953 int rm;
2954
2955 rm = state->getRender();
2956 writePSFmt("%d Tr\n", rm);
2957 rm &= 3;
2958 if (rm != 0 && rm != 3) {
2959 t3Cacheable = gFalse;
2960 }
2961 }
2962
2963 void PSOutputDev::updateRise(GfxState *state) {
2964 writePSFmt("%g Ts\n", state->getRise());
2965 }
2966
2967 void PSOutputDev::updateWordSpace(GfxState *state) {
2968 writePSFmt("%g Tw\n", state->getWordSpace());
2969 }
2970
2971 void PSOutputDev::updateHorizScaling(GfxState *state) {
2972 double h;
2973
2974 h = state->getHorizScaling();
2975 if (fabs(h) < 0.01) {
2976 h = 0.01;
2977 }
2978 writePSFmt("%g Tz\n", h);
2979 }
2980
2981 void PSOutputDev::updateTextPos(GfxState *state) {
2982 writePSFmt("%g %g Td\n", state->getLineX(), state->getLineY());
2983 }
2984
2985 void PSOutputDev::updateTextShift(GfxState *state, double shift) {
2986 if (state->getFont()->getWMode()) {
2987 writePSFmt("%g TJmV\n", shift);
2988 } else {
2989 writePSFmt("%g TJm\n", shift);
2990 }
2991 }
2992
2993 void PSOutputDev::stroke(GfxState *state) {
2994 doPath(state->getPath());
2995 if (t3String) {
2996 // if we're construct a cacheable Type 3 glyph, we need to do
2997 // everything in the fill color
2998 writePS("Sf\n");
2999 } else {
3000 writePS("S\n");
3001 }
3002 }
3003
3004 void PSOutputDev::fill(GfxState *state) {
3005 doPath(state->getPath());
3006 writePS("f\n");
3007 }
3008
3009 void PSOutputDev::eoFill(GfxState *state) {
3010 doPath(state->getPath());
3011 writePS("f*\n");
3012 }
3013
3014 void PSOutputDev::tilingPatternFill(GfxState *state, Object *str,
3015 int paintType, Dict *resDict,
3016 double *mat, double *bbox,
3017 int x0, int y0, int x1, int y1,
3018 double xStep, double yStep) {
3019 PDFRectangle box;
3020 Gfx *gfx;
3021
3022 // define a Type 3 font
3023 writePS("8 dict begin\n");
3024 writePS("/FontType 3 def\n");
3025 writePS("/FontMatrix [1 0 0 1 0 0] def\n");
3026 writePSFmt("/FontBBox [%g %g %g %g] def\n",
3027 bbox[0], bbox[1], bbox[2], bbox[3]);
3028 writePS("/Encoding 256 array def\n");
3029 writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
3030 writePS(" Encoding 120 /x put\n");
3031 writePS("/BuildGlyph {\n");
3032 writePS(" exch /CharProcs get exch\n");
3033 writePS(" 2 copy known not { pop /.notdef } if\n");
3034 writePS(" get exec\n");
3035 writePS("} bind def\n");
3036 writePS("/BuildChar {\n");
3037 writePS(" 1 index /Encoding get exch get\n");
3038 writePS(" 1 index /BuildGlyph get exec\n");
3039 writePS("} bind def\n");
3040 writePS("/CharProcs 1 dict def\n");
3041 writePS("CharProcs begin\n");
3042 box.x1 = bbox[0];
3043 box.y1 = bbox[1];
3044 box.x2 = bbox[2];
3045 box.y2 = bbox[3];
3046 gfx = new Gfx(xref, this, resDict, &box, NULL);
3047 writePS("/x {\n");
3048 if (paintType == 2) {
3049 writePSFmt("%g 0 %g %g %g %g setcachedevice\n",
3050 xStep, bbox[0], bbox[1], bbox[2], bbox[3]);
3051 } else {
3052 writePSFmt("%g 0 setcharwidth\n", xStep);
3053 }
3054 inType3Char = gTrue;
3055 ++numTilingPatterns;
3056 gfx->display(str);
3057 --numTilingPatterns;
3058 inType3Char = gFalse;
3059 writePS("} def\n");
3060 delete gfx;
3061 writePS("end\n");
3062 writePS("currentdict end\n");
3063 writePSFmt("/xpdfTile%d exch definefont pop\n", numTilingPatterns);
3064
3065 // draw the tiles
3066 writePSFmt("/xpdfTile%d findfont setfont\n", numTilingPatterns);
3067 writePSFmt("gsave [%g %g %g %g %g %g] concat\n",
3068 mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
3069 writePSFmt("%d 1 %d { %g exch %g mul m %d 1 %d { pop (x) show } for } for\n",
3070 y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1);
3071 writePS("grestore\n");
3072 }
3073
3074 void PSOutputDev::functionShadedFill(GfxState *state,
3075 GfxFunctionShading *shading) {
3076 double x0, y0, x1, y1;
3077 double *mat;
3078 int i;
3079
3080 shading->getDomain(&x0, &y0, &x1, &y1);
3081 mat = shading->getMatrix();
3082 writePSFmt("/mat [%g %g %g %g %g %g] def\n",
3083 mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
3084 writePSFmt("/n %d def\n", shading->getColorSpace()->getNComps());
3085 if (shading->getNFuncs() == 1) {
3086 writePS("/func ");
3087 cvtFunction(shading->getFunc(0));
3088 writePS("def\n");
3089 } else {
3090 writePS("/func {\n");
3091 for (i = 0; i < shading->getNFuncs(); ++i) {
3092 if (i < shading->getNFuncs() - 1) {
3093 writePS("2 copy\n");
3094 }
3095 cvtFunction(shading->getFunc(i));
3096 writePS("exec\n");
3097 if (i < shading->getNFuncs() - 1) {
3098 writePS("3 1 roll\n");
3099 }
3100 }
3101 writePS("} def\n");
3102 }
3103 writePSFmt("%g %g %g %g 0 funcSH\n", x0, y0, x1, y1);
3104 }
3105
3106 void PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading) {
3107 double xMin, yMin, xMax, yMax;
3108 double x0, y0, x1, y1, dx, dy, mul;
3109 double tMin, tMax, t, t0, t1;
3110 int i;
3111
3112 // get the clip region bbox
3113 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
3114
3115 // compute min and max t values, based on the four corners of the
3116 // clip region bbox
3117 shading->getCoords(&x0, &y0, &x1, &y1);
3118 dx = x1 - x0;
3119 dy = y1 - y0;
3120 mul = 1 / (dx * dx + dy * dy);
3121 tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
3122 t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
3123 if (t < tMin) {
3124 tMin = t;
3125 } else if (t > tMax) {
3126 tMax = t;
3127 }
3128 t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
3129 if (t < tMin) {
3130 tMin = t;
3131 } else if (t > tMax) {
3132 tMax = t;
3133 }
3134 t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
3135 if (t < tMin) {
3136 tMin = t;
3137 } else if (t > tMax) {
3138 tMax = t;
3139 }
3140 if (tMin < 0 && !shading->getExtend0()) {
3141 tMin = 0;
3142 }
3143 if (tMax > 1 && !shading->getExtend1()) {
3144 tMax = 1;
3145 }
3146
3147 // get the function domain
3148 t0 = shading->getDomain0();
3149 t1 = shading->getDomain1();
3150
3151 // generate the PS code
3152 writePSFmt("/t0 %g def\n", t0);
3153 writePSFmt("/t1 %g def\n", t1);
3154 writePSFmt("/dt %g def\n", t1 - t0);
3155 writePSFmt("/x0 %g def\n", x0);
3156 writePSFmt("/y0 %g def\n", y0);
3157 writePSFmt("/dx %g def\n", x1 - x0);
3158 writePSFmt("/x1 %g def\n", x1);
3159 writePSFmt("/y1 %g def\n", y1);
3160 writePSFmt("/dy %g def\n", y1 - y0);
3161 writePSFmt("/xMin %g def\n", xMin);
3162 writePSFmt("/yMin %g def\n", yMin);
3163 writePSFmt("/xMax %g def\n", xMax);
3164 writePSFmt("/yMax %g def\n", yMax);
3165 writePSFmt("/n %d def\n", shading->getColorSpace()->getNComps());
3166 if (shading->getNFuncs() == 1) {
3167 writePS("/func ");
3168 cvtFunction(shading->getFunc(0));
3169 writePS("def\n");
3170 } else {
3171 writePS("/func {\n");
3172 for (i = 0; i < shading->getNFuncs(); ++i) {
3173 if (i < shading->getNFuncs() - 1) {
3174 writePS("dup\n");
3175 }
3176 cvtFunction(shading->getFunc(i));
3177 writePS("exec\n");
3178 if (i < shading->getNFuncs() - 1) {
3179 writePS("exch\n");
3180 }
3181 }
3182 writePS("} def\n");
3183 }
3184 writePSFmt("%g %g 0 axialSH\n", tMin, tMax);
3185 }
3186
3187 void PSOutputDev::radialShadedFill(GfxState *state,
3188 GfxRadialShading *shading) {
3189 double x0, y0, r0, x1, y1, r1, t0, t1, sMin, sMax;
3190 double xMin, yMin, xMax, yMax;
3191 double d0, d1;
3192 int i;
3193
3194 // get the shading info
3195 shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
3196 t0 = shading->getDomain0();
3197 t1 = shading->getDomain1();
3198
3199 // compute the (possibly extended) s range
3200 sMin = 0;
3201 sMax = 1;
3202 if (shading->getExtend0()) {
3203 if (r0 < r1) {
3204 // extend the smaller end
3205 sMin = -r0 / (r1 - r0);
3206 } else {
3207 // extend the larger end
3208 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
3209 d0 = (x0 - xMin) * (x0 - xMin);
3210 d1 = (x0 - xMax) * (x0 - xMax);
3211 sMin = d0 > d1 ? d0 : d1;
3212 d0 = (y0 - yMin) * (y0 - yMin);
3213 d1 = (y0 - yMax) * (y0 - yMax);
3214 sMin += d0 > d1 ? d0 : d1;
3215 sMin = (sqrt(sMin) - r0) / (r1 - r0);
3216 if (sMin > 0) {
3217 sMin = 0;
3218 } else if (sMin < -20) {
3219 // sanity check
3220 sMin = -20;
3221 }
3222 }
3223 }
3224 if (shading->getExtend1()) {
3225 if (r1 < r0) {
3226 // extend the smaller end
3227 sMax = -r0 / (r1 - r0);
3228 } else if (r1 > r0) {
3229 // extend the larger end
3230 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
3231 d0 = (x1 - xMin) * (x1 - xMin);
3232 d1 = (x1 - xMax) * (x1 - xMax);
3233 sMax = d0 > d1 ? d0 : d1;
3234 d0 = (y1 - yMin) * (y1 - yMin);
3235 d1 = (y1 - yMax) * (y1 - yMax);
3236 sMax += d0 > d1 ? d0 : d1;
3237 sMax = (sqrt(sMax) - r0) / (r1 - r0);
3238 if (sMax < 1) {
3239 sMax = 1;
3240 } else if (sMax > 20) {
3241 // sanity check
3242 sMax = 20;
3243 }
3244 }
3245 }
3246
3247 // generate the PS code
3248 writePSFmt("/x0 %g def\n", x0);
3249 writePSFmt("/x1 %g def\n", x1);
3250 writePSFmt("/dx %g def\n", x1 - x0);
3251 writePSFmt("/y0 %g def\n", y0);
3252 writePSFmt("/y1 %g def\n", y1);
3253 writePSFmt("/dy %g def\n", y1 - y0);
3254 writePSFmt("/r0 %g def\n", r0);
3255 writePSFmt("/r1 %g def\n", r1);
3256 writePSFmt("/dr %g def\n", r1 - r0);
3257 writePSFmt("/t0 %g def\n", t0);
3258 writePSFmt("/t1 %g def\n", t1);
3259 writePSFmt("/dt %g def\n", t1 - t0);
3260 writePSFmt("/n %d def\n", shading->getColorSpace()->getNComps());
3261 if (shading->getNFuncs() == 1) {
3262 writePS("/func ");
3263 cvtFunction(shading->getFunc(0));
3264 writePS("def\n");
3265 } else {
3266 writePS("/func {\n");
3267 for (i = 0; i < shading->getNFuncs(); ++i) {
3268 if (i < shading->getNFuncs() - 1) {
3269 writePS("dup\n");
3270 }
3271 cvtFunction(shading->getFunc(i));
3272 writePS("exec\n");
3273 if (i < shading->getNFuncs() - 1) {
3274 writePS("exch\n");
3275 }
3276 }
3277 writePS("} def\n");
3278 }
3279 writePSFmt("%g %g 0 radialSH\n", sMin, sMax);
3280 }
3281
3282 void PSOutputDev::clip(GfxState *state) {
3283 doPath(state->getPath());
3284 writePS("W\n");
3285 }
3286
3287 void PSOutputDev::eoClip(GfxState *state) {
3288 doPath(state->getPath());
3289 writePS("W*\n");
3290 }
3291
3292 void PSOutputDev::doPath(GfxPath *path) {
3293 GfxSubpath *subpath;
3294 double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
3295 int n, m, i, j;
3296
3297 n = path->getNumSubpaths();
3298
3299 if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
3300 subpath = path->getSubpath(0);
3301 x0 = subpath->getX(0);
3302 y0 = subpath->getY(0);
3303 x4 = subpath->getX(4);
3304 y4 = subpath->getY(4);
3305 if (x4 == x0 && y4 == y0) {
3306 x1 = subpath->getX(1);
3307 y1 = subpath->getY(1);
3308 x2 = subpath->getX(2);
3309 y2 = subpath->getY(2);
3310 x3 = subpath->getX(3);
3311 y3 = subpath->getY(3);
3312 if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
3313 writePSFmt("%g %g %g %g re\n",
3314 x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
3315 fabs(x2 - x0), fabs(y1 - y0));
3316 return;
3317 } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
3318 writePSFmt("%g %g %g %g re\n",
3319 x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
3320 fabs(x1 - x0), fabs(y2 - y0));
3321 return;
3322 }
3323 }
3324 }
3325
3326 for (i = 0; i < n; ++i) {
3327 subpath = path->getSubpath(i);
3328 m = subpath->getNumPoints();
3329 writePSFmt("%g %g m\n", subpath->getX(0), subpath->getY(0));
3330 j = 1;
3331 while (j < m) {
3332 if (subpath->getCurve(j)) {
3333 writePSFmt("%g %g %g %g %g %g c\n", subpath->getX(j), subpath->getY(j),
3334 subpath->getX(j+1), subpath->getY(j+1),
3335 subpath->getX(j+2), subpath->getY(j+2));
3336 j += 3;
3337 } else {
3338 writePSFmt("%g %g l\n", subpath->getX(j), subpath->getY(j));
3339 ++j;
3340 }
3341 }
3342 if (subpath->isClosed()) {
3343 writePS("h\n");
3344 }
3345 }
3346 }
3347
3348 void PSOutputDev::drawString(GfxState *state, GString *s) {
3349 GfxFont *font;
3350 int wMode;
3351 GString *s2;
3352 double dx, dy, dx2, dy2, originX, originY;
3353 char *p;
3354 UnicodeMap *uMap;
3355 CharCode code;
3356 Unicode u[8];
3357 char buf[8];
3358 int len, nChars, uLen, n, m, i, j;
3359
3360 // check for invisible text -- this is used by Acrobat Capture
3361 if (state->getRender() == 3) {
3362 return;
3363 }
3364
3365 // ignore empty strings
3366 if (s->getLength() == 0) {
3367 return;
3368 }
3369
3370 // get the font
3371 if (!(font = state->getFont())) {
3372 return;
3373 }
3374 wMode = font->getWMode();
3375
3376 // check for a subtitute 16-bit font
3377 uMap = NULL;
3378 if (font->isCIDFont()) {
3379 for (i = 0; i < font16EncLen; ++i) {
3380 if (font->getID()->num == font16Enc[i].fontID.num &&
3381 font->getID()->gen == font16Enc[i].fontID.gen) {
3382 uMap = globalParams->getUnicodeMap(font16Enc[i].enc);
3383 break;
3384 }
3385 }
3386 }
3387
3388 // compute width of chars in string, ignoring char spacing and word
3389 // spacing -- the Tj operator will adjust for the metrics of the
3390 // font that's actually used
3391 dx = dy = 0;
3392 nChars = 0;
3393 p = s->getCString();
3394 len = s->getLength();
3395 if (font->isCIDFont()) {
3396 s2 = new GString();
3397 } else {
3398 s2 = s;
3399 }
3400 while (len > 0) {
3401 n = font->getNextChar(p, len, &code,
3402 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
3403 &dx2, &dy2, &originX, &originY);
3404 if (font->isCIDFont()) {
3405 if (uMap) {
3406 for (i = 0; i < uLen; ++i) {
3407 m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf));
3408 for (j = 0; j < m; ++j) {
3409 s2->append(buf[j]);
3410 }
3411 }
3412 //~ this really needs to get the number of chars in the target
3413 //~ encoding - which may be more than the number of Unicode
3414 //~ chars
3415 nChars += uLen;
3416 } else {
3417 s2->append((char)((code >> 8) & 0xff));
3418 s2->append((char)(code & 0xff));
3419 ++nChars;
3420 }
3421 }
3422 dx += dx2;
3423 dy += dy2;
3424 p += n;
3425 len -= n;
3426 }
3427 dx *= state->getFontSize() * state->getHorizScaling();
3428 dy *= state->getFontSize();
3429 if (uMap) {
3430 uMap->decRefCnt();
3431 }
3432
3433 if (s2->getLength() > 0) {
3434 writePSString(s2);
3435 if (font->isCIDFont()) {
3436 if (wMode) {
3437 writePSFmt(" %d %g Tj16V\n", nChars, dy);
3438 } else {
3439 writePSFmt(" %d %g Tj16\n", nChars, dx);
3440 }
3441 } else {
3442 writePSFmt(" %g Tj\n", dx);
3443 }
3444 }
3445 if (font->isCIDFont()) {
3446 delete s2;
3447 }
3448
3449 if (state->getRender() & 4) {
3450 haveTextClip = gTrue;
3451 }
3452 }
3453
3454 void PSOutputDev::endTextObject(GfxState *state) {
3455 if (haveTextClip) {
3456 writePS("Tclip\n");
3457 haveTextClip = gFalse;
3458 }
3459 }
3460
3461 void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
3462 int width, int height, GBool invert,
3463 GBool inlineImg) {
3464 int len;
3465
3466 len = height * ((width + 7) / 8);
3467 if (level == psLevel1 || level == psLevel1Sep) {
3468 doImageL1(ref, NULL, invert, inlineImg, str, width, height, len);
3469 } else {
3470 doImageL2(ref, NULL, invert, inlineImg, str, width, height, len,
3471 NULL, NULL, 0, 0, gFalse);
3472 }
3473 }
3474
3475 void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
3476 int width, int height, GfxImageColorMap *colorMap,
3477 int *maskColors, GBool inlineImg) {
3478 int len;
3479
3480 len = height * ((width * colorMap->getNumPixelComps() *
3481 colorMap->getBits() + 7) / 8);
3482 switch (level) {
3483 case psLevel1:
3484 doImageL1(ref, colorMap, gFalse, inlineImg, str, width, height, len);
3485 break;
3486 case psLevel1Sep:
3487 //~ handle indexed, separation, ... color spaces
3488 doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len);
3489 break;
3490 case psLevel2:
3491 case psLevel2Sep:
3492 case psLevel3:
3493 case psLevel3Sep:
3494 doImageL2(ref, colorMap, gFalse, inlineImg, str,
3495 width, height, len, maskColors, NULL, 0, 0, gFalse);
3496 break;
3497 }
3498 t3Cacheable = gFalse;
3499 }
3500
3501 void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
3502 int width, int height,
3503 GfxImageColorMap *colorMap,
3504 Stream *maskStr,
3505 int maskWidth, int maskHeight,
3506 GBool maskInvert) {
3507 int len;
3508
3509 len = height * ((width * colorMap->getNumPixelComps() *
3510 colorMap->getBits() + 7) / 8);
3511 switch (level) {
3512 case psLevel1:
3513 doImageL1(ref, colorMap, gFalse, gFalse, str, width, height, len);
3514 break;
3515 case psLevel1Sep:
3516 //~ handle indexed, separation, ... color spaces
3517 doImageL1Sep(colorMap, gFalse, gFalse, str, width, height, len);
3518 break;
3519 case psLevel2:
3520 case psLevel2Sep:
3521 case psLevel3:
3522 case psLevel3Sep:
3523 doImageL2(ref, colorMap, gFalse, gFalse, str, width, height, len,
3524 NULL, maskStr, maskWidth, maskHeight, maskInvert);
3525 break;
3526 }
3527 t3Cacheable = gFalse;
3528 }
3529
3530 void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap,
3531 GBool invert, GBool inlineImg,
3532 Stream *str, int width, int height, int len) {
3533 ImageStream *imgStr;
3534 Guchar pixBuf[gfxColorMaxComps];
3535 GfxGray gray;
3536 int col, x, y, c, i;
3537
3538 if (inType3Char && !colorMap) {
3539 if (inlineImg) {
3540 // create an array
3541 str = new FixedLengthEncoder(str, len);
3542 str = new ASCIIHexEncoder(str);
3543 str->reset();
3544 col = 0;
3545 writePS("[<");
3546 do {
3547 do {
3548 c = str->getChar();
3549 } while (c == '\n' || c == '\r');
3550 if (c == '>' || c == EOF) {
3551 break;
3552 }
3553 writePSChar(c);
3554 ++col;
3555 // each line is: "<...data...><eol>"
3556 // so max data length = 255 - 4 = 251
3557 // but make it 240 just to be safe
3558 // chunks are 2 bytes each, so we need to stop on an even col number
3559 if (col == 240) {
3560 writePS(">\n<");
3561 col = 0;
3562 }
3563 } while (c != '>' && c != EOF);
3564 writePS(">]\n");
3565 writePS("0\n");
3566 str->close();
3567 delete str;
3568 } else {
3569 // set up to use the array already created by setupImages()
3570 writePSFmt("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
3571 }
3572 }
3573
3574 // image/imagemask command
3575 if (inType3Char && !colorMap) {
3576 writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1a\n",
3577 width, height, invert ? "true" : "false",
3578 width, -height, height);
3579 } else if (colorMap) {
3580 writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n",
3581 width, height,
3582 width, -height, height);
3583 } else {
3584 writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n",
3585 width, height, invert ? "true" : "false",
3586 width, -height, height);
3587 }
3588
3589 // image data
3590 if (!(inType3Char && !colorMap)) {
3591
3592 if (colorMap) {
3593
3594 // set up to process the data stream
3595 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
3596 colorMap->getBits());
3597 imgStr->reset();
3598
3599 // process the data stream
3600 i = 0;
3601 for (y = 0; y < height; ++y) {
3602
3603 // write the line
3604 for (x = 0; x < width; ++x) {
3605 imgStr->getPixel(pixBuf);
3606 colorMap->getGray(pixBuf, &gray);
3607 writePSFmt("%02x", colToByte(gray));
3608 if (++i == 32) {
3609 writePSChar('\n');
3610 i = 0;
3611 }
3612 }
3613 }
3614 if (i != 0) {
3615 writePSChar('\n');
3616 }
3617 delete imgStr;
3618
3619 // imagemask
3620 } else {
3621 str->reset();
3622 i = 0;
3623 for (y = 0; y < height; ++y) {
3624 for (x = 0; x < width; x += 8) {
3625 writePSFmt("%02x", str->getChar() & 0xff);
3626 if (++i == 32) {
3627 writePSChar('\n');
3628 i = 0;
3629 }
3630 }
3631 }
3632 if (i != 0) {
3633 writePSChar('\n');
3634 }
3635 str->close();
3636 }
3637 }
3638 }
3639
3640 void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap,
3641 GBool invert, GBool inlineImg,
3642 Stream *str, int width, int height, int len) {
3643 ImageStream *imgStr;
3644 Guchar *lineBuf;
3645 Guchar pixBuf[gfxColorMaxComps];
3646 GfxCMYK cmyk;
3647 int x, y, i, comp;
3648
3649 // width, height, matrix, bits per component
3650 writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1Sep\n",
3651 width, height,
3652 width, -height, height);
3653
3654 // allocate a line buffer
3655 lineBuf = (Guchar *)gmalloc(4 * width);
3656
3657 // set up to process the data stream
3658 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
3659 colorMap->getBits());
3660 imgStr->reset();
3661
3662 // process the data stream
3663 i = 0;
3664 for (y = 0; y < height; ++y) {
3665
3666 // read the line
3667 for (x = 0; x < width; ++x) {
3668 imgStr->getPixel(pixBuf);
3669 colorMap->getCMYK(pixBuf, &cmyk);
3670 lineBuf[4*x+0] = colToByte(cmyk.c);
3671 lineBuf[4*x+1] = colToByte(cmyk.m);
3672 lineBuf[4*x+2] = colToByte(cmyk.y);
3673 lineBuf[4*x+3] = colToByte(cmyk.k);
3674 addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
3675 colToDbl(cmyk.y), colToDbl(cmyk.k));
3676 }
3677
3678 // write one line of each color component
3679 for (comp = 0; comp < 4; ++comp) {
3680 for (x = 0; x < width; ++x) {
3681 writePSFmt("%02x", lineBuf[4*x + comp]);
3682 if (++i == 32) {
3683 writePSChar('\n');
3684 i = 0;
3685 }
3686 }
3687 }
3688 }
3689
3690 if (i != 0) {
3691 writePSChar('\n');
3692 }
3693
3694 delete imgStr;
3695 gfree(lineBuf);
3696 }
3697
3698 void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
3699 GBool invert, GBool inlineImg,
3700 Stream *str, int width, int height, int len,
3701 int *maskColors, Stream *maskStr,
3702 int maskWidth, int maskHeight, GBool maskInvert) {
3703 ImageStream *imgStr;
3704 Guchar *line, *pix;
3705 GString *s;
3706 int n, numComps;
3707 GBool useRLE, useASCII, useASCIIHex, useCompressed;
3708 GfxSeparationColorSpace *sepCS;
3709 GfxColor color;
3710 GfxCMYK cmyk;
3711 int c;
3712 int col, i, x, x0, y, y0, maskXor;
3713
3714 // color key masking
3715 if (maskColors && colorMap && !inlineImg) {
3716 // can't read the stream twice for inline images -- but masking
3717 // isn't allowed with inline images anyway
3718 writePS("[\n");
3719 numComps = colorMap->getNumPixelComps();
3720 imgStr = new ImageStream(str, width, numComps, colorMap->getBits());
3721 imgStr->reset();
3722 for (y = 0, y0 = 0; y < height; ++y) {
3723 if (!(line = imgStr->getLine())) {
3724 break;
3725 }
3726 for (x = 0, x0 = 0, pix = line; x < width; ++x, pix += numComps) {
3727 for (i = 0; i < numComps; ++i) {
3728 if (pix[i] < maskColors[2*i] ||
3729 pix[i] > maskColors[2*i+1]) {
3730 break;
3731 }
3732 }
3733 if (i == numComps) {
3734 if (y0 < y) {
3735 writePSFmt("0 %d %d %d\n", height - y, width, y - y0);
3736 }
3737 if (x0 < x) {
3738 writePSFmt("%d %d %d 1\n", x0, height - y - 1, x - x0);
3739 }
3740 x0 = x + 1;
3741 y0 = y + 1;
3742 }
3743 }
3744 if (x0 > 0 && x0 < width) {
3745 writePSFmt("%d %d %d 1\n", x0, height - y - 1, width - x0);
3746 }
3747 }
3748 if (y0 < height) {
3749 writePSFmt("0 0 %d %d\n", width, height - y0);
3750 }
3751 delete imgStr;
3752 str->close();
3753 writePSFmt("] %d %d pdfImClip\n", width, height);
3754
3755 // explicit masking
3756 } else if (maskStr) {
3757 writePS("[\n");
3758 imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
3759 imgStr->reset();
3760 maskXor = maskInvert ? 1 : 0;
3761 for (y = 0, y0 = 0; y < maskHeight; ++y) {
3762 if (!(line = imgStr->getLine())) {
3763 break;
3764 }
3765 for (x = 0, x0 = 0, pix = line; x < maskWidth; ++x, ++pix) {
3766 if (*pix ^ maskXor) {
3767 if (y0 < y) {
3768 writePSFmt("0 %d %d %d\n", maskHeight - y, maskWidth, y - y0);
3769 }
3770 if (x0 < x) {
3771 writePSFmt("%d %d %d 1\n", x0, maskHeight - y - 1, x - x0);
3772 }
3773 x0 = x + 1;
3774 y0 = y + 1;
3775 }
3776 }
3777 if (x0 > 0 && x0 < maskWidth) {
3778 writePSFmt("%d %d %d 1\n", x0, maskHeight - y - 1, maskWidth - x0);
3779 }
3780 }
3781 if (y0 < maskHeight) {
3782 writePSFmt("0 0 %d %d\n", maskWidth, maskHeight - y0);
3783 }
3784 delete imgStr;
3785 maskStr->close();
3786 writePSFmt("] %d %d pdfImClip\n", maskWidth, maskHeight);
3787 }
3788
3789 // color space
3790 if (colorMap) {
3791 dumpColorSpaceL2(colorMap->getColorSpace(), gFalse, gTrue);
3792 writePS(" setcolorspace\n");
3793 }
3794
3795 useASCIIHex = globalParams->getPSASCIIHex();
3796
3797 // set up the image data
3798 if (mode == psModeForm || inType3Char) {
3799 if (inlineImg) {
3800 // create an array
3801 str = new FixedLengthEncoder(str, len);
3802 if (useASCIIHex) {
3803 str = new ASCIIHexEncoder(str);
3804 } else {
3805 str = new ASCII85Encoder(str);
3806 }
3807 str->reset();
3808 col = 0;
3809 writePS((char *)(useASCIIHex ? "[<" : "[<~"));
3810 do {
3811 do {
3812 c = str->getChar();
3813 } while (c == '\n' || c == '\r');
3814 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
3815 break;
3816 }
3817 if (c == 'z') {
3818 writePSChar(c);
3819 ++col;
3820 } else {
3821 writePSChar(c);
3822 ++col;
3823 for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
3824 do {
3825 c = str->getChar();
3826 } while (c == '\n' || c == '\r');
3827 if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
3828 break;
3829 }
3830 writePSChar(c);
3831 ++col;
3832 }
3833 }
3834 // each line is: "<~...data...~><eol>"
3835 // so max data length = 255 - 6 = 249
3836 // chunks are 1 or 5 bytes each, so we have to stop at 245
3837 // but make it 240 just to be safe
3838 if (col > 240) {
3839 writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
3840 col = 0;
3841 }
3842 } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
3843 writePS((char *)(useASCIIHex ? ">]\n" : "~>]\n"));
3844 writePS("0\n");
3845 str->close();
3846 delete str;
3847 } else {
3848 // set up to use the array already created by setupImages()
3849 writePSFmt("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
3850 }
3851 }
3852
3853 // image dictionary
3854 writePS("<<\n /ImageType 1\n");
3855
3856 // width, height, matrix, bits per component
3857 writePSFmt(" /Width %d\n", width);
3858 writePSFmt(" /Height %d\n", height);
3859 writePSFmt(" /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height);
3860 if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
3861 writePSFmt(" /BitsPerComponent 8\n");
3862 } else {
3863 writePSFmt(" /BitsPerComponent %d\n",
3864 colorMap ? colorMap->getBits() : 1);
3865 }
3866
3867 // decode
3868 if (colorMap) {
3869 writePS(" /Decode [");
3870 if ((level == psLevel2Sep || level == psLevel3Sep) &&
3871 colorMap->getColorSpace()->getMode() == csSeparation) {
3872 // this matches up with the code in the pdfImSep operator
3873 n = (1 << colorMap->getBits()) - 1;
3874 writePSFmt("%g %g", colorMap->getDecodeLow(0) * n,
3875 colorMap->getDecodeHigh(0) * n);
3876 } else if (colorMap->getColorSpace()->getMode() == csDeviceN) {
3877 numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
3878 getAlt()->getNComps();
3879 for (i = 0; i < numComps; ++i) {
3880 if (i > 0) {
3881 writePS(" ");
3882 }
3883 writePS("0 1");
3884 }
3885 } else {
3886 numComps = colorMap->getNumPixelComps();
3887 for (i = 0; i < numComps; ++i) {
3888 if (i > 0) {
3889 writePS(" ");
3890 }
3891 writePSFmt("%g %g", colorMap->getDecodeLow(i),
3892 colorMap->getDecodeHigh(i));
3893 }
3894 }
3895 writePS("]\n");
3896 } else {
3897 writePSFmt(" /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1);
3898 }
3899
3900 if (mode == psModeForm || inType3Char) {
3901
3902 // data source
3903 writePS(" /DataSource { 2 copy get exch 1 add exch }\n");
3904
3905 // end of image dictionary
3906 writePSFmt(">>\n%s\n", colorMap ? "image" : "imagemask");
3907
3908 // get rid of the array and index
3909 writePS("pop pop\n");
3910
3911 } else {
3912
3913 // data source
3914 writePS(" /DataSource currentfile\n");
3915 s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,
3916 " ");
3917 if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||
3918 inlineImg || !s) {
3919 useRLE = gTrue;
3920 useASCII = gTrue;
3921 useCompressed = gFalse;
3922 } else {
3923 useRLE = gFalse;
3924 useASCII = str->isBinary();
3925 useCompressed = gTrue;
3926 }
3927 if (useASCII) {
3928 writePSFmt(" /ASCII%sDecode filter\n",
3929 useASCIIHex ? "Hex" : "85");
3930 }
3931 if (useRLE) {
3932 writePS(" /RunLengthDecode filter\n");
3933 }
3934 if (useCompressed) {
3935 writePS(s->getCString());
3936 }
3937 if (s) {
3938 delete s;
3939 }
3940
3941 // cut off inline image streams at appropriate length
3942 if (inlineImg) {
3943 str = new FixedLengthEncoder(str, len);
3944 } else if (useCompressed) {
3945 str = str->getBaseStream();
3946 }
3947
3948 // recode DeviceN data
3949 if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
3950 str = new DeviceNRecoder(str, width, height, colorMap);
3951 }
3952
3953 // add RunLengthEncode and ASCIIHex/85 encode filters
3954 if (useRLE) {
3955 str = new RunLengthEncoder(str);
3956 }
3957 if (useASCII) {
3958 if (useASCIIHex) {
3959 str = new ASCIIHexEncoder(str);
3960 } else {
3961 str = new ASCII85Encoder(str);
3962 }
3963 }
3964
3965 // end of image dictionary
3966 writePS(">>\n");
3967 #if OPI_SUPPORT
3968 if (opi13Nest) {
3969 if (inlineImg) {
3970 // this can't happen -- OPI dictionaries are in XObjects
3971 error(-1, "Internal: OPI in inline image");
3972 n = 0;
3973 } else {
3974 // need to read the stream to count characters -- the length
3975 // is data-dependent (because of ASCII and RLE filters)
3976 str->reset();
3977 n = 0;
3978 while ((c = str->getChar()) != EOF) {
3979 ++n;
3980 }
3981 str->close();
3982 }
3983 // +6/7 for "pdfIm\n" / "pdfImM\n"
3984 // +8 for newline + trailer
3985 n += colorMap ? 14 : 15;
3986 writePSFmt("%%%%BeginData: %d Hex Bytes\n", n);
3987 }
3988 #endif
3989 if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
3990 colorMap->getColorSpace()->getMode() == csSeparation) {
3991 color.c[0] = gfxColorComp1;
3992 sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
3993 sepCS->getCMYK(&color, &cmyk);
3994 writePSFmt("%g %g %g %g (%s) pdfImSep\n",
3995 colToDbl(cmyk.c), colToDbl(cmyk.m),
3996 colToDbl(cmyk.y), colToDbl(cmyk.k),
3997 sepCS->getName()->getCString());
3998 } else {
3999 writePSFmt("%s\n", colorMap ? "pdfIm" : "pdfImM");
4000 }
4001
4002 // copy the stream data
4003 str->reset();
4004 while ((c = str->getChar()) != EOF) {
4005 writePSChar(c);
4006 }
4007 str->close();
4008
4009 // add newline and trailer to the end
4010 writePSChar('\n');
4011 writePS("%-EOD-\n");
4012 #if OPI_SUPPORT
4013 if (opi13Nest) {
4014 writePS("%%EndData\n");
4015 }
4016 #endif
4017
4018 // delete encoders
4019 if (useRLE || useASCII || inlineImg) {
4020 delete str;
4021 }
4022 }
4023
4024 if ((maskColors && colorMap && !inlineImg) || maskStr) {
4025 writePS("pdfImClipEnd\n");
4026 }
4027 }
4028
4029 void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace,
4030 GBool genXform, GBool updateColors) {
4031 GfxCalGrayColorSpace *calGrayCS;
4032 GfxCalRGBColorSpace *calRGBCS;
4033 GfxLabColorSpace *labCS;
4034 GfxIndexedColorSpace *indexedCS;
4035 GfxSeparationColorSpace *separationCS;
4036 GfxDeviceNColorSpace *deviceNCS;
4037 GfxColorSpace *baseCS;
4038 Guchar *lookup, *p;
4039 double x[gfxColorMaxComps], y[gfxColorMaxComps];
4040 GfxColor color;
4041 GfxCMYK cmyk;
4042 Function *func;
4043 int n, numComps, numAltComps;
4044 int byte;
4045 int i, j, k;
4046
4047 switch (colorSpace->getMode()) {
4048
4049 case csDeviceGray:
4050 writePS("/DeviceGray");
4051 if (genXform) {
4052 writePS(" {}");
4053 }
4054 if (updateColors) {
4055 processColors |= psProcessBlack;
4056 }
4057 break;
4058
4059 case csCalGray:
4060 calGrayCS = (GfxCalGrayColorSpace *)colorSpace;
4061 writePS("[/CIEBasedA <<\n");
4062 writePSFmt(" /DecodeA {%g exp} bind\n", calGrayCS->getGamma());
4063 writePSFmt(" /MatrixA [%g %g %g]\n",
4064 calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
4065 calGrayCS->getWhiteZ());
4066 writePSFmt(" /WhitePoint [%g %g %g]\n",
4067 calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
4068 calGrayCS->getWhiteZ());
4069 writePSFmt(" /BlackPoint [%g %g %g]\n",
4070 calGrayCS->getBlackX(), calGrayCS->getBlackY(),
4071 calGrayCS->getBlackZ());
4072 writePS(">>]");
4073 if (genXform) {
4074 writePS(" {}");
4075 }
4076 if (updateColors) {
4077 processColors |= psProcessBlack;
4078 }
4079 break;
4080
4081 case csDeviceRGB:
4082 writePS("/DeviceRGB");
4083 if (genXform) {
4084 writePS(" {}");
4085 }
4086 if (updateColors) {
4087 processColors |= psProcessCMYK;
4088 }
4089 break;
4090
4091 case csCalRGB:
4092 calRGBCS = (GfxCalRGBColorSpace *)colorSpace;
4093 writePS("[/CIEBasedABC <<\n");
4094 writePSFmt(" /DecodeABC [{%g exp} bind {%g exp} bind {%g exp} bind]\n",
4095 calRGBCS->getGammaR(), calRGBCS->getGammaG(),
4096 calRGBCS->getGammaB());
4097 writePSFmt(" /MatrixABC [%g %g %g %g %g %g %g %g %g]\n",
4098 calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1],
4099 calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3],
4100 calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5],
4101 calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7],
4102 calRGBCS->getMatrix()[8]);
4103 writePSFmt(" /WhitePoint [%g %g %g]\n",
4104 calRGBCS->getWhiteX(), calRGBCS->getWhiteY(),
4105 calRGBCS->getWhiteZ());
4106 writePSFmt(" /BlackPoint [%g %g %g]\n",
4107 calRGBCS->getBlackX(), calRGBCS->getBlackY(),
4108 calRGBCS->getBlackZ());
4109 writePS(">>]");
4110 if (genXform) {
4111 writePS(" {}");
4112 }
4113 if (updateColors) {
4114 processColors |= psProcessCMYK;
4115 }
4116 break;
4117
4118 case csDeviceCMYK:
4119 writePS("/DeviceCMYK");
4120 if (genXform) {
4121 writePS(" {}");
4122 }
4123 if (updateColors) {
4124 processColors |= psProcessCMYK;
4125 }
4126 break;
4127
4128 case csLab:
4129 labCS = (GfxLabColorSpace *)colorSpace;
4130 writePS("[/CIEBasedABC <<\n");
4131 writePSFmt(" /RangeABC [0 100 %g %g %g %g]\n",
4132 labCS->getAMin(), labCS->getAMax(),
4133 labCS->getBMin(), labCS->getBMax());
4134 writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
4135 writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
4136 writePS(" /DecodeLMN\n");
4137 writePS(" [{dup 6 29 div ge {dup dup mul mul}\n");
4138 writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
4139 labCS->getWhiteX());
4140 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
4141 writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
4142 labCS->getWhiteY());
4143 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
4144 writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n",
4145 labCS->getWhiteZ());
4146 writePSFmt(" /WhitePoint [%g %g %g]\n",
4147 labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());
4148 writePSFmt(" /BlackPoint [%g %g %g]\n",
4149 labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());
4150 writePS(">>]");
4151 if (genXform) {
4152 writePS(" {}");
4153 }
4154 if (updateColors) {
4155 processColors |= psProcessCMYK;
4156 }
4157 break;
4158
4159 case csICCBased:
4160 // there is no transform function to the alternate color space, so
4161 // we can use it directly
4162 dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt(),
4163 genXform, updateColors);
4164 break;
4165
4166 case csIndexed:
4167 indexedCS = (GfxIndexedColorSpace *)colorSpace;
4168 baseCS = indexedCS->getBase();
4169 writePS("[/Indexed ");
4170 dumpColorSpaceL2(baseCS, gFalse, gFalse);
4171 n = indexedCS->getIndexHigh();
4172 numComps = baseCS->getNComps();
4173 lookup = indexedCS->getLookup();
4174 writePSFmt(" %d <\n", n);
4175 if (baseCS->getMode() == csDeviceN) {
4176 func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();
4177 numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps();
4178 p = lookup;
4179 for (i = 0; i <= n; i += 8) {
4180 writePS(" ");
4181 for (j = i; j < i+8 && j <= n; ++j) {
4182 for (k = 0; k < numComps; ++k) {
4183 x[k] = *p++ / 255.0;
4184 }
4185 func->transform(x, y);
4186 for (k = 0; k < numAltComps; ++k) {
4187 byte = (int)(y[k] * 255 + 0.5);
4188 if (byte < 0) {
4189 byte = 0;
4190 } else if (byte > 255) {
4191 byte = 255;
4192 }
4193 writePSFmt("%02x", byte);
4194 }
4195 if (updateColors) {
4196 color.c[0] = dblToCol(j);
4197 indexedCS->getCMYK(&color, &cmyk);
4198 addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
4199 colToDbl(cmyk.y), colToDbl(cmyk.k));
4200 }
4201 }
4202 writePS("\n");
4203 }
4204 } else {
4205 for (i = 0; i <= n; i += 8) {
4206 writePS(" ");
4207 for (j = i; j < i+8 && j <= n; ++j) {
4208 for (k = 0; k < numComps; ++k) {
4209 writePSFmt("%02x", lookup[j * numComps + k]);
4210 }
4211 if (updateColors) {
4212 color.c[0] = dblToCol(j);
4213 indexedCS->getCMYK(&color, &cmyk);
4214 addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
4215 colToDbl(cmyk.y), colToDbl(cmyk.k));
4216 }
4217 }
4218 writePS("\n");
4219 }
4220 }
4221 writePS(">]");
4222 if (genXform) {
4223 writePS(" {}");
4224 }
4225 break;
4226
4227 case csSeparation:
4228 separationCS = (GfxSeparationColorSpace *)colorSpace;
4229 writePS("[/Separation /");
4230 writePSName(separationCS->getName()->getCString());
4231 writePS(" ");
4232 dumpColorSpaceL2(separationCS->getAlt(), gFalse, gFalse);
4233 writePS("\n");
4234 cvtFunction(separationCS->getFunc());
4235 writePS("]");
4236 if (genXform) {
4237 writePS(" {}");
4238 }
4239 if (updateColors) {
4240 addCustomColor(separationCS);
4241 }
4242 break;
4243
4244 case csDeviceN:
4245 // DeviceN color spaces are a Level 3 PostScript feature.
4246 deviceNCS = (GfxDeviceNColorSpace *)colorSpace;
4247 dumpColorSpaceL2(deviceNCS->getAlt(), gFalse, updateColors);
4248 if (genXform) {
4249 writePS(" ");
4250 cvtFunction(deviceNCS->getTintTransformFunc());
4251 }
4252 break;
4253
4254 case csPattern:
4255 //~ unimplemented
4256 break;
4257 }
4258 }
4259
4260 #if OPI_SUPPORT
4261 void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
4262 Object dict;
4263
4264 if (globalParams->getPSOPI()) {
4265 opiDict->lookup("2.0", &dict);
4266 if (dict.isDict()) {
4267 opiBegin20(state, dict.getDict());
4268 dict.free();
4269 } else {
4270 dict.free();
4271 opiDict->lookup("1.3", &dict);
4272 if (dict.isDict()) {
4273 opiBegin13(state, dict.getDict());
4274 }
4275 dict.free();
4276 }
4277 }
4278 }
4279
4280 void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
4281 Object obj1, obj2, obj3, obj4;
4282 double width, height, left, right, top, bottom;
4283 int w, h;
4284 int i;
4285
4286 writePS("%%BeginOPI: 2.0\n");
4287 writePS("%%Distilled\n");
4288
4289 dict->lookup("F", &obj1);
4290 if (getFileSpec(&obj1, &obj2)) {
4291 writePSFmt("%%%%ImageFileName: %s\n",
4292 obj2.getString()->getCString());
4293 obj2.free();
4294 }
4295 obj1.free();
4296
4297 dict->lookup("MainImage", &obj1);
4298 if (obj1.isString()) {
4299 writePSFmt("%%%%MainImage: %s\n", obj1.getString()->getCString());
4300 }
4301 obj1.free();
4302
4303 //~ ignoring 'Tags' entry
4304 //~ need to use writePSString() and deal with >255-char lines
4305
4306 dict->lookup("Size", &obj1);
4307 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4308 obj1.arrayGet(0, &obj2);
4309 width = obj2.getNum();
4310 obj2.free();
4311 obj1.arrayGet(1, &obj2);
4312 height = obj2.getNum();
4313 obj2.free();
4314 writePSFmt("%%%%ImageDimensions: %g %g\n", width, height);
4315 }
4316 obj1.free();
4317
4318 dict->lookup("CropRect", &obj1);
4319 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
4320 obj1.arrayGet(0, &obj2);
4321 left = obj2.getNum();
4322 obj2.free();
4323 obj1.arrayGet(1, &obj2);
4324 top = obj2.getNum();
4325 obj2.free();
4326 obj1.arrayGet(2, &obj2);
4327 right = obj2.getNum();
4328 obj2.free();
4329 obj1.arrayGet(3, &obj2);
4330 bottom = obj2.getNum();
4331 obj2.free();
4332 writePSFmt("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom);
4333 }
4334 obj1.free();
4335
4336 dict->lookup("Overprint", &obj1);
4337 if (obj1.isBool()) {
4338 writePSFmt("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
4339 }
4340 obj1.free();
4341
4342 dict->lookup("Inks", &obj1);
4343 if (obj1.isName()) {
4344 writePSFmt("%%%%ImageInks: %s\n", obj1.getName());
4345 } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
4346 obj1.arrayGet(0, &obj2);
4347 if (obj2.isName()) {
4348 writePSFmt("%%%%ImageInks: %s %d",
4349 obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
4350 for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) {
4351 obj1.arrayGet(i, &obj3);
4352 obj1.arrayGet(i+1, &obj4);
4353 if (obj3.isString() && obj4.isNum()) {
4354 writePS(" ");
4355 writePSString(obj3.getString());
4356 writePSFmt(" %g", obj4.getNum());
4357 }
4358 obj3.free();
4359 obj4.free();
4360 }
4361 writePS("\n");
4362 }
4363 obj2.free();
4364 }
4365 obj1.free();
4366
4367 writePS("gsave\n");
4368
4369 writePS("%%BeginIncludedImage\n");
4370
4371 dict->lookup("IncludedImageDimensions", &obj1);
4372 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4373 obj1.arrayGet(0, &obj2);
4374 w = obj2.getInt();
4375 obj2.free();
4376 obj1.arrayGet(1, &obj2);
4377 h = obj2.getInt();
4378 obj2.free();
4379 writePSFmt("%%%%IncludedImageDimensions: %d %d\n", w, h);
4380 }
4381 obj1.free();
4382
4383 dict->lookup("IncludedImageQuality", &obj1);
4384 if (obj1.isNum()) {
4385 writePSFmt("%%%%IncludedImageQuality: %g\n", obj1.getNum());
4386 }
4387 obj1.free();
4388
4389 ++opi20Nest;
4390 }
4391
4392 void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
4393 Object obj1, obj2;
4394 int left, right, top, bottom, samples, bits, width, height;
4395 double c, m, y, k;
4396 double llx, lly, ulx, uly, urx, ury, lrx, lry;
4397 double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
4398 double horiz, vert;
4399 int i, j;
4400
4401 writePS("save\n");
4402 writePS("/opiMatrix2 matrix currentmatrix def\n");
4403 writePS("opiMatrix setmatrix\n");
4404
4405 dict->lookup("F", &obj1);
4406 if (getFileSpec(&obj1, &obj2)) {
4407 writePSFmt("%%ALDImageFileName: %s\n",
4408 obj2.getString()->getCString());
4409 obj2.free();
4410 }
4411 obj1.free();
4412
4413 dict->lookup("CropRect", &obj1);
4414 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
4415 obj1.arrayGet(0, &obj2);
4416 left = obj2.getInt();
4417 obj2.free();
4418 obj1.arrayGet(1, &obj2);
4419 top = obj2.getInt();
4420 obj2.free();
4421 obj1.arrayGet(2, &obj2);
4422 right = obj2.getInt();
4423 obj2.free();
4424 obj1.arrayGet(3, &obj2);
4425 bottom = obj2.getInt();
4426 obj2.free();
4427 writePSFmt("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom);
4428 }
4429 obj1.free();
4430
4431 dict->lookup("Color", &obj1);
4432 if (obj1.isArray() && obj1.arrayGetLength() == 5) {
4433 obj1.arrayGet(0, &obj2);
4434 c = obj2.getNum();
4435 obj2.free();
4436 obj1.arrayGet(1, &obj2);
4437 m = obj2.getNum();
4438 obj2.free();
4439 obj1.arrayGet(2, &obj2);
4440 y = obj2.getNum();
4441 obj2.free();
4442 obj1.arrayGet(3, &obj2);
4443 k = obj2.getNum();
4444 obj2.free();
4445 obj1.arrayGet(4, &obj2);
4446 if (obj2.isString()) {
4447 writePSFmt("%%ALDImageColor: %g %g %g %g ", c, m, y, k);
4448 writePSString(obj2.getString());
4449 writePS("\n");
4450 }
4451 obj2.free();
4452 }
4453 obj1.free();
4454
4455 dict->lookup("ColorType", &obj1);
4456 if (obj1.isName()) {
4457 writePSFmt("%%ALDImageColorType: %s\n", obj1.getName());
4458 }
4459 obj1.free();
4460
4461 //~ ignores 'Comments' entry
4462 //~ need to handle multiple lines
4463
4464 dict->lookup("CropFixed", &obj1);
4465 if (obj1.isArray()) {
4466 obj1.arrayGet(0, &obj2);
4467 ulx = obj2.getNum();
4468 obj2.free();
4469 obj1.arrayGet(1, &obj2);
4470 uly = obj2.getNum();
4471 obj2.free();
4472 obj1.arrayGet(2, &obj2);
4473 lrx = obj2.getNum();
4474 obj2.free();
4475 obj1.arrayGet(3, &obj2);
4476 lry = obj2.getNum();
4477 obj2.free();
4478 writePSFmt("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry);
4479 }
4480 obj1.free();
4481
4482 dict->lookup("GrayMap", &obj1);
4483 if (obj1.isArray()) {
4484 writePS("%ALDImageGrayMap:");
4485 for (i = 0; i < obj1.arrayGetLength(); i += 16) {
4486 if (i > 0) {
4487 writePS("\n%%+");
4488 }
4489 for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
4490 obj1.arrayGet(i+j, &obj2);
4491 writePSFmt(" %d", obj2.getInt());
4492 obj2.free();
4493 }
4494 }
4495 writePS("\n");
4496 }
4497 obj1.free();
4498
4499 dict->lookup("ID", &obj1);
4500 if (obj1.isString()) {
4501 writePSFmt("%%ALDImageID: %s\n", obj1.getString()->getCString());
4502 }
4503 obj1.free();
4504
4505 dict->lookup("ImageType", &obj1);
4506 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4507 obj1.arrayGet(0, &obj2);
4508 samples = obj2.getInt();
4509 obj2.free();
4510 obj1.arrayGet(1, &obj2);
4511 bits = obj2.getInt();
4512 obj2.free();
4513 writePSFmt("%%ALDImageType: %d %d\n", samples, bits);
4514 }
4515 obj1.free();
4516
4517 dict->lookup("Overprint", &obj1);
4518 if (obj1.isBool()) {
4519 writePSFmt("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
4520 }
4521 obj1.free();
4522
4523 dict->lookup("Position", &obj1);
4524 if (obj1.isArray() && obj1.arrayGetLength() == 8) {
4525 obj1.arrayGet(0, &obj2);
4526 llx = obj2.getNum();
4527 obj2.free();
4528 obj1.arrayGet(1, &obj2);
4529 lly = obj2.getNum();
4530 obj2.free();
4531 obj1.arrayGet(2, &obj2);
4532 ulx = obj2.getNum();
4533 obj2.free();
4534 obj1.arrayGet(3, &obj2);
4535 uly = obj2.getNum();
4536 obj2.free();
4537 obj1.arrayGet(4, &obj2);
4538 urx = obj2.getNum();
4539 obj2.free();
4540 obj1.arrayGet(5, &obj2);
4541 ury = obj2.getNum();
4542 obj2.free();
4543 obj1.arrayGet(6, &obj2);
4544 lrx = obj2.getNum();
4545 obj2.free();
4546 obj1.arrayGet(7, &obj2);
4547 lry = obj2.getNum();
4548 obj2.free();
4549 opiTransform(state, llx, lly, &tllx, &tlly);
4550 opiTransform(state, ulx, uly, &tulx, &tuly);
4551 opiTransform(state, urx, ury, &turx, &tury);
4552 opiTransform(state, lrx, lry, &tlrx, &tlry);
4553 writePSFmt("%%ALDImagePosition: %g %g %g %g %g %g %g %g\n",
4554 tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
4555 obj2.free();
4556 }
4557 obj1.free();
4558
4559 dict->lookup("Resolution", &obj1);
4560 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4561 obj1.arrayGet(0, &obj2);
4562 horiz = obj2.getNum();
4563 obj2.free();
4564 obj1.arrayGet(1, &obj2);
4565 vert = obj2.getNum();
4566 obj2.free();
4567 writePSFmt("%%ALDImageResoution: %g %g\n", horiz, vert);
4568 obj2.free();
4569 }
4570 obj1.free();
4571
4572 dict->lookup("Size", &obj1);
4573 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4574 obj1.arrayGet(0, &obj2);
4575 width = obj2.getInt();
4576 obj2.free();
4577 obj1.arrayGet(1, &obj2);
4578 height = obj2.getInt();
4579 obj2.free();
4580 writePSFmt("%%ALDImageDimensions: %d %d\n", width, height);
4581 }
4582 obj1.free();
4583
4584 //~ ignoring 'Tags' entry
4585 //~ need to use writePSString() and deal with >255-char lines
4586
4587 dict->lookup("Tint", &obj1);
4588 if (obj1.isNum()) {
4589 writePSFmt("%%ALDImageTint: %g\n", obj1.getNum());
4590 }
4591 obj1.free();
4592
4593 dict->lookup("Transparency", &obj1);
4594 if (obj1.isBool()) {
4595 writePSFmt("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false");
4596 }
4597 obj1.free();
4598
4599 writePS("%%BeginObject: image\n");
4600 writePS("opiMatrix2 setmatrix\n");
4601 ++opi13Nest;
4602 }
4603
4604 // Convert PDF user space coordinates to PostScript default user space
4605 // coordinates. This has to account for both the PDF CTM and the
4606 // PSOutputDev page-fitting transform.
4607 void PSOutputDev::opiTransform(GfxState *state, double x0, double y0,
4608 double *x1, double *y1) {
4609 double t;
4610
4611 state->transform(x0, y0, x1, y1);
4612 *x1 += tx;
4613 *y1 += ty;
4614 if (rotate == 90) {
4615 t = *x1;
4616 *x1 = -*y1;
4617 *y1 = t;
4618 } else if (rotate == 180) {
4619 *x1 = -*x1;
4620 *y1 = -*y1;
4621 } else if (rotate == 270) {
4622 t = *x1;
4623 *x1 = *y1;
4624 *y1 = -t;
4625 }
4626 *x1 *= xScale;
4627 *y1 *= yScale;
4628 }
4629
4630 void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
4631 Object dict;
4632
4633 if (globalParams->getPSOPI()) {
4634 opiDict->lookup("2.0", &dict);
4635 if (dict.isDict()) {
4636 writePS("%%EndIncludedImage\n");
4637 writePS("%%EndOPI\n");
4638 writePS("grestore\n");
4639 --opi20Nest;
4640 dict.free();
4641 } else {
4642 dict.free();
4643 opiDict->lookup("1.3", &dict);
4644 if (dict.isDict()) {
4645 writePS("%%EndObject\n");
4646 writePS("restore\n");
4647 --opi13Nest;
4648 }
4649 dict.free();
4650 }
4651 }
4652 }
4653
4654 GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
4655 if (fileSpec->isString()) {
4656 fileSpec->copy(fileName);
4657 return gTrue;
4658 }
4659 if (fileSpec->isDict()) {
4660 fileSpec->dictLookup("DOS", fileName);
4661 if (fileName->isString()) {
4662 return gTrue;
4663 }
4664 fileName->free();
4665 fileSpec->dictLookup("Mac", fileName);
4666 if (fileName->isString()) {
4667 return gTrue;
4668 }
4669 fileName->free();
4670 fileSpec->dictLookup("Unix", fileName);
4671 if (fileName->isString()) {
4672 return gTrue;
4673 }
4674 fileName->free();
4675 fileSpec->dictLookup("F", fileName);
4676 if (fileName->isString()) {
4677 return gTrue;
4678 }
4679 fileName->free();
4680 }
4681 return gFalse;
4682 }
4683 #endif // OPI_SUPPORT
4684
4685 void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
4686 writePSFmt("%g %g setcharwidth\n", wx, wy);
4687 writePS("q\n");
4688 }
4689
4690 void PSOutputDev::type3D1(GfxState *state, double wx, double wy,
4691 double llx, double lly, double urx, double ury) {
4692 t3WX = wx;
4693 t3WY = wy;
4694 t3LLX = llx;
4695 t3LLY = lly;
4696 t3URX = urx;
4697 t3URY = ury;
4698 t3String = new GString();
4699 writePS("q\n");
4700 t3Cacheable = gTrue;
4701 }
4702
4703 void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
4704 Stream *str;
4705 int c;
4706
4707 if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
4708 str = level1Stream;
4709 } else {
4710 str = psStream;
4711 }
4712 str->reset();
4713 while ((c = str->getChar()) != EOF) {
4714 writePSChar(c);
4715 }
4716 str->close();
4717 }
4718
4719 //~ can nextFunc be reset to 0 -- maybe at the start of each page?
4720 //~ or maybe at the start of each color space / pattern?
4721 void PSOutputDev::cvtFunction(Function *func) {
4722 SampledFunction *func0;
4723 ExponentialFunction *func2;
4724 StitchingFunction *func3;
4725 PostScriptFunction *func4;
4726 int thisFunc, m, n, nSamples, i, j, k;
4727
4728 switch (func->getType()) {
4729
4730 case -1: // identity
4731 writePS("{}\n");
4732 break;
4733
4734 case 0: // sampled
4735 func0 = (SampledFunction *)func;
4736 thisFunc = nextFunc++;
4737 m = func0->getInputSize();
4738 n = func0->getOutputSize();
4739 nSamples = n;
4740 for (i = 0; i < m; ++i) {
4741 nSamples *= func0->getSampleSize(i);
4742 }
4743 writePSFmt("/xpdfSamples%d [\n", thisFunc);
4744 for (i = 0; i < nSamples; ++i) {
4745 writePSFmt("%g\n", func0->getSamples()[i]);
4746 }
4747 writePS("] def\n");
4748 writePSFmt("{ %d array %d array %d 2 roll\n", 2*m, m, m+2);
4749 // [e01] [efrac] x0 x1 ... xm-1
4750 for (i = m-1; i >= 0; --i) {
4751 // [e01] [efrac] x0 x1 ... xi
4752 writePSFmt("%g sub %g mul %g add\n",
4753 func0->getDomainMin(i),
4754 (func0->getEncodeMax(i) - func0->getEncodeMin(i)) /
4755 (func0->getDomainMax(i) - func0->getDomainMin(i)),
4756 func0->getEncodeMin(i));
4757 // [e01] [efrac] x0 x1 ... xi-1 xi'
4758 writePSFmt("dup 0 lt { pop 0 } { dup %d gt { pop %d } if } ifelse\n",
4759 func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1);
4760 // [e01] [efrac] x0 x1 ... xi-1 xi'
4761 writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n");
4762 // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi')
4763 writePSFmt("%d index %d 3 2 roll put\n", i+3, i);
4764 // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi')
4765 writePSFmt("%d index %d 3 2 roll put\n", i+3, 2*i+1);
4766 // [e01] [efrac] x0 x1 ... xi-1 floor(xi')
4767 writePSFmt("%d index %d 3 2 roll put\n", i+2, 2*i);
4768 // [e01] [efrac] x0 x1 ... xi-1
4769 }
4770 // [e01] [efrac]
4771 for (i = 0; i < n; ++i) {
4772 // [e01] [efrac] y(0) ... y(i-1)
4773 for (j = 0; j < (1<<m); ++j) {
4774 // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(j-1)
4775 writePSFmt("xpdfSamples%d\n", thisFunc);
4776 k = m - 1;
4777 writePSFmt("%d index %d get\n", i+j+2, 2 * k + ((j >> k) & 1));
4778 for (k = m - 2; k >= 0; --k) {
4779 writePSFmt("%d mul %d index %d get add\n",
4780 func0->getSampleSize(k),
4781 i + j + 3,
4782 2 * k + ((j >> k) & 1));
4783 }
4784 if (n > 1) {
4785 writePSFmt("%d mul %d add ", n, i);
4786 }
4787 writePS("get\n");
4788 }
4789 // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1)
4790 for (j = 0; j < m; ++j) {
4791 // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1)
4792 for (k = 0; k < (1 << (m - j)); k += 2) {
4793 // [e01] [efrac] y(0) ... y(i-1) <k/2 s' values> <2^(m-j)-k s values>
4794 writePSFmt("%d index %d get dup\n", i + k/2 + (1 << (m-j)) - k, j);
4795 writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n");
4796 writePSFmt("%d 1 roll\n", k/2 + (1 << m-j) - k - 1);
4797 }
4798 // [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1)
4799 }
4800 // [e01] [efrac] y(0) ... y(i-1) s
4801 writePSFmt("%g mul %g add\n",
4802 func0->getDecodeMax(i) - func0->getDecodeMin(i),
4803 func0->getDecodeMin(i));
4804 writePSFmt("dup %g lt { pop %g } { dup %g gt { pop %g } if } ifelse\n",
4805 func0->getRangeMin(i), func0->getRangeMin(i),
4806 func0->getRangeMax(i), func0->getRangeMax(i));
4807 // [e01] [efrac] y(0) ... y(i-1) y(i)
4808 }
4809 // [e01] [efrac] y(0) ... y(n-1)
4810 writePSFmt("%d %d roll pop pop }\n", n+2, n);
4811 break;
4812
4813 case 2: // exponential
4814 func2 = (ExponentialFunction *)func;
4815 n = func2->getOutputSize();
4816 writePSFmt("{ dup %g lt { pop %g } { dup %g gt { pop %g } if } ifelse\n",
4817 func2->getDomainMin(0), func2->getDomainMin(0),
4818 func2->getDomainMax(0), func2->getDomainMax(0));
4819 // x
4820 for (i = 0; i < n; ++i) {
4821 // x y(0) .. y(i-1)
4822 writePSFmt("%d index %g exp %g mul %g add\n",
4823 i, func2->getE(), func2->getC1()[i] - func2->getC0()[i],
4824 func2->getC0()[i]);
4825 if (func2->getHasRange()) {
4826 writePSFmt("dup %g lt { pop %g } { dup %g gt { pop %g } if } ifelse\n",
4827 func2->getRangeMin(i), func2->getRangeMin(i),
4828 func2->getRangeMax(i), func2->getRangeMax(i));
4829 }
4830 }
4831 // x y(0) .. y(n-1)
4832 writePSFmt("%d %d roll pop }\n", n+1, n);
4833 break;
4834
4835 case 3: // stitching
4836 func3 = (StitchingFunction *)func;
4837 thisFunc = nextFunc++;
4838 for (i = 0; i < func3->getNumFuncs(); ++i) {
4839 cvtFunction(func3->getFunc(i));
4840 writePSFmt("/xpdfFunc%d_%d exch def\n", thisFunc, i);
4841 }
4842 writePSFmt("{ dup %g lt { pop %g } { dup %g gt { pop %g } if } ifelse\n",
4843 func3->getDomainMin(0), func3->getDomainMin(0),
4844 func3->getDomainMax(0), func3->getDomainMax(0));
4845 for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
4846 writePSFmt("dup %g lt { %g sub %g mul %g add xpdfFunc%d_%d } {\n",
4847 func3->getBounds()[i+1],
4848 func3->getBounds()[i],
4849 (func3->getEncode()[2*i+1] - func3->getEncode()[2*i]) /
4850 (func3->getBounds()[i+1] - func3->getBounds()[i]),
4851 func3->getEncode()[2*i],
4852 thisFunc, i);
4853 }
4854 writePSFmt("%g sub %g mul %g add xpdfFunc%d_%d\n",
4855 func3->getBounds()[i],
4856 (func3->getEncode()[2*i+1] - func3->getEncode()[2*i]) /
4857 (func3->getBounds()[i+1] - func3->getBounds()[i]),
4858 func3->getEncode()[2*i],
4859 thisFunc, i);
4860 for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
4861 writePS("} ifelse\n");
4862 }
4863 writePS("}\n");
4864 break;
4865
4866 case 4: // PostScript
4867 func4 = (PostScriptFunction *)func;
4868 writePS(func4->getCodeString()->getCString());
4869 writePS("\n");
4870 break;
4871 }
4872 }
4873
4874 void PSOutputDev::writePSChar(char c) {
4875 if (t3String) {
4876 t3String->append(c);
4877 } else {
4878 (*outputFunc)(outputStream, &c, 1);
4879 }
4880 }
4881
4882 void PSOutputDev::writePS(char *s) {
4883 if (t3String) {
4884 t3String->append(s);
4885 } else {
4886 (*outputFunc)(outputStream, s, strlen(s));
4887 }
4888 }
4889
4890 void PSOutputDev::writePSFmt(const char *fmt, ...) {
4891 va_list args;
4892 char buf[512];
4893
4894 va_start(args, fmt);
4895 vsprintf(buf, fmt, args);
4896 va_end(args);
4897 if (t3String) {
4898 t3String->append(buf);
4899 } else {
4900 (*outputFunc)(outputStream, buf, strlen(buf));
4901 }
4902 }
4903
4904 void PSOutputDev::writePSString(GString *s) {
4905 Guchar *p;
4906 int n, line;
4907 char buf[8];
4908
4909 writePSChar('(');
4910 line = 1;
4911 for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
4912 if (line >= 64) {
4913 writePSChar('\\');
4914 writePSChar('\n');
4915 line = 0;
4916 }
4917 if (*p == '(' || *p == ')' || *p == '\\') {
4918 writePSChar('\\');
4919 writePSChar((char)*p);
4920 line += 2;
4921 } else if (*p < 0x20 || *p >= 0x80) {
4922 sprintf(buf, "\\%03o", *p);
4923 writePS(buf);
4924 line += 4;
4925 } else {
4926 writePSChar((char)*p);
4927 ++line;
4928 }
4929 }
4930 writePSChar(')');
4931 }
4932
4933 void PSOutputDev::writePSName(char *s) {
4934 char *p;
4935 char c;
4936
4937 p = s;
4938 while ((c = *p++)) {
4939 if (c <= (char)0x20 || c >= (char)0x7f ||
4940 c == '(' || c == ')' || c == '<' || c == '>' ||
4941 c == '[' || c == ']' || c == '{' || c == '}' ||
4942 c == '/' || c == '%') {
4943 writePSFmt("#%02x", c & 0xff);
4944 } else {
4945 writePSChar(c);
4946 }
4947 }
4948 }
4949
4950 GString *PSOutputDev::filterPSName(GString *name) {
4951 GString *name2;
4952 char buf[8];
4953 int i;
4954 char c;
4955
4956 name2 = new GString();
4957
4958 // ghostscript chokes on names that begin with out-of-limits
4959 // numbers, e.g., 1e4foo is handled correctly (as a name), but
4960 // 1e999foo generates a limitcheck error
4961 c = name->getChar(0);
4962 if (c >= '0' && c <= '9') {
4963 name2->append('f');
4964 }
4965
4966 for (i = 0; i < name->getLength(); ++i) {
4967 c = name->getChar(i);
4968 if (c <= (char)0x20 || c >= (char)0x7f ||
4969 c == '(' || c == ')' || c == '<' || c == '>' ||
4970 c == '[' || c == ']' || c == '{' || c == '}' ||
4971 c == '/' || c == '%') {
4972 sprintf(buf, "#%02x", c & 0xff);
4973 name2->append(buf);
4974 } else {
4975 name2->append(c);
4976 }
4977 }
4978 return name2;
4979 }
4980
4981 GBool /* O - gTrue if selected, gFalse otherwise */
4982 PSOutputDev::checkRange(int page) /* I - Page number */
4983 {
4984 const char *range; /* Pointer into range string */
4985 int lower, upper; /* Lower and upper page numbers */
4986
4987
4988 if (pageRanges == NULL)
4989 return (gTrue); /* No range, print all pages... */
4990
4991 for (range = pageRanges; *range != '\0';)
4992 {
4993 if (*range == '-')
4994 {
4995 lower = 1;
4996 range ++;
4997 upper = strtol(range, (char **)&range, 10);
4998 }
4999 else
5000 {
5001 lower = strtol(range, (char **)&range, 10);
5002
5003 if (*range == '-')
5004 {
5005 range ++;
5006 if (!isdigit(*range & 255))
5007 upper = 65535;
5008 else
5009 upper = strtol(range, (char **)&range, 10);
5010 }
5011 else
5012 upper = lower;
5013 }
5014
5015 if (page >= lower && page <= upper)
5016 return (gTrue);
5017
5018 if (*range == ',')
5019 range ++;
5020 else
5021 break;
5022 }
5023
5024 return (gFalse);
5025 }