]> git.ipfire.org Git - thirdparty/cups.git/blob - pdftops/PSOutputDev.cxx
Get rid of compiler warnings.
[thirdparty/cups.git] / pdftops / PSOutputDev.cxx
1 //========================================================================
2 //
3 // PSOutputDev.cc
4 //
5 // Copyright 1996 Derek B. Noonburg
6 //
7 //========================================================================
8
9 #ifdef __GNUC__
10 #pragma implementation
11 #endif
12
13 #include <stdio.h>
14 #include <stddef.h>
15 #include <stdarg.h>
16 #include <signal.h>
17 #include <math.h>
18 #include "GString.h"
19 #include "config.h"
20 #include "Object.h"
21 #include "Error.h"
22 #include "GfxState.h"
23 #include "GfxFont.h"
24 #include "FontFile.h"
25 #include "Catalog.h"
26 #include "Page.h"
27 #include "Stream.h"
28 #include "FormWidget.h"
29 #include "PSOutputDev.h"
30
31 #if JAPANESE_SUPPORT
32 #include "Japan12ToRKSJ.h"
33 #endif
34
35 #ifdef MACOS
36 // needed for setting type/creator of MacOS files
37 #include "ICSupport.h"
38 #endif
39
40 //------------------------------------------------------------------------
41 // Parameters
42 //------------------------------------------------------------------------
43
44 // Generate Level 1 PostScript?
45 GBool psOutLevel1 = gFalse;
46
47 // Generate Encapsulated PostScript?
48 GBool psOutEPS = gFalse;
49
50 #if OPI_SUPPORT
51 // Generate OPI comments?
52 GBool psOutOPI = gFalse;
53 #endif
54
55 int paperWidth = 612;
56 int paperHeight = 792;
57
58 //------------------------------------------------------------------------
59 // PostScript prolog and setup
60 //------------------------------------------------------------------------
61
62 static const char *prolog[] = {
63 "/xpdf 75 dict def xpdf begin",
64 "% PDF special state",
65 "/pdfDictSize 14 def",
66 #if 0 // Not used for CUPS - the pstops filter handles this...
67 "/pdfSetup {",
68 " 2 array astore",
69 " /setpagedevice where {",
70 " pop 3 dict dup begin",
71 " exch /PageSize exch def",
72 " /ImagingBBox null def",
73 " /Policies 1 dict dup begin /PageSize 3 def end def",
74 " end setpagedevice",
75 " } {",
76 " pop",
77 " } ifelse",
78 "} def",
79 #endif // 0
80 "/pdfStartPage {",
81 " pdfDictSize dict begin",
82 " /pdfFill [0] def",
83 " /pdfStroke [0] def",
84 " /pdfLastFill false def",
85 " /pdfLastStroke false def",
86 " /pdfTextMat [1 0 0 1 0 0] def",
87 " /pdfFontSize 0 def",
88 " /pdfCharSpacing 0 def",
89 " /pdfTextRender 0 def",
90 " /pdfTextRise 0 def",
91 " /pdfWordSpacing 0 def",
92 " /pdfHorizScaling 1 def",
93 "} def",
94 "/pdfEndPage { end } def",
95 "/sCol { pdfLastStroke not {",
96 " pdfStroke aload length",
97 " 1 eq { setgray } { setrgbcolor} ifelse",
98 " /pdfLastStroke true def /pdfLastFill false def",
99 " } if } def",
100 "/fCol { pdfLastFill not {",
101 " pdfFill aload length",
102 " 1 eq { setgray } { setrgbcolor } ifelse",
103 " /pdfLastFill true def /pdfLastStroke false def",
104 " } if } def",
105 "% build a font",
106 "/pdfMakeFont {",
107 " 4 3 roll findfont",
108 " 4 2 roll matrix scale makefont",
109 " dup length dict begin",
110 " { 1 index /FID ne { def } { pop pop } ifelse } forall",
111 " /Encoding exch def",
112 " currentdict",
113 " end",
114 " definefont pop",
115 "} def",
116 "/pdfMakeFont16 { findfont definefont pop } def",
117 "% graphics state operators",
118 "/q { gsave pdfDictSize dict begin } def",
119 "/Q { end grestore } def",
120 "/cm { concat } def",
121 "/d { setdash } def",
122 "/i { setflat } def",
123 "/j { setlinejoin } def",
124 "/J { setlinecap } def",
125 "/M { setmiterlimit } def",
126 "/w { setlinewidth } def",
127 "% color operators",
128 "/g { dup 1 array astore /pdfFill exch def setgray",
129 " /pdfLastFill true def /pdfLastStroke false def } def",
130 "/G { dup 1 array astore /pdfStroke exch def setgray",
131 " /pdfLastStroke true def /pdfLastFill false def } def",
132 "/rg { 3 copy 3 array astore /pdfFill exch def setrgbcolor",
133 " /pdfLastFill true def /pdfLastStroke false def } def",
134 "/RG { 3 copy 3 array astore /pdfStroke exch def setrgbcolor",
135 " /pdfLastStroke true def /pdfLastFill false def } def",
136 "% path segment operators",
137 "/m { moveto } def",
138 "/l { lineto } def",
139 "/c { curveto } def",
140 "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
141 " neg 0 rlineto closepath } def",
142 "/h { closepath } def",
143 "% path painting operators",
144 "/S { sCol stroke } def",
145 "/f { fCol fill } def",
146 "/f* { fCol eofill } def",
147 "% clipping operators",
148 "/W { clip newpath } def",
149 "/W* { eoclip newpath } def",
150 "% text state operators",
151 "/Tc { /pdfCharSpacing exch def } def",
152 "/Tf { dup /pdfFontSize exch def",
153 " dup pdfHorizScaling mul exch matrix scale",
154 " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
155 " exch findfont exch makefont setfont } def",
156 "/Tr { /pdfTextRender exch def } def",
157 "/Ts { /pdfTextRise exch def } def",
158 "/Tw { /pdfWordSpacing exch def } def",
159 "/Tz { /pdfHorizScaling exch def } def",
160 "% text positioning operators",
161 "/Td { pdfTextMat transform moveto } def",
162 "/Tm { /pdfTextMat exch def } def",
163 "% text string operators",
164 "/Tj { pdfTextRender 1 and 0 eq { fCol } { sCol } ifelse",
165 " 0 pdfTextRise pdfTextMat dtransform rmoveto",
166 " pdfFontSize mul pdfHorizScaling mul",
167 " 1 index stringwidth pdfTextMat idtransform pop",
168 " sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",
169 " pdfWordSpacing 0 pdfTextMat dtransform 32",
170 " 4 3 roll pdfCharSpacing add 0 pdfTextMat dtransform",
171 " 6 5 roll awidthshow",
172 " 0 pdfTextRise neg pdfTextMat dtransform rmoveto } def",
173 "/TJm { pdfFontSize 0.001 mul mul neg 0",
174 " pdfTextMat dtransform rmoveto } def",
175 "% Level 1 image operators",
176 "/pdfIm1 {",
177 " /pdfImBuf1 4 index string def",
178 " { currentfile pdfImBuf1 readhexstring pop } image",
179 "} def",
180 "/pdfImM1 {",
181 " /pdfImBuf1 4 index 7 add 8 idiv string def",
182 " { currentfile pdfImBuf1 readhexstring pop } imagemask",
183 "} def",
184 "% Level 2 image operators",
185 "/pdfImBuf 100 string def",
186 "/pdfIm {",
187 " image",
188 " { currentfile pdfImBuf readline",
189 " not { pop exit } if",
190 " (%-EOD-) eq { exit } if } loop",
191 "} def",
192 "/pdfImM {",
193 " fCol imagemask",
194 " { currentfile pdfImBuf readline",
195 " not { pop exit } if",
196 " (%-EOD-) eq { exit } if } loop",
197 "} def",
198 "end",
199 NULL
200 };
201
202 //------------------------------------------------------------------------
203 // Fonts
204 //------------------------------------------------------------------------
205
206 struct PSFont {
207 const char *name; // PDF name
208 const char *psName; // PostScript name
209 };
210
211 struct PSSubstFont {
212 const char *psName; // PostScript name
213 double mWidth; // width of 'm' character
214 };
215
216 static PSFont psFonts[] = {
217 {"Courier", "Courier"},
218 {"Courier-Bold", "Courier-Bold"},
219 {"Courier-Oblique", "Courier-Bold"},
220 {"Courier-BoldOblique", "Courier-BoldOblique"},
221 {"Helvetica", "Helvetica"},
222 {"Helvetica-Bold", "Helvetica-Bold"},
223 {"Helvetica-Oblique", "Helvetica-Oblique"},
224 {"Helvetica-BoldOblique", "Helvetica-BoldOblique"},
225 {"Symbol", "Symbol"},
226 {"Times-Roman", "Times-Roman"},
227 {"Times-Bold", "Times-Bold"},
228 {"Times-Italic", "Times-Italic"},
229 {"Times-BoldItalic", "Times-BoldItalic"},
230 {"ZapfDingbats", "ZapfDingbats"},
231 {NULL}
232 };
233
234 static PSSubstFont psSubstFonts[] = {
235 {"Helvetica", 0.833},
236 {"Helvetica-Oblique", 0.833},
237 {"Helvetica-Bold", 0.889},
238 {"Helvetica-BoldOblique", 0.889},
239 {"Times-Roman", 0.788},
240 {"Times-Italic", 0.722},
241 {"Times-Bold", 0.833},
242 {"Times-BoldItalic", 0.778},
243 {"Courier", 0.600},
244 {"Courier-Oblique", 0.600},
245 {"Courier-Bold", 0.600},
246 {"Courier-BoldOblique", 0.600}
247 };
248
249 //------------------------------------------------------------------------
250 // PSOutputDev
251 //------------------------------------------------------------------------
252
253 PSOutputDev::PSOutputDev(const char *fileName, Catalog *catalog,
254 int firstPage, int lastPage,
255 GBool embedType11, GBool doForm1) {
256 Page *page;
257 Dict *resDict;
258 FormWidgets *formWidgets;
259 const char **p;
260 int pg;
261 Object obj1, obj2;
262 int i;
263
264 // initialize
265 embedType1 = embedType11;
266 doForm = doForm1;
267 fontIDs = NULL;
268 fontFileIDs = NULL;
269 fontFileNames = NULL;
270 embFontList = NULL;
271 f = NULL;
272 if (doForm)
273 lastPage = firstPage;
274
275 // open file or pipe
276 ok = gTrue;
277 if (!strcmp(fileName, "-")) {
278 fileType = psStdout;
279 f = stdout;
280 } else if (fileName[0] == '|') {
281 fileType = psPipe;
282 #ifdef HAVE_POPEN
283 #ifndef WIN32
284 signal(SIGPIPE, SIG_IGN);
285 #endif
286 if (!(f = popen(fileName + 1, "w"))) {
287 error(-1, "Couldn't run print command '%s'", fileName);
288 ok = gFalse;
289 return;
290 }
291 #else
292 error(-1, "Print commands are not supported ('%s')", fileName);
293 ok = gFalse;
294 return;
295 #endif
296 } else {
297 fileType = psFile;
298 if (!(f = fopen(fileName, "w"))) {
299 error(-1, "Couldn't open PostScript file '%s'", fileName);
300 ok = gFalse;
301 return;
302 }
303 }
304
305 // initialize fontIDs, fontFileIDs, and fontFileNames lists
306 fontIDSize = 64;
307 fontIDLen = 0;
308 fontIDs = (Ref *)gmalloc(fontIDSize * sizeof(Ref));
309 fontFileIDSize = 64;
310 fontFileIDLen = 0;
311 fontFileIDs = (Ref *)gmalloc(fontFileIDSize * sizeof(Ref));
312 fontFileNameSize = 64;
313 fontFileNameLen = 0;
314 fontFileNames = (GString **)gmalloc(fontFileNameSize * sizeof(GString *));
315
316 // initialize embedded font resource comment list
317 embFontList = new GString();
318
319 // write header
320 if (doForm) {
321 writePS("%%!PS-Adobe-3.0 Resource-Form\n");
322 writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
323 writePS("%%%%LanguageLevel: %d\n", psOutLevel1 ? 1 : 2);
324 writePS("%%%%EndComments\n");
325 } else if (psOutEPS) {
326 writePS("%%!PS-Adobe-3.0 EPSF-3.0\n");
327 writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
328 writePS("%%%%LanguageLevel: %d\n", psOutLevel1 ? 1 : 2);
329 page = catalog->getPage(firstPage);
330 writePS("%%%%BoundingBox: %d %d %d %d\n",
331 (int)floor(page->getX1()), (int)floor(page->getY1()),
332 (int)ceil(page->getX2()), (int)ceil(page->getY2()));
333 if (floor(page->getX1()) != ceil(page->getX1()) ||
334 floor(page->getY1()) != ceil(page->getY1()) ||
335 floor(page->getX2()) != ceil(page->getX2()) ||
336 floor(page->getY2()) != ceil(page->getY2())) {
337 writePS("%%%%HiResBoundingBox: %g %g %g %g\n",
338 page->getX1(), page->getY1(),
339 page->getX2(), page->getY2());
340 }
341 writePS("%%%%DocumentSuppliedResources:\n");
342 writePS("%%%%+ font: (atend)\n");
343 writePS("%%%%EndComments\n");
344 } else {
345 writePS("%%!PS-Adobe-3.0\n");
346 writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
347 writePS("%%%%LanguageLevel: %d\n", psOutLevel1 ? 1 : 2);
348 writePS("%%%%DocumentMedia: plain %d %d 0 () ()\n",
349 paperWidth, paperHeight);
350 writePS("%%%%Pages: %d\n", lastPage - firstPage + 1);
351 writePS("%%%%EndComments\n");
352 writePS("%%%%BeginDefaults\n");
353 writePS("%%%%PageMedia: plain\n");
354 writePS("%%%%EndDefaults\n");
355 }
356
357 // write prolog
358 if (!doForm)
359 writePS("%%%%BeginProlog\n");
360 writePS("%%%%BeginResource: xpdf %s\n", xpdfVersion);
361 for (p = prolog; *p; ++p)
362 writePS("%s\n", *p);
363 writePS("%%%%EndResource\n");
364 if (!doForm)
365 writePS("%%%%EndProlog\n");
366
367 // set up fonts
368 if (!doForm)
369 writePS("%%%%BeginSetup\n");
370 writePS("xpdf begin\n");
371 for (pg = firstPage; pg <= lastPage; ++pg) {
372 page = catalog->getPage(pg);
373 if ((resDict = page->getResourceDict())) {
374 setupFonts(resDict);
375 }
376 formWidgets = new FormWidgets(page->getAnnots(&obj1));
377 obj1.free();
378 for (i = 0; i < formWidgets->getNumWidgets(); ++i) {
379 if (formWidgets->getWidget(i)->getAppearance(&obj1)->isStream()) {
380 obj1.streamGetDict()->lookup("Resources", &obj2);
381 if (obj2.isDict()) {
382 setupFonts(obj2.getDict());
383 }
384 obj2.free();
385 }
386 obj1.free();
387 }
388 delete formWidgets;
389 }
390 if (doForm) {
391 writePS("end\n");
392 } else {
393 #if OPI_SUPPORT
394 if (psOutOPI) {
395 writePS("/opiMatrix matrix currentmatrix def\n");
396 }
397 #endif
398 if (!psOutEPS) {
399 // pdfSetup not used for CUPS filter...
400 // writePS("%d %d pdfSetup\n", paperWidth, paperHeight);
401 }
402 writePS("%%%%EndSetup\n");
403 }
404
405 // write form header
406 if (doForm) {
407 page = catalog->getPage(firstPage);
408 writePS("4 dict dup begin\n");
409 writePS("/BBox [%d %d %d %d] def\n",
410 (int)page->getX1(), (int)page->getY1(),
411 (int)page->getX2(), (int)page->getY2());
412 writePS("/FormType 1 def\n");
413 writePS("/Matrix [1 0 0 1 0 0] def\n");
414 }
415
416 // initialize sequential page number
417 seqPage = 1;
418
419 #if OPI_SUPPORT
420 // initialize OPI nesting levels
421 opi13Nest = 0;
422 opi20Nest = 0;
423 #endif
424 }
425
426 PSOutputDev::~PSOutputDev() {
427 int i;
428
429 if (f) {
430 if (doForm) {
431 writePS("end\n");
432 writePS("/Foo exch /Form defineresource pop\n");
433 } else if (psOutEPS) {
434 writePS("%%%%Trailer\n");
435 writePS("end\n");
436 writePS("%%%%DocumentSuppliedResources:\n");
437 writePS("%s", embFontList->getCString());
438 writePS("%%%%EOF\n");
439 } else {
440 writePS("%%%%Trailer\n");
441 writePS("end\n");
442 writePS("%%%%EOF\n");
443 }
444 if (fileType == psFile) {
445 #ifdef MACOS
446 ICS_MapRefNumAndAssign((short)f->handle);
447 #endif
448 fclose(f);
449 }
450 #ifdef HAVE_POPEN
451 else if (fileType == psPipe) {
452 pclose(f);
453 #ifndef WIN32
454 signal(SIGPIPE, SIG_DFL);
455 #endif
456 }
457 #endif
458 }
459 if (embFontList) {
460 delete embFontList;
461 }
462 if (fontIDs) {
463 gfree(fontIDs);
464 }
465 if (fontFileIDs) {
466 gfree(fontFileIDs);
467 }
468 if (fontFileNames) {
469 for (i = 0; i < fontFileNameLen; ++i) {
470 delete fontFileNames[i];
471 }
472 gfree(fontFileNames);
473 }
474 }
475
476 void PSOutputDev::setupFonts(Dict *resDict) {
477 Object fontDict, xObjDict, xObj, resObj;
478 GfxFontDict *gfxFontDict;
479 GfxFont *font;
480 int i;
481
482 resDict->lookup("Font", &fontDict);
483 if (fontDict.isDict()) {
484 gfxFontDict = new GfxFontDict(fontDict.getDict());
485 for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
486 font = gfxFontDict->getFont(i);
487 setupFont(font);
488 }
489 delete gfxFontDict;
490 }
491 fontDict.free();
492
493 resDict->lookup("XObject", &xObjDict);
494 if (xObjDict.isDict()) {
495 for (i = 0; i < xObjDict.dictGetLength(); ++i) {
496 xObjDict.dictGetVal(i, &xObj);
497 if (xObj.isStream()) {
498 xObj.streamGetDict()->lookup("Resources", &resObj);
499 if (resObj.isDict())
500 setupFonts(resObj.getDict());
501 resObj.free();
502 }
503 xObj.free();
504 }
505 }
506 xObjDict.free();
507 }
508
509 void PSOutputDev::setupFont(GfxFont *font) {
510 Ref fontFileID;
511 GString *name;
512 const char *psName;
513 const char *charName;
514 double xs, ys;
515 GBool do16Bit;
516 int code;
517 double w1, w2;
518 double *fm;
519 int i, j;
520
521 // check if font is already set up
522 for (i = 0; i < fontIDLen; ++i) {
523 if (fontIDs[i].num == font->getID().num &&
524 fontIDs[i].gen == font->getID().gen)
525 return;
526 }
527
528 // add entry to fontIDs list
529 if (fontIDLen >= fontIDSize) {
530 fontIDSize += 64;
531 fontIDs = (Ref *)grealloc(fontIDs, fontIDSize * sizeof(Ref));
532 }
533 fontIDs[fontIDLen++] = font->getID();
534
535 xs = ys = 1;
536 do16Bit = gFalse;
537
538 // check for embedded Type 1 font
539 if (embedType1 && font->getType() == fontType1 &&
540 font->getEmbeddedFontID(&fontFileID)) {
541 psName = font->getEmbeddedFontName();
542 setupEmbeddedType1Font(&fontFileID, psName);
543
544 // check for external Type 1 font file
545 } else if (embedType1 && font->getType() == fontType1 &&
546 font->getExtFontFile()) {
547 // this assumes that the PS font name matches the PDF font name
548 psName = font->getName()->getCString();
549 setupEmbeddedType1Font(font->getExtFontFile(), psName);
550
551 // check for embedded Type 1C font
552 } else if (embedType1 && font->getType() == fontType1C &&
553 font->getEmbeddedFontID(&fontFileID)) {
554 psName = font->getEmbeddedFontName();
555 setupEmbeddedType1CFont(font, &fontFileID, psName);
556
557 } else if (font->is16Bit() && font->getCharSet16() == font16AdobeJapan12) {
558 psName = "Ryumin-Light-RKSJ";
559 do16Bit = gTrue;
560
561 // do font substitution
562 } else {
563 name = font->getName();
564 psName = NULL;
565 if (name) {
566 for (i = 0; psFonts[i].name; ++i) {
567 if (name->cmp(psFonts[i].name) == 0) {
568 psName = psFonts[i].psName;
569 break;
570 }
571 }
572 }
573 if (!psName) {
574 if (font->isFixedWidth())
575 i = 8;
576 else if (font->isSerif())
577 i = 4;
578 else
579 i = 0;
580 if (font->isBold())
581 i += 2;
582 if (font->isItalic())
583 i += 1;
584 psName = psSubstFonts[i].psName;
585 if ((code = font->getCharCode("m")) >= 0) {
586 w1 = font->getWidth(code);
587 } else {
588 w1 = 0;
589 }
590 w2 = psSubstFonts[i].mWidth;
591 xs = w1 / w2;
592 if (xs < 0.1) {
593 xs = 1;
594 }
595 if (font->getType() == fontType3) {
596 // This is a hack which makes it possible to substitute for some
597 // Type 3 fonts. The problem is that it's impossible to know what
598 // the base coordinate system used in the font is without actually
599 // rendering the font.
600 ys = xs;
601 fm = font->getFontMatrix();
602 if (fm[0] != 0) {
603 ys *= fm[3] / fm[0];
604 }
605 } else {
606 ys = 1;
607 }
608 }
609 }
610
611 // generate PostScript code to set up the font
612 if (do16Bit) {
613 writePS("/F%d_%d /%s pdfMakeFont16\n",
614 font->getID().num, font->getID().gen, psName);
615 } else {
616 writePS("/F%d_%d /%s %g %g\n",
617 font->getID().num, font->getID().gen, psName, xs, ys);
618 for (i = 0; i < 256; i += 8) {
619 writePS((i == 0) ? "[ " : " ");
620 for (j = 0; j < 8; ++j) {
621 charName = font->getCharName(i+j);
622 writePS("/%s", charName ? charName : ".notdef");
623 }
624 writePS((i == 256-8) ? "]\n" : "\n");
625 }
626 writePS("pdfMakeFont\n");
627 }
628 }
629
630 void PSOutputDev::setupEmbeddedType1Font(Ref *id, const char *psName) {
631 static char hexChar[17] = "0123456789abcdef";
632 Object refObj, strObj, obj1, obj2;
633 Dict *dict;
634 int length1, length2;
635 int c;
636 int start[4];
637 GBool binMode;
638 int i;
639
640 // check if font is already embedded
641 for (i = 0; i < fontFileIDLen; ++i) {
642 if (fontFileIDs[i].num == id->num &&
643 fontFileIDs[i].gen == id->gen)
644 return;
645 }
646
647 // add entry to fontFileIDs list
648 if (fontFileIDLen >= fontFileIDSize) {
649 fontFileIDSize += 64;
650 fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
651 }
652 fontFileIDs[fontFileIDLen++] = *id;
653
654 // get the font stream and info
655 refObj.initRef(id->num, id->gen);
656 refObj.fetch(&strObj);
657 refObj.free();
658 if (!strObj.isStream()) {
659 error(-1, "Embedded font file object is not a stream");
660 goto err1;
661 }
662 if (!(dict = strObj.streamGetDict())) {
663 error(-1, "Embedded font stream is missing its dictionary");
664 goto err1;
665 }
666 dict->lookup("Length1", &obj1);
667 dict->lookup("Length2", &obj2);
668 if (!obj1.isInt() || !obj2.isInt()) {
669 error(-1, "Missing length fields in embedded font stream dictionary");
670 obj1.free();
671 obj2.free();
672 goto err1;
673 }
674 length1 = obj1.getInt();
675 length2 = obj2.getInt();
676 obj1.free();
677 obj2.free();
678
679 // beginning comment
680 if (psOutEPS) {
681 writePS("%%%%BeginResource: font %s\n", psName);
682 embFontList->append("%%+ font ");
683 embFontList->append(psName);
684 embFontList->append("\n");
685 }
686
687 // copy ASCII portion of font
688 strObj.streamReset();
689 for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i)
690 fputc(c, f);
691
692 // figure out if encrypted portion is binary or ASCII
693 binMode = gFalse;
694 for (i = 0; i < 4; ++i) {
695 start[i] = strObj.streamGetChar();
696 if (start[i] == EOF) {
697 error(-1, "Unexpected end of file in embedded font stream");
698 goto err1;
699 }
700 if (!((start[i] >= '0' && start[i] <= '9') ||
701 (start[i] >= 'A' && start[i] <= 'F') ||
702 (start[i] >= 'a' && start[i] <= 'f')))
703 binMode = gTrue;
704 }
705
706 // convert binary data to ASCII
707 if (binMode) {
708 for (i = 0; i < 4; ++i) {
709 fputc(hexChar[(start[i] >> 4) & 0x0f], f);
710 fputc(hexChar[start[i] & 0x0f], f);
711 }
712 while (i < length2) {
713 if ((c = strObj.streamGetChar()) == EOF)
714 break;
715 fputc(hexChar[(c >> 4) & 0x0f], f);
716 fputc(hexChar[c & 0x0f], f);
717 if (++i % 32 == 0)
718 fputc('\n', f);
719 }
720 if (i % 32 > 0)
721 fputc('\n', f);
722
723 // already in ASCII format -- just copy it
724 } else {
725 for (i = 0; i < 4; ++i)
726 fputc(start[i], f);
727 for (i = 4; i < length2; ++i) {
728 if ((c = strObj.streamGetChar()) == EOF)
729 break;
730 fputc(c, f);
731 }
732 }
733
734 // write padding and "cleartomark"
735 for (i = 0; i < 8; ++i)
736 writePS("00000000000000000000000000000000"
737 "00000000000000000000000000000000\n");
738 writePS("cleartomark\n");
739
740 // ending comment
741 if (psOutEPS) {
742 writePS("%%%%EndResource\n");
743 }
744
745 err1:
746 strObj.free();
747 }
748
749 //~ This doesn't handle .pfb files or binary eexec data (which only
750 //~ happens in pfb files?).
751 void PSOutputDev::setupEmbeddedType1Font(GString *fileName, const char *psName) {
752 FILE *fontFile;
753 int c;
754 int i;
755
756 // check if font is already embedded
757 for (i = 0; i < fontFileNameLen; ++i) {
758 if (!fontFileNames[i]->cmp(fileName)) {
759 return;
760 }
761 }
762
763 // add entry to fontFileNames list
764 if (fontFileNameLen >= fontFileNameSize) {
765 fontFileNameSize += 64;
766 fontFileNames = (GString **)grealloc(fontFileNames,
767 fontFileNameSize * sizeof(GString *));
768 }
769 fontFileNames[fontFileNameLen++] = fileName->copy();
770
771 // beginning comment
772 if (psOutEPS) {
773 writePS("%%%%BeginResource: font %s\n", psName);
774 embFontList->append("%%+ font ");
775 embFontList->append(psName);
776 embFontList->append("\n");
777 }
778
779 // copy the font file
780 if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
781 error(-1, "Couldn't open external font file");
782 return;
783 }
784 while ((c = fgetc(fontFile)) != EOF)
785 fputc(c, f);
786 fclose(fontFile);
787
788 // ending comment
789 if (psOutEPS) {
790 writePS("%%%%EndResource\n");
791 }
792 }
793
794 void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
795 const char *psName) {
796 const char *fontBuf;
797 int fontLen;
798 Type1CFontConverter *cvt;
799 int i;
800
801 // check if font is already embedded
802 for (i = 0; i < fontFileIDLen; ++i) {
803 if (fontFileIDs[i].num == id->num &&
804 fontFileIDs[i].gen == id->gen)
805 return;
806 }
807
808 // add entry to fontFileIDs list
809 if (fontFileIDLen >= fontFileIDSize) {
810 fontFileIDSize += 64;
811 fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
812 }
813 fontFileIDs[fontFileIDLen++] = *id;
814
815 // beginning comment
816 if (psOutEPS) {
817 writePS("%%%%BeginResource: font %s\n", psName);
818 embFontList->append("%%+ font ");
819 embFontList->append(psName);
820 embFontList->append("\n");
821 }
822
823 // convert it to a Type 1 font
824 fontBuf = font->readEmbFontFile(&fontLen);
825 cvt = new Type1CFontConverter(fontBuf, fontLen, f);
826 cvt->convert();
827 delete cvt;
828 gfree((void *)fontBuf);
829
830 // ending comment
831 if (psOutEPS) {
832 writePS("%%%%EndResource\n");
833 }
834 }
835
836 void PSOutputDev::startPage(int pageNum, GfxState *state) {
837 int x1, y1, x2, y2, width, height, t;
838
839 if (doForm) {
840
841 writePS("/PaintProc {\n");
842 writePS("begin xpdf begin\n");
843 writePS("pdfStartPage\n");
844 tx = ty = 0;
845 xScale = yScale = 1;
846 landscape = gFalse;
847
848 } else if (psOutEPS) {
849
850 writePS("pdfStartPage\n");
851 tx = ty = 0;
852 xScale = yScale = 1;
853 landscape = gFalse;
854
855 } else {
856
857 writePS("%%%%Page: %d %d\n", pageNum, seqPage);
858 writePS("%%%%BeginPageSetup\n");
859
860 // rotate, translate, and scale page
861 x1 = (int)(state->getX1() + 0.5);
862 y1 = (int)(state->getY1() + 0.5);
863 x2 = (int)(state->getX2() + 0.5);
864 y2 = (int)(state->getY2() + 0.5);
865 width = x2 - x1;
866 height = y2 - y1;
867 if (width > height && width > paperWidth) {
868 landscape = gTrue;
869 writePS("%%%%PageOrientation: Landscape\n");
870 writePS("pdfStartPage\n");
871 writePS("90 rotate\n");
872 tx = -x1;
873 ty = -(y1 + paperWidth);
874 t = width;
875 width = height;
876 height = t;
877 } else {
878 landscape = gFalse;
879 writePS("%%%%PageOrientation: Portrait\n");
880 writePS("pdfStartPage\n");
881 tx = -x1;
882 ty = -y1;
883 }
884 if (width < paperWidth) {
885 tx += (paperWidth - width) / 2;
886 }
887 if (height < paperHeight) {
888 ty += (paperHeight - height) / 2;
889 }
890 if (tx != 0 || ty != 0) {
891 writePS("%g %g translate\n", tx, ty);
892 }
893 if (width > paperWidth || height > paperHeight) {
894 xScale = (double)paperWidth / (double)width;
895 yScale = (double)paperHeight / (double)height;
896 if (yScale < xScale) {
897 xScale = yScale;
898 }
899 writePS("%0.4f %0.4f scale\n", xScale, xScale);
900 } else {
901 xScale = yScale = 1;
902 }
903
904 writePS("%%%%EndPageSetup\n");
905 ++seqPage;
906 }
907 }
908
909 void PSOutputDev::endPage() {
910 if (doForm) {
911 writePS("pdfEndPage\n");
912 writePS("end end\n");
913 writePS("} def\n");
914 } else {
915 writePS("showpage\n");
916 writePS("%%%%PageTrailer\n");
917 writePS("pdfEndPage\n");
918 }
919 }
920
921 void PSOutputDev::saveState(GfxState *state) {
922 (void)state;
923
924 writePS("q\n");
925 }
926
927 void PSOutputDev::restoreState(GfxState *state) {
928 (void)state;
929
930 writePS("Q\n");
931 }
932
933 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
934 double m21, double m22, double m31, double m32) {
935 (void)state;
936
937 writePS("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32);
938 }
939
940 void PSOutputDev::updateLineDash(GfxState *state) {
941 double *dash;
942 double start;
943 int length, i;
944
945 state->getLineDash(&dash, &length, &start);
946 writePS("[");
947 for (i = 0; i < length; ++i)
948 writePS("%g%s", dash[i], (i == length-1) ? "" : " ");
949 writePS("] %g d\n", start);
950 }
951
952 void PSOutputDev::updateFlatness(GfxState *state) {
953 writePS("%d i\n", state->getFlatness());
954 }
955
956 void PSOutputDev::updateLineJoin(GfxState *state) {
957 writePS("%d j\n", state->getLineJoin());
958 }
959
960 void PSOutputDev::updateLineCap(GfxState *state) {
961 writePS("%d J\n", state->getLineCap());
962 }
963
964 void PSOutputDev::updateMiterLimit(GfxState *state) {
965 writePS("%g M\n", state->getMiterLimit());
966 }
967
968 void PSOutputDev::updateLineWidth(GfxState *state) {
969 writePS("%g w\n", state->getLineWidth());
970 }
971
972 void PSOutputDev::updateFillColor(GfxState *state) {
973 GfxColor *color;
974 double r, g, b;
975
976 color = state->getFillColor();
977 r = color->getR();
978 g = color->getG();
979 b = color->getB();
980 if (r == g && g == b)
981 writePS("%g g\n", r);
982 else
983 writePS("%g %g %g rg\n", r, g, b);
984 }
985
986 void PSOutputDev::updateStrokeColor(GfxState *state) {
987 GfxColor *color;
988 double r, g, b;
989
990 color = state->getStrokeColor();
991 r = color->getR();
992 g = color->getG();
993 b = color->getB();
994 if (r == g && g == b)
995 writePS("%g G\n", r);
996 else
997 writePS("%g %g %g RG\n", r, g, b);
998 }
999
1000 void PSOutputDev::updateFont(GfxState *state) {
1001 if (state->getFont()) {
1002 writePS("/F%d_%d %g Tf\n",
1003 state->getFont()->getID().num, state->getFont()->getID().gen,
1004 state->getFontSize());
1005 }
1006 }
1007
1008 void PSOutputDev::updateTextMat(GfxState *state) {
1009 double *mat;
1010
1011 mat = state->getTextMat();
1012 writePS("[%g %g %g %g %g %g] Tm\n",
1013 mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
1014 }
1015
1016 void PSOutputDev::updateCharSpace(GfxState *state) {
1017 writePS("%g Tc\n", state->getCharSpace());
1018 }
1019
1020 void PSOutputDev::updateRender(GfxState *state) {
1021 writePS("%d Tr\n", state->getRender());
1022 }
1023
1024 void PSOutputDev::updateRise(GfxState *state) {
1025 writePS("%g Ts\n", state->getRise());
1026 }
1027
1028 void PSOutputDev::updateWordSpace(GfxState *state) {
1029 writePS("%g Tw\n", state->getWordSpace());
1030 }
1031
1032 void PSOutputDev::updateHorizScaling(GfxState *state) {
1033 writePS("%g Tz\n", state->getHorizScaling());
1034 }
1035
1036 void PSOutputDev::updateTextPos(GfxState *state) {
1037 writePS("%g %g Td\n", state->getLineX(), state->getLineY());
1038 }
1039
1040 void PSOutputDev::updateTextShift(GfxState *state, double shift) {
1041 (void)state;
1042
1043 writePS("%g TJm\n", shift);
1044 }
1045
1046 void PSOutputDev::stroke(GfxState *state) {
1047 doPath(state->getPath());
1048 writePS("S\n");
1049 }
1050
1051 void PSOutputDev::fill(GfxState *state) {
1052 doPath(state->getPath());
1053 writePS("f\n");
1054 }
1055
1056 void PSOutputDev::eoFill(GfxState *state) {
1057 doPath(state->getPath());
1058 writePS("f*\n");
1059 }
1060
1061 void PSOutputDev::clip(GfxState *state) {
1062 doPath(state->getPath());
1063 writePS("W\n");
1064 }
1065
1066 void PSOutputDev::eoClip(GfxState *state) {
1067 doPath(state->getPath());
1068 writePS("W*\n");
1069 }
1070
1071 void PSOutputDev::doPath(GfxPath *path) {
1072 GfxSubpath *subpath;
1073 double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
1074 int n, m, i, j;
1075
1076 n = path->getNumSubpaths();
1077
1078 if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
1079 subpath = path->getSubpath(0);
1080 x0 = subpath->getX(0);
1081 y0 = subpath->getY(0);
1082 x4 = subpath->getX(4);
1083 y4 = subpath->getY(4);
1084 if (x4 == x0 && y4 == y0) {
1085 x1 = subpath->getX(1);
1086 y1 = subpath->getY(1);
1087 x2 = subpath->getX(2);
1088 y2 = subpath->getY(2);
1089 x3 = subpath->getX(3);
1090 y3 = subpath->getY(3);
1091 if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
1092 writePS("%g %g %g %g re\n",
1093 x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
1094 fabs(x2 - x0), fabs(y1 - y0));
1095 return;
1096 } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
1097 writePS("%g %g %g %g re\n",
1098 x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
1099 fabs(x1 - x0), fabs(y2 - y0));
1100 return;
1101 }
1102 }
1103 }
1104
1105 for (i = 0; i < n; ++i) {
1106 subpath = path->getSubpath(i);
1107 m = subpath->getNumPoints();
1108 writePS("%g %g m\n", subpath->getX(0), subpath->getY(0));
1109 j = 1;
1110 while (j < m) {
1111 if (subpath->getCurve(j)) {
1112 writePS("%g %g %g %g %g %g c\n", subpath->getX(j), subpath->getY(j),
1113 subpath->getX(j+1), subpath->getY(j+1),
1114 subpath->getX(j+2), subpath->getY(j+2));
1115 j += 3;
1116 } else {
1117 writePS("%g %g l\n", subpath->getX(j), subpath->getY(j));
1118 ++j;
1119 }
1120 }
1121 if (subpath->isClosed()) {
1122 writePS("h\n");
1123 }
1124 }
1125 }
1126
1127 void PSOutputDev::drawString(GfxState *state, GString *s) {
1128 // check for invisible text -- this is used by Acrobat Capture
1129 if ((state->getRender() & 3) == 3)
1130 return;
1131
1132 writePSString(s);
1133 writePS(" %g Tj\n", state->getFont()->getWidth(s));
1134 }
1135
1136 void PSOutputDev::drawString16(GfxState *state, GString *s) {
1137 #if defined(JAPANESE_SUPPORT) || defined(CHINESE_SUPPORT)
1138 int c1, c2;
1139 double w;
1140 int i;
1141 #else
1142 (void)s;
1143 #endif /* JAPANESE_SUPPORT || CHINESE_SUPPORT */
1144
1145 // check for invisible text -- this is used by Acrobat Capture
1146 if ((state->getRender() & 3) == 3)
1147 return;
1148
1149 switch (state->getFont()->getCharSet16()) {
1150
1151 case font16AdobeJapan12:
1152 #if JAPANESE_SUPPORT
1153 writePS("<");
1154 w = 0;
1155 for (i = 0; i < s->getLength(); i += 2) {
1156 c1 = ((s->getChar(i) & 0xff) << 8) + (s->getChar(i+1) & 0xff);
1157 if (c1 <= 8285) {
1158 c2 = japan12ToRKSJ[c1];
1159 } else {
1160 c2 = 0x20;
1161 }
1162 if (c2 <= 0xff) {
1163 writePS("%02x", c2);
1164 } else {
1165 writePS("%02x%02x", c2 >> 8, c2 & 0xff);
1166 }
1167 w += state->getFont()->getWidth16(c1);
1168 }
1169 writePS("> %g Tj\n", w);
1170 #endif
1171 break;
1172
1173 case font16AdobeGB12:
1174 break;
1175 }
1176 }
1177
1178 void PSOutputDev::drawImageMask(GfxState *state, Stream *str,
1179 int width, int height, GBool invert,
1180 GBool inlineImg) {
1181 int len;
1182
1183 (void)state;
1184
1185 len = height * ((width + 7) / 8);
1186 if (psOutLevel1)
1187 doImageL1(NULL, invert, inlineImg, str, width, height, len);
1188 else
1189 doImage(NULL, invert, inlineImg, str, width, height, len);
1190 }
1191
1192 void PSOutputDev::drawImage(GfxState *state, Stream *str, int width,
1193 int height, GfxImageColorMap *colorMap,
1194 GBool inlineImg) {
1195 int len;
1196
1197 (void)state;
1198
1199 len = height * ((width * colorMap->getNumPixelComps() *
1200 colorMap->getBits() + 7) / 8);
1201 if (psOutLevel1)
1202 doImageL1(colorMap, gFalse, inlineImg, str, width, height, len);
1203 else
1204 doImage(colorMap, gFalse, inlineImg, str, width, height, len);
1205 }
1206
1207 void PSOutputDev::doImageL1(GfxImageColorMap *colorMap,
1208 GBool invert, GBool inlineImg,
1209 Stream *str, int width, int height, int len) {
1210 ImageStream *imgStr;
1211 Guchar pixBuf[4];
1212 GfxColor color;
1213 int x, y, i;
1214
1215 (void)inlineImg;
1216 (void)len;
1217
1218 // width, height, matrix, bits per component
1219 if (colorMap) {
1220 writePS("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n",
1221 width, height,
1222 width, -height, height);
1223 } else {
1224 writePS("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n",
1225 width, height, invert ? "true" : "false",
1226 width, -height, height);
1227 }
1228
1229 // image
1230 if (colorMap) {
1231
1232 // set up to process the data stream
1233 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
1234 colorMap->getBits());
1235 imgStr->reset();
1236
1237 // process the data stream
1238 i = 0;
1239 for (y = 0; y < height; ++y) {
1240
1241 // write the line
1242 for (x = 0; x < width; ++x) {
1243 imgStr->getPixel(pixBuf);
1244 colorMap->getColor(pixBuf, &color);
1245 fprintf(f, "%02x", (int)(color.getGray() * 255 + 0.5));
1246 if (++i == 32) {
1247 fputc('\n', f);
1248 i = 0;
1249 }
1250 }
1251 }
1252 if (i != 0)
1253 fputc('\n', f);
1254 delete imgStr;
1255
1256 // imagemask
1257 } else {
1258 str->reset();
1259 i = 0;
1260 for (y = 0; y < height; ++y) {
1261 for (x = 0; x < width; x += 8) {
1262 fprintf(f, "%02x", str->getChar() & 0xff);
1263 if (++i == 32) {
1264 fputc('\n', f);
1265 i = 0;
1266 }
1267 }
1268 }
1269 if (i != 0)
1270 fputc('\n', f);
1271 }
1272 }
1273
1274 void PSOutputDev::doImage(GfxImageColorMap *colorMap,
1275 GBool invert, GBool inlineImg,
1276 Stream *str, int width, int height, int len) {
1277 GfxColorSpace *colorSpace;
1278 LabParams *labParams;
1279 GString *s;
1280 int n, numComps;
1281 Guchar *color;
1282 Guchar x[4];
1283 GfxColor rgb;
1284 GBool useRLE, useA85;
1285 int c;
1286 int i, j, k;
1287
1288 // color space
1289 if (colorMap) {
1290 colorSpace = colorMap->getColorSpace();
1291 if (colorSpace->isSeparation()) {
1292 //~ this is a kludge -- the correct thing would be to output
1293 //~ a separation color space
1294 n = (1 << colorMap->getBits()) - 1;
1295 writePS("[/Indexed /DeviceRGB %d <", n);
1296 for (i = 0; i <= n; i += 8) {
1297 writePS(" ");
1298 for (j = i; j < i+8 && j <= n; ++j) {
1299 x[0] = j;
1300 colorMap->getColor(x, &rgb);
1301 writePS("%02x%02x%02x",
1302 (int)(255 * rgb.getR() + 0.5),
1303 (int)(255 * rgb.getG() + 0.5),
1304 (int)(255 * rgb.getB() + 0.5));
1305 }
1306 writePS("\n");
1307 }
1308 writePS("> ] setcolorspace\n");
1309 } else {
1310 if (colorSpace->isIndexed()) {
1311 writePS("[/Indexed ");
1312 }
1313 switch (colorSpace->getMode()) {
1314 case colorGray:
1315 writePS("/DeviceGray ");
1316 break;
1317 case colorCMYK:
1318 writePS("/DeviceCMYK ");
1319 break;
1320 case colorRGB:
1321 writePS("/DeviceRGB ");
1322 break;
1323 case colorLab:
1324 labParams = colorSpace->getLabParams();
1325 writePS("[/CIEBasedABC <<\n");
1326 writePS(" /RangeABC [0 100 %g %g %g %g]\n",
1327 labParams->aMin, labParams->aMax,
1328 labParams->bMin, labParams->bMax);
1329 writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
1330 writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
1331 writePS(" /DecodeLMN\n");
1332 writePS(" [{dup 6 29 div ge {dup dup mul mul}\n");
1333 writePS(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
1334 labParams->whiteX);
1335 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
1336 writePS(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
1337 labParams->whiteY);
1338 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
1339 writePS(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n",
1340 labParams->whiteZ);
1341 writePS(" /WhitePoint [%g %g %g]\n",
1342 labParams->whiteX, labParams->whiteY, labParams->whiteZ);
1343 writePS(">>] ");
1344 break;
1345 }
1346 if (colorSpace->isIndexed()) {
1347 n = colorSpace->getIndexHigh();
1348 numComps = colorSpace->getNumColorComps();
1349 writePS("%d <\n", n);
1350 for (i = 0; i <= n; i += 8) {
1351 writePS(" ");
1352 for (j = i; j < i+8 && j <= n; ++j) {
1353 color = colorSpace->getLookupVal(j);
1354 for (k = 0; k < numComps; ++k) {
1355 writePS("%02x", color[k]);
1356 }
1357 }
1358 writePS("\n");
1359 }
1360 writePS("> ] setcolorspace\n");
1361 } else {
1362 writePS("setcolorspace\n");
1363 }
1364 }
1365 }
1366
1367 // image dictionary
1368 writePS("<<\n /ImageType 1\n");
1369
1370 // width, height, matrix, bits per component
1371 writePS(" /Width %d\n", width);
1372 writePS(" /Height %d\n", height);
1373 writePS(" /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height);
1374 writePS(" /BitsPerComponent %d\n",
1375 colorMap ? colorMap->getBits() : 1);
1376
1377 // decode
1378 if (colorMap) {
1379 writePS(" /Decode [");
1380 if (colorMap->getColorSpace()->isSeparation()) {
1381 //~ this is a kludge -- the correct thing would be to output
1382 //~ a separation color space
1383 n = (1 << colorMap->getBits()) - 1;
1384 writePS("%g %g", colorMap->getDecodeLow(0) * n,
1385 colorMap->getDecodeHigh(0) * n);
1386 } else {
1387 numComps = colorMap->getNumPixelComps();
1388 for (i = 0; i < numComps; ++i) {
1389 if (i > 0) {
1390 writePS(" ");
1391 }
1392 writePS("%g %g", colorMap->getDecodeLow(i),
1393 colorMap->getDecodeHigh(i));
1394 }
1395 }
1396 writePS("]\n");
1397 } else {
1398 writePS(" /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1);
1399 }
1400
1401 if (doForm) {
1402
1403 // data source
1404 writePS(" /DataSource <~\n");
1405
1406 // write image data stream, using ASCII85 encode filter
1407 if (inlineImg) {
1408 str = new FixedLengthEncoder(str, len);
1409 }
1410 str = new ASCII85Encoder(str);
1411 str->reset();
1412 while ((c = str->getChar()) != EOF)
1413 fputc(c, f);
1414 fputc('\n', f);
1415 delete str;
1416
1417 // end of image dictionary
1418 writePS(">>\n%s\n", colorMap ? "image" : "imagemask");
1419
1420 } else {
1421
1422 // data source
1423 writePS(" /DataSource currentfile\n");
1424 s = str->getPSFilter(" ");
1425 if (inlineImg || !s) {
1426 useRLE = gTrue;
1427 useA85 = gTrue;
1428 } else {
1429 useRLE = gFalse;
1430 useA85 = str->isBinary();
1431 }
1432 if (useA85)
1433 writePS(" /ASCII85Decode filter\n");
1434 if (useRLE)
1435 writePS(" /RunLengthDecode filter\n");
1436 else
1437 writePS("%s", s->getCString());
1438 if (s)
1439 delete s;
1440
1441 // cut off inline image streams at appropriate length
1442 if (inlineImg)
1443 str = new FixedLengthEncoder(str, len);
1444 else if (!useRLE)
1445 str = str->getBaseStream();
1446
1447 // add RunLengthEncode and ASCII85 encode filters
1448 if (useRLE)
1449 str = new RunLengthEncoder(str);
1450 if (useA85)
1451 str = new ASCII85Encoder(str);
1452
1453 // end of image dictionary
1454 writePS(">>\n");
1455 #if OPI_SUPPORT
1456 if (opi13Nest) {
1457 if (inlineImg) {
1458 // this can't happen -- OPI dictionaries are in XObjects
1459 error(-1, "Internal: OPI in inline image");
1460 n = 0;
1461 } else {
1462 // need to read the stream to count characters -- the length
1463 // is data-dependent (because of A85 and RLE filters)
1464 str->reset();
1465 n = 0;
1466 while ((c = str->getChar()) != EOF) {
1467 ++n;
1468 }
1469 }
1470 // +6/7 for "pdfIm\n" / "pdfImM\n"
1471 // +8 for newline + trailer
1472 n += colorMap ? 14 : 15;
1473 writePS("%%%%BeginData: %d Hex Bytes\n", n);
1474 }
1475 #endif
1476 writePS("%s\n", colorMap ? "pdfIm" : "pdfImM");
1477
1478 // copy the stream data
1479 str->reset();
1480 while ((c = str->getChar()) != EOF)
1481 fputc(c, f);
1482
1483 // add newline and trailer to the end
1484 fputc('\n', f);
1485 fputs("%-EOD-\n", f);
1486 #if OPI_SUPPORT
1487 if (opi13Nest) {
1488 writePS("%%%%EndData\n");
1489 }
1490 #endif
1491
1492 // delete encoders
1493 if (useRLE || useA85)
1494 delete str;
1495 }
1496 }
1497
1498 #if OPI_SUPPORT
1499 void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
1500 Object dict;
1501
1502 if (psOutOPI) {
1503 opiDict->lookup("2.0", &dict);
1504 if (dict.isDict()) {
1505 opiBegin20(state, dict.getDict());
1506 dict.free();
1507 } else {
1508 dict.free();
1509 opiDict->lookup("1.3", &dict);
1510 if (dict.isDict()) {
1511 opiBegin13(state, dict.getDict());
1512 }
1513 dict.free();
1514 }
1515 }
1516 }
1517
1518 void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
1519 Object obj1, obj2, obj3, obj4;
1520 double width, height, left, right, top, bottom;
1521 int w, h;
1522 int i;
1523
1524 writePS("%%%%BeginOPI: 2.0\n");
1525 writePS("%%%%Distilled\n");
1526
1527 dict->lookup("F", &obj1);
1528 if (getFileSpec(&obj1, &obj2)) {
1529 writePS("%%%%ImageFileName: %s\n",
1530 obj2.getString()->getCString());
1531 obj2.free();
1532 }
1533 obj1.free();
1534
1535 dict->lookup("MainImage", &obj1);
1536 if (obj1.isString()) {
1537 writePS("%%%%MainImage: %s\n", obj1.getString()->getCString());
1538 }
1539 obj1.free();
1540
1541 //~ ignoring 'Tags' entry
1542 //~ need to use writePSString() and deal with >255-char lines
1543
1544 dict->lookup("Size", &obj1);
1545 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
1546 obj1.arrayGet(0, &obj2);
1547 width = obj2.getNum();
1548 obj2.free();
1549 obj1.arrayGet(1, &obj2);
1550 height = obj2.getNum();
1551 obj2.free();
1552 writePS("%%%%ImageDimensions: %g %g\n", width, height);
1553 }
1554 obj1.free();
1555
1556 dict->lookup("CropRect", &obj1);
1557 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
1558 obj1.arrayGet(0, &obj2);
1559 left = obj2.getNum();
1560 obj2.free();
1561 obj1.arrayGet(1, &obj2);
1562 top = obj2.getNum();
1563 obj2.free();
1564 obj1.arrayGet(2, &obj2);
1565 right = obj2.getNum();
1566 obj2.free();
1567 obj1.arrayGet(3, &obj2);
1568 bottom = obj2.getNum();
1569 obj2.free();
1570 writePS("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom);
1571 }
1572 obj1.free();
1573
1574 dict->lookup("Overprint", &obj1);
1575 if (obj1.isBool()) {
1576 writePS("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
1577 }
1578 obj1.free();
1579
1580 dict->lookup("Inks", &obj1);
1581 if (obj1.isName()) {
1582 writePS("%%%%ImageInks: %s\n", obj1.getName());
1583 } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
1584 obj1.arrayGet(0, &obj2);
1585 if (obj2.isName()) {
1586 writePS("%%%%ImageInks: %s %d",
1587 obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
1588 for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) {
1589 obj1.arrayGet(i, &obj3);
1590 obj1.arrayGet(i+1, &obj4);
1591 if (obj3.isString() && obj4.isNum()) {
1592 writePS(" ");
1593 writePSString(obj3.getString());
1594 writePS(" %g", obj4.getNum());
1595 }
1596 obj3.free();
1597 obj4.free();
1598 }
1599 writePS("\n");
1600 }
1601 obj2.free();
1602 }
1603 obj1.free();
1604
1605 writePS("gsave\n");
1606
1607 writePS("%%%%BeginIncludedImage\n");
1608
1609 dict->lookup("IncludedImageDimensions", &obj1);
1610 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
1611 obj1.arrayGet(0, &obj2);
1612 w = obj2.getInt();
1613 obj2.free();
1614 obj1.arrayGet(1, &obj2);
1615 h = obj2.getInt();
1616 obj2.free();
1617 writePS("%%%%IncludedImageDimensions: %d %d\n", w, h);
1618 }
1619 obj1.free();
1620
1621 dict->lookup("IncludedImageQuality", &obj1);
1622 if (obj1.isNum()) {
1623 writePS("%%%%IncludedImageQuality: %g\n", obj1.getNum());
1624 }
1625 obj1.free();
1626
1627 ++opi20Nest;
1628 }
1629
1630 void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
1631 Object obj1, obj2;
1632 int left, right, top, bottom, samples, bits, width, height;
1633 double c, m, y, k;
1634 double llx, lly, ulx, uly, urx, ury, lrx, lry;
1635 double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
1636 double horiz, vert;
1637 int i, j;
1638
1639 writePS("save\n");
1640 writePS("/opiMatrix2 matrix currentmatrix def\n");
1641 writePS("opiMatrix setmatrix\n");
1642
1643 dict->lookup("F", &obj1);
1644 if (getFileSpec(&obj1, &obj2)) {
1645 writePS("%%ALDImageFileName: %s\n",
1646 obj2.getString()->getCString());
1647 obj2.free();
1648 }
1649 obj1.free();
1650
1651 dict->lookup("CropRect", &obj1);
1652 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
1653 obj1.arrayGet(0, &obj2);
1654 left = obj2.getInt();
1655 obj2.free();
1656 obj1.arrayGet(1, &obj2);
1657 top = obj2.getInt();
1658 obj2.free();
1659 obj1.arrayGet(2, &obj2);
1660 right = obj2.getInt();
1661 obj2.free();
1662 obj1.arrayGet(3, &obj2);
1663 bottom = obj2.getInt();
1664 obj2.free();
1665 writePS("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom);
1666 }
1667 obj1.free();
1668
1669 dict->lookup("Color", &obj1);
1670 if (obj1.isArray() && obj1.arrayGetLength() == 5) {
1671 obj1.arrayGet(0, &obj2);
1672 c = obj2.getNum();
1673 obj2.free();
1674 obj1.arrayGet(1, &obj2);
1675 m = obj2.getNum();
1676 obj2.free();
1677 obj1.arrayGet(2, &obj2);
1678 y = obj2.getNum();
1679 obj2.free();
1680 obj1.arrayGet(3, &obj2);
1681 k = obj2.getNum();
1682 obj2.free();
1683 obj1.arrayGet(4, &obj2);
1684 if (obj2.isString()) {
1685 writePS("%%ALDImageColor: %g %g %g %g ", c, m, y, k);
1686 writePSString(obj2.getString());
1687 writePS("\n");
1688 }
1689 obj2.free();
1690 }
1691 obj1.free();
1692
1693 dict->lookup("ColorType", &obj1);
1694 if (obj1.isName()) {
1695 writePS("%%ALDImageColorType: %s\n", obj1.getName());
1696 }
1697 obj1.free();
1698
1699 //~ ignores 'Comments' entry
1700 //~ need to handle multiple lines
1701
1702 dict->lookup("CropFixed", &obj1);
1703 if (obj1.isArray()) {
1704 obj1.arrayGet(0, &obj2);
1705 ulx = obj2.getNum();
1706 obj2.free();
1707 obj1.arrayGet(1, &obj2);
1708 uly = obj2.getNum();
1709 obj2.free();
1710 obj1.arrayGet(2, &obj2);
1711 lrx = obj2.getNum();
1712 obj2.free();
1713 obj1.arrayGet(3, &obj2);
1714 lry = obj2.getNum();
1715 obj2.free();
1716 writePS("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry);
1717 }
1718 obj1.free();
1719
1720 dict->lookup("GrayMap", &obj1);
1721 if (obj1.isArray()) {
1722 writePS("%%ALDImageGrayMap:");
1723 for (i = 0; i < obj1.arrayGetLength(); i += 16) {
1724 if (i > 0) {
1725 writePS("\n%%%%+");
1726 }
1727 for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
1728 obj1.arrayGet(i+j, &obj2);
1729 writePS(" %d", obj2.getInt());
1730 obj2.free();
1731 }
1732 }
1733 writePS("\n");
1734 }
1735 obj1.free();
1736
1737 dict->lookup("ID", &obj1);
1738 if (obj1.isString()) {
1739 writePS("%%ALDImageID: %s\n", obj1.getString()->getCString());
1740 }
1741 obj1.free();
1742
1743 dict->lookup("ImageType", &obj1);
1744 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
1745 obj1.arrayGet(0, &obj2);
1746 samples = obj2.getInt();
1747 obj2.free();
1748 obj1.arrayGet(1, &obj2);
1749 bits = obj2.getInt();
1750 obj2.free();
1751 writePS("%%ALDImageType: %d %d\n", samples, bits);
1752 }
1753 obj1.free();
1754
1755 dict->lookup("Overprint", &obj1);
1756 if (obj1.isBool()) {
1757 writePS("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
1758 }
1759 obj1.free();
1760
1761 dict->lookup("Position", &obj1);
1762 if (obj1.isArray() && obj1.arrayGetLength() == 8) {
1763 obj1.arrayGet(0, &obj2);
1764 llx = obj2.getNum();
1765 obj2.free();
1766 obj1.arrayGet(1, &obj2);
1767 lly = obj2.getNum();
1768 obj2.free();
1769 obj1.arrayGet(2, &obj2);
1770 ulx = obj2.getNum();
1771 obj2.free();
1772 obj1.arrayGet(3, &obj2);
1773 uly = obj2.getNum();
1774 obj2.free();
1775 obj1.arrayGet(4, &obj2);
1776 urx = obj2.getNum();
1777 obj2.free();
1778 obj1.arrayGet(5, &obj2);
1779 ury = obj2.getNum();
1780 obj2.free();
1781 obj1.arrayGet(6, &obj2);
1782 lrx = obj2.getNum();
1783 obj2.free();
1784 obj1.arrayGet(7, &obj2);
1785 lry = obj2.getNum();
1786 obj2.free();
1787 opiTransform(state, llx, lly, &tllx, &tlly);
1788 opiTransform(state, ulx, uly, &tulx, &tuly);
1789 opiTransform(state, urx, ury, &turx, &tury);
1790 opiTransform(state, lrx, lry, &tlrx, &tlry);
1791 writePS("%%ALDImagePosition: %g %g %g %g %g %g %g %g\n",
1792 tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
1793 obj2.free();
1794 }
1795 obj1.free();
1796
1797 dict->lookup("Resolution", &obj1);
1798 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
1799 obj1.arrayGet(0, &obj2);
1800 horiz = obj2.getNum();
1801 obj2.free();
1802 obj1.arrayGet(1, &obj2);
1803 vert = obj2.getNum();
1804 obj2.free();
1805 writePS("%%ALDImageResoution: %g %g\n", horiz, vert);
1806 obj2.free();
1807 }
1808 obj1.free();
1809
1810 dict->lookup("Size", &obj1);
1811 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
1812 obj1.arrayGet(0, &obj2);
1813 width = obj2.getInt();
1814 obj2.free();
1815 obj1.arrayGet(1, &obj2);
1816 height = obj2.getInt();
1817 obj2.free();
1818 writePS("%%ALDImageSize: %d %d\n", width, height);
1819 }
1820 obj1.free();
1821
1822 //~ ignoring 'Tags' entry
1823 //~ need to use writePSString() and deal with >255-char lines
1824
1825 dict->lookup("Tint", &obj1);
1826 if (obj1.isNum()) {
1827 writePS("%%ALDImageTint: %g\n", obj1.getNum());
1828 }
1829 obj1.free();
1830
1831 dict->lookup("Transparency", &obj1);
1832 if (obj1.isBool()) {
1833 writePS("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false");
1834 }
1835 obj1.free();
1836
1837 writePS("%%%%BeginObject: image\n");
1838 writePS("opiMatrix2 setmatrix\n");
1839 ++opi13Nest;
1840 }
1841
1842 // Convert PDF user space coordinates to PostScript default user space
1843 // coordinates. This has to account for both the PDF CTM and the
1844 // PSOutputDev page-fitting transform.
1845 void PSOutputDev::opiTransform(GfxState *state, double x0, double y0,
1846 double *x1, double *y1) {
1847 double t;
1848
1849 state->transform(x0, y0, x1, y1);
1850 *x1 += tx;
1851 *y1 += ty;
1852 if (landscape) {
1853 t = *x1;
1854 *x1 = -*y1;
1855 *y1 = t;
1856 }
1857 *x1 *= xScale;
1858 *y1 *= yScale;
1859 }
1860
1861 void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
1862 Object dict;
1863
1864 if (psOutOPI) {
1865 opiDict->lookup("2.0", &dict);
1866 if (dict.isDict()) {
1867 writePS("%%%%EndIncludedImage\n");
1868 writePS("%%%%EndOPI\n");
1869 writePS("grestore\n");
1870 --opi20Nest;
1871 dict.free();
1872 } else {
1873 dict.free();
1874 opiDict->lookup("1.3", &dict);
1875 if (dict.isDict()) {
1876 writePS("%%%%EndObject\n");
1877 writePS("restore\n");
1878 --opi13Nest;
1879 }
1880 dict.free();
1881 }
1882 }
1883 }
1884
1885 GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
1886 if (fileSpec->isString()) {
1887 fileSpec->copy(fileName);
1888 return gTrue;
1889 }
1890 if (fileSpec->isDict()) {
1891 fileSpec->dictLookup("DOS", fileName);
1892 if (fileName->isString()) {
1893 return gTrue;
1894 }
1895 fileName->free();
1896 fileSpec->dictLookup("Mac", fileName);
1897 if (fileName->isString()) {
1898 return gTrue;
1899 }
1900 fileName->free();
1901 fileSpec->dictLookup("Unix", fileName);
1902 if (fileName->isString()) {
1903 return gTrue;
1904 }
1905 fileName->free();
1906 fileSpec->dictLookup("F", fileName);
1907 if (fileName->isString()) {
1908 return gTrue;
1909 }
1910 fileName->free();
1911 }
1912 return gFalse;
1913 }
1914 #endif // OPI_SUPPORT
1915
1916 void PSOutputDev::writePS(const char *fmt, ...) {
1917 va_list args;
1918
1919 va_start(args, fmt);
1920 vfprintf(f, fmt, args);
1921 va_end(args);
1922 }
1923
1924 void PSOutputDev::writePSString(GString *s) {
1925 Guchar *p;
1926 int n;
1927
1928 fputc('(', f);
1929 for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
1930 if (*p == '(' || *p == ')' || *p == '\\')
1931 fprintf(f, "\\%c", *p);
1932 else if (*p < 0x20 || *p >= 0x80)
1933 fprintf(f, "\\%03o", *p);
1934 else
1935 fputc(*p, f);
1936 }
1937 fputc(')', f);
1938 }