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