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