]>
Commit | Line | Data |
---|---|---|
9c72faab | 1 | //======================================================================== |
2 | // | |
3 | // PSOutputDev.cc | |
4 | // | |
0e2e516e | 5 | // Copyright 1996-2003 Glyph & Cog, LLC |
9c72faab | 6 | // |
7 | //======================================================================== | |
8 | ||
ae65da6c | 9 | #include <config.h> |
10 | ||
11 | #ifdef USE_GCC_PRAGMAS | |
9c72faab | 12 | #pragma implementation |
13 | #endif | |
14 | ||
15 | #include <stdio.h> | |
16 | #include <stddef.h> | |
17 | #include <stdarg.h> | |
18 | #include <signal.h> | |
19 | #include <math.h> | |
20 | #include "GString.h" | |
0e2e516e | 21 | #include "GList.h" |
9c72faab | 22 | #include "config.h" |
be8c3862 | 23 | #include "GlobalParams.h" |
9c72faab | 24 | #include "Object.h" |
25 | #include "Error.h" | |
753453e4 | 26 | #include "Function.h" |
be8c3862 | 27 | #include "Gfx.h" |
9c72faab | 28 | #include "GfxState.h" |
29 | #include "GfxFont.h" | |
be8c3862 | 30 | #include "UnicodeMap.h" |
9c72faab | 31 | #include "FontFile.h" |
32 | #include "Catalog.h" | |
33 | #include "Page.h" | |
34 | #include "Stream.h" | |
be8c3862 | 35 | #include "Annot.h" |
9c72faab | 36 | #include "PSOutputDev.h" |
37 | ||
52118ca3 | 38 | #ifdef MACOS |
39 | // needed for setting type/creator of MacOS files | |
40 | #include "ICSupport.h" | |
41 | #endif | |
42 | ||
9c72faab | 43 | //------------------------------------------------------------------------ |
44 | // PostScript prolog and setup | |
45 | //------------------------------------------------------------------------ | |
46 | ||
8dc4af74 | 47 | static const char *prolog[] = { |
9c72faab | 48 | "/xpdf 75 dict def xpdf begin", |
49 | "% PDF special state", | |
891091df | 50 | "/pdfDictSize 15 def", |
51 | "~1", | |
52 | "/pdfStates 64 array def", | |
53 | " 0 1 63 {", | |
54 | " pdfStates exch pdfDictSize dict", | |
55 | " dup /pdfStateIdx 3 index put", | |
56 | " put", | |
57 | " } for", | |
58 | "~a", | |
9c72faab | 59 | "/pdfSetup {", |
09d1832b | 60 | #if 0 /* NOT FOR CUPS */ |
be8c3862 | 61 | " 3 1 roll 2 array astore", |
9c72faab | 62 | " /setpagedevice where {", |
be8c3862 | 63 | " pop 3 dict begin", |
64 | " /PageSize exch def", | |
9c72faab | 65 | " /ImagingBBox null def", |
66 | " /Policies 1 dict dup begin /PageSize 3 def end def", | |
ae65da6c | 67 | " { /Duplex true def } if", |
be8c3862 | 68 | " currentdict end setpagedevice", |
9c72faab | 69 | " } {", |
be8c3862 | 70 | " pop pop", |
9c72faab | 71 | " } ifelse", |
09d1832b | 72 | #else |
73 | " pop pop pop", | |
74 | #endif /* 0 */ | |
9c72faab | 75 | "} def", |
891091df | 76 | "~1", |
77 | "/pdfOpNames [", | |
78 | " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke", | |
79 | " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender", | |
80 | " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath", | |
81 | "] def", | |
82 | "~a", | |
9c72faab | 83 | "/pdfStartPage {", |
891091df | 84 | "~1", |
85 | " pdfStates 0 get begin", | |
86 | "~2", | |
9c72faab | 87 | " pdfDictSize dict begin", |
891091df | 88 | "~a", |
9c72faab | 89 | " /pdfFill [0] def", |
90 | " /pdfStroke [0] def", | |
91 | " /pdfLastFill false def", | |
92 | " /pdfLastStroke false def", | |
93 | " /pdfTextMat [1 0 0 1 0 0] def", | |
94 | " /pdfFontSize 0 def", | |
95 | " /pdfCharSpacing 0 def", | |
96 | " /pdfTextRender 0 def", | |
97 | " /pdfTextRise 0 def", | |
98 | " /pdfWordSpacing 0 def", | |
99 | " /pdfHorizScaling 1 def", | |
891091df | 100 | " /pdfTextClipPath [] def", |
9c72faab | 101 | "} def", |
102 | "/pdfEndPage { end } def", | |
753453e4 | 103 | "% separation convention operators", |
104 | "/findcmykcustomcolor where {", | |
105 | " pop", | |
106 | "}{", | |
107 | " /findcmykcustomcolor { 5 array astore } def", | |
108 | "} ifelse", | |
109 | "/setcustomcolor where {", | |
110 | " pop", | |
111 | "}{", | |
112 | " /setcustomcolor {", | |
113 | " exch", | |
114 | " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", | |
115 | " 0 4 getinterval cvx", | |
116 | " [ exch /dup load exch { mul exch dup } /forall load", | |
117 | " /pop load dup ] cvx", | |
118 | " ] setcolorspace setcolor", | |
119 | " } def", | |
120 | "} ifelse", | |
121 | "/customcolorimage where {", | |
122 | " pop", | |
123 | "}{", | |
124 | " /customcolorimage {", | |
125 | " gsave", | |
126 | " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", | |
0e2e516e | 127 | " 0 4 getinterval", |
753453e4 | 128 | " [ exch /dup load exch { mul exch dup } /forall load", |
129 | " /pop load dup ] cvx", | |
130 | " ] setcolorspace", | |
131 | " 10 dict begin", | |
132 | " /ImageType 1 def", | |
133 | " /DataSource exch def", | |
134 | " /ImageMatrix exch def", | |
135 | " /BitsPerComponent exch def", | |
136 | " /Height exch def", | |
137 | " /Width exch def", | |
138 | " /Decode [1 0] def", | |
139 | " currentdict end", | |
140 | " image", | |
141 | " grestore", | |
142 | " } def", | |
143 | "} ifelse", | |
144 | "% PDF color state", | |
b5cb0608 | 145 | "/sCol {", |
146 | " pdfLastStroke not {", | |
147 | " pdfStroke aload length", | |
753453e4 | 148 | " dup 1 eq {", |
149 | " pop setgray", | |
150 | " }{", | |
151 | " dup 3 eq {", | |
152 | " pop setrgbcolor", | |
153 | " }{", | |
154 | " 4 eq {", | |
155 | " setcmykcolor", | |
156 | " }{", | |
157 | " findcmykcustomcolor exch setcustomcolor", | |
158 | " } ifelse", | |
159 | " } ifelse", | |
160 | " } ifelse", | |
b5cb0608 | 161 | " /pdfLastStroke true def /pdfLastFill false def", |
162 | " } if", | |
163 | "} def", | |
164 | "/fCol {", | |
165 | " pdfLastFill not {", | |
166 | " pdfFill aload length", | |
753453e4 | 167 | " dup 1 eq {", |
168 | " pop setgray", | |
169 | " }{", | |
170 | " dup 3 eq {", | |
171 | " pop setrgbcolor", | |
172 | " }{", | |
173 | " 4 eq {", | |
174 | " setcmykcolor", | |
175 | " }{", | |
176 | " findcmykcustomcolor exch setcustomcolor", | |
177 | " } ifelse", | |
178 | " } ifelse", | |
179 | " } ifelse", | |
b5cb0608 | 180 | " /pdfLastFill true def /pdfLastStroke false def", |
181 | " } if", | |
182 | "} def", | |
9c72faab | 183 | "% build a font", |
184 | "/pdfMakeFont {", | |
52118ca3 | 185 | " 4 3 roll findfont", |
186 | " 4 2 roll matrix scale makefont", | |
9c72faab | 187 | " dup length dict begin", |
188 | " { 1 index /FID ne { def } { pop pop } ifelse } forall", | |
189 | " /Encoding exch def", | |
190 | " currentdict", | |
191 | " end", | |
192 | " definefont pop", | |
193 | "} def", | |
be8c3862 | 194 | "/pdfMakeFont16 {", |
195 | " exch findfont", | |
196 | " dup length dict begin", | |
197 | " { 1 index /FID ne { def } { pop pop } ifelse } forall", | |
198 | " /WMode exch def", | |
199 | " currentdict", | |
200 | " end", | |
201 | " definefont pop", | |
202 | "} def", | |
203 | "/pdfMakeFont16L3 {", | |
204 | " 1 index /CIDFont resourcestatus {", | |
205 | " pop pop 1 index /CIDFont findresource /CIDFontType known", | |
206 | " } {", | |
207 | " false", | |
208 | " } ifelse", | |
209 | " {", | |
210 | " 0 eq { /Identity-H } { /Identity-V } ifelse", | |
211 | " exch 1 array astore composefont pop", | |
212 | " } {", | |
213 | " pdfMakeFont16", | |
214 | " } ifelse", | |
215 | "} def", | |
9c72faab | 216 | "% graphics state operators", |
891091df | 217 | "~1", |
218 | "/q {", | |
219 | " gsave", | |
220 | " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for", | |
221 | " pdfStates pdfStateIdx 1 add get begin", | |
222 | " pdfOpNames { exch def } forall", | |
223 | "} def", | |
224 | "~2", | |
9c72faab | 225 | "/q { gsave pdfDictSize dict begin } def", |
891091df | 226 | "~a", |
9c72faab | 227 | "/Q { end grestore } def", |
228 | "/cm { concat } def", | |
229 | "/d { setdash } def", | |
230 | "/i { setflat } def", | |
231 | "/j { setlinejoin } def", | |
232 | "/J { setlinecap } def", | |
233 | "/M { setmiterlimit } def", | |
234 | "/w { setlinewidth } def", | |
235 | "% color operators", | |
236 | "/g { dup 1 array astore /pdfFill exch def setgray", | |
b5cb0608 | 237 | " /pdfLastFill true def /pdfLastStroke false def } def", |
9c72faab | 238 | "/G { dup 1 array astore /pdfStroke exch def setgray", |
239 | " /pdfLastStroke true def /pdfLastFill false def } def", | |
240 | "/rg { 3 copy 3 array astore /pdfFill exch def setrgbcolor", | |
b5cb0608 | 241 | " /pdfLastFill true def /pdfLastStroke false def } def", |
9c72faab | 242 | "/RG { 3 copy 3 array astore /pdfStroke exch def setrgbcolor", |
b5cb0608 | 243 | " /pdfLastStroke true def /pdfLastFill false def } def", |
244 | "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", | |
245 | " /pdfLastFill true def /pdfLastStroke false def } def", | |
246 | "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", | |
9c72faab | 247 | " /pdfLastStroke true def /pdfLastFill false def } def", |
753453e4 | 248 | "/ck { 6 copy 6 array astore /pdfFill exch def", |
249 | " findcmykcustomcolor exch setcustomcolor", | |
250 | " /pdfLastFill true def /pdfLastStroke false def } def", | |
251 | "/CK { 6 copy 6 array astore /pdfStroke exch def", | |
252 | " findcmykcustomcolor exch setcustomcolor", | |
253 | " /pdfLastStroke true def /pdfLastFill false def } def", | |
9c72faab | 254 | "% path segment operators", |
255 | "/m { moveto } def", | |
256 | "/l { lineto } def", | |
257 | "/c { curveto } def", | |
258 | "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto", | |
259 | " neg 0 rlineto closepath } def", | |
52118ca3 | 260 | "/h { closepath } def", |
9c72faab | 261 | "% path painting operators", |
262 | "/S { sCol stroke } def", | |
be8c3862 | 263 | "/Sf { fCol stroke } def", |
9c72faab | 264 | "/f { fCol fill } def", |
265 | "/f* { fCol eofill } def", | |
266 | "% clipping operators", | |
267 | "/W { clip newpath } def", | |
268 | "/W* { eoclip newpath } def", | |
269 | "% text state operators", | |
270 | "/Tc { /pdfCharSpacing exch def } def", | |
271 | "/Tf { dup /pdfFontSize exch def", | |
272 | " dup pdfHorizScaling mul exch matrix scale", | |
273 | " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put", | |
274 | " exch findfont exch makefont setfont } def", | |
275 | "/Tr { /pdfTextRender exch def } def", | |
276 | "/Ts { /pdfTextRise exch def } def", | |
277 | "/Tw { /pdfWordSpacing exch def } def", | |
278 | "/Tz { /pdfHorizScaling exch def } def", | |
279 | "% text positioning operators", | |
280 | "/Td { pdfTextMat transform moveto } def", | |
281 | "/Tm { /pdfTextMat exch def } def", | |
282 | "% text string operators", | |
891091df | 283 | "/cshow where {", |
284 | " pop", | |
285 | " /cshow2 {", | |
286 | " dup {", | |
287 | " pop pop", | |
288 | " 1 string dup 0 3 index put 3 index exec", | |
289 | " } exch cshow", | |
290 | " pop pop", | |
291 | " } def", | |
292 | "}{", | |
293 | " /cshow2 {", | |
294 | " currentfont /FontType get 0 eq {", | |
295 | " 0 2 2 index length 1 sub {", | |
296 | " 2 copy get exch 1 add 2 index exch get", | |
297 | " 2 copy exch 256 mul add", | |
298 | " 2 string dup 0 6 5 roll put dup 1 5 4 roll put", | |
299 | " 3 index exec", | |
300 | " } for", | |
301 | " } {", | |
302 | " dup {", | |
303 | " 1 string dup 0 3 index put 3 index exec", | |
be8c3862 | 304 | " } forall", |
891091df | 305 | " } ifelse", |
306 | " pop pop", | |
307 | " } def", | |
308 | "} ifelse", | |
309 | "/awcp {", // awidthcharpath | |
310 | " exch {", | |
311 | " false charpath", | |
312 | " 5 index 5 index rmoveto", | |
313 | " 6 index eq { 7 index 7 index rmoveto } if", | |
314 | " } exch cshow2", | |
be8c3862 | 315 | " 6 {pop} repeat", |
316 | "} def", | |
891091df | 317 | "/Tj {", |
318 | " fCol", // because stringwidth has to draw Type 3 chars | |
9c72faab | 319 | " 1 index stringwidth pdfTextMat idtransform pop", |
320 | " sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse", | |
753453e4 | 321 | " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32", |
322 | " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0", | |
323 | " pdfTextMat dtransform", | |
891091df | 324 | " 6 5 roll Tj1", |
325 | "} def", | |
326 | "/Tj16 {", | |
327 | " fCol", // because stringwidth has to draw Type 3 chars | |
be8c3862 | 328 | " 2 index stringwidth pdfTextMat idtransform pop", |
329 | " sub exch div", | |
330 | " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32", | |
331 | " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0", | |
332 | " pdfTextMat dtransform", | |
891091df | 333 | " 6 5 roll Tj1", |
334 | "} def", | |
335 | "/Tj16V {", | |
336 | " fCol", // because stringwidth has to draw Type 3 chars | |
be8c3862 | 337 | " 2 index stringwidth pdfTextMat idtransform exch pop", |
338 | " sub exch div", | |
339 | " 0 pdfWordSpacing pdfTextMat dtransform 32", | |
340 | " 4 3 roll pdfCharSpacing add 0 exch", | |
341 | " pdfTextMat dtransform", | |
891091df | 342 | " 6 5 roll Tj1", |
343 | "} def", | |
344 | "/Tj1 {", | |
345 | " 0 pdfTextRise pdfTextMat dtransform rmoveto", | |
346 | " currentpoint 8 2 roll", | |
347 | " pdfTextRender 1 and 0 eq {", | |
348 | " 6 copy awidthshow", | |
349 | " } if", | |
350 | " pdfTextRender 3 and dup 1 eq exch 2 eq or {", | |
351 | " 7 index 7 index moveto", | |
352 | " 6 copy", | |
353 | " currentfont /FontType get 3 eq { fCol } { sCol } ifelse", | |
354 | " false awcp currentpoint stroke moveto", | |
355 | " } if", | |
356 | " pdfTextRender 4 and 0 ne {", | |
357 | " 8 6 roll moveto", | |
358 | " false awcp", | |
359 | " /pdfTextClipPath [ pdfTextClipPath aload pop", | |
360 | " {/moveto cvx}", | |
361 | " {/lineto cvx}", | |
362 | " {/curveto cvx}", | |
363 | " {/closepath cvx}", | |
364 | " pathforall ] def", | |
365 | " currentpoint newpath moveto", | |
366 | " } {", | |
367 | " 8 {pop} repeat", | |
368 | " } ifelse", | |
369 | " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", | |
370 | "} def", | |
9c72faab | 371 | "/TJm { pdfFontSize 0.001 mul mul neg 0", |
372 | " pdfTextMat dtransform rmoveto } def", | |
be8c3862 | 373 | "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch", |
374 | " pdfTextMat dtransform rmoveto } def", | |
891091df | 375 | "/Tclip { pdfTextClipPath cvx exec clip", |
376 | " /pdfTextClipPath [] def } def", | |
9c72faab | 377 | "% Level 1 image operators", |
378 | "/pdfIm1 {", | |
379 | " /pdfImBuf1 4 index string def", | |
380 | " { currentfile pdfImBuf1 readhexstring pop } image", | |
381 | "} def", | |
b5cb0608 | 382 | "/pdfIm1Sep {", |
383 | " /pdfImBuf1 4 index string def", | |
384 | " /pdfImBuf2 4 index string def", | |
385 | " /pdfImBuf3 4 index string def", | |
386 | " /pdfImBuf4 4 index string def", | |
387 | " { currentfile pdfImBuf1 readhexstring pop }", | |
388 | " { currentfile pdfImBuf2 readhexstring pop }", | |
389 | " { currentfile pdfImBuf3 readhexstring pop }", | |
390 | " { currentfile pdfImBuf4 readhexstring pop }", | |
391 | " true 4 colorimage", | |
392 | "} def", | |
9c72faab | 393 | "/pdfImM1 {", |
394 | " /pdfImBuf1 4 index 7 add 8 idiv string def", | |
395 | " { currentfile pdfImBuf1 readhexstring pop } imagemask", | |
396 | "} def", | |
397 | "% Level 2 image operators", | |
398 | "/pdfImBuf 100 string def", | |
399 | "/pdfIm {", | |
400 | " image", | |
401 | " { currentfile pdfImBuf readline", | |
402 | " not { pop exit } if", | |
403 | " (%-EOD-) eq { exit } if } loop", | |
404 | "} def", | |
753453e4 | 405 | "/pdfImSep {", |
406 | " findcmykcustomcolor exch", | |
407 | " dup /Width get /pdfImBuf1 exch string def", | |
0e2e516e | 408 | " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def", |
409 | " /pdfImDecodeLow exch def", | |
753453e4 | 410 | " begin Width Height BitsPerComponent ImageMatrix DataSource end", |
411 | " /pdfImData exch def", | |
412 | " { pdfImData pdfImBuf1 readstring pop", | |
413 | " 0 1 2 index length 1 sub {", | |
0e2e516e | 414 | " 1 index exch 2 copy get", |
415 | " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi", | |
416 | " 255 exch sub put", | |
753453e4 | 417 | " } for }", |
418 | " 6 5 roll customcolorimage", | |
419 | " { currentfile pdfImBuf readline", | |
420 | " not { pop exit } if", | |
421 | " (%-EOD-) eq { exit } if } loop", | |
422 | "} def", | |
9c72faab | 423 | "/pdfImM {", |
424 | " fCol imagemask", | |
425 | " { currentfile pdfImBuf readline", | |
426 | " not { pop exit } if", | |
427 | " (%-EOD-) eq { exit } if } loop", | |
428 | "} def", | |
429 | "end", | |
430 | NULL | |
431 | }; | |
432 | ||
8dc4af74 | 433 | static const char *cmapProlog[] = { |
be8c3862 | 434 | "/CIDInit /ProcSet findresource begin", |
435 | "10 dict begin", | |
436 | " begincmap", | |
437 | " /CMapType 1 def", | |
438 | " /CMapName /Identity-H def", | |
439 | " /CIDSystemInfo 3 dict dup begin", | |
440 | " /Registry (Adobe) def", | |
441 | " /Ordering (Identity) def", | |
442 | " /Supplement 0 def", | |
443 | " end def", | |
444 | " 1 begincodespacerange", | |
445 | " <0000> <ffff>", | |
446 | " endcodespacerange", | |
447 | " 0 usefont", | |
448 | " 1 begincidrange", | |
449 | " <0000> <ffff> 0", | |
450 | " endcidrange", | |
451 | " endcmap", | |
452 | " currentdict CMapName exch /CMap defineresource pop", | |
453 | "end", | |
454 | "10 dict begin", | |
455 | " begincmap", | |
456 | " /CMapType 1 def", | |
457 | " /CMapName /Identity-V def", | |
458 | " /CIDSystemInfo 3 dict dup begin", | |
459 | " /Registry (Adobe) def", | |
460 | " /Ordering (Identity) def", | |
461 | " /Supplement 0 def", | |
462 | " end def", | |
463 | " /WMode 1 def", | |
464 | " 1 begincodespacerange", | |
465 | " <0000> <ffff>", | |
466 | " endcodespacerange", | |
467 | " 0 usefont", | |
468 | " 1 begincidrange", | |
469 | " <0000> <ffff> 0", | |
470 | " endcidrange", | |
471 | " endcmap", | |
472 | " currentdict CMapName exch /CMap defineresource pop", | |
473 | "end", | |
474 | "end", | |
475 | NULL | |
476 | }; | |
477 | ||
9c72faab | 478 | //------------------------------------------------------------------------ |
479 | // Fonts | |
480 | //------------------------------------------------------------------------ | |
481 | ||
9c72faab | 482 | struct PSSubstFont { |
8dc4af74 | 483 | const char *psName; // PostScript name |
9c72faab | 484 | double mWidth; // width of 'm' character |
485 | }; | |
486 | ||
8dc4af74 | 487 | static const char *psFonts[] = { |
be8c3862 | 488 | "Courier", |
489 | "Courier-Bold", | |
490 | "Courier-Oblique", | |
491 | "Courier-BoldOblique", | |
492 | "Helvetica", | |
493 | "Helvetica-Bold", | |
494 | "Helvetica-Oblique", | |
495 | "Helvetica-BoldOblique", | |
496 | "Symbol", | |
497 | "Times-Roman", | |
498 | "Times-Bold", | |
499 | "Times-Italic", | |
500 | "Times-BoldItalic", | |
501 | "ZapfDingbats", | |
502 | NULL | |
9c72faab | 503 | }; |
504 | ||
505 | static PSSubstFont psSubstFonts[] = { | |
506 | {"Helvetica", 0.833}, | |
507 | {"Helvetica-Oblique", 0.833}, | |
508 | {"Helvetica-Bold", 0.889}, | |
509 | {"Helvetica-BoldOblique", 0.889}, | |
510 | {"Times-Roman", 0.788}, | |
511 | {"Times-Italic", 0.722}, | |
512 | {"Times-Bold", 0.833}, | |
513 | {"Times-BoldItalic", 0.778}, | |
514 | {"Courier", 0.600}, | |
515 | {"Courier-Oblique", 0.600}, | |
516 | {"Courier-Bold", 0.600}, | |
517 | {"Courier-BoldOblique", 0.600} | |
518 | }; | |
519 | ||
be8c3862 | 520 | // Encoding info for substitute 16-bit font |
521 | struct PSFont16Enc { | |
522 | Ref fontID; | |
523 | GString *enc; | |
524 | }; | |
525 | ||
753453e4 | 526 | //------------------------------------------------------------------------ |
527 | // process colors | |
528 | //------------------------------------------------------------------------ | |
529 | ||
530 | #define psProcessCyan 1 | |
531 | #define psProcessMagenta 2 | |
532 | #define psProcessYellow 4 | |
533 | #define psProcessBlack 8 | |
534 | #define psProcessCMYK 15 | |
535 | ||
536 | //------------------------------------------------------------------------ | |
537 | // PSOutCustomColor | |
538 | //------------------------------------------------------------------------ | |
539 | ||
540 | class PSOutCustomColor { | |
541 | public: | |
542 | ||
543 | PSOutCustomColor(double cA, double mA, | |
544 | double yA, double kA, GString *nameA); | |
545 | ~PSOutCustomColor(); | |
546 | ||
547 | double c, m, y, k; | |
548 | GString *name; | |
549 | PSOutCustomColor *next; | |
550 | }; | |
551 | ||
552 | PSOutCustomColor::PSOutCustomColor(double cA, double mA, | |
553 | double yA, double kA, GString *nameA) { | |
554 | c = cA; | |
555 | m = mA; | |
556 | y = yA; | |
557 | k = kA; | |
558 | name = nameA; | |
559 | next = NULL; | |
560 | } | |
561 | ||
562 | PSOutCustomColor::~PSOutCustomColor() { | |
563 | delete name; | |
564 | } | |
565 | ||
9c72faab | 566 | //------------------------------------------------------------------------ |
567 | // PSOutputDev | |
568 | //------------------------------------------------------------------------ | |
569 | ||
753453e4 | 570 | extern "C" { |
571 | typedef void (*SignalFunc)(int); | |
572 | } | |
573 | ||
8dc4af74 | 574 | static void outputToFile(void *stream, const char *data, int len) { |
ae65da6c | 575 | fwrite(data, 1, len, (FILE *)stream); |
576 | } | |
577 | ||
8dc4af74 | 578 | PSOutputDev::PSOutputDev(const char *fileName, XRef *xrefA, Catalog *catalog, |
891091df | 579 | int firstPage, int lastPage, PSOutMode modeA, |
580 | int paperWidthA, int paperHeightA, | |
581 | GBool manualCtrlA) { | |
ae65da6c | 582 | FILE *f; |
583 | PSFileType fileTypeA; | |
9c72faab | 584 | |
891091df | 585 | underlayCbk = NULL; |
586 | underlayCbkData = NULL; | |
587 | overlayCbk = NULL; | |
588 | overlayCbkData = NULL; | |
589 | ||
9c72faab | 590 | fontIDs = NULL; |
591 | fontFileIDs = NULL; | |
592 | fontFileNames = NULL; | |
be8c3862 | 593 | font16Enc = NULL; |
0e2e516e | 594 | xobjStack = NULL; |
52118ca3 | 595 | embFontList = NULL; |
753453e4 | 596 | customColors = NULL; |
891091df | 597 | haveTextClip = gFalse; |
be8c3862 | 598 | t3String = NULL; |
599 | ||
9c72faab | 600 | // open file or pipe |
9c72faab | 601 | if (!strcmp(fileName, "-")) { |
ae65da6c | 602 | fileTypeA = psStdout; |
9c72faab | 603 | f = stdout; |
604 | } else if (fileName[0] == '|') { | |
ae65da6c | 605 | fileTypeA = psPipe; |
9c72faab | 606 | #ifdef HAVE_POPEN |
607 | #ifndef WIN32 | |
753453e4 | 608 | signal(SIGPIPE, (SignalFunc)SIG_IGN); |
9c72faab | 609 | #endif |
610 | if (!(f = popen(fileName + 1, "w"))) { | |
611 | error(-1, "Couldn't run print command '%s'", fileName); | |
612 | ok = gFalse; | |
613 | return; | |
614 | } | |
615 | #else | |
616 | error(-1, "Print commands are not supported ('%s')", fileName); | |
617 | ok = gFalse; | |
618 | return; | |
619 | #endif | |
620 | } else { | |
ae65da6c | 621 | fileTypeA = psFile; |
9c72faab | 622 | if (!(f = fopen(fileName, "w"))) { |
623 | error(-1, "Couldn't open PostScript file '%s'", fileName); | |
624 | ok = gFalse; | |
625 | return; | |
626 | } | |
627 | } | |
628 | ||
ae65da6c | 629 | init(outputToFile, f, fileTypeA, |
891091df | 630 | xrefA, catalog, firstPage, lastPage, modeA, |
631 | paperWidthA, paperHeightA, manualCtrlA); | |
ae65da6c | 632 | } |
633 | ||
634 | PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, | |
635 | XRef *xrefA, Catalog *catalog, | |
891091df | 636 | int firstPage, int lastPage, PSOutMode modeA, |
637 | int paperWidthA, int paperHeightA, | |
638 | GBool manualCtrlA) { | |
ae65da6c | 639 | fontIDs = NULL; |
640 | fontFileIDs = NULL; | |
641 | fontFileNames = NULL; | |
642 | font16Enc = NULL; | |
0e2e516e | 643 | xobjStack = NULL; |
ae65da6c | 644 | embFontList = NULL; |
645 | customColors = NULL; | |
891091df | 646 | haveTextClip = gFalse; |
ae65da6c | 647 | t3String = NULL; |
648 | ||
649 | init(outputFuncA, outputStreamA, psGeneric, | |
891091df | 650 | xrefA, catalog, firstPage, lastPage, modeA, |
651 | paperWidthA, paperHeightA, manualCtrlA); | |
ae65da6c | 652 | } |
653 | ||
654 | void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA, | |
655 | PSFileType fileTypeA, XRef *xrefA, Catalog *catalog, | |
891091df | 656 | int firstPage, int lastPage, PSOutMode modeA, |
657 | int paperWidthA, int paperHeightA, | |
658 | GBool manualCtrlA) { | |
ae65da6c | 659 | Page *page; |
ae65da6c | 660 | |
661 | // initialize | |
662 | ok = gTrue; | |
663 | outputFunc = outputFuncA; | |
664 | outputStream = outputStreamA; | |
665 | fileType = fileTypeA; | |
666 | xref = xrefA; | |
667 | level = globalParams->getPSLevel(); | |
668 | mode = modeA; | |
891091df | 669 | paperWidth = paperWidthA; |
670 | paperHeight = paperHeightA; | |
671 | if (paperWidth == 0) { | |
ae65da6c | 672 | paperWidth = globalParams->getPSPaperWidth(); |
891091df | 673 | } |
674 | if (paperHeight == 0) { | |
ae65da6c | 675 | paperHeight = globalParams->getPSPaperHeight(); |
891091df | 676 | } |
0e2e516e | 677 | if (paperWidth < 0 || paperHeight < 0) { |
678 | page = catalog->getPage(firstPage); | |
679 | paperWidth = (int)(page->getWidth() + 0.5); | |
680 | paperHeight = (int)(page->getHeight() + 0.5); | |
681 | } | |
891091df | 682 | manualCtrl = manualCtrlA; |
ae65da6c | 683 | if (mode == psModeForm) { |
684 | lastPage = firstPage; | |
685 | } | |
686 | processColors = 0; | |
687 | inType3Char = gFalse; | |
688 | ||
689 | #if OPI_SUPPORT | |
690 | // initialize OPI nesting levels | |
691 | opi13Nest = 0; | |
692 | opi20Nest = 0; | |
693 | #endif | |
694 | ||
9c72faab | 695 | // initialize fontIDs, fontFileIDs, and fontFileNames lists |
696 | fontIDSize = 64; | |
697 | fontIDLen = 0; | |
698 | fontIDs = (Ref *)gmalloc(fontIDSize * sizeof(Ref)); | |
699 | fontFileIDSize = 64; | |
700 | fontFileIDLen = 0; | |
701 | fontFileIDs = (Ref *)gmalloc(fontFileIDSize * sizeof(Ref)); | |
702 | fontFileNameSize = 64; | |
703 | fontFileNameLen = 0; | |
52118ca3 | 704 | fontFileNames = (GString **)gmalloc(fontFileNameSize * sizeof(GString *)); |
891091df | 705 | nextTrueTypeNum = 0; |
be8c3862 | 706 | font16EncLen = 0; |
707 | font16EncSize = 0; | |
891091df | 708 | |
0e2e516e | 709 | xobjStack = new GList(); |
891091df | 710 | numSaves = 0; |
52118ca3 | 711 | |
712 | // initialize embedded font resource comment list | |
713 | embFontList = new GString(); | |
9c72faab | 714 | |
891091df | 715 | if (!manualCtrl) { |
716 | writeHeader(firstPage, lastPage, catalog->getPage(firstPage)->getBox()); | |
717 | if (mode != psModeForm) { | |
718 | writePS("%%BeginProlog\n"); | |
719 | } | |
720 | writeXpdfProcset(); | |
721 | if (mode != psModeForm) { | |
722 | writePS("%%EndProlog\n"); | |
723 | writePS("%%BeginSetup\n"); | |
724 | } | |
725 | writeDocSetup(catalog, firstPage, lastPage); | |
726 | if (mode != psModeForm) { | |
727 | writePS("%%EndSetup\n"); | |
728 | } | |
729 | } | |
730 | ||
731 | // initialize sequential page number | |
732 | seqPage = 1; | |
733 | } | |
734 | ||
735 | PSOutputDev::~PSOutputDev() { | |
736 | PSOutCustomColor *cc; | |
737 | int i; | |
738 | ||
739 | if (ok) { | |
740 | if (!manualCtrl) { | |
741 | writePS("%%Trailer\n"); | |
742 | writeTrailer(); | |
743 | if (mode != psModeForm) { | |
744 | writePS("%%EOF\n"); | |
745 | } | |
746 | } | |
747 | if (fileType == psFile) { | |
748 | #ifdef MACOS | |
749 | ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle); | |
750 | #endif | |
751 | fclose((FILE *)outputStream); | |
752 | } | |
753 | #ifdef HAVE_POPEN | |
754 | else if (fileType == psPipe) { | |
755 | pclose((FILE *)outputStream); | |
756 | #ifndef WIN32 | |
757 | signal(SIGPIPE, (SignalFunc)SIG_DFL); | |
758 | #endif | |
759 | } | |
760 | #endif | |
761 | } | |
762 | if (embFontList) { | |
763 | delete embFontList; | |
764 | } | |
765 | if (fontIDs) { | |
766 | gfree(fontIDs); | |
767 | } | |
768 | if (fontFileIDs) { | |
769 | gfree(fontFileIDs); | |
770 | } | |
771 | if (fontFileNames) { | |
772 | for (i = 0; i < fontFileNameLen; ++i) { | |
773 | delete fontFileNames[i]; | |
774 | } | |
775 | gfree(fontFileNames); | |
776 | } | |
777 | if (font16Enc) { | |
778 | for (i = 0; i < font16EncLen; ++i) { | |
779 | delete font16Enc[i].enc; | |
780 | } | |
781 | gfree(font16Enc); | |
782 | } | |
783 | if (xobjStack) { | |
784 | delete xobjStack; | |
785 | } | |
786 | while (customColors) { | |
787 | cc = customColors; | |
788 | customColors = cc->next; | |
789 | delete cc; | |
790 | } | |
791 | } | |
792 | ||
793 | void PSOutputDev::writeHeader(int firstPage, int lastPage, PDFRectangle *box) { | |
753453e4 | 794 | switch (mode) { |
795 | case psModePS: | |
ae65da6c | 796 | writePS("%!PS-Adobe-3.0\n"); |
797 | writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion); | |
798 | writePSFmt("%%%%LanguageLevel: %d\n", | |
799 | (level == psLevel1 || level == psLevel1Sep) ? 1 : | |
800 | (level == psLevel2 || level == psLevel2Sep) ? 2 : 3); | |
be8c3862 | 801 | if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { |
ae65da6c | 802 | writePS("%%DocumentProcessColors: (atend)\n"); |
803 | writePS("%%DocumentCustomColors: (atend)\n"); | |
804 | } | |
805 | writePS("%%DocumentSuppliedResources: (atend)\n"); | |
806 | writePSFmt("%%%%DocumentMedia: plain %d %d 0 () ()\n", | |
807 | paperWidth, paperHeight); | |
891091df | 808 | writePSFmt("%%%%BoundingBox: 0 0 %d %d\n", paperWidth, paperHeight); |
ae65da6c | 809 | writePSFmt("%%%%Pages: %d\n", lastPage - firstPage + 1); |
a0db7254 | 810 | writePSFmt("%%%%BoundingBox: 0 0 %d %d\n", paperWidth, paperHeight); |
ae65da6c | 811 | writePS("%%EndComments\n"); |
812 | writePS("%%BeginDefaults\n"); | |
813 | writePS("%%PageMedia: plain\n"); | |
814 | writePS("%%EndDefaults\n"); | |
753453e4 | 815 | break; |
816 | case psModeEPS: | |
ae65da6c | 817 | writePS("%!PS-Adobe-3.0 EPSF-3.0\n"); |
818 | writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion); | |
819 | writePSFmt("%%%%LanguageLevel: %d\n", | |
820 | (level == psLevel1 || level == psLevel1Sep) ? 1 : | |
821 | (level == psLevel2 || level == psLevel2Sep) ? 2 : 3); | |
be8c3862 | 822 | if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { |
ae65da6c | 823 | writePS("%%DocumentProcessColors: (atend)\n"); |
824 | writePS("%%DocumentCustomColors: (atend)\n"); | |
b5cb0608 | 825 | } |
ae65da6c | 826 | writePSFmt("%%%%BoundingBox: %d %d %d %d\n", |
827 | (int)floor(box->x1), (int)floor(box->y1), | |
828 | (int)ceil(box->x2), (int)ceil(box->y2)); | |
753453e4 | 829 | if (floor(box->x1) != ceil(box->x1) || |
830 | floor(box->y1) != ceil(box->y1) || | |
831 | floor(box->x2) != ceil(box->x2) || | |
832 | floor(box->y2) != ceil(box->y2)) { | |
ae65da6c | 833 | writePSFmt("%%%%HiResBoundingBox: %g %g %g %g\n", |
834 | box->x1, box->y1, box->x2, box->y2); | |
52118ca3 | 835 | } |
ae65da6c | 836 | writePS("%%DocumentSuppliedResources: (atend)\n"); |
837 | writePS("%%EndComments\n"); | |
753453e4 | 838 | break; |
839 | case psModeForm: | |
ae65da6c | 840 | writePS("%!PS-Adobe-3.0 Resource-Form\n"); |
841 | writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion); | |
842 | writePSFmt("%%%%LanguageLevel: %d\n", | |
843 | (level == psLevel1 || level == psLevel1Sep) ? 1 : | |
844 | (level == psLevel2 || level == psLevel2Sep) ? 2 : 3); | |
be8c3862 | 845 | if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { |
ae65da6c | 846 | writePS("%%DocumentProcessColors: (atend)\n"); |
847 | writePS("%%DocumentCustomColors: (atend)\n"); | |
b5cb0608 | 848 | } |
ae65da6c | 849 | writePS("%%DocumentSuppliedResources: (atend)\n"); |
850 | writePS("%%EndComments\n"); | |
753453e4 | 851 | writePS("32 dict dup begin\n"); |
ae65da6c | 852 | writePSFmt("/BBox [%d %d %d %d] def\n", |
853 | (int)box->x1, (int)box->y1, (int)box->x2, (int)box->y2); | |
753453e4 | 854 | writePS("/FormType 1 def\n"); |
855 | writePS("/Matrix [1 0 0 1 0 0] def\n"); | |
856 | break; | |
9c72faab | 857 | } |
b5cb0608 | 858 | } |
891091df | 859 | |
860 | void PSOutputDev::writeXpdfProcset() { | |
861 | char prologLevel; | |
862 | const char **p; | |
863 | ||
ae65da6c | 864 | writePSFmt("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion); |
891091df | 865 | prologLevel = 'a'; |
b5cb0608 | 866 | for (p = prolog; *p; ++p) { |
891091df | 867 | if ((*p)[0] == '~' && (*p)[1] == '1') { |
868 | prologLevel = '1'; | |
869 | } else if ((*p)[0] == '~' && (*p)[1] == '2') { | |
870 | prologLevel = '2'; | |
871 | } else if ((*p)[0] == '~' && (*p)[1] == 'a') { | |
872 | prologLevel = 'a'; | |
873 | } else if (prologLevel == 'a' || | |
874 | (prologLevel == '1' && level < psLevel2) || | |
875 | (prologLevel == '2' && level >= psLevel2)) { | |
ae65da6c | 876 | writePSFmt("%s\n", *p); |
b5cb0608 | 877 | } |
891091df | 878 | } |
ae65da6c | 879 | writePS("%%EndResource\n"); |
891091df | 880 | |
be8c3862 | 881 | if (level >= psLevel3) { |
882 | for (p = cmapProlog; *p; ++p) { | |
ae65da6c | 883 | writePSFmt("%s\n", *p); |
be8c3862 | 884 | } |
885 | } | |
b5cb0608 | 886 | } |
9c72faab | 887 | |
891091df | 888 | void PSOutputDev::writeDocSetup(Catalog *catalog, |
889 | int firstPage, int lastPage) { | |
890 | Page *page; | |
891 | Dict *resDict; | |
892 | Annots *annots; | |
893 | Object obj1, obj2; | |
894 | int pg, i; | |
895 | ||
753453e4 | 896 | if (mode == psModeForm) { |
b5cb0608 | 897 | // swap the form and xpdf dicts |
898 | writePS("xpdf end begin dup begin\n"); | |
899 | } else { | |
b5cb0608 | 900 | writePS("xpdf begin\n"); |
901 | } | |
9c72faab | 902 | for (pg = firstPage; pg <= lastPage; ++pg) { |
52118ca3 | 903 | page = catalog->getPage(pg); |
904 | if ((resDict = page->getResourceDict())) { | |
b5cb0608 | 905 | setupResources(resDict); |
52118ca3 | 906 | } |
be8c3862 | 907 | annots = new Annots(xref, page->getAnnots(&obj1)); |
52118ca3 | 908 | obj1.free(); |
be8c3862 | 909 | for (i = 0; i < annots->getNumAnnots(); ++i) { |
910 | if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) { | |
52118ca3 | 911 | obj1.streamGetDict()->lookup("Resources", &obj2); |
912 | if (obj2.isDict()) { | |
b5cb0608 | 913 | setupResources(obj2.getDict()); |
52118ca3 | 914 | } |
915 | obj2.free(); | |
916 | } | |
917 | obj1.free(); | |
918 | } | |
be8c3862 | 919 | delete annots; |
9c72faab | 920 | } |
753453e4 | 921 | if (mode != psModeForm) { |
891091df | 922 | if (mode != psModeEPS && !manualCtrl) { |
ae65da6c | 923 | writePSFmt("%d %d %s pdfSetup\n", |
924 | paperWidth, paperHeight, | |
925 | globalParams->getPSDuplex() ? "true" : "false"); | |
be8c3862 | 926 | } |
52118ca3 | 927 | #if OPI_SUPPORT |
be8c3862 | 928 | if (globalParams->getPSOPI()) { |
52118ca3 | 929 | writePS("/opiMatrix matrix currentmatrix def\n"); |
930 | } | |
931 | #endif | |
891091df | 932 | } |
9c72faab | 933 | } |
934 | ||
891091df | 935 | void PSOutputDev::writePageTrailer() { |
936 | if (mode != psModeForm) { | |
937 | writePS("pdfEndPage\n"); | |
938 | } | |
9c72faab | 939 | } |
940 | ||
891091df | 941 | void PSOutputDev::writeTrailer() { |
753453e4 | 942 | PSOutCustomColor *cc; |
52118ca3 | 943 | |
753453e4 | 944 | if (mode == psModeForm) { |
9c72faab | 945 | writePS("/Foo exch /Form defineresource pop\n"); |
be8c3862 | 946 | } else { |
52118ca3 | 947 | writePS("end\n"); |
ae65da6c | 948 | writePS("%%DocumentSuppliedResources:\n"); |
949 | writePS(embFontList->getCString()); | |
be8c3862 | 950 | if (level == psLevel1Sep || level == psLevel2Sep || |
951 | level == psLevel3Sep) { | |
ae65da6c | 952 | writePS("%%DocumentProcessColors:"); |
753453e4 | 953 | if (processColors & psProcessCyan) { |
954 | writePS(" Cyan"); | |
955 | } | |
956 | if (processColors & psProcessMagenta) { | |
957 | writePS(" Magenta"); | |
958 | } | |
959 | if (processColors & psProcessYellow) { | |
960 | writePS(" Yellow"); | |
961 | } | |
962 | if (processColors & psProcessBlack) { | |
963 | writePS(" Black"); | |
964 | } | |
965 | writePS("\n"); | |
ae65da6c | 966 | writePS("%%DocumentCustomColors:"); |
753453e4 | 967 | for (cc = customColors; cc; cc = cc->next) { |
ae65da6c | 968 | writePSFmt(" (%s)", cc->name->getCString()); |
753453e4 | 969 | } |
970 | writePS("\n"); | |
ae65da6c | 971 | writePS("%%CMYKCustomColor:\n"); |
753453e4 | 972 | for (cc = customColors; cc; cc = cc->next) { |
ae65da6c | 973 | writePSFmt("%%%%+ %g %g %g %g (%s)\n", |
753453e4 | 974 | cc->c, cc->m, cc->y, cc->k, cc->name->getCString()); |
975 | } | |
976 | } | |
753453e4 | 977 | } |
9c72faab | 978 | } |
979 | ||
b5cb0608 | 980 | void PSOutputDev::setupResources(Dict *resDict) { |
0e2e516e | 981 | Object xObjDict, xObjRef, xObj, resObj; |
982 | Ref ref0, ref1; | |
983 | GBool skip; | |
984 | int i, j; | |
9c72faab | 985 | |
b5cb0608 | 986 | setupFonts(resDict); |
987 | setupImages(resDict); | |
9c72faab | 988 | |
989 | resDict->lookup("XObject", &xObjDict); | |
990 | if (xObjDict.isDict()) { | |
991 | for (i = 0; i < xObjDict.dictGetLength(); ++i) { | |
0e2e516e | 992 | |
993 | // avoid infinite recursion on XObjects | |
994 | skip = gFalse; | |
995 | if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) { | |
996 | ref0 = xObjRef.getRef(); | |
997 | for (j = 0; j < xobjStack->getLength(); ++j) { | |
998 | ref1 = *(Ref *)xobjStack->get(j); | |
999 | if (ref1.num == ref0.num && ref1.gen == ref0.gen) { | |
1000 | skip = gTrue; | |
1001 | break; | |
1002 | } | |
1003 | } | |
1004 | if (!skip) { | |
1005 | xobjStack->append(&ref0); | |
b5cb0608 | 1006 | } |
9c72faab | 1007 | } |
0e2e516e | 1008 | if (!skip) { |
1009 | ||
1010 | // process the XObject's resource dictionary | |
1011 | xObjDict.dictGetVal(i, &xObj); | |
1012 | if (xObj.isStream()) { | |
1013 | xObj.streamGetDict()->lookup("Resources", &resObj); | |
1014 | if (resObj.isDict()) { | |
1015 | setupResources(resObj.getDict()); | |
1016 | } | |
1017 | resObj.free(); | |
1018 | } | |
1019 | xObj.free(); | |
1020 | } | |
1021 | ||
1022 | if (xObjRef.isRef() && !skip) { | |
1023 | xobjStack->del(xobjStack->getLength() - 1); | |
1024 | } | |
1025 | xObjRef.free(); | |
9c72faab | 1026 | } |
1027 | } | |
1028 | xObjDict.free(); | |
1029 | } | |
1030 | ||
b5cb0608 | 1031 | void PSOutputDev::setupFonts(Dict *resDict) { |
891091df | 1032 | Object obj1, obj2; |
1033 | Ref r; | |
b5cb0608 | 1034 | GfxFontDict *gfxFontDict; |
1035 | GfxFont *font; | |
1036 | int i; | |
1037 | ||
891091df | 1038 | gfxFontDict = NULL; |
1039 | resDict->lookupNF("Font", &obj1); | |
1040 | if (obj1.isRef()) { | |
1041 | obj1.fetch(xref, &obj2); | |
1042 | if (obj2.isDict()) { | |
1043 | r = obj1.getRef(); | |
1044 | gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict()); | |
1045 | } | |
1046 | obj2.free(); | |
1047 | } else if (obj1.isDict()) { | |
1048 | gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict()); | |
1049 | } | |
1050 | if (gfxFontDict) { | |
b5cb0608 | 1051 | for (i = 0; i < gfxFontDict->getNumFonts(); ++i) { |
1052 | font = gfxFontDict->getFont(i); | |
be8c3862 | 1053 | setupFont(font, resDict); |
b5cb0608 | 1054 | } |
1055 | delete gfxFontDict; | |
1056 | } | |
891091df | 1057 | obj1.free(); |
b5cb0608 | 1058 | } |
1059 | ||
be8c3862 | 1060 | void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) { |
9c72faab | 1061 | Ref fontFileID; |
1062 | GString *name; | |
be8c3862 | 1063 | PSFontParam *fontParam; |
891091df | 1064 | GString *psName; |
ae65da6c | 1065 | char type3Name[64], buf[16]; |
0e2e516e | 1066 | GBool subst; |
be8c3862 | 1067 | UnicodeMap *uMap; |
8dc4af74 | 1068 | const char *charName; |
52118ca3 | 1069 | double xs, ys; |
52118ca3 | 1070 | int code; |
1071 | double w1, w2; | |
1072 | double *fm; | |
9c72faab | 1073 | int i, j; |
1074 | ||
1075 | // check if font is already set up | |
1076 | for (i = 0; i < fontIDLen; ++i) { | |
be8c3862 | 1077 | if (fontIDs[i].num == font->getID()->num && |
1078 | fontIDs[i].gen == font->getID()->gen) { | |
9c72faab | 1079 | return; |
be8c3862 | 1080 | } |
9c72faab | 1081 | } |
1082 | ||
1083 | // add entry to fontIDs list | |
1084 | if (fontIDLen >= fontIDSize) { | |
1085 | fontIDSize += 64; | |
1086 | fontIDs = (Ref *)grealloc(fontIDs, fontIDSize * sizeof(Ref)); | |
1087 | } | |
be8c3862 | 1088 | fontIDs[fontIDLen++] = *font->getID(); |
9c72faab | 1089 | |
52118ca3 | 1090 | xs = ys = 1; |
0e2e516e | 1091 | subst = gFalse; |
be8c3862 | 1092 | |
1093 | // check for resident 8-bit font | |
1094 | if (font->getName() && | |
1095 | (fontParam = globalParams->getPSFont(font->getName()))) { | |
891091df | 1096 | psName = new GString(fontParam->psFontName->getCString()); |
52118ca3 | 1097 | |
9c72faab | 1098 | // check for embedded Type 1 font |
be8c3862 | 1099 | } else if (globalParams->getPSEmbedType1() && |
1100 | font->getType() == fontType1 && | |
1101 | font->getEmbeddedFontID(&fontFileID)) { | |
891091df | 1102 | psName = filterPSName(font->getEmbeddedFontName()); |
52118ca3 | 1103 | setupEmbeddedType1Font(&fontFileID, psName); |
9c72faab | 1104 | |
be8c3862 | 1105 | // check for embedded Type 1C font |
1106 | } else if (globalParams->getPSEmbedType1() && | |
1107 | font->getType() == fontType1C && | |
1108 | font->getEmbeddedFontID(&fontFileID)) { | |
891091df | 1109 | psName = filterPSName(font->getEmbeddedFontName()); |
be8c3862 | 1110 | setupEmbeddedType1CFont(font, &fontFileID, psName); |
1111 | ||
9c72faab | 1112 | // check for external Type 1 font file |
be8c3862 | 1113 | } else if (globalParams->getPSEmbedType1() && |
1114 | font->getType() == fontType1 && | |
9c72faab | 1115 | font->getExtFontFile()) { |
9c72faab | 1116 | // this assumes that the PS font name matches the PDF font name |
891091df | 1117 | psName = font->getName()->copy(); |
be8c3862 | 1118 | setupExternalType1Font(font->getExtFontFile(), psName); |
52118ca3 | 1119 | |
753453e4 | 1120 | // check for embedded TrueType font |
be8c3862 | 1121 | } else if (globalParams->getPSEmbedTrueType() && |
1122 | font->getType() == fontTrueType && | |
753453e4 | 1123 | font->getEmbeddedFontID(&fontFileID)) { |
891091df | 1124 | psName = filterPSName(font->getEmbeddedFontName()); |
753453e4 | 1125 | setupEmbeddedTrueTypeFont(font, &fontFileID, psName); |
1126 | ||
be8c3862 | 1127 | // check for external TrueType font file |
1128 | } else if (globalParams->getPSEmbedTrueType() && | |
1129 | font->getType() == fontTrueType && | |
1130 | font->getExtFontFile()) { | |
891091df | 1131 | psName = filterPSName(font->getName()); |
be8c3862 | 1132 | setupExternalTrueTypeFont(font, psName); |
9c72faab | 1133 | |
be8c3862 | 1134 | // check for embedded CID PostScript font |
1135 | } else if (globalParams->getPSEmbedCIDPostScript() && | |
1136 | font->getType() == fontCIDType0C && | |
1137 | font->getEmbeddedFontID(&fontFileID)) { | |
891091df | 1138 | psName = filterPSName(font->getEmbeddedFontName()); |
be8c3862 | 1139 | setupEmbeddedCIDType0Font(font, &fontFileID, psName); |
1140 | ||
1141 | // check for embedded CID TrueType font | |
1142 | } else if (globalParams->getPSEmbedCIDTrueType() && | |
1143 | font->getType() == fontCIDType2 && | |
1144 | font->getEmbeddedFontID(&fontFileID)) { | |
891091df | 1145 | psName = filterPSName(font->getEmbeddedFontName()); |
be8c3862 | 1146 | setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName); |
1147 | ||
1148 | } else if (font->getType() == fontType3) { | |
1149 | sprintf(type3Name, "T3_%d_%d", | |
1150 | font->getID()->num, font->getID()->gen); | |
891091df | 1151 | psName = new GString(type3Name); |
be8c3862 | 1152 | setupType3Font(font, psName, parentResDict); |
1153 | ||
1154 | // do 8-bit font substitution | |
1155 | } else if (!font->isCIDFont()) { | |
0e2e516e | 1156 | subst = gTrue; |
9c72faab | 1157 | name = font->getName(); |
1158 | psName = NULL; | |
9c72faab | 1159 | if (name) { |
be8c3862 | 1160 | for (i = 0; psFonts[i]; ++i) { |
1161 | if (name->cmp(psFonts[i]) == 0) { | |
891091df | 1162 | psName = new GString(psFonts[i]); |
9c72faab | 1163 | break; |
1164 | } | |
1165 | } | |
1166 | } | |
1167 | if (!psName) { | |
be8c3862 | 1168 | if (font->isFixedWidth()) { |
9c72faab | 1169 | i = 8; |
be8c3862 | 1170 | } else if (font->isSerif()) { |
9c72faab | 1171 | i = 4; |
be8c3862 | 1172 | } else { |
9c72faab | 1173 | i = 0; |
be8c3862 | 1174 | } |
1175 | if (font->isBold()) { | |
9c72faab | 1176 | i += 2; |
be8c3862 | 1177 | } |
1178 | if (font->isItalic()) { | |
9c72faab | 1179 | i += 1; |
be8c3862 | 1180 | } |
891091df | 1181 | psName = new GString(psSubstFonts[i].psName); |
be8c3862 | 1182 | for (code = 0; code < 256; ++code) { |
1183 | if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) && | |
1184 | charName[0] == 'm' && charName[1] == '\0') { | |
1185 | break; | |
1186 | } | |
1187 | } | |
1188 | if (code < 256) { | |
1189 | w1 = ((Gfx8BitFont *)font)->getWidth(code); | |
52118ca3 | 1190 | } else { |
1191 | w1 = 0; | |
1192 | } | |
1193 | w2 = psSubstFonts[i].mWidth; | |
1194 | xs = w1 / w2; | |
1195 | if (xs < 0.1) { | |
1196 | xs = 1; | |
1197 | } | |
1198 | if (font->getType() == fontType3) { | |
1199 | // This is a hack which makes it possible to substitute for some | |
1200 | // Type 3 fonts. The problem is that it's impossible to know what | |
1201 | // the base coordinate system used in the font is without actually | |
1202 | // rendering the font. | |
1203 | ys = xs; | |
1204 | fm = font->getFontMatrix(); | |
1205 | if (fm[0] != 0) { | |
1206 | ys *= fm[3] / fm[0]; | |
1207 | } | |
1208 | } else { | |
1209 | ys = 1; | |
1210 | } | |
9c72faab | 1211 | } |
be8c3862 | 1212 | |
1213 | // do 16-bit font substitution | |
1214 | } else if ((fontParam = globalParams-> | |
1215 | getPSFont16(font->getName(), | |
1216 | ((GfxCIDFont *)font)->getCollection(), | |
1217 | font->getWMode()))) { | |
0e2e516e | 1218 | subst = gTrue; |
891091df | 1219 | psName = fontParam->psFontName->copy(); |
be8c3862 | 1220 | if (font16EncLen >= font16EncSize) { |
1221 | font16EncSize += 16; | |
1222 | font16Enc = (PSFont16Enc *)grealloc(font16Enc, | |
1223 | font16EncSize * sizeof(PSFont16Enc)); | |
1224 | } | |
1225 | font16Enc[font16EncLen].fontID = *font->getID(); | |
1226 | font16Enc[font16EncLen].enc = fontParam->encoding->copy(); | |
1227 | if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) { | |
1228 | uMap->decRefCnt(); | |
1229 | ++font16EncLen; | |
1230 | } else { | |
1231 | error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'", | |
1232 | font16Enc[font16EncLen].enc->getCString()); | |
1233 | } | |
1234 | ||
1235 | // give up - can't do anything with this font | |
1236 | } else { | |
1237 | error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)", | |
1238 | font->getName() ? font->getName()->getCString() : "(unnamed)", | |
1239 | ((GfxCIDFont *)font)->getCollection() | |
1240 | ? ((GfxCIDFont *)font)->getCollection()->getCString() | |
1241 | : "(unknown)"); | |
1242 | return; | |
9c72faab | 1243 | } |
1244 | ||
1245 | // generate PostScript code to set up the font | |
be8c3862 | 1246 | if (font->isCIDFont()) { |
1247 | if (level == psLevel3 || level == psLevel3Sep) { | |
ae65da6c | 1248 | writePSFmt("/F%d_%d /%s %d pdfMakeFont16L3\n", |
891091df | 1249 | font->getID()->num, font->getID()->gen, psName->getCString(), |
ae65da6c | 1250 | font->getWMode()); |
be8c3862 | 1251 | } else { |
ae65da6c | 1252 | writePSFmt("/F%d_%d /%s %d pdfMakeFont16\n", |
891091df | 1253 | font->getID()->num, font->getID()->gen, psName->getCString(), |
ae65da6c | 1254 | font->getWMode()); |
be8c3862 | 1255 | } |
52118ca3 | 1256 | } else { |
ae65da6c | 1257 | writePSFmt("/F%d_%d /%s %g %g\n", |
891091df | 1258 | font->getID()->num, font->getID()->gen, psName->getCString(), |
1259 | xs, ys); | |
52118ca3 | 1260 | for (i = 0; i < 256; i += 8) { |
ae65da6c | 1261 | writePSFmt((i == 0) ? "[ " : " "); |
52118ca3 | 1262 | for (j = 0; j < 8; ++j) { |
ae65da6c | 1263 | if (font->getType() == fontTrueType && |
0e2e516e | 1264 | !subst && |
ae65da6c | 1265 | !((Gfx8BitFont *)font)->getHasEncoding()) { |
1266 | sprintf(buf, "c%02x", i+j); | |
1267 | charName = buf; | |
1268 | } else { | |
1269 | charName = ((Gfx8BitFont *)font)->getCharName(i+j); | |
1270 | // this is a kludge for broken PDF files that encode char 32 | |
1271 | // as .notdef | |
1272 | if (i+j == 32 && charName && !strcmp(charName, ".notdef")) { | |
1273 | charName = "space"; | |
1274 | } | |
753453e4 | 1275 | } |
ae65da6c | 1276 | writePS("/"); |
8dc4af74 | 1277 | writePSName(charName ? charName : ".notdef"); |
52118ca3 | 1278 | } |
8dc4af74 | 1279 | writePS((i == 256-8) ? "]\n" : "\n"); |
9c72faab | 1280 | } |
52118ca3 | 1281 | writePS("pdfMakeFont\n"); |
9c72faab | 1282 | } |
be8c3862 | 1283 | |
891091df | 1284 | delete psName; |
9c72faab | 1285 | } |
1286 | ||
891091df | 1287 | void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) { |
9c72faab | 1288 | static char hexChar[17] = "0123456789abcdef"; |
891091df | 1289 | Object refObj, strObj, obj1, obj2, obj3; |
9c72faab | 1290 | Dict *dict; |
891091df | 1291 | int length1, length2, length3; |
9c72faab | 1292 | int c; |
1293 | int start[4]; | |
1294 | GBool binMode; | |
1295 | int i; | |
1296 | ||
1297 | // check if font is already embedded | |
1298 | for (i = 0; i < fontFileIDLen; ++i) { | |
1299 | if (fontFileIDs[i].num == id->num && | |
1300 | fontFileIDs[i].gen == id->gen) | |
1301 | return; | |
1302 | } | |
1303 | ||
1304 | // add entry to fontFileIDs list | |
1305 | if (fontFileIDLen >= fontFileIDSize) { | |
1306 | fontFileIDSize += 64; | |
1307 | fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref)); | |
1308 | } | |
1309 | fontFileIDs[fontFileIDLen++] = *id; | |
1310 | ||
1311 | // get the font stream and info | |
1312 | refObj.initRef(id->num, id->gen); | |
753453e4 | 1313 | refObj.fetch(xref, &strObj); |
9c72faab | 1314 | refObj.free(); |
1315 | if (!strObj.isStream()) { | |
1316 | error(-1, "Embedded font file object is not a stream"); | |
1317 | goto err1; | |
1318 | } | |
1319 | if (!(dict = strObj.streamGetDict())) { | |
1320 | error(-1, "Embedded font stream is missing its dictionary"); | |
1321 | goto err1; | |
1322 | } | |
1323 | dict->lookup("Length1", &obj1); | |
1324 | dict->lookup("Length2", &obj2); | |
891091df | 1325 | dict->lookup("Length3", &obj3); |
1326 | if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) { | |
9c72faab | 1327 | error(-1, "Missing length fields in embedded font stream dictionary"); |
1328 | obj1.free(); | |
1329 | obj2.free(); | |
891091df | 1330 | obj3.free(); |
9c72faab | 1331 | goto err1; |
1332 | } | |
1333 | length1 = obj1.getInt(); | |
1334 | length2 = obj2.getInt(); | |
891091df | 1335 | length3 = obj3.getInt(); |
9c72faab | 1336 | obj1.free(); |
1337 | obj2.free(); | |
891091df | 1338 | obj3.free(); |
9c72faab | 1339 | |
52118ca3 | 1340 | // beginning comment |
891091df | 1341 | writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); |
753453e4 | 1342 | embFontList->append("%%+ font "); |
891091df | 1343 | embFontList->append(psName->getCString()); |
753453e4 | 1344 | embFontList->append("\n"); |
52118ca3 | 1345 | |
9c72faab | 1346 | // copy ASCII portion of font |
1347 | strObj.streamReset(); | |
be8c3862 | 1348 | for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) { |
1349 | writePSChar(c); | |
1350 | } | |
9c72faab | 1351 | |
1352 | // figure out if encrypted portion is binary or ASCII | |
1353 | binMode = gFalse; | |
1354 | for (i = 0; i < 4; ++i) { | |
1355 | start[i] = strObj.streamGetChar(); | |
1356 | if (start[i] == EOF) { | |
1357 | error(-1, "Unexpected end of file in embedded font stream"); | |
1358 | goto err1; | |
1359 | } | |
1360 | if (!((start[i] >= '0' && start[i] <= '9') || | |
1361 | (start[i] >= 'A' && start[i] <= 'F') || | |
1362 | (start[i] >= 'a' && start[i] <= 'f'))) | |
1363 | binMode = gTrue; | |
1364 | } | |
1365 | ||
1366 | // convert binary data to ASCII | |
1367 | if (binMode) { | |
1368 | for (i = 0; i < 4; ++i) { | |
be8c3862 | 1369 | writePSChar(hexChar[(start[i] >> 4) & 0x0f]); |
1370 | writePSChar(hexChar[start[i] & 0x0f]); | |
9c72faab | 1371 | } |
891091df | 1372 | // if Length2 is incorrect (too small), font data gets chopped, so |
1373 | // we take a few extra characters from the trailer just in case | |
1374 | length2 += length3 >= 8 ? 8 : length3; | |
9c72faab | 1375 | while (i < length2) { |
be8c3862 | 1376 | if ((c = strObj.streamGetChar()) == EOF) { |
9c72faab | 1377 | break; |
be8c3862 | 1378 | } |
1379 | writePSChar(hexChar[(c >> 4) & 0x0f]); | |
1380 | writePSChar(hexChar[c & 0x0f]); | |
1381 | if (++i % 32 == 0) { | |
1382 | writePSChar('\n'); | |
1383 | } | |
1384 | } | |
1385 | if (i % 32 > 0) { | |
1386 | writePSChar('\n'); | |
9c72faab | 1387 | } |
9c72faab | 1388 | |
1389 | // already in ASCII format -- just copy it | |
1390 | } else { | |
be8c3862 | 1391 | for (i = 0; i < 4; ++i) { |
1392 | writePSChar(start[i]); | |
1393 | } | |
9c72faab | 1394 | for (i = 4; i < length2; ++i) { |
be8c3862 | 1395 | if ((c = strObj.streamGetChar()) == EOF) { |
9c72faab | 1396 | break; |
be8c3862 | 1397 | } |
1398 | writePSChar(c); | |
9c72faab | 1399 | } |
1400 | } | |
1401 | ||
1402 | // write padding and "cleartomark" | |
be8c3862 | 1403 | for (i = 0; i < 8; ++i) { |
9c72faab | 1404 | writePS("00000000000000000000000000000000" |
1405 | "00000000000000000000000000000000\n"); | |
be8c3862 | 1406 | } |
9c72faab | 1407 | writePS("cleartomark\n"); |
1408 | ||
52118ca3 | 1409 | // ending comment |
ae65da6c | 1410 | writePS("%%EndResource\n"); |
52118ca3 | 1411 | |
9c72faab | 1412 | err1: |
b5cb0608 | 1413 | strObj.streamClose(); |
9c72faab | 1414 | strObj.free(); |
1415 | } | |
1416 | ||
1417 | //~ This doesn't handle .pfb files or binary eexec data (which only | |
1418 | //~ happens in pfb files?). | |
891091df | 1419 | void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) { |
9c72faab | 1420 | FILE *fontFile; |
1421 | int c; | |
1422 | int i; | |
1423 | ||
1424 | // check if font is already embedded | |
1425 | for (i = 0; i < fontFileNameLen; ++i) { | |
52118ca3 | 1426 | if (!fontFileNames[i]->cmp(fileName)) { |
9c72faab | 1427 | return; |
52118ca3 | 1428 | } |
9c72faab | 1429 | } |
1430 | ||
1431 | // add entry to fontFileNames list | |
1432 | if (fontFileNameLen >= fontFileNameSize) { | |
1433 | fontFileNameSize += 64; | |
52118ca3 | 1434 | fontFileNames = (GString **)grealloc(fontFileNames, |
1435 | fontFileNameSize * sizeof(GString *)); | |
1436 | } | |
1437 | fontFileNames[fontFileNameLen++] = fileName->copy(); | |
1438 | ||
1439 | // beginning comment | |
891091df | 1440 | writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); |
753453e4 | 1441 | embFontList->append("%%+ font "); |
891091df | 1442 | embFontList->append(psName->getCString()); |
753453e4 | 1443 | embFontList->append("\n"); |
9c72faab | 1444 | |
1445 | // copy the font file | |
52118ca3 | 1446 | if (!(fontFile = fopen(fileName->getCString(), "rb"))) { |
9c72faab | 1447 | error(-1, "Couldn't open external font file"); |
1448 | return; | |
1449 | } | |
be8c3862 | 1450 | while ((c = fgetc(fontFile)) != EOF) { |
1451 | writePSChar(c); | |
1452 | } | |
9c72faab | 1453 | fclose(fontFile); |
52118ca3 | 1454 | |
1455 | // ending comment | |
ae65da6c | 1456 | writePS("%%EndResource\n"); |
9c72faab | 1457 | } |
1458 | ||
52118ca3 | 1459 | void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id, |
891091df | 1460 | GString *psName) { |
753453e4 | 1461 | char *fontBuf; |
9c72faab | 1462 | int fontLen; |
be8c3862 | 1463 | Type1CFontFile *t1cFile; |
9c72faab | 1464 | int i; |
1465 | ||
1466 | // check if font is already embedded | |
1467 | for (i = 0; i < fontFileIDLen; ++i) { | |
1468 | if (fontFileIDs[i].num == id->num && | |
1469 | fontFileIDs[i].gen == id->gen) | |
1470 | return; | |
1471 | } | |
1472 | ||
1473 | // add entry to fontFileIDs list | |
1474 | if (fontFileIDLen >= fontFileIDSize) { | |
1475 | fontFileIDSize += 64; | |
1476 | fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref)); | |
1477 | } | |
1478 | fontFileIDs[fontFileIDLen++] = *id; | |
1479 | ||
52118ca3 | 1480 | // beginning comment |
891091df | 1481 | writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); |
753453e4 | 1482 | embFontList->append("%%+ font "); |
891091df | 1483 | embFontList->append(psName->getCString()); |
753453e4 | 1484 | embFontList->append("\n"); |
52118ca3 | 1485 | |
9c72faab | 1486 | // convert it to a Type 1 font |
753453e4 | 1487 | fontBuf = font->readEmbFontFile(xref, &fontLen); |
be8c3862 | 1488 | t1cFile = new Type1CFontFile(fontBuf, fontLen); |
0e2e516e | 1489 | if (t1cFile->isOk()) { |
1490 | t1cFile->convertToType1(outputFunc, outputStream); | |
1491 | } | |
be8c3862 | 1492 | delete t1cFile; |
753453e4 | 1493 | gfree(fontBuf); |
52118ca3 | 1494 | |
1495 | // ending comment | |
ae65da6c | 1496 | writePS("%%EndResource\n"); |
753453e4 | 1497 | } |
1498 | ||
1499 | void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, | |
891091df | 1500 | GString *psName) { |
1501 | char unique[32]; | |
753453e4 | 1502 | char *fontBuf; |
1503 | int fontLen; | |
1504 | TrueTypeFontFile *ttFile; | |
891091df | 1505 | Gushort *codeToGID; |
753453e4 | 1506 | int i; |
1507 | ||
1508 | // check if font is already embedded | |
1509 | for (i = 0; i < fontFileIDLen; ++i) { | |
1510 | if (fontFileIDs[i].num == id->num && | |
891091df | 1511 | fontFileIDs[i].gen == id->gen) { |
1512 | sprintf(unique, "_%d", nextTrueTypeNum++); | |
1513 | psName->append(unique); | |
1514 | break; | |
1515 | } | |
753453e4 | 1516 | } |
1517 | ||
1518 | // add entry to fontFileIDs list | |
891091df | 1519 | if (i == fontFileIDLen) { |
753453e4 | 1520 | if (fontFileIDLen >= fontFileIDSize) { |
1521 | fontFileIDSize += 64; | |
1522 | fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref)); | |
52118ca3 | 1523 | } |
753453e4 | 1524 | fontFileIDs[fontFileIDLen++] = *id; |
891091df | 1525 | } |
753453e4 | 1526 | |
1527 | // beginning comment | |
891091df | 1528 | writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); |
753453e4 | 1529 | embFontList->append("%%+ font "); |
891091df | 1530 | embFontList->append(psName->getCString()); |
753453e4 | 1531 | embFontList->append("\n"); |
1532 | ||
1533 | // convert it to a Type 42 font | |
1534 | fontBuf = font->readEmbFontFile(xref, &fontLen); | |
1535 | ttFile = new TrueTypeFontFile(fontBuf, fontLen); | |
891091df | 1536 | codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ttFile); |
1537 | ttFile->convertToType42(psName->getCString(), | |
1538 | ((Gfx8BitFont *)font)->getEncoding(), | |
1539 | ((Gfx8BitFont *)font)->getHasEncoding(), | |
1540 | codeToGID, | |
ae65da6c | 1541 | outputFunc, outputStream); |
891091df | 1542 | gfree(codeToGID); |
be8c3862 | 1543 | delete ttFile; |
1544 | gfree(fontBuf); | |
1545 | ||
1546 | // ending comment | |
ae65da6c | 1547 | writePS("%%EndResource\n"); |
be8c3862 | 1548 | } |
1549 | ||
891091df | 1550 | void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *psName) { |
1551 | char unique[32]; | |
be8c3862 | 1552 | GString *fileName; |
1553 | char *fontBuf; | |
1554 | int fontLen; | |
1555 | TrueTypeFontFile *ttFile; | |
891091df | 1556 | Gushort *codeToGID; |
be8c3862 | 1557 | int i; |
1558 | ||
1559 | // check if font is already embedded | |
1560 | fileName = font->getExtFontFile(); | |
1561 | for (i = 0; i < fontFileNameLen; ++i) { | |
1562 | if (!fontFileNames[i]->cmp(fileName)) { | |
891091df | 1563 | sprintf(unique, "_%d", nextTrueTypeNum++); |
1564 | psName->append(unique); | |
1565 | break; | |
be8c3862 | 1566 | } |
1567 | } | |
1568 | ||
1569 | // add entry to fontFileNames list | |
891091df | 1570 | if (i == fontFileNameLen) { |
be8c3862 | 1571 | if (fontFileNameLen >= fontFileNameSize) { |
1572 | fontFileNameSize += 64; | |
891091df | 1573 | fontFileNames = |
1574 | (GString **)grealloc(fontFileNames, | |
be8c3862 | 1575 | fontFileNameSize * sizeof(GString *)); |
1576 | } | |
891091df | 1577 | } |
be8c3862 | 1578 | fontFileNames[fontFileNameLen++] = fileName->copy(); |
1579 | ||
1580 | // beginning comment | |
891091df | 1581 | writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); |
be8c3862 | 1582 | embFontList->append("%%+ font "); |
891091df | 1583 | embFontList->append(psName->getCString()); |
be8c3862 | 1584 | embFontList->append("\n"); |
1585 | ||
1586 | // convert it to a Type 42 font | |
1587 | fontBuf = font->readExtFontFile(&fontLen); | |
1588 | ttFile = new TrueTypeFontFile(fontBuf, fontLen); | |
891091df | 1589 | codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ttFile); |
1590 | ttFile->convertToType42(psName->getCString(), | |
1591 | ((Gfx8BitFont *)font)->getEncoding(), | |
1592 | ((Gfx8BitFont *)font)->getHasEncoding(), | |
1593 | codeToGID, | |
ae65da6c | 1594 | outputFunc, outputStream); |
be8c3862 | 1595 | delete ttFile; |
1596 | gfree(fontBuf); | |
1597 | ||
1598 | // ending comment | |
ae65da6c | 1599 | writePS("%%EndResource\n"); |
be8c3862 | 1600 | } |
1601 | ||
1602 | void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, | |
891091df | 1603 | GString *psName) { |
be8c3862 | 1604 | char *fontBuf; |
1605 | int fontLen; | |
1606 | Type1CFontFile *t1cFile; | |
1607 | int i; | |
1608 | ||
1609 | // check if font is already embedded | |
1610 | for (i = 0; i < fontFileIDLen; ++i) { | |
1611 | if (fontFileIDs[i].num == id->num && | |
1612 | fontFileIDs[i].gen == id->gen) | |
1613 | return; | |
1614 | } | |
1615 | ||
1616 | // add entry to fontFileIDs list | |
1617 | if (fontFileIDLen >= fontFileIDSize) { | |
1618 | fontFileIDSize += 64; | |
1619 | fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref)); | |
1620 | } | |
1621 | fontFileIDs[fontFileIDLen++] = *id; | |
1622 | ||
1623 | // beginning comment | |
891091df | 1624 | writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); |
be8c3862 | 1625 | embFontList->append("%%+ font "); |
891091df | 1626 | embFontList->append(psName->getCString()); |
be8c3862 | 1627 | embFontList->append("\n"); |
1628 | ||
1629 | // convert it to a Type 0 font | |
1630 | fontBuf = font->readEmbFontFile(xref, &fontLen); | |
1631 | t1cFile = new Type1CFontFile(fontBuf, fontLen); | |
0e2e516e | 1632 | if (t1cFile->isOk()) { |
1633 | if (globalParams->getPSLevel() >= psLevel3) { | |
1634 | // Level 3: use a CID font | |
891091df | 1635 | t1cFile->convertToCIDType0(psName->getCString(), |
1636 | outputFunc, outputStream); | |
0e2e516e | 1637 | } else { |
1638 | // otherwise: use a non-CID composite font | |
891091df | 1639 | t1cFile->convertToType0(psName->getCString(), outputFunc, outputStream); |
0e2e516e | 1640 | } |
be8c3862 | 1641 | } |
1642 | delete t1cFile; | |
1643 | gfree(fontBuf); | |
1644 | ||
1645 | // ending comment | |
ae65da6c | 1646 | writePS("%%EndResource\n"); |
be8c3862 | 1647 | } |
1648 | ||
1649 | void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, | |
891091df | 1650 | GString *psName) { |
be8c3862 | 1651 | char *fontBuf; |
1652 | int fontLen; | |
1653 | TrueTypeFontFile *ttFile; | |
1654 | int i; | |
1655 | ||
1656 | // check if font is already embedded | |
1657 | for (i = 0; i < fontFileIDLen; ++i) { | |
1658 | if (fontFileIDs[i].num == id->num && | |
1659 | fontFileIDs[i].gen == id->gen) | |
1660 | return; | |
1661 | } | |
1662 | ||
1663 | // add entry to fontFileIDs list | |
1664 | if (fontFileIDLen >= fontFileIDSize) { | |
1665 | fontFileIDSize += 64; | |
1666 | fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref)); | |
1667 | } | |
1668 | fontFileIDs[fontFileIDLen++] = *id; | |
1669 | ||
1670 | // beginning comment | |
891091df | 1671 | writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); |
be8c3862 | 1672 | embFontList->append("%%+ font "); |
891091df | 1673 | embFontList->append(psName->getCString()); |
be8c3862 | 1674 | embFontList->append("\n"); |
1675 | ||
1676 | // convert it to a Type 0 font | |
1677 | fontBuf = font->readEmbFontFile(xref, &fontLen); | |
1678 | ttFile = new TrueTypeFontFile(fontBuf, fontLen); | |
1679 | if (globalParams->getPSLevel() >= psLevel3) { | |
891091df | 1680 | ttFile->convertToCIDType2(psName->getCString(), |
be8c3862 | 1681 | ((GfxCIDFont *)font)->getCIDToGID(), |
ae65da6c | 1682 | ((GfxCIDFont *)font)->getCIDToGIDLen(), |
1683 | outputFunc, outputStream); | |
be8c3862 | 1684 | } else { |
1685 | // otherwise: use a non-CID composite font | |
891091df | 1686 | ttFile->convertToType0(psName->getCString(), |
1687 | ((GfxCIDFont *)font)->getCIDToGID(), | |
ae65da6c | 1688 | ((GfxCIDFont *)font)->getCIDToGIDLen(), |
1689 | outputFunc, outputStream); | |
be8c3862 | 1690 | } |
753453e4 | 1691 | delete ttFile; |
1692 | gfree(fontBuf); | |
1693 | ||
1694 | // ending comment | |
ae65da6c | 1695 | writePS("%%EndResource\n"); |
9c72faab | 1696 | } |
1697 | ||
891091df | 1698 | void PSOutputDev::setupType3Font(GfxFont *font, GString *psName, |
be8c3862 | 1699 | Dict *parentResDict) { |
1700 | Dict *resDict; | |
1701 | Dict *charProcs; | |
1702 | Object charProc; | |
1703 | Gfx *gfx; | |
1704 | PDFRectangle box; | |
1705 | double *m; | |
ae65da6c | 1706 | char buf[256]; |
be8c3862 | 1707 | int i; |
1708 | ||
1709 | // set up resources used by font | |
1710 | if ((resDict = ((Gfx8BitFont *)font)->getResources())) { | |
891091df | 1711 | inType3Char = gTrue; |
be8c3862 | 1712 | setupResources(resDict); |
891091df | 1713 | inType3Char = gFalse; |
be8c3862 | 1714 | } else { |
1715 | resDict = parentResDict; | |
1716 | } | |
1717 | ||
1718 | // beginning comment | |
891091df | 1719 | writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); |
be8c3862 | 1720 | embFontList->append("%%+ font "); |
891091df | 1721 | embFontList->append(psName->getCString()); |
be8c3862 | 1722 | embFontList->append("\n"); |
1723 | ||
1724 | // font dictionary | |
1725 | writePS("7 dict begin\n"); | |
1726 | writePS("/FontType 3 def\n"); | |
1727 | m = font->getFontMatrix(); | |
ae65da6c | 1728 | writePSFmt("/FontMatrix [%g %g %g %g %g %g] def\n", |
1729 | m[0], m[1], m[2], m[3], m[4], m[5]); | |
be8c3862 | 1730 | m = font->getFontBBox(); |
ae65da6c | 1731 | writePSFmt("/FontBBox [%g %g %g %g] def\n", |
1732 | m[0], m[1], m[2], m[3]); | |
be8c3862 | 1733 | writePS("/Encoding 256 array def\n"); |
1734 | writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); | |
1735 | writePS("/BuildGlyph {\n"); | |
1736 | writePS(" exch /CharProcs get exch\n"); | |
1737 | writePS(" 2 copy known not { pop /.notdef } if\n"); | |
1738 | writePS(" get exec\n"); | |
1739 | writePS("} bind def\n"); | |
1740 | writePS("/BuildChar {\n"); | |
1741 | writePS(" 1 index /Encoding get exch get\n"); | |
1742 | writePS(" 1 index /BuildGlyph get exec\n"); | |
1743 | writePS("} bind def\n"); | |
1744 | if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) { | |
ae65da6c | 1745 | writePSFmt("/CharProcs %d dict def\n", charProcs->getLength()); |
be8c3862 | 1746 | writePS("CharProcs begin\n"); |
1747 | box.x1 = m[0]; | |
1748 | box.y1 = m[1]; | |
1749 | box.x2 = m[2]; | |
1750 | box.y2 = m[3]; | |
1751 | gfx = new Gfx(xref, this, resDict, &box, gFalse, NULL); | |
1752 | inType3Char = gTrue; | |
ae65da6c | 1753 | t3Cacheable = gFalse; |
be8c3862 | 1754 | for (i = 0; i < charProcs->getLength(); ++i) { |
ae65da6c | 1755 | writePS("/"); |
1756 | writePSName(charProcs->getKey(i)); | |
1757 | writePS(" {\n"); | |
be8c3862 | 1758 | gfx->display(charProcs->getVal(i, &charProc)); |
1759 | charProc.free(); | |
1760 | if (t3String) { | |
1761 | if (t3Cacheable) { | |
ae65da6c | 1762 | sprintf(buf, "%g %g %g %g %g %g setcachedevice\n", |
be8c3862 | 1763 | t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY); |
1764 | } else { | |
ae65da6c | 1765 | sprintf(buf, "%g %g setcharwidth\n", t3WX, t3WY); |
be8c3862 | 1766 | } |
ae65da6c | 1767 | (*outputFunc)(outputStream, buf, strlen(buf)); |
1768 | (*outputFunc)(outputStream, t3String->getCString(), | |
1769 | t3String->getLength()); | |
be8c3862 | 1770 | delete t3String; |
1771 | t3String = NULL; | |
1772 | } | |
ae65da6c | 1773 | (*outputFunc)(outputStream, "Q\n", 2); |
be8c3862 | 1774 | writePS("} def\n"); |
1775 | } | |
1776 | inType3Char = gFalse; | |
1777 | delete gfx; | |
1778 | writePS("end\n"); | |
1779 | } | |
1780 | writePS("currentdict end\n"); | |
891091df | 1781 | writePSFmt("/%s exch definefont pop\n", psName->getCString()); |
be8c3862 | 1782 | |
1783 | // ending comment | |
ae65da6c | 1784 | writePS("%%EndResource\n"); |
be8c3862 | 1785 | } |
1786 | ||
b5cb0608 | 1787 | void PSOutputDev::setupImages(Dict *resDict) { |
1788 | Object xObjDict, xObj, xObjRef, subtypeObj; | |
1789 | int i; | |
1790 | ||
891091df | 1791 | if (!(mode == psModeForm || inType3Char)) { |
b5cb0608 | 1792 | return; |
1793 | } | |
1794 | ||
1795 | resDict->lookup("XObject", &xObjDict); | |
1796 | if (xObjDict.isDict()) { | |
1797 | for (i = 0; i < xObjDict.dictGetLength(); ++i) { | |
1798 | xObjDict.dictGetValNF(i, &xObjRef); | |
1799 | xObjDict.dictGetVal(i, &xObj); | |
1800 | if (xObj.isStream()) { | |
1801 | xObj.streamGetDict()->lookup("Subtype", &subtypeObj); | |
1802 | if (subtypeObj.isName("Image")) { | |
1803 | if (xObjRef.isRef()) { | |
1804 | setupImage(xObjRef.getRef(), xObj.getStream()); | |
1805 | } else { | |
1806 | error(-1, "Image in resource dict is not an indirect reference"); | |
1807 | } | |
1808 | } | |
1809 | subtypeObj.free(); | |
1810 | } | |
1811 | xObj.free(); | |
1812 | xObjRef.free(); | |
1813 | } | |
1814 | } | |
1815 | xObjDict.free(); | |
1816 | } | |
1817 | ||
1818 | void PSOutputDev::setupImage(Ref id, Stream *str) { | |
1819 | int c; | |
1820 | int size, line, col, i; | |
1821 | ||
1822 | // construct an encoder stream | |
be8c3862 | 1823 | if (globalParams->getPSASCIIHex()) { |
1824 | str = new ASCIIHexEncoder(str); | |
1825 | } else { | |
1826 | str = new ASCII85Encoder(str); | |
1827 | } | |
b5cb0608 | 1828 | |
1829 | // compute image data size | |
1830 | str->reset(); | |
1831 | col = size = 0; | |
1832 | do { | |
1833 | do { | |
1834 | c = str->getChar(); | |
1835 | } while (c == '\n' || c == '\r'); | |
1836 | if (c == '~' || c == EOF) { | |
1837 | break; | |
1838 | } | |
1839 | if (c == 'z') { | |
1840 | ++col; | |
1841 | } else { | |
1842 | ++col; | |
1843 | for (i = 1; i <= 4; ++i) { | |
1844 | do { | |
1845 | c = str->getChar(); | |
1846 | } while (c == '\n' || c == '\r'); | |
1847 | if (c == '~' || c == EOF) { | |
1848 | break; | |
1849 | } | |
1850 | ++col; | |
1851 | } | |
1852 | } | |
1853 | if (col > 225) { | |
1854 | ++size; | |
1855 | col = 0; | |
1856 | } | |
1857 | } while (c != '~' && c != EOF); | |
1858 | ++size; | |
ae65da6c | 1859 | writePSFmt("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen); |
891091df | 1860 | str->close(); |
b5cb0608 | 1861 | |
1862 | // write the data into the array | |
1863 | str->reset(); | |
1864 | line = col = 0; | |
1865 | writePS("dup 0 <~"); | |
1866 | do { | |
1867 | do { | |
1868 | c = str->getChar(); | |
1869 | } while (c == '\n' || c == '\r'); | |
1870 | if (c == '~' || c == EOF) { | |
1871 | break; | |
1872 | } | |
1873 | if (c == 'z') { | |
be8c3862 | 1874 | writePSChar(c); |
b5cb0608 | 1875 | ++col; |
1876 | } else { | |
be8c3862 | 1877 | writePSChar(c); |
b5cb0608 | 1878 | ++col; |
1879 | for (i = 1; i <= 4; ++i) { | |
1880 | do { | |
1881 | c = str->getChar(); | |
1882 | } while (c == '\n' || c == '\r'); | |
1883 | if (c == '~' || c == EOF) { | |
1884 | break; | |
1885 | } | |
be8c3862 | 1886 | writePSChar(c); |
b5cb0608 | 1887 | ++col; |
1888 | } | |
1889 | } | |
1890 | // each line is: "dup nnnnn <~...data...~> put<eol>" | |
1891 | // so max data length = 255 - 20 = 235 | |
1892 | // chunks are 1 or 4 bytes each, so we have to stop at 232 | |
1893 | // but make it 225 just to be safe | |
1894 | if (col > 225) { | |
1895 | writePS("~> put\n"); | |
1896 | ++line; | |
ae65da6c | 1897 | writePSFmt("dup %d <~", line); |
b5cb0608 | 1898 | col = 0; |
1899 | } | |
1900 | } while (c != '~' && c != EOF); | |
1901 | writePS("~> put\n"); | |
1902 | writePS("pop\n"); | |
891091df | 1903 | str->close(); |
b5cb0608 | 1904 | |
1905 | delete str; | |
1906 | } | |
1907 | ||
9c72faab | 1908 | void PSOutputDev::startPage(int pageNum, GfxState *state) { |
e02e59e4 | 1909 | int x1, y1, x2, y2; |
a0db7254 | 1910 | int imageWidth, imageHeight; |
1911 | int left, bottom, right, top; | |
e02e59e4 | 1912 | double width, height, t; |
9c72faab | 1913 | |
c50b7b12 | 1914 | if (globalParams->getPSFit()) { |
1915 | globalParams->getPSImageableArea(left, bottom, right, top); | |
1916 | } else { | |
1917 | left = bottom = 0; | |
1918 | right = globalParams->getPSPaperWidth(); | |
1919 | top = globalParams->getPSPaperHeight(); | |
1920 | } | |
1921 | ||
a0db7254 | 1922 | imageWidth = right - left; |
1923 | imageHeight = top - bottom; | |
ae65da6c | 1924 | |
753453e4 | 1925 | switch (mode) { |
9c72faab | 1926 | |
753453e4 | 1927 | case psModePS: |
ae65da6c | 1928 | writePSFmt("%%%%Page: %d %d\n", pageNum, seqPage); |
9c72faab | 1929 | |
c50b7b12 | 1930 | // possibly rotate, translate, and scale page |
9c72faab | 1931 | x1 = (int)(state->getX1() + 0.5); |
1932 | y1 = (int)(state->getY1() + 0.5); | |
1933 | x2 = (int)(state->getX2() + 0.5); | |
1934 | y2 = (int)(state->getY2() + 0.5); | |
a0db7254 | 1935 | writePSFmt("%%%%PageBoundingBox: %d %d %d %d\n", |
6f83172d | 1936 | (int)floor((float)x1), (int)floor((float)y1), |
1937 | (int)ceil((float)x2), (int)ceil((float)y2)); | |
a0db7254 | 1938 | writePS("%%BeginPageSetup\n"); |
9c72faab | 1939 | width = x2 - x1; |
1940 | height = y2 - y1; | |
c50b7b12 | 1941 | tx = 0; |
1942 | ty = 0; | |
a0db7254 | 1943 | if (width > height && width > imageWidth) { |
52118ca3 | 1944 | landscape = gTrue; |
ae65da6c | 1945 | writePSFmt("%%%%PageOrientation: %s\n", |
1946 | state->getCTM()[0] ? "Landscape" : "Portrait"); | |
9c72faab | 1947 | writePS("pdfStartPage\n"); |
1948 | writePS("90 rotate\n"); | |
c50b7b12 | 1949 | ty = -globalParams->getPSPaperWidth(); |
9c72faab | 1950 | t = width; |
1951 | width = height; | |
1952 | height = t; | |
1953 | } else { | |
52118ca3 | 1954 | landscape = gFalse; |
ae65da6c | 1955 | writePSFmt("%%%%PageOrientation: %s\n", |
1956 | state->getCTM()[0] ? "Portrait" : "Landscape"); | |
9c72faab | 1957 | writePS("pdfStartPage\n"); |
c50b7b12 | 1958 | } |
e02e59e4 | 1959 | if ((width > imageWidth || height > imageHeight) && globalParams->getPSFit()) { |
1960 | xScale = (double)imageWidth / (double)width; | |
1961 | yScale = (double)imageHeight / (double)height; | |
1962 | if (yScale < xScale) { | |
1963 | xScale = yScale; | |
1964 | } else { | |
1965 | yScale = xScale; | |
1966 | } | |
1967 | } else { | |
1968 | xScale = yScale = 1; | |
1969 | } | |
c50b7b12 | 1970 | if (globalParams->getPSFit()) { |
e02e59e4 | 1971 | tx -= x1 * xScale; |
1972 | ty -= y1 * yScale; | |
1973 | width *= xScale; | |
1974 | height *= yScale; | |
52118ca3 | 1975 | } |
a0db7254 | 1976 | tx += left; |
1977 | ty += bottom; | |
1978 | if (width < imageWidth) { | |
e02e59e4 | 1979 | if(landscape) { |
1980 | ty += (imageWidth - width) / 2; | |
1981 | } else { | |
1982 | tx += (imageWidth - width) / 2; | |
1983 | } | |
52118ca3 | 1984 | } |
a0db7254 | 1985 | if (height < imageHeight) { |
e02e59e4 | 1986 | if(landscape) { |
1987 | tx += (imageHeight - height) / 2; | |
1988 | } else { | |
1989 | ty += (imageHeight - height) / 2; | |
1990 | } | |
52118ca3 | 1991 | } |
1992 | if (tx != 0 || ty != 0) { | |
ae65da6c | 1993 | writePSFmt("%g %g translate\n", tx, ty); |
9c72faab | 1994 | } |
e02e59e4 | 1995 | if (xScale != 1) { |
ae65da6c | 1996 | writePSFmt("%0.4f %0.4f scale\n", xScale, xScale); |
9c72faab | 1997 | } |
1998 | ||
ae65da6c | 1999 | writePS("%%EndPageSetup\n"); |
9c72faab | 2000 | ++seqPage; |
753453e4 | 2001 | break; |
2002 | ||
2003 | case psModeEPS: | |
2004 | writePS("pdfStartPage\n"); | |
2005 | tx = ty = 0; | |
2006 | xScale = yScale = 1; | |
2007 | landscape = gFalse; | |
2008 | break; | |
2009 | ||
2010 | case psModeForm: | |
2011 | writePS("/PaintProc {\n"); | |
2012 | writePS("begin xpdf begin\n"); | |
2013 | writePS("pdfStartPage\n"); | |
2014 | tx = ty = 0; | |
2015 | xScale = yScale = 1; | |
2016 | landscape = gFalse; | |
2017 | break; | |
9c72faab | 2018 | } |
891091df | 2019 | |
2020 | if (underlayCbk) { | |
2021 | (*underlayCbk)(this, underlayCbkData); | |
2022 | } | |
9c72faab | 2023 | } |
2024 | ||
2025 | void PSOutputDev::endPage() { | |
891091df | 2026 | if (overlayCbk) { |
2027 | (*overlayCbk)(this, overlayCbkData); | |
2028 | } | |
2029 | ||
ae65da6c | 2030 | |
753453e4 | 2031 | if (mode == psModeForm) { |
9c72faab | 2032 | writePS("pdfEndPage\n"); |
2033 | writePS("end end\n"); | |
2034 | writePS("} def\n"); | |
b5cb0608 | 2035 | writePS("end end\n"); |
9c72faab | 2036 | } else { |
891091df | 2037 | if (!manualCtrl) { |
9c72faab | 2038 | writePS("showpage\n"); |
ae65da6c | 2039 | writePS("%%PageTrailer\n"); |
891091df | 2040 | writePageTrailer(); |
2041 | } | |
9c72faab | 2042 | } |
2043 | } | |
2044 | ||
2045 | void PSOutputDev::saveState(GfxState *state) { | |
2046 | writePS("q\n"); | |
891091df | 2047 | ++numSaves; |
9c72faab | 2048 | } |
2049 | ||
2050 | void PSOutputDev::restoreState(GfxState *state) { | |
2051 | writePS("Q\n"); | |
891091df | 2052 | --numSaves; |
9c72faab | 2053 | } |
2054 | ||
2055 | void PSOutputDev::updateCTM(GfxState *state, double m11, double m12, | |
2056 | double m21, double m22, double m31, double m32) { | |
ae65da6c | 2057 | writePSFmt("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32); |
9c72faab | 2058 | } |
2059 | ||
2060 | void PSOutputDev::updateLineDash(GfxState *state) { | |
2061 | double *dash; | |
2062 | double start; | |
2063 | int length, i; | |
2064 | ||
2065 | state->getLineDash(&dash, &length, &start); | |
2066 | writePS("["); | |
2067 | for (i = 0; i < length; ++i) | |
ae65da6c | 2068 | writePSFmt("%g%s", dash[i], (i == length-1) ? "" : " "); |
2069 | writePSFmt("] %g d\n", start); | |
9c72faab | 2070 | } |
2071 | ||
2072 | void PSOutputDev::updateFlatness(GfxState *state) { | |
ae65da6c | 2073 | writePSFmt("%d i\n", state->getFlatness()); |
9c72faab | 2074 | } |
2075 | ||
2076 | void PSOutputDev::updateLineJoin(GfxState *state) { | |
ae65da6c | 2077 | writePSFmt("%d j\n", state->getLineJoin()); |
9c72faab | 2078 | } |
2079 | ||
2080 | void PSOutputDev::updateLineCap(GfxState *state) { | |
ae65da6c | 2081 | writePSFmt("%d J\n", state->getLineCap()); |
9c72faab | 2082 | } |
2083 | ||
2084 | void PSOutputDev::updateMiterLimit(GfxState *state) { | |
ae65da6c | 2085 | writePSFmt("%g M\n", state->getMiterLimit()); |
9c72faab | 2086 | } |
2087 | ||
2088 | void PSOutputDev::updateLineWidth(GfxState *state) { | |
ae65da6c | 2089 | writePSFmt("%g w\n", state->getLineWidth()); |
9c72faab | 2090 | } |
2091 | ||
2092 | void PSOutputDev::updateFillColor(GfxState *state) { | |
753453e4 | 2093 | GfxColor color; |
2094 | double gray; | |
b5cb0608 | 2095 | GfxRGB rgb; |
2096 | GfxCMYK cmyk; | |
753453e4 | 2097 | GfxSeparationColorSpace *sepCS; |
b5cb0608 | 2098 | |
753453e4 | 2099 | switch (level) { |
2100 | case psLevel1: | |
2101 | state->getFillGray(&gray); | |
ae65da6c | 2102 | writePSFmt("%g g\n", gray); |
753453e4 | 2103 | break; |
2104 | case psLevel1Sep: | |
b5cb0608 | 2105 | state->getFillCMYK(&cmyk); |
ae65da6c | 2106 | writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); |
2107 | addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k); | |
753453e4 | 2108 | break; |
2109 | case psLevel2: | |
be8c3862 | 2110 | case psLevel3: |
753453e4 | 2111 | if (state->getFillColorSpace()->getMode() == csDeviceCMYK) { |
2112 | state->getFillCMYK(&cmyk); | |
ae65da6c | 2113 | writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); |
b5cb0608 | 2114 | } else { |
753453e4 | 2115 | state->getFillRGB(&rgb); |
2116 | if (rgb.r == rgb.g && rgb.g == rgb.b) { | |
ae65da6c | 2117 | writePSFmt("%g g\n", rgb.r); |
753453e4 | 2118 | } else { |
ae65da6c | 2119 | writePSFmt("%g %g %g rg\n", rgb.r, rgb.g, rgb.b); |
753453e4 | 2120 | } |
b5cb0608 | 2121 | } |
753453e4 | 2122 | break; |
2123 | case psLevel2Sep: | |
be8c3862 | 2124 | case psLevel3Sep: |
753453e4 | 2125 | if (state->getFillColorSpace()->getMode() == csSeparation) { |
2126 | sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace(); | |
2127 | color.c[0] = 1; | |
2128 | sepCS->getCMYK(&color, &cmyk); | |
ae65da6c | 2129 | writePSFmt("%g %g %g %g %g (%s) ck\n", |
2130 | state->getFillColor()->c[0], | |
2131 | cmyk.c, cmyk.m, cmyk.y, cmyk.k, | |
2132 | sepCS->getName()->getCString()); | |
753453e4 | 2133 | addCustomColor(sepCS); |
2134 | } else { | |
2135 | state->getFillCMYK(&cmyk); | |
ae65da6c | 2136 | writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); |
753453e4 | 2137 | addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k); |
2138 | } | |
2139 | break; | |
b5cb0608 | 2140 | } |
be8c3862 | 2141 | t3Cacheable = gFalse; |
9c72faab | 2142 | } |
2143 | ||
2144 | void PSOutputDev::updateStrokeColor(GfxState *state) { | |
753453e4 | 2145 | GfxColor color; |
2146 | double gray; | |
b5cb0608 | 2147 | GfxRGB rgb; |
2148 | GfxCMYK cmyk; | |
753453e4 | 2149 | GfxSeparationColorSpace *sepCS; |
b5cb0608 | 2150 | |
753453e4 | 2151 | switch (level) { |
2152 | case psLevel1: | |
2153 | state->getStrokeGray(&gray); | |
ae65da6c | 2154 | writePSFmt("%g G\n", gray); |
753453e4 | 2155 | break; |
2156 | case psLevel1Sep: | |
b5cb0608 | 2157 | state->getStrokeCMYK(&cmyk); |
ae65da6c | 2158 | writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); |
2159 | addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k); | |
753453e4 | 2160 | break; |
2161 | case psLevel2: | |
be8c3862 | 2162 | case psLevel3: |
753453e4 | 2163 | if (state->getStrokeColorSpace()->getMode() == csDeviceCMYK) { |
2164 | state->getStrokeCMYK(&cmyk); | |
ae65da6c | 2165 | writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); |
b5cb0608 | 2166 | } else { |
753453e4 | 2167 | state->getStrokeRGB(&rgb); |
2168 | if (rgb.r == rgb.g && rgb.g == rgb.b) { | |
ae65da6c | 2169 | writePSFmt("%g G\n", rgb.r); |
753453e4 | 2170 | } else { |
ae65da6c | 2171 | writePSFmt("%g %g %g RG\n", rgb.r, rgb.g, rgb.b); |
753453e4 | 2172 | } |
2173 | } | |
2174 | break; | |
2175 | case psLevel2Sep: | |
be8c3862 | 2176 | case psLevel3Sep: |
753453e4 | 2177 | if (state->getStrokeColorSpace()->getMode() == csSeparation) { |
2178 | sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace(); | |
2179 | color.c[0] = 1; | |
2180 | sepCS->getCMYK(&color, &cmyk); | |
ae65da6c | 2181 | writePSFmt("%g %g %g %g %g (%s) CK\n", |
2182 | state->getStrokeColor()->c[0], | |
2183 | cmyk.c, cmyk.m, cmyk.y, cmyk.k, | |
2184 | sepCS->getName()->getCString()); | |
753453e4 | 2185 | addCustomColor(sepCS); |
2186 | } else { | |
2187 | state->getStrokeCMYK(&cmyk); | |
ae65da6c | 2188 | writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); |
753453e4 | 2189 | addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k); |
2190 | } | |
2191 | break; | |
2192 | } | |
be8c3862 | 2193 | t3Cacheable = gFalse; |
753453e4 | 2194 | } |
2195 | ||
2196 | void PSOutputDev::addProcessColor(double c, double m, double y, double k) { | |
2197 | if (c > 0) { | |
2198 | processColors |= psProcessCyan; | |
2199 | } | |
2200 | if (m > 0) { | |
2201 | processColors |= psProcessMagenta; | |
2202 | } | |
2203 | if (y > 0) { | |
2204 | processColors |= psProcessYellow; | |
2205 | } | |
2206 | if (k > 0) { | |
2207 | processColors |= psProcessBlack; | |
2208 | } | |
2209 | } | |
2210 | ||
2211 | void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) { | |
2212 | PSOutCustomColor *cc; | |
2213 | GfxColor color; | |
2214 | GfxCMYK cmyk; | |
2215 | ||
2216 | for (cc = customColors; cc; cc = cc->next) { | |
2217 | if (!cc->name->cmp(sepCS->getName())) { | |
2218 | return; | |
b5cb0608 | 2219 | } |
2220 | } | |
753453e4 | 2221 | color.c[0] = 1; |
2222 | sepCS->getCMYK(&color, &cmyk); | |
2223 | cc = new PSOutCustomColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k, | |
2224 | sepCS->getName()->copy()); | |
2225 | cc->next = customColors; | |
2226 | customColors = cc; | |
9c72faab | 2227 | } |
2228 | ||
2229 | void PSOutputDev::updateFont(GfxState *state) { | |
2230 | if (state->getFont()) { | |
ae65da6c | 2231 | writePSFmt("/F%d_%d %g Tf\n", |
2232 | state->getFont()->getID()->num, state->getFont()->getID()->gen, | |
2233 | state->getFontSize()); | |
9c72faab | 2234 | } |
2235 | } | |
2236 | ||
2237 | void PSOutputDev::updateTextMat(GfxState *state) { | |
2238 | double *mat; | |
2239 | ||
2240 | mat = state->getTextMat(); | |
ae65da6c | 2241 | writePSFmt("[%g %g %g %g %g %g] Tm\n", |
2242 | mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); | |
9c72faab | 2243 | } |
2244 | ||
2245 | void PSOutputDev::updateCharSpace(GfxState *state) { | |
ae65da6c | 2246 | writePSFmt("%g Tc\n", state->getCharSpace()); |
9c72faab | 2247 | } |
2248 | ||
2249 | void PSOutputDev::updateRender(GfxState *state) { | |
be8c3862 | 2250 | int rm; |
2251 | ||
2252 | rm = state->getRender(); | |
ae65da6c | 2253 | writePSFmt("%d Tr\n", rm); |
be8c3862 | 2254 | rm &= 3; |
2255 | if (rm != 0 && rm != 3) { | |
2256 | t3Cacheable = gFalse; | |
2257 | } | |
9c72faab | 2258 | } |
2259 | ||
2260 | void PSOutputDev::updateRise(GfxState *state) { | |
ae65da6c | 2261 | writePSFmt("%g Ts\n", state->getRise()); |
9c72faab | 2262 | } |
2263 | ||
2264 | void PSOutputDev::updateWordSpace(GfxState *state) { | |
ae65da6c | 2265 | writePSFmt("%g Tw\n", state->getWordSpace()); |
9c72faab | 2266 | } |
2267 | ||
2268 | void PSOutputDev::updateHorizScaling(GfxState *state) { | |
ae65da6c | 2269 | writePSFmt("%g Tz\n", state->getHorizScaling()); |
9c72faab | 2270 | } |
2271 | ||
2272 | void PSOutputDev::updateTextPos(GfxState *state) { | |
ae65da6c | 2273 | writePSFmt("%g %g Td\n", state->getLineX(), state->getLineY()); |
9c72faab | 2274 | } |
2275 | ||
2276 | void PSOutputDev::updateTextShift(GfxState *state, double shift) { | |
be8c3862 | 2277 | if (state->getFont()->getWMode()) { |
ae65da6c | 2278 | writePSFmt("%g TJmV\n", shift); |
be8c3862 | 2279 | } else { |
ae65da6c | 2280 | writePSFmt("%g TJm\n", shift); |
be8c3862 | 2281 | } |
9c72faab | 2282 | } |
2283 | ||
2284 | void PSOutputDev::stroke(GfxState *state) { | |
2285 | doPath(state->getPath()); | |
be8c3862 | 2286 | if (t3String) { |
2287 | // if we're construct a cacheable Type 3 glyph, we need to do | |
2288 | // everything in the fill color | |
2289 | writePS("Sf\n"); | |
2290 | } else { | |
2291 | writePS("S\n"); | |
2292 | } | |
9c72faab | 2293 | } |
2294 | ||
2295 | void PSOutputDev::fill(GfxState *state) { | |
2296 | doPath(state->getPath()); | |
2297 | writePS("f\n"); | |
2298 | } | |
2299 | ||
2300 | void PSOutputDev::eoFill(GfxState *state) { | |
2301 | doPath(state->getPath()); | |
2302 | writePS("f*\n"); | |
2303 | } | |
2304 | ||
2305 | void PSOutputDev::clip(GfxState *state) { | |
2306 | doPath(state->getPath()); | |
2307 | writePS("W\n"); | |
2308 | } | |
2309 | ||
2310 | void PSOutputDev::eoClip(GfxState *state) { | |
2311 | doPath(state->getPath()); | |
2312 | writePS("W*\n"); | |
2313 | } | |
2314 | ||
2315 | void PSOutputDev::doPath(GfxPath *path) { | |
2316 | GfxSubpath *subpath; | |
2317 | double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4; | |
2318 | int n, m, i, j; | |
2319 | ||
2320 | n = path->getNumSubpaths(); | |
2321 | ||
2322 | if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) { | |
2323 | subpath = path->getSubpath(0); | |
2324 | x0 = subpath->getX(0); | |
2325 | y0 = subpath->getY(0); | |
2326 | x4 = subpath->getX(4); | |
2327 | y4 = subpath->getY(4); | |
2328 | if (x4 == x0 && y4 == y0) { | |
2329 | x1 = subpath->getX(1); | |
2330 | y1 = subpath->getY(1); | |
2331 | x2 = subpath->getX(2); | |
2332 | y2 = subpath->getY(2); | |
2333 | x3 = subpath->getX(3); | |
2334 | y3 = subpath->getY(3); | |
2335 | if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) { | |
ae65da6c | 2336 | writePSFmt("%g %g %g %g re\n", |
2337 | x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1, | |
2338 | fabs(x2 - x0), fabs(y1 - y0)); | |
9c72faab | 2339 | return; |
2340 | } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) { | |
ae65da6c | 2341 | writePSFmt("%g %g %g %g re\n", |
2342 | x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2, | |
2343 | fabs(x1 - x0), fabs(y2 - y0)); | |
9c72faab | 2344 | return; |
2345 | } | |
2346 | } | |
2347 | } | |
2348 | ||
2349 | for (i = 0; i < n; ++i) { | |
2350 | subpath = path->getSubpath(i); | |
2351 | m = subpath->getNumPoints(); | |
ae65da6c | 2352 | writePSFmt("%g %g m\n", subpath->getX(0), subpath->getY(0)); |
9c72faab | 2353 | j = 1; |
2354 | while (j < m) { | |
2355 | if (subpath->getCurve(j)) { | |
ae65da6c | 2356 | writePSFmt("%g %g %g %g %g %g c\n", subpath->getX(j), subpath->getY(j), |
2357 | subpath->getX(j+1), subpath->getY(j+1), | |
2358 | subpath->getX(j+2), subpath->getY(j+2)); | |
9c72faab | 2359 | j += 3; |
2360 | } else { | |
ae65da6c | 2361 | writePSFmt("%g %g l\n", subpath->getX(j), subpath->getY(j)); |
9c72faab | 2362 | ++j; |
2363 | } | |
2364 | } | |
52118ca3 | 2365 | if (subpath->isClosed()) { |
2366 | writePS("h\n"); | |
2367 | } | |
9c72faab | 2368 | } |
2369 | } | |
2370 | ||
2371 | void PSOutputDev::drawString(GfxState *state, GString *s) { | |
be8c3862 | 2372 | GfxFont *font; |
2373 | int wMode; | |
2374 | GString *s2; | |
2375 | double dx, dy, dx2, dy2, originX, originY; | |
2376 | char *p; | |
2377 | UnicodeMap *uMap; | |
2378 | CharCode code; | |
2379 | Unicode u[8]; | |
2380 | char buf[8]; | |
2381 | int len, nChars, uLen, n, m, i, j; | |
2382 | ||
9c72faab | 2383 | // check for invisible text -- this is used by Acrobat Capture |
891091df | 2384 | if (state->getRender() == 3) { |
9c72faab | 2385 | return; |
be8c3862 | 2386 | } |
9c72faab | 2387 | |
be8c3862 | 2388 | // ignore empty strings |
2389 | if (s->getLength() == 0) { | |
2390 | return; | |
2391 | } | |
52118ca3 | 2392 | |
be8c3862 | 2393 | // get the font |
2394 | if (!(font = state->getFont())) { | |
52118ca3 | 2395 | return; |
be8c3862 | 2396 | } |
2397 | wMode = font->getWMode(); | |
52118ca3 | 2398 | |
be8c3862 | 2399 | // check for a subtitute 16-bit font |
2400 | uMap = NULL; | |
2401 | if (font->isCIDFont()) { | |
2402 | for (i = 0; i < font16EncLen; ++i) { | |
2403 | if (font->getID()->num == font16Enc[i].fontID.num && | |
2404 | font->getID()->gen == font16Enc[i].fontID.gen) { | |
2405 | uMap = globalParams->getUnicodeMap(font16Enc[i].enc); | |
2406 | break; | |
2407 | } | |
2408 | } | |
2409 | } | |
52118ca3 | 2410 | |
be8c3862 | 2411 | // compute width of chars in string, ignoring char spacing and word |
2412 | // spacing -- the Tj operator will adjust for the metrics of the | |
2413 | // font that's actually used | |
2414 | dx = dy = 0; | |
2415 | nChars = 0; | |
2416 | p = s->getCString(); | |
2417 | len = s->getLength(); | |
2418 | if (font->isCIDFont()) { | |
2419 | s2 = new GString(); | |
2420 | } else { | |
2421 | s2 = s; | |
2422 | } | |
2423 | while (len > 0) { | |
2424 | n = font->getNextChar(p, len, &code, | |
2425 | u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, | |
2426 | &dx2, &dy2, &originX, &originY); | |
2427 | if (font->isCIDFont()) { | |
2428 | if (uMap) { | |
2429 | for (i = 0; i < uLen; ++i) { | |
2430 | m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf)); | |
2431 | for (j = 0; j < m; ++j) { | |
2432 | s2->append(buf[j]); | |
2433 | } | |
2434 | } | |
2435 | //~ this really needs to get the number of chars in the target | |
2436 | //~ encoding - which may be more than the number of Unicode | |
2437 | //~ chars | |
2438 | nChars += uLen; | |
52118ca3 | 2439 | } else { |
be8c3862 | 2440 | s2->append((char)((code >> 8) & 0xff)); |
2441 | s2->append((char)(code & 0xff)); | |
2442 | ++nChars; | |
52118ca3 | 2443 | } |
be8c3862 | 2444 | } |
2445 | dx += dx2; | |
2446 | dy += dy2; | |
2447 | p += n; | |
2448 | len -= n; | |
2449 | } | |
2450 | dx *= state->getFontSize() * state->getHorizScaling(); | |
2451 | dy *= state->getFontSize(); | |
2452 | if (uMap) { | |
2453 | uMap->decRefCnt(); | |
2454 | } | |
2455 | ||
2456 | if (s2->getLength() > 0) { | |
2457 | writePSString(s2); | |
2458 | if (font->isCIDFont()) { | |
2459 | if (wMode) { | |
ae65da6c | 2460 | writePSFmt(" %d %g Tj16V\n", nChars, dy); |
52118ca3 | 2461 | } else { |
ae65da6c | 2462 | writePSFmt(" %d %g Tj16\n", nChars, dx); |
52118ca3 | 2463 | } |
be8c3862 | 2464 | } else { |
ae65da6c | 2465 | writePSFmt(" %g Tj\n", dx); |
52118ca3 | 2466 | } |
be8c3862 | 2467 | } |
2468 | if (font->isCIDFont()) { | |
2469 | delete s2; | |
52118ca3 | 2470 | } |
891091df | 2471 | |
2472 | if (state->getRender() & 4) { | |
2473 | haveTextClip = gTrue; | |
2474 | } | |
2475 | } | |
2476 | ||
2477 | void PSOutputDev::endTextObject(GfxState *state) { | |
2478 | if (haveTextClip) { | |
2479 | writePS("Tclip\n"); | |
2480 | haveTextClip = gFalse; | |
2481 | } | |
52118ca3 | 2482 | } |
2483 | ||
b5cb0608 | 2484 | void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, |
9c72faab | 2485 | int width, int height, GBool invert, |
2486 | GBool inlineImg) { | |
2487 | int len; | |
2488 | ||
2489 | len = height * ((width + 7) / 8); | |
753453e4 | 2490 | if (level == psLevel1 || level == psLevel1Sep) { |
9c72faab | 2491 | doImageL1(NULL, invert, inlineImg, str, width, height, len); |
b5cb0608 | 2492 | } else { |
2493 | doImageL2(ref, NULL, invert, inlineImg, str, width, height, len); | |
2494 | } | |
9c72faab | 2495 | } |
2496 | ||
b5cb0608 | 2497 | void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, |
2498 | int width, int height, GfxImageColorMap *colorMap, | |
753453e4 | 2499 | int *maskColors, GBool inlineImg) { |
9c72faab | 2500 | int len; |
2501 | ||
2502 | len = height * ((width * colorMap->getNumPixelComps() * | |
2503 | colorMap->getBits() + 7) / 8); | |
753453e4 | 2504 | switch (level) { |
2505 | case psLevel1: | |
9c72faab | 2506 | doImageL1(colorMap, gFalse, inlineImg, str, width, height, len); |
753453e4 | 2507 | break; |
2508 | case psLevel1Sep: | |
b5cb0608 | 2509 | //~ handle indexed, separation, ... color spaces |
2510 | doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len); | |
753453e4 | 2511 | break; |
2512 | case psLevel2: | |
2513 | case psLevel2Sep: | |
be8c3862 | 2514 | case psLevel3: |
2515 | case psLevel3Sep: | |
b5cb0608 | 2516 | doImageL2(ref, colorMap, gFalse, inlineImg, str, width, height, len); |
753453e4 | 2517 | break; |
b5cb0608 | 2518 | } |
be8c3862 | 2519 | t3Cacheable = gFalse; |
9c72faab | 2520 | } |
2521 | ||
2522 | void PSOutputDev::doImageL1(GfxImageColorMap *colorMap, | |
2523 | GBool invert, GBool inlineImg, | |
2524 | Stream *str, int width, int height, int len) { | |
2525 | ImageStream *imgStr; | |
b5cb0608 | 2526 | Guchar pixBuf[gfxColorMaxComps]; |
2527 | double gray; | |
9c72faab | 2528 | int x, y, i; |
2529 | ||
2530 | // width, height, matrix, bits per component | |
2531 | if (colorMap) { | |
ae65da6c | 2532 | writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n", |
2533 | width, height, | |
2534 | width, -height, height); | |
9c72faab | 2535 | } else { |
ae65da6c | 2536 | writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n", |
2537 | width, height, invert ? "true" : "false", | |
2538 | width, -height, height); | |
9c72faab | 2539 | } |
2540 | ||
2541 | // image | |
2542 | if (colorMap) { | |
2543 | ||
2544 | // set up to process the data stream | |
2545 | imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), | |
2546 | colorMap->getBits()); | |
2547 | imgStr->reset(); | |
2548 | ||
2549 | // process the data stream | |
2550 | i = 0; | |
2551 | for (y = 0; y < height; ++y) { | |
2552 | ||
2553 | // write the line | |
2554 | for (x = 0; x < width; ++x) { | |
2555 | imgStr->getPixel(pixBuf); | |
b5cb0608 | 2556 | colorMap->getGray(pixBuf, &gray); |
ae65da6c | 2557 | writePSFmt("%02x", (int)(gray * 255 + 0.5)); |
9c72faab | 2558 | if (++i == 32) { |
be8c3862 | 2559 | writePSChar('\n'); |
9c72faab | 2560 | i = 0; |
2561 | } | |
2562 | } | |
2563 | } | |
be8c3862 | 2564 | if (i != 0) { |
2565 | writePSChar('\n'); | |
2566 | } | |
9c72faab | 2567 | delete imgStr; |
2568 | ||
2569 | // imagemask | |
2570 | } else { | |
2571 | str->reset(); | |
2572 | i = 0; | |
2573 | for (y = 0; y < height; ++y) { | |
2574 | for (x = 0; x < width; x += 8) { | |
ae65da6c | 2575 | writePSFmt("%02x", str->getChar() & 0xff); |
9c72faab | 2576 | if (++i == 32) { |
be8c3862 | 2577 | writePSChar('\n'); |
9c72faab | 2578 | i = 0; |
2579 | } | |
2580 | } | |
2581 | } | |
be8c3862 | 2582 | if (i != 0) { |
2583 | writePSChar('\n'); | |
2584 | } | |
2585 | str->close(); | |
9c72faab | 2586 | } |
2587 | } | |
2588 | ||
b5cb0608 | 2589 | void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap, |
2590 | GBool invert, GBool inlineImg, | |
2591 | Stream *str, int width, int height, int len) { | |
2592 | ImageStream *imgStr; | |
2593 | Guchar *lineBuf; | |
2594 | Guchar pixBuf[gfxColorMaxComps]; | |
2595 | GfxCMYK cmyk; | |
2596 | int x, y, i, comp; | |
2597 | ||
2598 | // width, height, matrix, bits per component | |
ae65da6c | 2599 | writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1Sep\n", |
2600 | width, height, | |
2601 | width, -height, height); | |
b5cb0608 | 2602 | |
2603 | // allocate a line buffer | |
2604 | lineBuf = (Guchar *)gmalloc(4 * width); | |
2605 | ||
2606 | // set up to process the data stream | |
2607 | imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), | |
2608 | colorMap->getBits()); | |
2609 | imgStr->reset(); | |
2610 | ||
2611 | // process the data stream | |
2612 | i = 0; | |
2613 | for (y = 0; y < height; ++y) { | |
2614 | ||
2615 | // read the line | |
2616 | for (x = 0; x < width; ++x) { | |
2617 | imgStr->getPixel(pixBuf); | |
2618 | colorMap->getCMYK(pixBuf, &cmyk); | |
2619 | lineBuf[4*x+0] = (int)(255 * cmyk.c + 0.5); | |
2620 | lineBuf[4*x+1] = (int)(255 * cmyk.m + 0.5); | |
2621 | lineBuf[4*x+2] = (int)(255 * cmyk.y + 0.5); | |
2622 | lineBuf[4*x+3] = (int)(255 * cmyk.k + 0.5); | |
ae65da6c | 2623 | addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k); |
b5cb0608 | 2624 | } |
2625 | ||
2626 | // write one line of each color component | |
2627 | for (comp = 0; comp < 4; ++comp) { | |
2628 | for (x = 0; x < width; ++x) { | |
ae65da6c | 2629 | writePSFmt("%02x", lineBuf[4*x + comp]); |
b5cb0608 | 2630 | if (++i == 32) { |
be8c3862 | 2631 | writePSChar('\n'); |
b5cb0608 | 2632 | i = 0; |
2633 | } | |
2634 | } | |
2635 | } | |
2636 | } | |
2637 | ||
2638 | if (i != 0) { | |
be8c3862 | 2639 | writePSChar('\n'); |
b5cb0608 | 2640 | } |
2641 | ||
2642 | delete imgStr; | |
2643 | gfree(lineBuf); | |
2644 | } | |
2645 | ||
2646 | void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap, | |
2647 | GBool invert, GBool inlineImg, | |
2648 | Stream *str, int width, int height, int len) { | |
9c72faab | 2649 | GString *s; |
2650 | int n, numComps; | |
be8c3862 | 2651 | GBool useRLE, useASCII, useCompressed; |
753453e4 | 2652 | GfxSeparationColorSpace *sepCS; |
2653 | GfxColor color; | |
2654 | GfxCMYK cmyk; | |
9c72faab | 2655 | int c; |
ae65da6c | 2656 | int line, col, i; |
9c72faab | 2657 | |
2658 | // color space | |
2659 | if (colorMap) { | |
b5cb0608 | 2660 | dumpColorSpaceL2(colorMap->getColorSpace()); |
2661 | writePS(" setcolorspace\n"); | |
2662 | } | |
2663 | ||
ae65da6c | 2664 | // set up the image data |
2665 | if (mode == psModeForm || inType3Char) { | |
2666 | if (inlineImg) { | |
2667 | // create an array | |
2668 | str = new FixedLengthEncoder(str, len); | |
2669 | if (globalParams->getPSASCIIHex()) { | |
2670 | str = new ASCIIHexEncoder(str); | |
2671 | } else { | |
2672 | str = new ASCII85Encoder(str); | |
2673 | } | |
2674 | str->reset(); | |
2675 | line = col = 0; | |
2676 | writePS("[<~"); | |
2677 | do { | |
2678 | do { | |
2679 | c = str->getChar(); | |
2680 | } while (c == '\n' || c == '\r'); | |
2681 | if (c == '~' || c == EOF) { | |
2682 | break; | |
2683 | } | |
2684 | if (c == 'z') { | |
2685 | writePSChar(c); | |
2686 | ++col; | |
2687 | } else { | |
2688 | writePSChar(c); | |
2689 | ++col; | |
2690 | for (i = 1; i <= 4; ++i) { | |
2691 | do { | |
2692 | c = str->getChar(); | |
2693 | } while (c == '\n' || c == '\r'); | |
2694 | if (c == '~' || c == EOF) { | |
2695 | break; | |
2696 | } | |
2697 | writePSChar(c); | |
2698 | ++col; | |
2699 | } | |
2700 | } | |
2701 | // each line is: "dup nnnnn <~...data...~> put<eol>" | |
2702 | // so max data length = 255 - 20 = 235 | |
2703 | // chunks are 1 or 4 bytes each, so we have to stop at 232 | |
2704 | // but make it 225 just to be safe | |
2705 | if (col > 225) { | |
2706 | writePS("~>\n"); | |
2707 | ++line; | |
2708 | writePSFmt("<~", line); | |
2709 | col = 0; | |
2710 | } | |
2711 | } while (c != '~' && c != EOF); | |
2712 | writePS("~>]\n"); | |
2713 | writePS("0\n"); | |
891091df | 2714 | str->close(); |
ae65da6c | 2715 | delete str; |
2716 | } else { | |
2717 | // set up to use the array already created by setupImages() | |
2718 | writePSFmt("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen()); | |
2719 | } | |
9c72faab | 2720 | } |
2721 | ||
2722 | // image dictionary | |
2723 | writePS("<<\n /ImageType 1\n"); | |
2724 | ||
2725 | // width, height, matrix, bits per component | |
ae65da6c | 2726 | writePSFmt(" /Width %d\n", width); |
2727 | writePSFmt(" /Height %d\n", height); | |
2728 | writePSFmt(" /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height); | |
2729 | writePSFmt(" /BitsPerComponent %d\n", | |
2730 | colorMap ? colorMap->getBits() : 1); | |
9c72faab | 2731 | |
2732 | // decode | |
2733 | if (colorMap) { | |
2734 | writePS(" /Decode ["); | |
b5cb0608 | 2735 | if (colorMap->getColorSpace()->getMode() == csSeparation) { |
2736 | //~ this is a kludge -- see comment in dumpColorSpaceL2 | |
52118ca3 | 2737 | n = (1 << colorMap->getBits()) - 1; |
ae65da6c | 2738 | writePSFmt("%g %g", colorMap->getDecodeLow(0) * n, |
2739 | colorMap->getDecodeHigh(0) * n); | |
52118ca3 | 2740 | } else { |
2741 | numComps = colorMap->getNumPixelComps(); | |
2742 | for (i = 0; i < numComps; ++i) { | |
2743 | if (i > 0) { | |
2744 | writePS(" "); | |
2745 | } | |
ae65da6c | 2746 | writePSFmt("%g %g", colorMap->getDecodeLow(i), |
2747 | colorMap->getDecodeHigh(i)); | |
52118ca3 | 2748 | } |
9c72faab | 2749 | } |
2750 | writePS("]\n"); | |
2751 | } else { | |
ae65da6c | 2752 | writePSFmt(" /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1); |
9c72faab | 2753 | } |
2754 | ||
be8c3862 | 2755 | if (mode == psModeForm || inType3Char) { |
9c72faab | 2756 | |
ae65da6c | 2757 | // data source |
2758 | writePS(" /DataSource { 2 copy get exch 1 add exch }\n"); | |
9c72faab | 2759 | |
2760 | // end of image dictionary | |
ae65da6c | 2761 | writePSFmt(">>\n%s\n", colorMap ? "image" : "imagemask"); |
9c72faab | 2762 | |
b5cb0608 | 2763 | // get rid of the array and index |
ae65da6c | 2764 | writePS("pop pop\n"); |
b5cb0608 | 2765 | |
9c72faab | 2766 | } else { |
2767 | ||
2768 | // data source | |
2769 | writePS(" /DataSource currentfile\n"); | |
891091df | 2770 | s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, |
2771 | " "); | |
9c72faab | 2772 | if (inlineImg || !s) { |
2773 | useRLE = gTrue; | |
be8c3862 | 2774 | useASCII = gTrue; |
2775 | useCompressed = gFalse; | |
9c72faab | 2776 | } else { |
2777 | useRLE = gFalse; | |
be8c3862 | 2778 | useASCII = str->isBinary(); |
2779 | useCompressed = gTrue; | |
9c72faab | 2780 | } |
be8c3862 | 2781 | if (useASCII) { |
ae65da6c | 2782 | writePSFmt(" /ASCII%sDecode filter\n", |
2783 | globalParams->getPSASCIIHex() ? "Hex" : "85"); | |
753453e4 | 2784 | } |
2785 | if (useRLE) { | |
9c72faab | 2786 | writePS(" /RunLengthDecode filter\n"); |
be8c3862 | 2787 | } |
2788 | if (useCompressed) { | |
ae65da6c | 2789 | writePS(s->getCString()); |
753453e4 | 2790 | } |
2791 | if (s) { | |
9c72faab | 2792 | delete s; |
753453e4 | 2793 | } |
9c72faab | 2794 | |
9c72faab | 2795 | // cut off inline image streams at appropriate length |
753453e4 | 2796 | if (inlineImg) { |
9c72faab | 2797 | str = new FixedLengthEncoder(str, len); |
be8c3862 | 2798 | } else if (useCompressed) { |
9c72faab | 2799 | str = str->getBaseStream(); |
753453e4 | 2800 | } |
9c72faab | 2801 | |
be8c3862 | 2802 | // add RunLengthEncode and ASCIIHex/85 encode filters |
753453e4 | 2803 | if (useRLE) { |
9c72faab | 2804 | str = new RunLengthEncoder(str); |
753453e4 | 2805 | } |
be8c3862 | 2806 | if (useASCII) { |
2807 | if (globalParams->getPSASCIIHex()) { | |
2808 | str = new ASCIIHexEncoder(str); | |
2809 | } else { | |
2810 | str = new ASCII85Encoder(str); | |
2811 | } | |
753453e4 | 2812 | } |
9c72faab | 2813 | |
52118ca3 | 2814 | // end of image dictionary |
2815 | writePS(">>\n"); | |
2816 | #if OPI_SUPPORT | |
2817 | if (opi13Nest) { | |
2818 | if (inlineImg) { | |
2819 | // this can't happen -- OPI dictionaries are in XObjects | |
2820 | error(-1, "Internal: OPI in inline image"); | |
2821 | n = 0; | |
2822 | } else { | |
2823 | // need to read the stream to count characters -- the length | |
be8c3862 | 2824 | // is data-dependent (because of ASCII and RLE filters) |
52118ca3 | 2825 | str->reset(); |
2826 | n = 0; | |
2827 | while ((c = str->getChar()) != EOF) { | |
2828 | ++n; | |
2829 | } | |
be8c3862 | 2830 | str->close(); |
52118ca3 | 2831 | } |
2832 | // +6/7 for "pdfIm\n" / "pdfImM\n" | |
2833 | // +8 for newline + trailer | |
2834 | n += colorMap ? 14 : 15; | |
ae65da6c | 2835 | writePSFmt("%%%%BeginData: %d Hex Bytes\n", n); |
52118ca3 | 2836 | } |
2837 | #endif | |
be8c3862 | 2838 | if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap && |
753453e4 | 2839 | colorMap->getColorSpace()->getMode() == csSeparation) { |
2840 | color.c[0] = 1; | |
2841 | sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace(); | |
2842 | sepCS->getCMYK(&color, &cmyk); | |
ae65da6c | 2843 | writePSFmt("%g %g %g %g (%s) pdfImSep\n", |
2844 | cmyk.c, cmyk.m, cmyk.y, cmyk.k, | |
2845 | sepCS->getName()->getCString()); | |
753453e4 | 2846 | } else { |
ae65da6c | 2847 | writePSFmt("%s\n", colorMap ? "pdfIm" : "pdfImM"); |
753453e4 | 2848 | } |
52118ca3 | 2849 | |
9c72faab | 2850 | // copy the stream data |
2851 | str->reset(); | |
753453e4 | 2852 | while ((c = str->getChar()) != EOF) { |
be8c3862 | 2853 | writePSChar(c); |
753453e4 | 2854 | } |
be8c3862 | 2855 | str->close(); |
9c72faab | 2856 | |
2857 | // add newline and trailer to the end | |
be8c3862 | 2858 | writePSChar('\n'); |
ae65da6c | 2859 | writePS("%-EOD-\n"); |
52118ca3 | 2860 | #if OPI_SUPPORT |
2861 | if (opi13Nest) { | |
ae65da6c | 2862 | writePS("%%EndData\n"); |
52118ca3 | 2863 | } |
2864 | #endif | |
9c72faab | 2865 | |
2866 | // delete encoders | |
be8c3862 | 2867 | if (useRLE || useASCII || inlineImg) { |
9c72faab | 2868 | delete str; |
753453e4 | 2869 | } |
9c72faab | 2870 | } |
2871 | } | |
2872 | ||
b5cb0608 | 2873 | void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) { |
2874 | GfxCalGrayColorSpace *calGrayCS; | |
2875 | GfxCalRGBColorSpace *calRGBCS; | |
2876 | GfxLabColorSpace *labCS; | |
2877 | GfxIndexedColorSpace *indexedCS; | |
2878 | GfxSeparationColorSpace *separationCS; | |
ae65da6c | 2879 | GfxColorSpace *baseCS; |
2880 | Guchar *lookup, *p; | |
753453e4 | 2881 | double x[gfxColorMaxComps], y[gfxColorMaxComps]; |
2882 | GfxColor color; | |
2883 | GfxCMYK cmyk; | |
ae65da6c | 2884 | Function *func; |
2885 | int n, numComps, numAltComps; | |
2886 | int byte; | |
b5cb0608 | 2887 | int i, j, k; |
2888 | ||
2889 | switch (colorSpace->getMode()) { | |
2890 | ||
2891 | case csDeviceGray: | |
2892 | writePS("/DeviceGray"); | |
753453e4 | 2893 | processColors |= psProcessBlack; |
b5cb0608 | 2894 | break; |
2895 | ||
2896 | case csCalGray: | |
2897 | calGrayCS = (GfxCalGrayColorSpace *)colorSpace; | |
2898 | writePS("[/CIEBasedA <<\n"); | |
ae65da6c | 2899 | writePSFmt(" /DecodeA {%g exp} bind\n", calGrayCS->getGamma()); |
2900 | writePSFmt(" /MatrixA [%g %g %g]\n", | |
2901 | calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), | |
2902 | calGrayCS->getWhiteZ()); | |
2903 | writePSFmt(" /WhitePoint [%g %g %g]\n", | |
2904 | calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), | |
2905 | calGrayCS->getWhiteZ()); | |
2906 | writePSFmt(" /BlackPoint [%g %g %g]\n", | |
2907 | calGrayCS->getBlackX(), calGrayCS->getBlackY(), | |
2908 | calGrayCS->getBlackZ()); | |
b5cb0608 | 2909 | writePS(">>]"); |
753453e4 | 2910 | processColors |= psProcessBlack; |
b5cb0608 | 2911 | break; |
2912 | ||
2913 | case csDeviceRGB: | |
2914 | writePS("/DeviceRGB"); | |
753453e4 | 2915 | processColors |= psProcessCMYK; |
b5cb0608 | 2916 | break; |
2917 | ||
2918 | case csCalRGB: | |
2919 | calRGBCS = (GfxCalRGBColorSpace *)colorSpace; | |
2920 | writePS("[/CIEBasedABC <<\n"); | |
ae65da6c | 2921 | writePSFmt(" /DecodeABC [{%g exp} bind {%g exp} bind {%g exp} bind]\n", |
2922 | calRGBCS->getGammaR(), calRGBCS->getGammaG(), | |
2923 | calRGBCS->getGammaB()); | |
2924 | writePSFmt(" /MatrixABC [%g %g %g %g %g %g %g %g %g]\n", | |
2925 | calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1], | |
2926 | calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3], | |
2927 | calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5], | |
2928 | calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7], | |
2929 | calRGBCS->getMatrix()[8]); | |
2930 | writePSFmt(" /WhitePoint [%g %g %g]\n", | |
2931 | calRGBCS->getWhiteX(), calRGBCS->getWhiteY(), | |
2932 | calRGBCS->getWhiteZ()); | |
2933 | writePSFmt(" /BlackPoint [%g %g %g]\n", | |
2934 | calRGBCS->getBlackX(), calRGBCS->getBlackY(), | |
2935 | calRGBCS->getBlackZ()); | |
b5cb0608 | 2936 | writePS(">>]"); |
753453e4 | 2937 | processColors |= psProcessCMYK; |
b5cb0608 | 2938 | break; |
2939 | ||
2940 | case csDeviceCMYK: | |
2941 | writePS("/DeviceCMYK"); | |
753453e4 | 2942 | processColors |= psProcessCMYK; |
b5cb0608 | 2943 | break; |
2944 | ||
2945 | case csLab: | |
2946 | labCS = (GfxLabColorSpace *)colorSpace; | |
2947 | writePS("[/CIEBasedABC <<\n"); | |
ae65da6c | 2948 | writePSFmt(" /RangeABC [0 100 %g %g %g %g]\n", |
2949 | labCS->getAMin(), labCS->getAMax(), | |
2950 | labCS->getBMin(), labCS->getBMax()); | |
b5cb0608 | 2951 | writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n"); |
2952 | writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n"); | |
2953 | writePS(" /DecodeLMN\n"); | |
2954 | writePS(" [{dup 6 29 div ge {dup dup mul mul}\n"); | |
ae65da6c | 2955 | writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n", |
2956 | labCS->getWhiteX()); | |
b5cb0608 | 2957 | writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); |
ae65da6c | 2958 | writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n", |
2959 | labCS->getWhiteY()); | |
b5cb0608 | 2960 | writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); |
ae65da6c | 2961 | writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n", |
2962 | labCS->getWhiteZ()); | |
2963 | writePSFmt(" /WhitePoint [%g %g %g]\n", | |
2964 | labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ()); | |
2965 | writePSFmt(" /BlackPoint [%g %g %g]\n", | |
2966 | labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ()); | |
b5cb0608 | 2967 | writePS(">>]"); |
753453e4 | 2968 | processColors |= psProcessCMYK; |
b5cb0608 | 2969 | break; |
2970 | ||
2971 | case csICCBased: | |
2972 | dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt()); | |
2973 | break; | |
2974 | ||
2975 | case csIndexed: | |
2976 | indexedCS = (GfxIndexedColorSpace *)colorSpace; | |
ae65da6c | 2977 | baseCS = indexedCS->getBase(); |
b5cb0608 | 2978 | writePS("[/Indexed "); |
ae65da6c | 2979 | dumpColorSpaceL2(baseCS); |
b5cb0608 | 2980 | n = indexedCS->getIndexHigh(); |
ae65da6c | 2981 | numComps = baseCS->getNComps(); |
b5cb0608 | 2982 | lookup = indexedCS->getLookup(); |
ae65da6c | 2983 | writePSFmt(" %d <\n", n); |
2984 | if (baseCS->getMode() == csDeviceN) { | |
2985 | func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc(); | |
2986 | numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps(); | |
2987 | p = lookup; | |
2988 | for (i = 0; i <= n; i += 8) { | |
2989 | writePS(" "); | |
2990 | for (j = i; j < i+8 && j <= n; ++j) { | |
2991 | for (k = 0; k < numComps; ++k) { | |
2992 | x[k] = *p++ / 255.0; | |
2993 | } | |
2994 | func->transform(x, y); | |
2995 | for (k = 0; k < numAltComps; ++k) { | |
2996 | byte = (int)(y[k] * 255 + 0.5); | |
2997 | if (byte < 0) { | |
2998 | byte = 0; | |
2999 | } else if (byte > 255) { | |
3000 | byte = 255; | |
3001 | } | |
3002 | writePSFmt("%02x", byte); | |
3003 | } | |
3004 | color.c[0] = j; | |
3005 | indexedCS->getCMYK(&color, &cmyk); | |
3006 | addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k); | |
b5cb0608 | 3007 | } |
ae65da6c | 3008 | writePS("\n"); |
3009 | } | |
3010 | } else { | |
3011 | for (i = 0; i <= n; i += 8) { | |
3012 | writePS(" "); | |
3013 | for (j = i; j < i+8 && j <= n; ++j) { | |
3014 | for (k = 0; k < numComps; ++k) { | |
3015 | writePSFmt("%02x", lookup[j * numComps + k]); | |
3016 | } | |
3017 | color.c[0] = j; | |
3018 | indexedCS->getCMYK(&color, &cmyk); | |
3019 | addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k); | |
3020 | } | |
3021 | writePS("\n"); | |
b5cb0608 | 3022 | } |
b5cb0608 | 3023 | } |
3024 | writePS(">]"); | |
3025 | break; | |
3026 | ||
3027 | case csSeparation: | |
3028 | //~ this is a kludge -- the correct thing would to ouput a | |
3029 | //~ separation color space, with the specified alternate color | |
3030 | //~ space and tint transform | |
3031 | separationCS = (GfxSeparationColorSpace *)colorSpace; | |
753453e4 | 3032 | writePS("[/Indexed "); |
b5cb0608 | 3033 | dumpColorSpaceL2(separationCS->getAlt()); |
3034 | writePS(" 255 <\n"); | |
3035 | numComps = separationCS->getAlt()->getNComps(); | |
3036 | for (i = 0; i <= 255; i += 8) { | |
3037 | writePS(" "); | |
3038 | for (j = i; j < i+8 && j <= 255; ++j) { | |
3039 | x[0] = (double)j / 255.0; | |
3040 | separationCS->getFunc()->transform(x, y); | |
3041 | for (k = 0; k < numComps; ++k) { | |
ae65da6c | 3042 | writePSFmt("%02x", (int)(255 * y[k] + 0.5)); |
b5cb0608 | 3043 | } |
3044 | } | |
3045 | writePS("\n"); | |
3046 | } | |
3047 | writePS(">]"); | |
891091df | 3048 | #if 0 //~ this shouldn't be here since the PS file doesn't actually refer |
3049 | //~ to this colorant (it's converted to CMYK instead) | |
753453e4 | 3050 | addCustomColor(separationCS); |
891091df | 3051 | #endif |
b5cb0608 | 3052 | break; |
3053 | ||
3054 | case csDeviceN: | |
3055 | // DeviceN color spaces are a Level 3 PostScript feature. | |
3056 | dumpColorSpaceL2(((GfxDeviceNColorSpace *)colorSpace)->getAlt()); | |
3057 | break; | |
3058 | ||
3059 | case csPattern: | |
3060 | //~ unimplemented | |
3061 | break; | |
3062 | ||
3063 | } | |
3064 | } | |
3065 | ||
52118ca3 | 3066 | #if OPI_SUPPORT |
3067 | void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) { | |
3068 | Object dict; | |
3069 | ||
be8c3862 | 3070 | if (globalParams->getPSOPI()) { |
52118ca3 | 3071 | opiDict->lookup("2.0", &dict); |
3072 | if (dict.isDict()) { | |
3073 | opiBegin20(state, dict.getDict()); | |
3074 | dict.free(); | |
3075 | } else { | |
3076 | dict.free(); | |
3077 | opiDict->lookup("1.3", &dict); | |
3078 | if (dict.isDict()) { | |
3079 | opiBegin13(state, dict.getDict()); | |
3080 | } | |
3081 | dict.free(); | |
3082 | } | |
3083 | } | |
3084 | } | |
3085 | ||
3086 | void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) { | |
3087 | Object obj1, obj2, obj3, obj4; | |
3088 | double width, height, left, right, top, bottom; | |
3089 | int w, h; | |
3090 | int i; | |
3091 | ||
ae65da6c | 3092 | writePS("%%BeginOPI: 2.0\n"); |
3093 | writePS("%%Distilled\n"); | |
52118ca3 | 3094 | |
3095 | dict->lookup("F", &obj1); | |
3096 | if (getFileSpec(&obj1, &obj2)) { | |
ae65da6c | 3097 | writePSFmt("%%%%ImageFileName: %s\n", |
3098 | obj2.getString()->getCString()); | |
52118ca3 | 3099 | obj2.free(); |
3100 | } | |
3101 | obj1.free(); | |
3102 | ||
3103 | dict->lookup("MainImage", &obj1); | |
3104 | if (obj1.isString()) { | |
ae65da6c | 3105 | writePSFmt("%%%%MainImage: %s\n", obj1.getString()->getCString()); |
52118ca3 | 3106 | } |
3107 | obj1.free(); | |
3108 | ||
3109 | //~ ignoring 'Tags' entry | |
3110 | //~ need to use writePSString() and deal with >255-char lines | |
3111 | ||
3112 | dict->lookup("Size", &obj1); | |
3113 | if (obj1.isArray() && obj1.arrayGetLength() == 2) { | |
3114 | obj1.arrayGet(0, &obj2); | |
3115 | width = obj2.getNum(); | |
3116 | obj2.free(); | |
3117 | obj1.arrayGet(1, &obj2); | |
3118 | height = obj2.getNum(); | |
3119 | obj2.free(); | |
ae65da6c | 3120 | writePSFmt("%%%%ImageDimensions: %g %g\n", width, height); |
52118ca3 | 3121 | } |
3122 | obj1.free(); | |
3123 | ||
3124 | dict->lookup("CropRect", &obj1); | |
3125 | if (obj1.isArray() && obj1.arrayGetLength() == 4) { | |
3126 | obj1.arrayGet(0, &obj2); | |
3127 | left = obj2.getNum(); | |
3128 | obj2.free(); | |
3129 | obj1.arrayGet(1, &obj2); | |
3130 | top = obj2.getNum(); | |
3131 | obj2.free(); | |
3132 | obj1.arrayGet(2, &obj2); | |
3133 | right = obj2.getNum(); | |
3134 | obj2.free(); | |
3135 | obj1.arrayGet(3, &obj2); | |
3136 | bottom = obj2.getNum(); | |
3137 | obj2.free(); | |
ae65da6c | 3138 | writePSFmt("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom); |
52118ca3 | 3139 | } |
3140 | obj1.free(); | |
3141 | ||
3142 | dict->lookup("Overprint", &obj1); | |
3143 | if (obj1.isBool()) { | |
ae65da6c | 3144 | writePSFmt("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false"); |
52118ca3 | 3145 | } |
3146 | obj1.free(); | |
3147 | ||
3148 | dict->lookup("Inks", &obj1); | |
3149 | if (obj1.isName()) { | |
ae65da6c | 3150 | writePSFmt("%%%%ImageInks: %s\n", obj1.getName()); |
52118ca3 | 3151 | } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) { |
3152 | obj1.arrayGet(0, &obj2); | |
3153 | if (obj2.isName()) { | |
ae65da6c | 3154 | writePSFmt("%%%%ImageInks: %s %d", |
3155 | obj2.getName(), (obj1.arrayGetLength() - 1) / 2); | |
52118ca3 | 3156 | for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) { |
3157 | obj1.arrayGet(i, &obj3); | |
3158 | obj1.arrayGet(i+1, &obj4); | |
3159 | if (obj3.isString() && obj4.isNum()) { | |
3160 | writePS(" "); | |
3161 | writePSString(obj3.getString()); | |
ae65da6c | 3162 | writePSFmt(" %g", obj4.getNum()); |
52118ca3 | 3163 | } |
3164 | obj3.free(); | |
3165 | obj4.free(); | |
3166 | } | |
3167 | writePS("\n"); | |
3168 | } | |
3169 | obj2.free(); | |
3170 | } | |
3171 | obj1.free(); | |
3172 | ||
3173 | writePS("gsave\n"); | |
3174 | ||
ae65da6c | 3175 | writePS("%%BeginIncludedImage\n"); |
52118ca3 | 3176 | |
3177 | dict->lookup("IncludedImageDimensions", &obj1); | |
3178 | if (obj1.isArray() && obj1.arrayGetLength() == 2) { | |
3179 | obj1.arrayGet(0, &obj2); | |
3180 | w = obj2.getInt(); | |
3181 | obj2.free(); | |
3182 | obj1.arrayGet(1, &obj2); | |
3183 | h = obj2.getInt(); | |
3184 | obj2.free(); | |
ae65da6c | 3185 | writePSFmt("%%%%IncludedImageDimensions: %d %d\n", w, h); |
52118ca3 | 3186 | } |
3187 | obj1.free(); | |
3188 | ||
3189 | dict->lookup("IncludedImageQuality", &obj1); | |
3190 | if (obj1.isNum()) { | |
ae65da6c | 3191 | writePSFmt("%%%%IncludedImageQuality: %g\n", obj1.getNum()); |
52118ca3 | 3192 | } |
3193 | obj1.free(); | |
3194 | ||
3195 | ++opi20Nest; | |
3196 | } | |
3197 | ||
3198 | void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { | |
3199 | Object obj1, obj2; | |
3200 | int left, right, top, bottom, samples, bits, width, height; | |
3201 | double c, m, y, k; | |
3202 | double llx, lly, ulx, uly, urx, ury, lrx, lry; | |
3203 | double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry; | |
3204 | double horiz, vert; | |
3205 | int i, j; | |
3206 | ||
3207 | writePS("save\n"); | |
3208 | writePS("/opiMatrix2 matrix currentmatrix def\n"); | |
3209 | writePS("opiMatrix setmatrix\n"); | |
3210 | ||
3211 | dict->lookup("F", &obj1); | |
3212 | if (getFileSpec(&obj1, &obj2)) { | |
ae65da6c | 3213 | writePSFmt("%%ALDImageFileName: %s\n", |
3214 | obj2.getString()->getCString()); | |
52118ca3 | 3215 | obj2.free(); |
3216 | } | |
3217 | obj1.free(); | |
3218 | ||
3219 | dict->lookup("CropRect", &obj1); | |
3220 | if (obj1.isArray() && obj1.arrayGetLength() == 4) { | |
3221 | obj1.arrayGet(0, &obj2); | |
3222 | left = obj2.getInt(); | |
3223 | obj2.free(); | |
3224 | obj1.arrayGet(1, &obj2); | |
3225 | top = obj2.getInt(); | |
3226 | obj2.free(); | |
3227 | obj1.arrayGet(2, &obj2); | |
3228 | right = obj2.getInt(); | |
3229 | obj2.free(); | |
3230 | obj1.arrayGet(3, &obj2); | |
3231 | bottom = obj2.getInt(); | |
3232 | obj2.free(); | |
ae65da6c | 3233 | writePSFmt("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom); |
52118ca3 | 3234 | } |
3235 | obj1.free(); | |
3236 | ||
3237 | dict->lookup("Color", &obj1); | |
3238 | if (obj1.isArray() && obj1.arrayGetLength() == 5) { | |
3239 | obj1.arrayGet(0, &obj2); | |
3240 | c = obj2.getNum(); | |
3241 | obj2.free(); | |
3242 | obj1.arrayGet(1, &obj2); | |
3243 | m = obj2.getNum(); | |
3244 | obj2.free(); | |
3245 | obj1.arrayGet(2, &obj2); | |
3246 | y = obj2.getNum(); | |
3247 | obj2.free(); | |
3248 | obj1.arrayGet(3, &obj2); | |
3249 | k = obj2.getNum(); | |
3250 | obj2.free(); | |
3251 | obj1.arrayGet(4, &obj2); | |
3252 | if (obj2.isString()) { | |
ae65da6c | 3253 | writePSFmt("%%ALDImageColor: %g %g %g %g ", c, m, y, k); |
52118ca3 | 3254 | writePSString(obj2.getString()); |
3255 | writePS("\n"); | |
3256 | } | |
3257 | obj2.free(); | |
3258 | } | |
3259 | obj1.free(); | |
3260 | ||
3261 | dict->lookup("ColorType", &obj1); | |
3262 | if (obj1.isName()) { | |
ae65da6c | 3263 | writePSFmt("%%ALDImageColorType: %s\n", obj1.getName()); |
52118ca3 | 3264 | } |
3265 | obj1.free(); | |
3266 | ||
3267 | //~ ignores 'Comments' entry | |
3268 | //~ need to handle multiple lines | |
3269 | ||
3270 | dict->lookup("CropFixed", &obj1); | |
3271 | if (obj1.isArray()) { | |
3272 | obj1.arrayGet(0, &obj2); | |
3273 | ulx = obj2.getNum(); | |
3274 | obj2.free(); | |
3275 | obj1.arrayGet(1, &obj2); | |
3276 | uly = obj2.getNum(); | |
3277 | obj2.free(); | |
3278 | obj1.arrayGet(2, &obj2); | |
3279 | lrx = obj2.getNum(); | |
3280 | obj2.free(); | |
3281 | obj1.arrayGet(3, &obj2); | |
3282 | lry = obj2.getNum(); | |
3283 | obj2.free(); | |
ae65da6c | 3284 | writePSFmt("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry); |
52118ca3 | 3285 | } |
3286 | obj1.free(); | |
3287 | ||
3288 | dict->lookup("GrayMap", &obj1); | |
3289 | if (obj1.isArray()) { | |
ae65da6c | 3290 | writePS("%ALDImageGrayMap:"); |
52118ca3 | 3291 | for (i = 0; i < obj1.arrayGetLength(); i += 16) { |
3292 | if (i > 0) { | |
ae65da6c | 3293 | writePS("\n%%+"); |
52118ca3 | 3294 | } |
3295 | for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) { | |
3296 | obj1.arrayGet(i+j, &obj2); | |
ae65da6c | 3297 | writePSFmt(" %d", obj2.getInt()); |
52118ca3 | 3298 | obj2.free(); |
3299 | } | |
3300 | } | |
3301 | writePS("\n"); | |
3302 | } | |
3303 | obj1.free(); | |
3304 | ||
3305 | dict->lookup("ID", &obj1); | |
3306 | if (obj1.isString()) { | |
ae65da6c | 3307 | writePSFmt("%%ALDImageID: %s\n", obj1.getString()->getCString()); |
52118ca3 | 3308 | } |
3309 | obj1.free(); | |
3310 | ||
3311 | dict->lookup("ImageType", &obj1); | |
3312 | if (obj1.isArray() && obj1.arrayGetLength() == 2) { | |
3313 | obj1.arrayGet(0, &obj2); | |
3314 | samples = obj2.getInt(); | |
3315 | obj2.free(); | |
3316 | obj1.arrayGet(1, &obj2); | |
3317 | bits = obj2.getInt(); | |
3318 | obj2.free(); | |
ae65da6c | 3319 | writePSFmt("%%ALDImageType: %d %d\n", samples, bits); |
52118ca3 | 3320 | } |
3321 | obj1.free(); | |
3322 | ||
3323 | dict->lookup("Overprint", &obj1); | |
3324 | if (obj1.isBool()) { | |
ae65da6c | 3325 | writePSFmt("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false"); |
52118ca3 | 3326 | } |
3327 | obj1.free(); | |
3328 | ||
3329 | dict->lookup("Position", &obj1); | |
3330 | if (obj1.isArray() && obj1.arrayGetLength() == 8) { | |
3331 | obj1.arrayGet(0, &obj2); | |
3332 | llx = obj2.getNum(); | |
3333 | obj2.free(); | |
3334 | obj1.arrayGet(1, &obj2); | |
3335 | lly = obj2.getNum(); | |
3336 | obj2.free(); | |
3337 | obj1.arrayGet(2, &obj2); | |
3338 | ulx = obj2.getNum(); | |
3339 | obj2.free(); | |
3340 | obj1.arrayGet(3, &obj2); | |
3341 | uly = obj2.getNum(); | |
3342 | obj2.free(); | |
3343 | obj1.arrayGet(4, &obj2); | |
3344 | urx = obj2.getNum(); | |
3345 | obj2.free(); | |
3346 | obj1.arrayGet(5, &obj2); | |
3347 | ury = obj2.getNum(); | |
3348 | obj2.free(); | |
3349 | obj1.arrayGet(6, &obj2); | |
3350 | lrx = obj2.getNum(); | |
3351 | obj2.free(); | |
3352 | obj1.arrayGet(7, &obj2); | |
3353 | lry = obj2.getNum(); | |
3354 | obj2.free(); | |
3355 | opiTransform(state, llx, lly, &tllx, &tlly); | |
3356 | opiTransform(state, ulx, uly, &tulx, &tuly); | |
3357 | opiTransform(state, urx, ury, &turx, &tury); | |
3358 | opiTransform(state, lrx, lry, &tlrx, &tlry); | |
ae65da6c | 3359 | writePSFmt("%%ALDImagePosition: %g %g %g %g %g %g %g %g\n", |
3360 | tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry); | |
52118ca3 | 3361 | obj2.free(); |
3362 | } | |
3363 | obj1.free(); | |
3364 | ||
3365 | dict->lookup("Resolution", &obj1); | |
3366 | if (obj1.isArray() && obj1.arrayGetLength() == 2) { | |
3367 | obj1.arrayGet(0, &obj2); | |
3368 | horiz = obj2.getNum(); | |
3369 | obj2.free(); | |
3370 | obj1.arrayGet(1, &obj2); | |
3371 | vert = obj2.getNum(); | |
3372 | obj2.free(); | |
ae65da6c | 3373 | writePSFmt("%%ALDImageResoution: %g %g\n", horiz, vert); |
52118ca3 | 3374 | obj2.free(); |
3375 | } | |
3376 | obj1.free(); | |
3377 | ||
3378 | dict->lookup("Size", &obj1); | |
3379 | if (obj1.isArray() && obj1.arrayGetLength() == 2) { | |
3380 | obj1.arrayGet(0, &obj2); | |
3381 | width = obj2.getInt(); | |
3382 | obj2.free(); | |
3383 | obj1.arrayGet(1, &obj2); | |
3384 | height = obj2.getInt(); | |
3385 | obj2.free(); | |
ae65da6c | 3386 | writePSFmt("%%ALDImageDimensions: %d %d\n", width, height); |
52118ca3 | 3387 | } |
3388 | obj1.free(); | |
3389 | ||
3390 | //~ ignoring 'Tags' entry | |
3391 | //~ need to use writePSString() and deal with >255-char lines | |
3392 | ||
3393 | dict->lookup("Tint", &obj1); | |
3394 | if (obj1.isNum()) { | |
ae65da6c | 3395 | writePSFmt("%%ALDImageTint: %g\n", obj1.getNum()); |
52118ca3 | 3396 | } |
3397 | obj1.free(); | |
3398 | ||
3399 | dict->lookup("Transparency", &obj1); | |
3400 | if (obj1.isBool()) { | |
ae65da6c | 3401 | writePSFmt("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false"); |
52118ca3 | 3402 | } |
3403 | obj1.free(); | |
3404 | ||
ae65da6c | 3405 | writePS("%%BeginObject: image\n"); |
52118ca3 | 3406 | writePS("opiMatrix2 setmatrix\n"); |
3407 | ++opi13Nest; | |
3408 | } | |
3409 | ||
3410 | // Convert PDF user space coordinates to PostScript default user space | |
3411 | // coordinates. This has to account for both the PDF CTM and the | |
3412 | // PSOutputDev page-fitting transform. | |
3413 | void PSOutputDev::opiTransform(GfxState *state, double x0, double y0, | |
3414 | double *x1, double *y1) { | |
3415 | double t; | |
3416 | ||
3417 | state->transform(x0, y0, x1, y1); | |
3418 | *x1 += tx; | |
3419 | *y1 += ty; | |
3420 | if (landscape) { | |
3421 | t = *x1; | |
3422 | *x1 = -*y1; | |
3423 | *y1 = t; | |
3424 | } | |
3425 | *x1 *= xScale; | |
3426 | *y1 *= yScale; | |
3427 | } | |
3428 | ||
3429 | void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) { | |
3430 | Object dict; | |
3431 | ||
be8c3862 | 3432 | if (globalParams->getPSOPI()) { |
52118ca3 | 3433 | opiDict->lookup("2.0", &dict); |
3434 | if (dict.isDict()) { | |
ae65da6c | 3435 | writePS("%%EndIncludedImage\n"); |
3436 | writePS("%%EndOPI\n"); | |
52118ca3 | 3437 | writePS("grestore\n"); |
3438 | --opi20Nest; | |
3439 | dict.free(); | |
3440 | } else { | |
3441 | dict.free(); | |
3442 | opiDict->lookup("1.3", &dict); | |
3443 | if (dict.isDict()) { | |
ae65da6c | 3444 | writePS("%%EndObject\n"); |
52118ca3 | 3445 | writePS("restore\n"); |
3446 | --opi13Nest; | |
3447 | } | |
3448 | dict.free(); | |
3449 | } | |
3450 | } | |
3451 | } | |
3452 | ||
3453 | GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) { | |
3454 | if (fileSpec->isString()) { | |
3455 | fileSpec->copy(fileName); | |
3456 | return gTrue; | |
3457 | } | |
3458 | if (fileSpec->isDict()) { | |
3459 | fileSpec->dictLookup("DOS", fileName); | |
3460 | if (fileName->isString()) { | |
3461 | return gTrue; | |
3462 | } | |
3463 | fileName->free(); | |
3464 | fileSpec->dictLookup("Mac", fileName); | |
3465 | if (fileName->isString()) { | |
3466 | return gTrue; | |
3467 | } | |
3468 | fileName->free(); | |
3469 | fileSpec->dictLookup("Unix", fileName); | |
3470 | if (fileName->isString()) { | |
3471 | return gTrue; | |
3472 | } | |
3473 | fileName->free(); | |
3474 | fileSpec->dictLookup("F", fileName); | |
3475 | if (fileName->isString()) { | |
3476 | return gTrue; | |
3477 | } | |
3478 | fileName->free(); | |
3479 | } | |
3480 | return gFalse; | |
3481 | } | |
3482 | #endif // OPI_SUPPORT | |
3483 | ||
be8c3862 | 3484 | void PSOutputDev::type3D0(GfxState *state, double wx, double wy) { |
ae65da6c | 3485 | writePSFmt("%g %g setcharwidth\n", wx, wy); |
be8c3862 | 3486 | writePS("q\n"); |
3487 | } | |
3488 | ||
3489 | void PSOutputDev::type3D1(GfxState *state, double wx, double wy, | |
3490 | double llx, double lly, double urx, double ury) { | |
3491 | t3WX = wx; | |
3492 | t3WY = wy; | |
3493 | t3LLX = llx; | |
3494 | t3LLY = lly; | |
3495 | t3URX = urx; | |
3496 | t3URY = ury; | |
3497 | t3String = new GString(); | |
3498 | writePS("q\n"); | |
3499 | t3Cacheable = gTrue; | |
3500 | } | |
3501 | ||
3502 | void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) { | |
3503 | Stream *str; | |
3504 | int c; | |
3505 | ||
3506 | if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) { | |
3507 | str = level1Stream; | |
3508 | } else { | |
3509 | str = psStream; | |
3510 | } | |
3511 | str->reset(); | |
3512 | while ((c = str->getChar()) != EOF) { | |
3513 | writePSChar(c); | |
3514 | } | |
3515 | str->close(); | |
3516 | } | |
3517 | ||
ae65da6c | 3518 | void PSOutputDev::writePSChar(char c) { |
3519 | if (t3String) { | |
3520 | t3String->append(c); | |
3521 | } else { | |
3522 | (*outputFunc)(outputStream, &c, 1); | |
3523 | } | |
3524 | } | |
3525 | ||
8dc4af74 | 3526 | void PSOutputDev::writePS(const char *s) { |
ae65da6c | 3527 | if (t3String) { |
3528 | t3String->append(s); | |
3529 | } else { | |
3530 | (*outputFunc)(outputStream, s, strlen(s)); | |
3531 | } | |
3532 | } | |
3533 | ||
3534 | void PSOutputDev::writePSFmt(const char *fmt, ...) { | |
9c72faab | 3535 | va_list args; |
be8c3862 | 3536 | char buf[512]; |
9c72faab | 3537 | |
3538 | va_start(args, fmt); | |
ae65da6c | 3539 | vsprintf(buf, fmt, args); |
3540 | va_end(args); | |
be8c3862 | 3541 | if (t3String) { |
be8c3862 | 3542 | t3String->append(buf); |
3543 | } else { | |
ae65da6c | 3544 | (*outputFunc)(outputStream, buf, strlen(buf)); |
be8c3862 | 3545 | } |
9c72faab | 3546 | } |
3547 | ||
3548 | void PSOutputDev::writePSString(GString *s) { | |
3549 | Guchar *p; | |
3550 | int n; | |
be8c3862 | 3551 | char buf[8]; |
9c72faab | 3552 | |
be8c3862 | 3553 | writePSChar('('); |
9c72faab | 3554 | for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) { |
be8c3862 | 3555 | if (*p == '(' || *p == ')' || *p == '\\') { |
3556 | writePSChar('\\'); | |
3557 | writePSChar((char)*p); | |
3558 | } else if (*p < 0x20 || *p >= 0x80) { | |
ae65da6c | 3559 | sprintf(buf, "\\%03o", *p); |
be8c3862 | 3560 | if (t3String) { |
be8c3862 | 3561 | t3String->append(buf); |
3562 | } else { | |
ae65da6c | 3563 | (*outputFunc)(outputStream, buf, strlen(buf)); |
be8c3862 | 3564 | } |
3565 | } else { | |
3566 | writePSChar((char)*p); | |
3567 | } | |
3568 | } | |
3569 | writePSChar(')'); | |
3570 | } | |
3571 | ||
8dc4af74 | 3572 | void PSOutputDev::writePSName(const char *s) { |
3573 | const char *p; | |
ae65da6c | 3574 | char c; |
3575 | ||
3576 | p = s; | |
3577 | while ((c = *p++)) { | |
3578 | if (c <= (char)0x20 || c >= (char)0x7f || | |
3579 | c == '(' || c == ')' || c == '<' || c == '>' || | |
3580 | c == '[' || c == ']' || c == '{' || c == '}' || | |
3581 | c == '/' || c == '%') { | |
3582 | writePSFmt("#%02x", c & 0xff); | |
3583 | } else { | |
3584 | writePSChar(c); | |
3585 | } | |
be8c3862 | 3586 | } |
3587 | } | |
3588 | ||
3589 | GString *PSOutputDev::filterPSName(GString *name) { | |
3590 | GString *name2; | |
3591 | char buf[8]; | |
3592 | int i; | |
3593 | char c; | |
3594 | ||
3595 | name2 = new GString(); | |
ae65da6c | 3596 | |
3597 | // ghostscript chokes on names that begin with out-of-limits | |
3598 | // numbers, e.g., 1e4foo is handled correctly (as a name), but | |
3599 | // 1e999foo generates a limitcheck error | |
8ee2dc56 | 3600 | c = name->getChar(0); |
3601 | if (c >= '0' && c <= '9') { | |
3602 | name2->append('f'); | |
3603 | } | |
ae65da6c | 3604 | |
be8c3862 | 3605 | for (i = 0; i < name->getLength(); ++i) { |
3606 | c = name->getChar(i); | |
3607 | if (c <= (char)0x20 || c >= (char)0x7f || | |
3608 | c == '(' || c == ')' || c == '<' || c == '>' || | |
3609 | c == '[' || c == ']' || c == '{' || c == '}' || | |
3610 | c == '/' || c == '%') { | |
3611 | sprintf(buf, "#%02x", c & 0xff); | |
3612 | name2->append(buf); | |
3613 | } else { | |
3614 | name2->append(c); | |
3615 | } | |
3616 | } | |
3617 | return name2; | |
9c72faab | 3618 | } |