]>
Commit | Line | Data |
---|---|---|
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 | ||
58 | static char *prolog[] = { | |
59 | "/xpdf 75 dict def xpdf begin", | |
60 | "% PDF special state", | |
61 | "/pdfDictSize 15 def", | |
62 | "~1sn", | |
63 | "/pdfStates 64 array def", | |
64 | " 0 1 63 {", | |
65 | " pdfStates exch pdfDictSize dict", | |
66 | " dup /pdfStateIdx 3 index put", | |
67 | " put", | |
68 | " } for", | |
69 | "~123sn", | |
70 | "/pdfSetup {", | |
71 | " 3 1 roll 2 array astore", | |
72 | " /setpagedevice where {", | |
73 | " pop 3 dict begin", | |
74 | " /PageSize exch def", | |
75 | " /ImagingBBox null def", | |
76 | " /Policies 1 dict dup begin /PageSize 3 def end def", | |
77 | " { /Duplex true def } if", | |
78 | " currentdict end setpagedevice", | |
79 | " } {", | |
80 | " pop pop", | |
81 | " } ifelse", | |
82 | "} def", | |
83 | "~1sn", | |
84 | "/pdfOpNames [", | |
85 | " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke", | |
86 | " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender", | |
87 | " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath", | |
88 | "] def", | |
89 | "~123sn", | |
90 | "/pdfStartPage {", | |
91 | "~1sn", | |
92 | " pdfStates 0 get begin", | |
93 | "~23sn", | |
94 | " pdfDictSize dict begin", | |
95 | "~23n", | |
96 | " /pdfFillCS [] def", | |
97 | " /pdfFillXform {} def", | |
98 | " /pdfStrokeCS [] def", | |
99 | " /pdfStrokeXform {} def", | |
100 | "~1n", | |
101 | " /pdfFill 0 def", | |
102 | " /pdfStroke 0 def", | |
103 | "~1s", | |
104 | " /pdfFill [0 0 0 1] def", | |
105 | " /pdfStroke [0 0 0 1] def", | |
106 | "~23sn", | |
107 | " /pdfFill [0] def", | |
108 | " /pdfStroke [0] def", | |
109 | " /pdfFillOP false def", | |
110 | " /pdfStrokeOP false def", | |
111 | "~123sn", | |
112 | " /pdfLastFill false def", | |
113 | " /pdfLastStroke false def", | |
114 | " /pdfTextMat [1 0 0 1 0 0] def", | |
115 | " /pdfFontSize 0 def", | |
116 | " /pdfCharSpacing 0 def", | |
117 | " /pdfTextRender 0 def", | |
118 | " /pdfTextRise 0 def", | |
119 | " /pdfWordSpacing 0 def", | |
120 | " /pdfHorizScaling 1 def", | |
121 | " /pdfTextClipPath [] def", | |
122 | "} def", | |
123 | "/pdfEndPage { end } def", | |
124 | "~23s", | |
125 | "% separation convention operators", | |
126 | "/findcmykcustomcolor where {", | |
127 | " pop", | |
128 | "}{", | |
129 | " /findcmykcustomcolor { 5 array astore } def", | |
130 | "} ifelse", | |
131 | "/setcustomcolor where {", | |
132 | " pop", | |
133 | "}{", | |
134 | " /setcustomcolor {", | |
135 | " exch", | |
136 | " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", | |
137 | " 0 4 getinterval cvx", | |
138 | " [ exch /dup load exch { mul exch dup } /forall load", | |
139 | " /pop load dup ] cvx", | |
140 | " ] setcolorspace setcolor", | |
141 | " } def", | |
142 | "} ifelse", | |
143 | "/customcolorimage where {", | |
144 | " pop", | |
145 | "}{", | |
146 | " /customcolorimage {", | |
147 | " gsave", | |
148 | " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", | |
149 | " 0 4 getinterval", | |
150 | " [ exch /dup load exch { mul exch dup } /forall load", | |
151 | " /pop load dup ] cvx", | |
152 | " ] setcolorspace", | |
153 | " 10 dict begin", | |
154 | " /ImageType 1 def", | |
155 | " /DataSource exch def", | |
156 | " /ImageMatrix exch def", | |
157 | " /BitsPerComponent exch def", | |
158 | " /Height exch def", | |
159 | " /Width exch def", | |
160 | " /Decode [1 0] def", | |
161 | " currentdict end", | |
162 | " image", | |
163 | " grestore", | |
164 | " } def", | |
165 | "} ifelse", | |
166 | "~123sn", | |
167 | "% PDF color state", | |
168 | "~1n", | |
169 | "/g { dup /pdfFill exch def setgray", | |
170 | " /pdfLastFill true def /pdfLastStroke false def } def", | |
171 | "/G { dup /pdfStroke exch def setgray", | |
172 | " /pdfLastStroke true def /pdfLastFill false def } def", | |
173 | "/fCol {", | |
174 | " pdfLastFill not {", | |
175 | " pdfFill setgray", | |
176 | " /pdfLastFill true def /pdfLastStroke false def", | |
177 | " } if", | |
178 | "} def", | |
179 | "/sCol {", | |
180 | " pdfLastStroke not {", | |
181 | " pdfStroke setgray", | |
182 | " /pdfLastStroke true def /pdfLastFill false def", | |
183 | " } if", | |
184 | "} def", | |
185 | "~1s", | |
186 | "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", | |
187 | " /pdfLastFill true def /pdfLastStroke false def } def", | |
188 | "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", | |
189 | " /pdfLastStroke true def /pdfLastFill false def } def", | |
190 | "/fCol {", | |
191 | " pdfLastFill not {", | |
192 | " pdfFill aload pop setcmykcolor", | |
193 | " /pdfLastFill true def /pdfLastStroke false def", | |
194 | " } if", | |
195 | "} def", | |
196 | "/sCol {", | |
197 | " pdfLastStroke not {", | |
198 | " pdfStroke aload pop setcmykcolor", | |
199 | " /pdfLastStroke true def /pdfLastFill false def", | |
200 | " } if", | |
201 | "} def", | |
202 | "~23n", | |
203 | "/cs { /pdfFillXform exch def dup /pdfFillCS exch def", | |
204 | " setcolorspace } def", | |
205 | "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def", | |
206 | " setcolorspace } def", | |
207 | "/sc { pdfLastFill not { pdfFillCS setcolorspace } if", | |
208 | " dup /pdfFill exch def aload pop pdfFillXform setcolor", | |
209 | " /pdfLastFill true def /pdfLastStroke false def } def", | |
210 | "/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } if", | |
211 | " dup /pdfStroke exch def aload pop pdfStrokeXform setcolor", | |
212 | " /pdfLastStroke true def /pdfLastFill false def } def", | |
213 | "/op { /pdfFillOP exch def", | |
214 | " pdfLastFill { pdfFillOP setoverprint } if } def", | |
215 | "/OP { /pdfStrokeOP exch def", | |
216 | " pdfLastStroke { pdfStrokeOP setoverprint } if } def", | |
217 | "/fCol {", | |
218 | " pdfLastFill not {", | |
219 | " pdfFillCS setcolorspace", | |
220 | " pdfFill aload pop pdfFillXform setcolor", | |
221 | " pdfFillOP setoverprint", | |
222 | " /pdfLastFill true def /pdfLastStroke false def", | |
223 | " } if", | |
224 | "} def", | |
225 | "/sCol {", | |
226 | " pdfLastStroke not {", | |
227 | " pdfStrokeCS setcolorspace", | |
228 | " pdfStroke aload pop pdfStrokeXform setcolor", | |
229 | " pdfStrokeOP setoverprint", | |
230 | " /pdfLastStroke true def /pdfLastFill false def", | |
231 | " } if", | |
232 | "} def", | |
233 | "~23s", | |
234 | "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", | |
235 | " /pdfLastFill true def /pdfLastStroke false def } def", | |
236 | "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", | |
237 | " /pdfLastStroke true def /pdfLastFill false def } def", | |
238 | "/ck { 6 copy 6 array astore /pdfFill exch def", | |
239 | " findcmykcustomcolor exch setcustomcolor", | |
240 | " /pdfLastFill true def /pdfLastStroke false def } def", | |
241 | "/CK { 6 copy 6 array astore /pdfStroke exch def", | |
242 | " findcmykcustomcolor exch setcustomcolor", | |
243 | " /pdfLastStroke true def /pdfLastFill false def } def", | |
244 | "/op { /pdfFillOP exch def", | |
245 | " pdfLastFill { pdfFillOP setoverprint } if } def", | |
246 | "/OP { /pdfStrokeOP exch def", | |
247 | " pdfLastStroke { pdfStrokeOP setoverprint } if } def", | |
248 | "/fCol {", | |
249 | " pdfLastFill not {", | |
250 | " pdfFill aload length 4 eq {", | |
251 | " setcmykcolor", | |
252 | " }{", | |
253 | " findcmykcustomcolor exch setcustomcolor", | |
254 | " } ifelse", | |
255 | " pdfFillOP setoverprint", | |
256 | " /pdfLastFill true def /pdfLastStroke false def", | |
257 | " } if", | |
258 | "} def", | |
259 | "/sCol {", | |
260 | " pdfLastStroke not {", | |
261 | " pdfStroke aload length 4 eq {", | |
262 | " setcmykcolor", | |
263 | " }{", | |
264 | " findcmykcustomcolor exch setcustomcolor", | |
265 | " } ifelse", | |
266 | " pdfStrokeOP setoverprint", | |
267 | " /pdfLastStroke true def /pdfLastFill false def", | |
268 | " } if", | |
269 | "} def", | |
270 | "~123sn", | |
271 | "% build a font", | |
272 | "/pdfMakeFont {", | |
273 | " 4 3 roll findfont", | |
274 | " 4 2 roll matrix scale makefont", | |
275 | " dup length dict begin", | |
276 | " { 1 index /FID ne { def } { pop pop } ifelse } forall", | |
277 | " /Encoding exch def", | |
278 | " currentdict", | |
279 | " end", | |
280 | " definefont pop", | |
281 | "} def", | |
282 | "/pdfMakeFont16 {", | |
283 | " exch findfont", | |
284 | " dup length dict begin", | |
285 | " { 1 index /FID ne { def } { pop pop } ifelse } forall", | |
286 | " /WMode exch def", | |
287 | " currentdict", | |
288 | " end", | |
289 | " definefont pop", | |
290 | "} def", | |
291 | "~3sn", | |
292 | "/pdfMakeFont16L3 {", | |
293 | " 1 index /CIDFont resourcestatus {", | |
294 | " pop pop 1 index /CIDFont findresource /CIDFontType known", | |
295 | " } {", | |
296 | " false", | |
297 | " } ifelse", | |
298 | " {", | |
299 | " 0 eq { /Identity-H } { /Identity-V } ifelse", | |
300 | " exch 1 array astore composefont pop", | |
301 | " } {", | |
302 | " pdfMakeFont16", | |
303 | " } ifelse", | |
304 | "} def", | |
305 | "~123sn", | |
306 | "% graphics state operators", | |
307 | "~1sn", | |
308 | "/q {", | |
309 | " gsave", | |
310 | " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for", | |
311 | " pdfStates pdfStateIdx 1 add get begin", | |
312 | " pdfOpNames { exch def } forall", | |
313 | "} def", | |
314 | "/Q { end grestore } def", | |
315 | "~23sn", | |
316 | "/q { gsave pdfDictSize dict begin } def", | |
317 | "/Q {", | |
318 | " end grestore", | |
319 | " /pdfLastFill where {", | |
320 | " pop", | |
321 | " pdfLastFill {", | |
322 | " pdfFillOP setoverprint", | |
323 | " } {", | |
324 | " pdfStrokeOP setoverprint", | |
325 | " } ifelse", | |
326 | " } if", | |
327 | "} def", | |
328 | "~123sn", | |
329 | "/cm { concat } def", | |
330 | "/d { setdash } def", | |
331 | "/i { setflat } def", | |
332 | "/j { setlinejoin } def", | |
333 | "/J { setlinecap } def", | |
334 | "/M { setmiterlimit } def", | |
335 | "/w { setlinewidth } def", | |
336 | "% path segment operators", | |
337 | "/m { moveto } def", | |
338 | "/l { lineto } def", | |
339 | "/c { curveto } def", | |
340 | "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto", | |
341 | " neg 0 rlineto closepath } def", | |
342 | "/h { closepath } def", | |
343 | "% path painting operators", | |
344 | "/S { sCol stroke } def", | |
345 | "/Sf { fCol stroke } def", | |
346 | "/f { fCol fill } def", | |
347 | "/f* { fCol eofill } def", | |
348 | "% clipping operators", | |
349 | "/W { clip newpath } def", | |
350 | "/W* { eoclip newpath } def", | |
351 | "% text state operators", | |
352 | "/Tc { /pdfCharSpacing exch def } def", | |
353 | "/Tf { dup /pdfFontSize exch def", | |
354 | " dup pdfHorizScaling mul exch matrix scale", | |
355 | " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put", | |
356 | " exch findfont exch makefont setfont } def", | |
357 | "/Tr { /pdfTextRender exch def } def", | |
358 | "/Ts { /pdfTextRise exch def } def", | |
359 | "/Tw { /pdfWordSpacing exch def } def", | |
360 | "/Tz { /pdfHorizScaling exch def } def", | |
361 | "% text positioning operators", | |
362 | "/Td { pdfTextMat transform moveto } def", | |
363 | "/Tm { /pdfTextMat exch def } def", | |
364 | "% text string operators", | |
365 | "/cshow where {", | |
366 | " pop", | |
367 | " /cshow2 {", | |
368 | " dup {", | |
369 | " pop pop", | |
370 | " 1 string dup 0 3 index put 3 index exec", | |
371 | " } exch cshow", | |
372 | " pop pop", | |
373 | " } def", | |
374 | "}{", | |
375 | " /cshow2 {", | |
376 | " currentfont /FontType get 0 eq {", | |
377 | " 0 2 2 index length 1 sub {", | |
378 | " 2 copy get exch 1 add 2 index exch get", | |
379 | " 2 copy exch 256 mul add", | |
380 | " 2 string dup 0 6 5 roll put dup 1 5 4 roll put", | |
381 | " 3 index exec", | |
382 | " } for", | |
383 | " } {", | |
384 | " dup {", | |
385 | " 1 string dup 0 3 index put 3 index exec", | |
386 | " } forall", | |
387 | " } ifelse", | |
388 | " pop pop", | |
389 | " } def", | |
390 | "} ifelse", | |
391 | "/awcp {", // awidthcharpath | |
392 | " exch {", | |
393 | " false charpath", | |
394 | " 5 index 5 index rmoveto", | |
395 | " 6 index eq { 7 index 7 index rmoveto } if", | |
396 | " } exch cshow2", | |
397 | " 6 {pop} repeat", | |
398 | "} def", | |
399 | "/Tj {", | |
400 | " fCol", // because stringwidth has to draw Type 3 chars | |
401 | " 1 index stringwidth pdfTextMat idtransform pop", | |
402 | " sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse", | |
403 | " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32", | |
404 | " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0", | |
405 | " pdfTextMat dtransform", | |
406 | " 6 5 roll Tj1", | |
407 | "} def", | |
408 | "/Tj16 {", | |
409 | " fCol", // because stringwidth has to draw Type 3 chars | |
410 | " 2 index stringwidth pdfTextMat idtransform pop", | |
411 | " sub exch div", | |
412 | " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32", | |
413 | " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0", | |
414 | " pdfTextMat dtransform", | |
415 | " 6 5 roll Tj1", | |
416 | "} def", | |
417 | "/Tj16V {", | |
418 | " fCol", // because stringwidth has to draw Type 3 chars | |
419 | " 2 index stringwidth pdfTextMat idtransform exch pop", | |
420 | " sub exch div", | |
421 | " 0 pdfWordSpacing pdfTextMat dtransform 32", | |
422 | " 4 3 roll pdfCharSpacing add 0 exch", | |
423 | " pdfTextMat dtransform", | |
424 | " 6 5 roll Tj1", | |
425 | "} def", | |
426 | "/Tj1 {", | |
427 | " 0 pdfTextRise pdfTextMat dtransform rmoveto", | |
428 | " currentpoint 8 2 roll", | |
429 | " pdfTextRender 1 and 0 eq {", | |
430 | " 6 copy awidthshow", | |
431 | " } if", | |
432 | " pdfTextRender 3 and dup 1 eq exch 2 eq or {", | |
433 | " 7 index 7 index moveto", | |
434 | " 6 copy", | |
435 | " currentfont /FontType get 3 eq { fCol } { sCol } ifelse", | |
436 | " false awcp currentpoint stroke moveto", | |
437 | " } if", | |
438 | " pdfTextRender 4 and 0 ne {", | |
439 | " 8 6 roll moveto", | |
440 | " false awcp", | |
441 | " /pdfTextClipPath [ pdfTextClipPath aload pop", | |
442 | " {/moveto cvx}", | |
443 | " {/lineto cvx}", | |
444 | " {/curveto cvx}", | |
445 | " {/closepath cvx}", | |
446 | " pathforall ] def", | |
447 | " currentpoint newpath moveto", | |
448 | " } {", | |
449 | " 8 {pop} repeat", | |
450 | " } ifelse", | |
451 | " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", | |
452 | "} def", | |
453 | "/TJm { pdfFontSize 0.001 mul mul neg 0", | |
454 | " pdfTextMat dtransform rmoveto } def", | |
455 | "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch", | |
456 | " pdfTextMat dtransform rmoveto } def", | |
457 | "/Tclip { pdfTextClipPath cvx exec clip newpath", | |
458 | " /pdfTextClipPath [] def } def", | |
459 | "~1ns", | |
460 | "% Level 1 image operators", | |
461 | "~1n", | |
462 | "/pdfIm1 {", | |
463 | " /pdfImBuf1 4 index string def", | |
464 | " { currentfile pdfImBuf1 readhexstring pop } image", | |
465 | "} def", | |
466 | "~1s", | |
467 | "/pdfIm1Sep {", | |
468 | " /pdfImBuf1 4 index string def", | |
469 | " /pdfImBuf2 4 index string def", | |
470 | " /pdfImBuf3 4 index string def", | |
471 | " /pdfImBuf4 4 index string def", | |
472 | " { currentfile pdfImBuf1 readhexstring pop }", | |
473 | " { currentfile pdfImBuf2 readhexstring pop }", | |
474 | " { currentfile pdfImBuf3 readhexstring pop }", | |
475 | " { currentfile pdfImBuf4 readhexstring pop }", | |
476 | " true 4 colorimage", | |
477 | "} def", | |
478 | "~1ns", | |
479 | "/pdfImM1 {", | |
480 | " fCol /pdfImBuf1 4 index 7 add 8 idiv string def", | |
481 | " { currentfile pdfImBuf1 readhexstring pop } imagemask", | |
482 | "} def", | |
483 | "/pdfImM1a {", | |
484 | " { 2 copy get exch 1 add exch } imagemask", | |
485 | " pop pop", | |
486 | "} def", | |
487 | "~23sn", | |
488 | "% Level 2 image operators", | |
489 | "/pdfImBuf 100 string def", | |
490 | "/pdfIm {", | |
491 | " image", | |
492 | " { currentfile pdfImBuf readline", | |
493 | " not { pop exit } if", | |
494 | " (%-EOD-) eq { exit } if } loop", | |
495 | "} def", | |
496 | "~23s", | |
497 | "/pdfImSep {", | |
498 | " findcmykcustomcolor exch", | |
499 | " dup /Width get /pdfImBuf1 exch string def", | |
500 | " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def", | |
501 | " /pdfImDecodeLow exch def", | |
502 | " begin Width Height BitsPerComponent ImageMatrix DataSource end", | |
503 | " /pdfImData exch def", | |
504 | " { pdfImData pdfImBuf1 readstring pop", | |
505 | " 0 1 2 index length 1 sub {", | |
506 | " 1 index exch 2 copy get", | |
507 | " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi", | |
508 | " 255 exch sub put", | |
509 | " } for }", | |
510 | " 6 5 roll customcolorimage", | |
511 | " { currentfile pdfImBuf readline", | |
512 | " not { pop exit } if", | |
513 | " (%-EOD-) eq { exit } if } loop", | |
514 | "} def", | |
515 | "~23sn", | |
516 | "/pdfImM {", | |
517 | " fCol imagemask", | |
518 | " { currentfile pdfImBuf readline", | |
519 | " not { pop exit } if", | |
520 | " (%-EOD-) eq { exit } if } loop", | |
521 | "} def", | |
522 | "/pdfImClip {", | |
523 | " gsave", | |
524 | " 0 2 4 index length 1 sub {", | |
525 | " dup 4 index exch 2 copy", | |
526 | " get 5 index div put", | |
527 | " 1 add 3 index exch 2 copy", | |
528 | " get 3 index div put", | |
529 | " } for", | |
530 | " pop pop rectclip", | |
531 | "} def", | |
532 | "/pdfImClipEnd { grestore } def", | |
533 | "~23n", | |
534 | "% shading operators", | |
535 | "/colordelta {", | |
536 | " false 0 1 3 index length 1 sub {", | |
537 | " dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {", | |
538 | " pop true", | |
539 | " } if", | |
540 | " } for", | |
541 | " exch pop exch pop", | |
542 | "} def", | |
543 | "/funcCol { func n array astore } def", | |
544 | "/funcSH {", | |
545 | " dup 0 eq {", | |
546 | " true", | |
547 | " } {", | |
548 | " dup 6 eq {", | |
549 | " false", | |
550 | " } {", | |
551 | " 4 index 4 index funcCol dup", | |
552 | " 6 index 4 index funcCol dup", | |
553 | " 3 1 roll colordelta 3 1 roll", | |
554 | " 5 index 5 index funcCol dup", | |
555 | " 3 1 roll colordelta 3 1 roll", | |
556 | " 6 index 8 index funcCol dup", | |
557 | " 3 1 roll colordelta 3 1 roll", | |
558 | " colordelta or or or", | |
559 | " } ifelse", | |
560 | " } ifelse", | |
561 | " {", | |
562 | " 1 add", | |
563 | " 4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch", | |
564 | " 6 index 6 index 4 index 4 index 4 index funcSH", | |
565 | " 2 index 6 index 6 index 4 index 4 index funcSH", | |
566 | " 6 index 2 index 4 index 6 index 4 index funcSH", | |
567 | " 5 3 roll 3 2 roll funcSH pop pop", | |
568 | " } {", | |
569 | " pop 3 index 2 index add 0.5 mul 3 index 2 index add 0.5 mul", | |
570 | " funcCol sc", | |
571 | " dup 4 index exch mat transform m", | |
572 | " 3 index 3 index mat transform l", | |
573 | " 1 index 3 index mat transform l", | |
574 | " mat transform l pop pop h f*", | |
575 | " } ifelse", | |
576 | "} def", | |
577 | "/axialCol {", | |
578 | " dup 0 lt {", | |
579 | " pop t0", | |
580 | " } {", | |
581 | " dup 1 gt {", | |
582 | " pop t1", | |
583 | " } {", | |
584 | " dt mul t0 add", | |
585 | " } ifelse", | |
586 | " } ifelse", | |
587 | " func n array astore", | |
588 | "} def", | |
589 | "/axialSH {", | |
590 | " dup 0 eq {", | |
591 | " true", | |
592 | " } {", | |
593 | " dup 8 eq {", | |
594 | " false", | |
595 | " } {", | |
596 | " 2 index axialCol 2 index axialCol colordelta", | |
597 | " } ifelse", | |
598 | " } ifelse", | |
599 | " {", | |
600 | " 1 add 3 1 roll 2 copy add 0.5 mul", | |
601 | " dup 4 3 roll exch 4 index axialSH", | |
602 | " exch 3 2 roll axialSH", | |
603 | " } {", | |
604 | " pop 2 copy add 0.5 mul axialCol sc", | |
605 | " exch dup dx mul x0 add exch dy mul y0 add", | |
606 | " 3 2 roll dup dx mul x0 add exch dy mul y0 add", | |
607 | " dx abs dy abs ge {", | |
608 | " 2 copy yMin sub dy mul dx div add yMin m", | |
609 | " yMax sub dy mul dx div add yMax l", | |
610 | " 2 copy yMax sub dy mul dx div add yMax l", | |
611 | " yMin sub dy mul dx div add yMin l", | |
612 | " h f*", | |
613 | " } {", | |
614 | " exch 2 copy xMin sub dx mul dy div add xMin exch m", | |
615 | " xMax sub dx mul dy div add xMax exch l", | |
616 | " exch 2 copy xMax sub dx mul dy div add xMax exch l", | |
617 | " xMin sub dx mul dy div add xMin exch l", | |
618 | " h f*", | |
619 | " } ifelse", | |
620 | " } ifelse", | |
621 | "} def", | |
622 | "/radialCol {", | |
623 | " dup t0 lt {", | |
624 | " pop t0", | |
625 | " } {", | |
626 | " dup t1 gt {", | |
627 | " pop t1", | |
628 | " } if", | |
629 | " } ifelse", | |
630 | " func n array astore", | |
631 | "} def", | |
632 | "/radialSH {", | |
633 | " dup 0 eq {", | |
634 | " true", | |
635 | " } {", | |
636 | " dup 8 eq {", | |
637 | " false", | |
638 | " } {", | |
639 | " 2 index dt mul t0 add radialCol", | |
640 | " 2 index dt mul t0 add radialCol colordelta", | |
641 | " } ifelse", | |
642 | " } ifelse", | |
643 | " {", | |
644 | " 1 add 3 1 roll 2 copy add 0.5 mul", | |
645 | " dup 4 3 roll exch 4 index radialSH", | |
646 | " exch 3 2 roll radialSH", | |
647 | " } {", | |
648 | " pop 2 copy add 0.5 mul dt mul t0 add axialCol sc", | |
649 | " exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", | |
650 | " 0 360 arc h", | |
651 | " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", | |
652 | " 0 360 arc h f*", | |
653 | " } ifelse", | |
654 | "} def", | |
655 | "~123sn", | |
656 | "end", | |
657 | NULL | |
658 | }; | |
659 | ||
660 | static char *cmapProlog[] = { | |
661 | "/CIDInit /ProcSet findresource begin", | |
662 | "10 dict begin", | |
663 | " begincmap", | |
664 | " /CMapType 1 def", | |
665 | " /CMapName /Identity-H def", | |
666 | " /CIDSystemInfo 3 dict dup begin", | |
667 | " /Registry (Adobe) def", | |
668 | " /Ordering (Identity) def", | |
669 | " /Supplement 0 def", | |
670 | " end def", | |
671 | " 1 begincodespacerange", | |
672 | " <0000> <ffff>", | |
673 | " endcodespacerange", | |
674 | " 0 usefont", | |
675 | " 1 begincidrange", | |
676 | " <0000> <ffff> 0", | |
677 | " endcidrange", | |
678 | " endcmap", | |
679 | " currentdict CMapName exch /CMap defineresource pop", | |
680 | "end", | |
681 | "10 dict begin", | |
682 | " begincmap", | |
683 | " /CMapType 1 def", | |
684 | " /CMapName /Identity-V def", | |
685 | " /CIDSystemInfo 3 dict dup begin", | |
686 | " /Registry (Adobe) def", | |
687 | " /Ordering (Identity) def", | |
688 | " /Supplement 0 def", | |
689 | " end def", | |
690 | " /WMode 1 def", | |
691 | " 1 begincodespacerange", | |
692 | " <0000> <ffff>", | |
693 | " endcodespacerange", | |
694 | " 0 usefont", | |
695 | " 1 begincidrange", | |
696 | " <0000> <ffff> 0", | |
697 | " endcidrange", | |
698 | " endcmap", | |
699 | " currentdict CMapName exch /CMap defineresource pop", | |
700 | "end", | |
701 | "end", | |
702 | NULL | |
703 | }; | |
704 | ||
705 | //------------------------------------------------------------------------ | |
706 | // Fonts | |
707 | //------------------------------------------------------------------------ | |
708 | ||
709 | struct PSSubstFont { | |
710 | char *psName; // PostScript name | |
711 | double mWidth; // width of 'm' character | |
712 | }; | |
713 | ||
714 | static char *psFonts[] = { | |
715 | "Courier", | |
716 | "Courier-Bold", | |
717 | "Courier-Oblique", | |
718 | "Courier-BoldOblique", | |
719 | "Helvetica", | |
720 | "Helvetica-Bold", | |
721 | "Helvetica-Oblique", | |
722 | "Helvetica-BoldOblique", | |
723 | "Symbol", | |
724 | "Times-Roman", | |
725 | "Times-Bold", | |
726 | "Times-Italic", | |
727 | "Times-BoldItalic", | |
728 | "ZapfDingbats", | |
729 | NULL | |
730 | }; | |
731 | ||
732 | static PSSubstFont psSubstFonts[] = { | |
733 | {"Helvetica", 0.833}, | |
734 | {"Helvetica-Oblique", 0.833}, | |
735 | {"Helvetica-Bold", 0.889}, | |
736 | {"Helvetica-BoldOblique", 0.889}, | |
737 | {"Times-Roman", 0.788}, | |
738 | {"Times-Italic", 0.722}, | |
739 | {"Times-Bold", 0.833}, | |
740 | {"Times-BoldItalic", 0.778}, | |
741 | {"Courier", 0.600}, | |
742 | {"Courier-Oblique", 0.600}, | |
743 | {"Courier-Bold", 0.600}, | |
744 | {"Courier-BoldOblique", 0.600} | |
745 | }; | |
746 | ||
747 | // Encoding info for substitute 16-bit font | |
748 | struct PSFont16Enc { | |
749 | Ref fontID; | |
750 | GString *enc; | |
751 | }; | |
752 | ||
753 | //------------------------------------------------------------------------ | |
754 | // process colors | |
755 | //------------------------------------------------------------------------ | |
756 | ||
757 | #define psProcessCyan 1 | |
758 | #define psProcessMagenta 2 | |
759 | #define psProcessYellow 4 | |
760 | #define psProcessBlack 8 | |
761 | #define psProcessCMYK 15 | |
762 | ||
763 | //------------------------------------------------------------------------ | |
764 | // PSOutCustomColor | |
765 | //------------------------------------------------------------------------ | |
766 | ||
767 | class PSOutCustomColor { | |
768 | public: | |
769 | ||
770 | PSOutCustomColor(double cA, double mA, | |
771 | double yA, double kA, GString *nameA); | |
772 | ~PSOutCustomColor(); | |
773 | ||
774 | double c, m, y, k; | |
775 | GString *name; | |
776 | PSOutCustomColor *next; | |
777 | }; | |
778 | ||
779 | PSOutCustomColor::PSOutCustomColor(double cA, double mA, | |
780 | double yA, double kA, GString *nameA) { | |
781 | c = cA; | |
782 | m = mA; | |
783 | y = yA; | |
784 | k = kA; | |
785 | name = nameA; | |
786 | next = NULL; | |
787 | } | |
788 | ||
789 | PSOutCustomColor::~PSOutCustomColor() { | |
790 | delete name; | |
791 | } | |
792 | ||
793 | //------------------------------------------------------------------------ | |
794 | // DeviceNRecoder | |
795 | //------------------------------------------------------------------------ | |
796 | ||
797 | class DeviceNRecoder: public FilterStream { | |
798 | public: | |
799 | ||
800 | DeviceNRecoder(Stream *strA, int widthA, int heightA, | |
801 | GfxImageColorMap *colorMapA); | |
802 | virtual ~DeviceNRecoder(); | |
803 | virtual StreamKind getKind() { return strWeird; } | |
804 | virtual void reset(); | |
805 | virtual int getChar() | |
806 | { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; } | |
807 | virtual int lookChar() | |
808 | { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; } | |
809 | virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; } | |
810 | virtual GBool isBinary(GBool last = gTrue) { return gTrue; } | |
811 | virtual GBool isEncoder() { return gTrue; } | |
812 | ||
813 | private: | |
814 | ||
815 | GBool fillBuf(); | |
816 | ||
817 | int width, height; | |
818 | GfxImageColorMap *colorMap; | |
819 | Function *func; | |
820 | ImageStream *imgStr; | |
821 | int buf[gfxColorMaxComps]; | |
822 | int pixelIdx; | |
823 | int bufIdx; | |
824 | int bufSize; | |
825 | }; | |
826 | ||
827 | DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA, | |
828 | GfxImageColorMap *colorMapA): | |
829 | FilterStream(strA) { | |
830 | width = widthA; | |
831 | height = heightA; | |
832 | colorMap = colorMapA; | |
833 | imgStr = NULL; | |
834 | pixelIdx = 0; | |
835 | bufIdx = gfxColorMaxComps; | |
836 | bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> | |
837 | getAlt()->getNComps(); | |
838 | func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> | |
839 | getTintTransformFunc(); | |
840 | } | |
841 | ||
842 | DeviceNRecoder::~DeviceNRecoder() { | |
843 | if (imgStr) { | |
844 | delete imgStr; | |
845 | } | |
846 | } | |
847 | ||
848 | void DeviceNRecoder::reset() { | |
849 | imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), | |
850 | colorMap->getBits()); | |
851 | imgStr->reset(); | |
852 | } | |
853 | ||
854 | GBool DeviceNRecoder::fillBuf() { | |
855 | Guchar pixBuf[gfxColorMaxComps]; | |
856 | GfxColor color; | |
857 | double x[gfxColorMaxComps], y[gfxColorMaxComps]; | |
858 | int i; | |
859 | ||
860 | if (pixelIdx >= width * height) { | |
861 | return gFalse; | |
862 | } | |
863 | imgStr->getPixel(pixBuf); | |
864 | colorMap->getColor(pixBuf, &color); | |
865 | for (i = 0; | |
866 | i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps(); | |
867 | ++i) { | |
868 | x[i] = colToDbl(color.c[i]); | |
869 | } | |
870 | func->transform(x, y); | |
871 | for (i = 0; i < bufSize; ++i) { | |
872 | buf[i] = (int)(y[i] * 255 + 0.5); | |
873 | } | |
874 | bufIdx = 0; | |
875 | ++pixelIdx; | |
876 | return gTrue; | |
877 | } | |
878 | ||
879 | //------------------------------------------------------------------------ | |
880 | // PSOutputDev | |
881 | //------------------------------------------------------------------------ | |
882 | ||
883 | extern "C" { | |
884 | typedef void (*SignalFunc)(int); | |
885 | } | |
886 | ||
887 | static void outputToFile(void *stream, char *data, int len) { | |
888 | fwrite(data, 1, len, (FILE *)stream); | |
889 | } | |
890 | ||
891 | PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog, | |
892 | int firstPage, int lastPage, PSOutMode modeA, | |
893 | int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, | |
894 | GBool manualCtrlA, const char *pageRangesA) { | |
895 | FILE *f; | |
896 | PSFileType fileTypeA; | |
897 | ||
898 | underlayCbk = NULL; | |
899 | underlayCbkData = NULL; | |
900 | overlayCbk = NULL; | |
901 | overlayCbkData = NULL; | |
902 | ||
903 | fontIDs = NULL; | |
904 | fontFileIDs = NULL; | |
905 | fontFileNames = NULL; | |
906 | font16Enc = NULL; | |
907 | xobjStack = NULL; | |
908 | embFontList = NULL; | |
909 | customColors = NULL; | |
910 | haveTextClip = gFalse; | |
911 | t3String = NULL; | |
912 | ||
913 | // open file or pipe | |
914 | if (!strcmp(fileName, "-")) { | |
915 | fileTypeA = psStdout; | |
916 | f = stdout; | |
917 | } else if (fileName[0] == '|') { | |
918 | fileTypeA = psPipe; | |
919 | #ifdef HAVE_POPEN | |
920 | #ifndef WIN32 | |
921 | signal(SIGPIPE, (SignalFunc)SIG_IGN); | |
922 | #endif | |
923 | if (!(f = popen(fileName + 1, "w"))) { | |
924 | error(-1, "Couldn't run print command '%s'", fileName); | |
925 | ok = gFalse; | |
926 | return; | |
927 | } | |
928 | #else | |
929 | error(-1, "Print commands are not supported ('%s')", fileName); | |
930 | ok = gFalse; | |
931 | return; | |
932 | #endif | |
933 | } else { | |
934 | fileTypeA = psFile; | |
935 | if (!(f = fopen(fileName, "w"))) { | |
936 | error(-1, "Couldn't open PostScript file '%s'", fileName); | |
937 | ok = gFalse; | |
938 | return; | |
939 | } | |
940 | } | |
941 | ||
942 | init(outputToFile, f, fileTypeA, | |
943 | xrefA, catalog, firstPage, lastPage, modeA, | |
944 | imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, pageRangesA); | |
945 | } | |
946 | ||
947 | PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, | |
948 | XRef *xrefA, Catalog *catalog, | |
949 | int firstPage, int lastPage, PSOutMode modeA, | |
950 | int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, | |
951 | GBool manualCtrlA, const char *pageRangesA) { | |
952 | underlayCbk = NULL; | |
953 | underlayCbkData = NULL; | |
954 | overlayCbk = NULL; | |
955 | overlayCbkData = NULL; | |
956 | ||
957 | fontIDs = NULL; | |
958 | fontFileIDs = NULL; | |
959 | fontFileNames = NULL; | |
960 | font16Enc = NULL; | |
961 | xobjStack = NULL; | |
962 | embFontList = NULL; | |
963 | customColors = NULL; | |
964 | haveTextClip = gFalse; | |
965 | t3String = NULL; | |
966 | ||
967 | init(outputFuncA, outputStreamA, psGeneric, | |
968 | xrefA, catalog, firstPage, lastPage, modeA, | |
969 | imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, pageRangesA); | |
970 | } | |
971 | ||
972 | void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA, | |
973 | PSFileType fileTypeA, XRef *xrefA, Catalog *catalog, | |
974 | int firstPage, int lastPage, PSOutMode modeA, | |
975 | int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, | |
976 | GBool manualCtrlA, const char *pageRangesA) { | |
977 | Page *page; | |
978 | PDFRectangle *box; | |
979 | ||
980 | // initialize | |
981 | ok = gTrue; | |
982 | outputFunc = outputFuncA; | |
983 | outputStream = outputStreamA; | |
984 | fileType = fileTypeA; | |
985 | xref = xrefA; | |
986 | level = globalParams->getPSLevel(); | |
987 | mode = modeA; | |
988 | paperWidth = globalParams->getPSPaperWidth(); | |
989 | paperHeight = globalParams->getPSPaperHeight(); | |
990 | imgLLX = imgLLXA; | |
991 | imgLLY = imgLLYA; | |
992 | imgURX = imgURXA; | |
993 | imgURY = imgURYA; | |
994 | pageRanges = pageRangesA; | |
995 | if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) { | |
996 | globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY); | |
997 | } | |
998 | if (paperWidth < 0 || paperHeight < 0) { | |
999 | // this check is needed in case the document has zero pages | |
1000 | if (firstPage > 0 && firstPage <= catalog->getNumPages()) { | |
1001 | page = catalog->getPage(firstPage); | |
1002 | paperWidth = (int)ceil(page->getMediaWidth()); | |
1003 | paperHeight = (int)ceil(page->getMediaHeight()); | |
1004 | } else { | |
1005 | paperWidth = 1; | |
1006 | paperHeight = 1; | |
1007 | } | |
1008 | imgLLX = imgLLY = 0; | |
1009 | imgURX = paperWidth; | |
1010 | imgURY = paperHeight; | |
1011 | } | |
1012 | manualCtrl = manualCtrlA; | |
1013 | if (mode == psModeForm) { | |
1014 | lastPage = firstPage; | |
1015 | } | |
1016 | processColors = 0; | |
1017 | inType3Char = gFalse; | |
1018 | ||
1019 | #if OPI_SUPPORT | |
1020 | // initialize OPI nesting levels | |
1021 | opi13Nest = 0; | |
1022 | opi20Nest = 0; | |
1023 | #endif | |
1024 | ||
1025 | tx0 = ty0 = 0; | |
1026 | xScale0 = yScale0 = 0; | |
1027 | rotate0 = -1; | |
1028 | clipLLX0 = clipLLY0 = 0; | |
1029 | clipURX0 = clipURY0 = -1; | |
1030 | ||
1031 | // initialize fontIDs, fontFileIDs, and fontFileNames lists | |
1032 | fontIDSize = 64; | |
1033 | fontIDLen = 0; | |
1034 | fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref)); | |
1035 | fontFileIDSize = 64; | |
1036 | fontFileIDLen = 0; | |
1037 | fontFileIDs = (Ref *)gmallocn(fontFileIDSize, sizeof(Ref)); | |
1038 | fontFileNameSize = 64; | |
1039 | fontFileNameLen = 0; | |
1040 | fontFileNames = (GString **)gmallocn(fontFileNameSize, sizeof(GString *)); | |
1041 | nextTrueTypeNum = 0; | |
1042 | font16EncLen = 0; | |
1043 | font16EncSize = 0; | |
1044 | ||
1045 | xobjStack = new GList(); | |
1046 | numSaves = 0; | |
1047 | numTilingPatterns = 0; | |
1048 | nextFunc = 0; | |
1049 | ||
1050 | // initialize embedded font resource comment list | |
1051 | embFontList = new GString(); | |
1052 | ||
1053 | if (!manualCtrl) { | |
1054 | // this check is needed in case the document has zero pages | |
1055 | if (firstPage > 0 && firstPage <= catalog->getNumPages()) { | |
1056 | writeHeader(firstPage, lastPage, | |
1057 | catalog->getPage(firstPage)->getMediaBox(), | |
1058 | catalog->getPage(firstPage)->getCropBox(), | |
1059 | catalog->getPage(firstPage)->getRotate()); | |
1060 | } else { | |
1061 | box = new PDFRectangle(0, 0, 1, 1); | |
1062 | writeHeader(firstPage, lastPage, box, box, 0); | |
1063 | delete box; | |
1064 | } | |
1065 | if (mode != psModeForm) { | |
1066 | writePS("%%BeginProlog\n"); | |
1067 | } | |
1068 | writeXpdfProcset(); | |
1069 | if (mode != psModeForm) { | |
1070 | writePS("%%EndProlog\n"); | |
1071 | writePS("%%BeginSetup\n"); | |
1072 | } | |
1073 | writeDocSetup(catalog, firstPage, lastPage); | |
1074 | if (mode != psModeForm) { | |
1075 | writePS("%%EndSetup\n"); | |
1076 | } | |
1077 | } | |
1078 | ||
1079 | // initialize sequential page number | |
1080 | seqPage = 1; | |
1081 | } | |
1082 | ||
1083 | PSOutputDev::~PSOutputDev() { | |
1084 | PSOutCustomColor *cc; | |
1085 | int i; | |
1086 | ||
1087 | if (ok) { | |
1088 | if (!manualCtrl) { | |
1089 | writePS("%%Trailer\n"); | |
1090 | writeTrailer(); | |
1091 | if (mode != psModeForm) { | |
1092 | writePS("%%EOF\n"); | |
1093 | } | |
1094 | } | |
1095 | if (fileType == psFile) { | |
1096 | #ifdef MACOS | |
1097 | ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle); | |
1098 | #endif | |
1099 | fclose((FILE *)outputStream); | |
1100 | } | |
1101 | #ifdef HAVE_POPEN | |
1102 | else if (fileType == psPipe) { | |
1103 | pclose((FILE *)outputStream); | |
1104 | #ifndef WIN32 | |
1105 | signal(SIGPIPE, (SignalFunc)SIG_DFL); | |
1106 | #endif | |
1107 | } | |
1108 | #endif | |
1109 | } | |
1110 | if (embFontList) { | |
1111 | delete embFontList; | |
1112 | } | |
1113 | if (fontIDs) { | |
1114 | gfree(fontIDs); | |
1115 | } | |
1116 | if (fontFileIDs) { | |
1117 | gfree(fontFileIDs); | |
1118 | } | |
1119 | if (fontFileNames) { | |
1120 | for (i = 0; i < fontFileNameLen; ++i) { | |
1121 | delete fontFileNames[i]; | |
1122 | } | |
1123 | gfree(fontFileNames); | |
1124 | } | |
1125 | if (font16Enc) { | |
1126 | for (i = 0; i < font16EncLen; ++i) { | |
1127 | delete font16Enc[i].enc; | |
1128 | } | |
1129 | gfree(font16Enc); | |
1130 | } | |
1131 | if (xobjStack) { | |
1132 | delete xobjStack; | |
1133 | } | |
1134 | while (customColors) { | |
1135 | cc = customColors; | |
1136 | customColors = cc->next; | |
1137 | delete cc; | |
1138 | } | |
1139 | } | |
1140 | ||
1141 | void PSOutputDev::writeHeader(int firstPage, int lastPage, | |
1142 | PDFRectangle *mediaBox, PDFRectangle *cropBox, | |
1143 | int pageRotate) { | |
1144 | Object info, obj1; | |
1145 | double x1, y1, x2, y2; | |
1146 | GString *s; | |
1147 | int i; | |
1148 | ||
1149 | switch (mode) { | |
1150 | case psModePS: | |
1151 | writePS("%!PS-Adobe-3.0\n"); | |
1152 | break; | |
1153 | case psModeEPS: | |
1154 | writePS("%!PS-Adobe-3.0 EPSF-3.0\n"); | |
1155 | break; | |
1156 | case psModeForm: | |
1157 | writePS("%!PS-Adobe-3.0 Resource-Form\n"); | |
1158 | break; | |
1159 | } | |
1160 | ||
1161 | writePSFmt("% 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 | ||
1303 | void 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 | ||
1340 | void 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 | ||
1387 | void PSOutputDev::writePageTrailer() { | |
1388 | if (mode != psModeForm) { | |
1389 | writePS("pdfEndPage\n"); | |
1390 | } | |
1391 | } | |
1392 | ||
1393 | void 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 | ||
1432 | void 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 | ||
1528 | void 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 | ||
1558 | void 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 | ||
1792 | void 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?). | |
1924 | void 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 | ||
1964 | void 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 | ||
2003 | void 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 | ||
2055 | void 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 | ||
2108 | void 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 | ||
2153 | void 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 | ||
2211 | void 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 | ||
2300 | void 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 | ||
2331 | void 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 | ||
2424 | GBool 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 | ||
2599 | void 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 | ||
2619 | void PSOutputDev::saveState(GfxState *state) { | |
2620 | writePS("q\n"); | |
2621 | ++numSaves; | |
2622 | } | |
2623 | ||
2624 | void PSOutputDev::restoreState(GfxState *state) { | |
2625 | writePS("Q\n"); | |
2626 | --numSaves; | |
2627 | } | |
2628 | ||
2629 | void 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 | ||
2634 | void 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 | ||
2649 | void PSOutputDev::updateFlatness(GfxState *state) { | |
2650 | writePSFmt("%d i\n", state->getFlatness()); | |
2651 | } | |
2652 | ||
2653 | void PSOutputDev::updateLineJoin(GfxState *state) { | |
2654 | writePSFmt("%d j\n", state->getLineJoin()); | |
2655 | } | |
2656 | ||
2657 | void PSOutputDev::updateLineCap(GfxState *state) { | |
2658 | writePSFmt("%d J\n", state->getLineCap()); | |
2659 | } | |
2660 | ||
2661 | void PSOutputDev::updateMiterLimit(GfxState *state) { | |
2662 | writePSFmt("%g M\n", state->getMiterLimit()); | |
2663 | } | |
2664 | ||
2665 | void PSOutputDev::updateLineWidth(GfxState *state) { | |
2666 | writePSFmt("%g w\n", state->getLineWidth()); | |
2667 | } | |
2668 | ||
2669 | void 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 | ||
2687 | void 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 | ||
2705 | void 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 | ||
2768 | void 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 | ||
2831 | void 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 | ||
2846 | void 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 | ||
2865 | void PSOutputDev::updateFillOverprint(GfxState *state) { | |
2866 | if (level >= psLevel2) { | |
2867 | writePSFmt("%s op\n", state->getFillOverprint() ? "true" : "false"); | |
2868 | } | |
2869 | } | |
2870 | ||
2871 | void PSOutputDev::updateStrokeOverprint(GfxState *state) { | |
2872 | if (level >= psLevel2) { | |
2873 | writePSFmt("%s OP\n", state->getStrokeOverprint() ? "true" : "false"); | |
2874 | } | |
2875 | } | |
2876 | ||
2877 | void 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 | ||
2886 | void 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 | ||
2899 | void PSOutputDev::updateCharSpace(GfxState *state) { | |
2900 | writePSFmt("%g Tc\n", state->getCharSpace()); | |
2901 | } | |
2902 | ||
2903 | void 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 | ||
2914 | void PSOutputDev::updateRise(GfxState *state) { | |
2915 | writePSFmt("%g Ts\n", state->getRise()); | |
2916 | } | |
2917 | ||
2918 | void PSOutputDev::updateWordSpace(GfxState *state) { | |
2919 | writePSFmt("%g Tw\n", state->getWordSpace()); | |
2920 | } | |
2921 | ||
2922 | void 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 | ||
2932 | void PSOutputDev::updateTextPos(GfxState *state) { | |
2933 | writePSFmt("%g %g Td\n", state->getLineX(), state->getLineY()); | |
2934 | } | |
2935 | ||
2936 | void 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 | ||
2944 | void 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 | ||
2955 | void PSOutputDev::fill(GfxState *state) { | |
2956 | doPath(state->getPath()); | |
2957 | writePS("f\n"); | |
2958 | } | |
2959 | ||
2960 | void PSOutputDev::eoFill(GfxState *state) { | |
2961 | doPath(state->getPath()); | |
2962 | writePS("f*\n"); | |
2963 | } | |
2964 | ||
2965 | void 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 | ||
3025 | void 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 | ||
3057 | void 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 | ||
3138 | void 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 | ||
3233 | void PSOutputDev::clip(GfxState *state) { | |
3234 | doPath(state->getPath()); | |
3235 | writePS("W\n"); | |
3236 | } | |
3237 | ||
3238 | void PSOutputDev::eoClip(GfxState *state) { | |
3239 | doPath(state->getPath()); | |
3240 | writePS("W*\n"); | |
3241 | } | |
3242 | ||
3243 | void 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 | ||
3299 | void 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 | ||
3405 | void PSOutputDev::endTextObject(GfxState *state) { | |
3406 | if (haveTextClip) { | |
3407 | writePS("Tclip\n"); | |
3408 | haveTextClip = gFalse; | |
3409 | } | |
3410 | } | |
3411 | ||
3412 | void 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 | ||
3426 | void 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 | ||
3452 | void 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 | ||
3481 | void 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 | ||
3591 | void 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 | ||
3649 | void 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 | ||
3980 | void 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 | |
4212 | void 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 | ||
4231 | void 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 | ||
4343 | void 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. | |
4558 | void 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 | ||
4581 | void 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 | ||
4605 | GBool 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 | ||
4636 | void PSOutputDev::type3D0(GfxState *state, double wx, double wy) { | |
4637 | writePSFmt("%g %g setcharwidth\n", wx, wy); | |
4638 | writePS("q\n"); | |
4639 | } | |
4640 | ||
4641 | void 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 | ||
4654 | void 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? | |
4672 | void 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 | ||
4825 | void PSOutputDev::writePSChar(char c) { | |
4826 | if (t3String) { | |
4827 | t3String->append(c); | |
4828 | } else { | |
4829 | (*outputFunc)(outputStream, &c, 1); | |
4830 | } | |
4831 | } | |
4832 | ||
4833 | void PSOutputDev::writePS(char *s) { | |
4834 | if (t3String) { | |
4835 | t3String->append(s); | |
4836 | } else { | |
4837 | (*outputFunc)(outputStream, s, strlen(s)); | |
4838 | } | |
4839 | } | |
4840 | ||
4841 | void 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 | ||
4855 | void 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 | ||
4884 | void 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 | ||
4901 | GString *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 | ||
4932 | GBool /* O - gTrue if selected, gFalse otherwise */ | |
4933 | PSOutputDev::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 | } |