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