]> git.ipfire.org Git - thirdparty/cups.git/blame - pdftops/PSOutputDev.cxx
Mirror 1.1.x changes.
[thirdparty/cups.git] / pdftops / PSOutputDev.cxx
CommitLineData
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 47static 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 433static 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 482struct PSSubstFont {
8dc4af74 483 const char *psName; // PostScript name
9c72faab 484 double mWidth; // width of 'm' character
485};
486
8dc4af74 487static 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
505static 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
521struct 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
540class PSOutCustomColor {
541public:
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
552PSOutCustomColor::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
562PSOutCustomColor::~PSOutCustomColor() {
563 delete name;
564}
565
9c72faab 566//------------------------------------------------------------------------
567// PSOutputDev
568//------------------------------------------------------------------------
569
753453e4 570extern "C" {
571typedef void (*SignalFunc)(int);
572}
573
8dc4af74 574static void outputToFile(void *stream, const char *data, int len) {
ae65da6c 575 fwrite(data, 1, len, (FILE *)stream);
576}
577
8dc4af74 578PSOutputDev::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
634PSOutputDev::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
654void 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
735PSOutputDev::~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
793void 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
860void 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 888void 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 935void PSOutputDev::writePageTrailer() {
936 if (mode != psModeForm) {
937 writePS("pdfEndPage\n");
938 }
9c72faab 939}
940
891091df 941void 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 980void 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 1031void 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 1060void 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 1287void 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 1419void 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 1459void 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
1499void 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 1550void 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
1602void 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
1649void 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 1698void 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 1787void 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
1818void 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 1908void 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
2025void 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
2045void PSOutputDev::saveState(GfxState *state) {
2046 writePS("q\n");
891091df 2047 ++numSaves;
9c72faab 2048}
2049
2050void PSOutputDev::restoreState(GfxState *state) {
2051 writePS("Q\n");
891091df 2052 --numSaves;
9c72faab 2053}
2054
2055void 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
2060void 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
2072void PSOutputDev::updateFlatness(GfxState *state) {
ae65da6c 2073 writePSFmt("%d i\n", state->getFlatness());
9c72faab 2074}
2075
2076void PSOutputDev::updateLineJoin(GfxState *state) {
ae65da6c 2077 writePSFmt("%d j\n", state->getLineJoin());
9c72faab 2078}
2079
2080void PSOutputDev::updateLineCap(GfxState *state) {
ae65da6c 2081 writePSFmt("%d J\n", state->getLineCap());
9c72faab 2082}
2083
2084void PSOutputDev::updateMiterLimit(GfxState *state) {
ae65da6c 2085 writePSFmt("%g M\n", state->getMiterLimit());
9c72faab 2086}
2087
2088void PSOutputDev::updateLineWidth(GfxState *state) {
ae65da6c 2089 writePSFmt("%g w\n", state->getLineWidth());
9c72faab 2090}
2091
2092void 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
2144void 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
2196void 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
2211void 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
2229void 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
2237void 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
2245void PSOutputDev::updateCharSpace(GfxState *state) {
ae65da6c 2246 writePSFmt("%g Tc\n", state->getCharSpace());
9c72faab 2247}
2248
2249void 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
2260void PSOutputDev::updateRise(GfxState *state) {
ae65da6c 2261 writePSFmt("%g Ts\n", state->getRise());
9c72faab 2262}
2263
2264void PSOutputDev::updateWordSpace(GfxState *state) {
ae65da6c 2265 writePSFmt("%g Tw\n", state->getWordSpace());
9c72faab 2266}
2267
2268void PSOutputDev::updateHorizScaling(GfxState *state) {
ae65da6c 2269 writePSFmt("%g Tz\n", state->getHorizScaling());
9c72faab 2270}
2271
2272void PSOutputDev::updateTextPos(GfxState *state) {
ae65da6c 2273 writePSFmt("%g %g Td\n", state->getLineX(), state->getLineY());
9c72faab 2274}
2275
2276void 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
2284void 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
2295void PSOutputDev::fill(GfxState *state) {
2296 doPath(state->getPath());
2297 writePS("f\n");
2298}
2299
2300void PSOutputDev::eoFill(GfxState *state) {
2301 doPath(state->getPath());
2302 writePS("f*\n");
2303}
2304
2305void PSOutputDev::clip(GfxState *state) {
2306 doPath(state->getPath());
2307 writePS("W\n");
2308}
2309
2310void PSOutputDev::eoClip(GfxState *state) {
2311 doPath(state->getPath());
2312 writePS("W*\n");
2313}
2314
2315void 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
2371void 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
2477void PSOutputDev::endTextObject(GfxState *state) {
2478 if (haveTextClip) {
2479 writePS("Tclip\n");
2480 haveTextClip = gFalse;
2481 }
52118ca3 2482}
2483
b5cb0608 2484void 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 2497void 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
2522void 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 2589void 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
2646void 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 2873void 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
3067void 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
3086void 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
3198void 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.
3413void 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
3429void 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
3453GBool 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 3484void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
ae65da6c 3485 writePSFmt("%g %g setcharwidth\n", wx, wy);
be8c3862 3486 writePS("q\n");
3487}
3488
3489void 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
3502void 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 3518void PSOutputDev::writePSChar(char c) {
3519 if (t3String) {
3520 t3String->append(c);
3521 } else {
3522 (*outputFunc)(outputStream, &c, 1);
3523 }
3524}
3525
8dc4af74 3526void PSOutputDev::writePS(const char *s) {
ae65da6c 3527 if (t3String) {
3528 t3String->append(s);
3529 } else {
3530 (*outputFunc)(outputStream, s, strlen(s));
3531 }
3532}
3533
3534void 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
3548void 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 3572void 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
3589GString *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}