]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/PSOutputDev.cc
embryonic PDF viewer as a Bonobo container...
[evince.git] / pdf / xpdf / PSOutputDev.cc
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 "PSOutputDev.h"
29
30 //------------------------------------------------------------------------
31 // Parameters
32 //------------------------------------------------------------------------
33
34 // Generate Level 1 PostScript?
35 GBool psOutLevel1 = gFalse;
36
37 int paperWidth = defPaperWidth;
38 int paperHeight = defPaperHeight;
39
40 //------------------------------------------------------------------------
41 // PostScript prolog and setup
42 //------------------------------------------------------------------------
43
44 static char *prolog[] = {
45   "/xpdf 75 dict def xpdf begin",
46   "% PDF special state",
47   "/pdfDictSize 14 def",
48   "/pdfSetup {",
49   "  2 array astore",
50   "  /setpagedevice where {",
51   "    pop 3 dict dup begin",
52   "      exch /PageSize exch def",
53   "      /ImagingBBox null def",
54   "      /Policies 1 dict dup begin /PageSize 3 def end def",
55   "    end setpagedevice",
56   "  } {",
57   "    pop",
58   "  } ifelse",
59   "} def",
60   "/pdfStartPage {",
61   "  pdfDictSize dict begin",
62   "  /pdfFill [0] def",
63   "  /pdfStroke [0] def",
64   "  /pdfLastFill false def",
65   "  /pdfLastStroke false def",
66   "  /pdfTextMat [1 0 0 1 0 0] def",
67   "  /pdfFontSize 0 def",
68   "  /pdfCharSpacing 0 def",
69   "  /pdfTextRender 0 def",
70   "  /pdfTextRise 0 def",
71   "  /pdfWordSpacing 0 def",
72   "  /pdfHorizScaling 1 def",
73   "} def",
74   "/pdfEndPage { end } def",
75   "/sCol { pdfLastStroke not {",
76   "          pdfStroke aload length",
77   "          1 eq { setgray } { setrgbcolor} ifelse",
78   "          /pdfLastStroke true def /pdfLastFill false def",
79   "        } if } def",
80   "/fCol { pdfLastFill not {",
81   "          pdfFill aload length",
82   "          1 eq { setgray } { setrgbcolor } ifelse",
83   "          /pdfLastFill true def /pdfLastStroke false def",
84   "        } if } def",
85   "% build a font",
86   "/pdfMakeFont {",
87   "  3 2 roll findfont",
88   "  3 2 roll 1 matrix scale makefont",
89   "  dup length dict begin",
90   "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
91   "    /Encoding exch def",
92   "    currentdict",
93   "  end",
94   "  definefont pop",
95   "} def",
96   "% graphics state operators",
97   "/q { gsave pdfDictSize dict begin } def",
98   "/Q { end grestore } def",
99   "/cm { concat } def",
100   "/d { setdash } def",
101   "/i { setflat } def",
102   "/j { setlinejoin } def",
103   "/J { setlinecap } def",
104   "/M { setmiterlimit } def",
105   "/w { setlinewidth } def",
106   "% color operators",
107   "/g { dup 1 array astore /pdfFill exch def setgray",
108   "    /pdfLastFill true def /pdfLastStroke false def } def",
109   "/G { dup 1 array astore /pdfStroke exch def setgray",
110   "     /pdfLastStroke true def /pdfLastFill false def } def",
111   "/rg { 3 copy 3 array astore /pdfFill exch def setrgbcolor",
112   "     /pdfLastFill true def /pdfLastStroke false def } def",
113   "/RG { 3 copy 3 array astore /pdfStroke exch def setrgbcolor",
114   "     /pdfLastStroke true def /pdfLastFill false def } def",
115   "% path segment operators",
116   "/m { moveto } def",
117   "/l { lineto } def",
118   "/c { curveto } def",
119   "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
120   "      neg 0 rlineto closepath } def",
121   "% path painting operators",
122   "/S { sCol stroke } def",
123   "/f { fCol fill } def",
124   "/f* { fCol eofill } def",
125   "% clipping operators",
126   "/W { clip newpath } def",
127   "/W* { eoclip newpath } def",
128   "% text state operators",
129   "/Tc { /pdfCharSpacing exch def } def",
130   "/Tf { dup /pdfFontSize exch def",
131   "      dup pdfHorizScaling mul exch matrix scale",
132   "      pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
133   "      exch findfont exch makefont setfont } def",
134   "/Tr { /pdfTextRender exch def } def",
135   "/Ts { /pdfTextRise exch def } def",
136   "/Tw { /pdfWordSpacing exch def } def",
137   "/Tz { /pdfHorizScaling exch def } def",
138   "% text positioning operators",
139   "/Td { pdfTextMat transform moveto } def",
140   "/Tm { /pdfTextMat exch def } def",
141   "% text string operators",
142   "/Tj { pdfTextRender 1 and 0 eq { fCol } { sCol } ifelse",
143   "      0 pdfTextRise pdfTextMat dtransform rmoveto",
144   "      pdfFontSize mul pdfHorizScaling mul",
145   "      1 index stringwidth pdfTextMat idtransform pop",
146   "      sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",
147   "      pdfWordSpacing 0 pdfTextMat dtransform 32",
148   "      4 3 roll pdfCharSpacing add 0 pdfTextMat dtransform",
149   "      6 5 roll awidthshow",
150   "      0 pdfTextRise neg pdfTextMat dtransform rmoveto } def",
151   "/TJm { pdfFontSize 0.001 mul mul neg 0",
152   "       pdfTextMat dtransform rmoveto } def",
153   "% Level 1 image operators",
154   "/pdfIm1 {",
155   "  /pdfImBuf1 4 index string def",
156   "  { currentfile pdfImBuf1 readhexstring pop } image",
157   "} def",
158   "/pdfImM1 {",
159   "  /pdfImBuf1 4 index 7 add 8 idiv string def",
160   "  { currentfile pdfImBuf1 readhexstring pop } imagemask",
161   "} def",
162   "% Level 2 image operators",
163   "/pdfImBuf 100 string def",
164   "/pdfIm {",
165   "  image",
166   "  { currentfile pdfImBuf readline",
167   "    not { pop exit } if",
168   "    (%-EOD-) eq { exit } if } loop",
169   "} def",
170   "/pdfImM {",
171   "  fCol imagemask",
172   "  { currentfile pdfImBuf readline",
173   "    not { pop exit } if",
174   "    (%-EOD-) eq { exit } if } loop",
175   "} def",
176   "end",
177   NULL
178 };
179
180 //------------------------------------------------------------------------
181 // Fonts
182 //------------------------------------------------------------------------
183
184 struct PSFont {
185   char *name;                   // PDF name
186   char *psName;                 // PostScript name
187 };
188
189 struct PSSubstFont {
190   char *psName;                 // PostScript name
191   double mWidth;                // width of 'm' character
192 };
193
194 static PSFont psFonts[] = {
195   {"Courier",               "Courier"},
196   {"Courier-Bold",          "Courier-Bold"},
197   {"Courier-Oblique",       "Courier-Bold"},
198   {"Courier-BoldOblique",   "Courier-BoldOblique"},
199   {"Helvetica",             "Helvetica"},
200   {"Helvetica-Bold",        "Helvetica-Bold"},
201   {"Helvetica-Oblique",     "Helvetica-Oblique"},
202   {"Helvetica-BoldOblique", "Helvetica-BoldOblique"},
203   {"Symbol",                "Symbol"},
204   {"Times-Roman",           "Times-Roman"},
205   {"Times-Bold",            "Times-Bold"},
206   {"Times-Italic",          "Times-Italic"},
207   {"Times-BoldItalic",      "Times-BoldItalic"},
208   {"ZapfDingbats",          "ZapfDingbats"},
209   {NULL}
210 };
211
212 static PSSubstFont psSubstFonts[] = {
213   {"Helvetica",             0.833},
214   {"Helvetica-Oblique",     0.833},
215   {"Helvetica-Bold",        0.889},
216   {"Helvetica-BoldOblique", 0.889},
217   {"Times-Roman",           0.788},
218   {"Times-Italic",          0.722},
219   {"Times-Bold",            0.833},
220   {"Times-BoldItalic",      0.778},
221   {"Courier",               0.600},
222   {"Courier-Oblique",       0.600},
223   {"Courier-Bold",          0.600},
224   {"Courier-BoldOblique",   0.600}
225 };
226
227 //------------------------------------------------------------------------
228 // PSOutputDev
229 //------------------------------------------------------------------------
230
231 PSOutputDev::PSOutputDev(char *fileName, Catalog *catalog,
232                          int firstPage, int lastPage,
233                          GBool embedType11, GBool doForm1) {
234   Page *page;
235   Dict *resDict;
236   char **p;
237   int pg;
238
239   // initialize
240   embedType1 = embedType11;
241   doForm = doForm1;
242   fontIDs = NULL;
243   fontFileIDs = NULL;
244   fontFileNames = NULL;
245   f = NULL;
246   if (doForm)
247     lastPage = firstPage;
248
249   // open file or pipe
250   ok = gTrue;
251   if (!strcmp(fileName, "-")) {
252     fileType = psStdout;
253     f = stdout;
254   } else if (fileName[0] == '|') {
255     fileType = psPipe;
256 #ifdef HAVE_POPEN
257 #ifndef WIN32
258     signal(SIGPIPE, (void (*)(int))SIG_IGN);
259 #endif
260     if (!(f = popen(fileName + 1, "w"))) {
261       error(-1, "Couldn't run print command '%s'", fileName);
262       ok = gFalse;
263       return;
264     }
265 #else
266     error(-1, "Print commands are not supported ('%s')", fileName);
267     ok = gFalse;
268     return;
269 #endif
270   } else {
271     fileType = psFile;
272     if (!(f = fopen(fileName, "w"))) {
273       error(-1, "Couldn't open PostScript file '%s'", fileName);
274       ok = gFalse;
275       return;
276     }
277   }
278
279   // initialize fontIDs, fontFileIDs, and fontFileNames lists
280   fontIDSize = 64;
281   fontIDLen = 0;
282   fontIDs = (Ref *)gmalloc(fontIDSize * sizeof(Ref));
283   fontFileIDSize = 64;
284   fontFileIDLen = 0;
285   fontFileIDs = (Ref *)gmalloc(fontFileIDSize * sizeof(Ref));
286   fontFileNameSize = 64;
287   fontFileNameLen = 0;
288   fontFileNames = (char **)gmalloc(fontFileNameSize * sizeof(char *));
289
290   // write header
291   if (doForm) {
292     writePS("%%!PS-Adobe-3.0 Resource-Form\n");
293     writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
294     writePS("%%%%EndComments\n");
295   } else {
296     writePS("%%!PS-Adobe-3.0\n");
297     writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
298     writePS("%%%%Pages: %d\n", lastPage - firstPage + 1);
299     writePS("%%%%EndComments\n");
300   }
301
302   // write prolog
303   if (!doForm)
304     writePS("%%%%BeginProlog\n");
305   writePS("%%%%BeginResource: xpdf %s\n", xpdfVersion);
306   for (p = prolog; *p; ++p)
307     writePS("%s\n", *p);
308   writePS("%%%%EndResource\n");
309   if (!doForm)
310     writePS("%%%%EndProlog\n");
311
312   // set up fonts
313   if (!doForm)
314     writePS("%%%%BeginSetup\n");
315   writePS("xpdf begin\n");
316   for (pg = firstPage; pg <= lastPage; ++pg) {
317     if ((resDict = catalog->getPage(pg)->getResourceDict()))
318       setupFonts(resDict);
319   }
320   if (doForm) {
321     writePS("end\n");
322   } else {
323     writePS("%d %d pdfSetup\n", paperWidth, paperHeight);
324     writePS("%%%%EndSetup\n");
325   }
326
327   // write form header
328   if (doForm) {
329     page = catalog->getPage(firstPage);
330     writePS("4 dict dup begin\n");
331     writePS("/BBox [%d %d %d %d] def\n",
332             (int)page->getX1(), (int)page->getY1(),
333             (int)page->getX2(), (int)page->getY2());
334     writePS("/FormType 1 def\n");
335     writePS("/Matrix [1 0 0 1 0 0] def\n");
336   }
337
338   // initialize sequential page number
339   seqPage = 1;
340 }
341
342 PSOutputDev::~PSOutputDev() {
343   if (f) {
344     if (doForm) {
345       writePS("end\n");
346       writePS("/Foo exch /Form defineresource pop\n");
347     } else {
348       writePS("%%%%Trailer\n");
349       writePS("end\n");
350       writePS("%%%%EOF\n");
351     }
352     if (fileType == psFile) {
353       fclose(f);
354     }
355 #ifdef HAVE_POPEN
356     else if (fileType == psPipe) {
357       pclose(f);
358 #ifndef WIN32
359       signal(SIGPIPE, (void (*)(int))SIG_DFL);
360 #endif
361     }
362 #endif
363   }
364   if (fontIDs)
365     gfree(fontIDs);
366   if (fontFileIDs)
367     gfree(fontFileIDs);
368   if (fontFileNames)
369     gfree(fontFileNames);
370 }
371
372 void PSOutputDev::setupFonts(Dict *resDict) {
373   Object fontDict, xObjDict, xObj, resObj;
374   GfxFontDict *gfxFontDict;
375   GfxFont *font;
376   int i;
377
378   resDict->lookup("Font", &fontDict);
379   if (fontDict.isDict()) {
380     gfxFontDict = new GfxFontDict(fontDict.getDict());
381     for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
382       font = gfxFontDict->getFont(i);
383       setupFont(font);
384     }
385     delete gfxFontDict;
386   }
387   fontDict.free();
388
389   resDict->lookup("XObject", &xObjDict);
390   if (xObjDict.isDict()) {
391     for (i = 0; i < xObjDict.dictGetLength(); ++i) {
392       xObjDict.dictGetVal(i, &xObj);
393       if (xObj.isStream()) {
394         xObj.streamGetDict()->lookup("Resources", &resObj);
395         if (resObj.isDict())
396           setupFonts(resObj.getDict());
397         resObj.free();
398       }
399       xObj.free();
400     }
401   }
402   xObjDict.free();
403 }
404
405 void PSOutputDev::setupFont(GfxFont *font) {
406   Ref fontFileID;
407   GString *name;
408   char *psName;
409   char *charName;
410   double scale;
411   int i, j;
412
413   // check if font is already set up
414   for (i = 0; i < fontIDLen; ++i) {
415     if (fontIDs[i].num == font->getID().num &&
416         fontIDs[i].gen == font->getID().gen)
417       return;
418   }
419
420   // add entry to fontIDs list
421   if (fontIDLen >= fontIDSize) {
422     fontIDSize += 64;
423     fontIDs = (Ref *)grealloc(fontIDs, fontIDSize * sizeof(Ref));
424   }
425   fontIDs[fontIDLen++] = font->getID();
426
427   // check for embedded Type 1 font
428   if (embedType1 && font->getType() == fontType1 &&
429       font->getEmbeddedFontID(&fontFileID)) {
430     setupEmbeddedType1Font(&fontFileID);
431     psName = font->getEmbeddedFontName();
432     scale = 1;
433
434   // check for external Type 1 font file
435   } else if (embedType1 && font->getType() == fontType1 &&
436              font->getExtFontFile()) {
437     setupEmbeddedType1Font(font->getExtFontFile());
438     // this assumes that the PS font name matches the PDF font name
439     psName = font->getName()->getCString();
440     scale = 1;
441
442   // check for embedded Type 1C font
443   } else if (embedType1 && font->getType() == fontType1C &&
444              font->getEmbeddedFontID(&fontFileID)) {
445     setupEmbeddedType1CFont(font, &fontFileID);
446     psName = font->getEmbeddedFontName();
447     scale = 1;
448
449   // do font substitution
450   } else {
451     name = font->getName();
452     psName = NULL;
453     scale = 1.0;
454     if (name) {
455       for (i = 0; psFonts[i].name; ++i) {
456         if (name->cmp(psFonts[i].name) == 0) {
457           psName = psFonts[i].psName;
458           break;
459         }
460       }
461     }
462     if (!psName) {
463       if (font->isFixedWidth())
464         i = 8;
465       else if (font->isSerif())
466         i = 4;
467       else
468         i = 0;
469       if (font->isBold())
470         i += 2;
471       if (font->isItalic())
472         i += 1;
473       psName = psSubstFonts[i].psName;
474       scale = font->getWidth('m') / psSubstFonts[i].mWidth;
475       if (scale < 0.1)
476         scale = 1;
477     }
478   }
479
480   // generate PostScript code to set up the font
481   writePS("/F%d_%d /%s %g\n",
482           font->getID().num, font->getID().gen, psName, scale);
483   for (i = 0; i < 256; i += 8) {
484     writePS((i == 0) ? "[ " : "  ");
485     for (j = 0; j < 8; ++j) {
486       charName = font->getCharName(i+j);
487       writePS("/%s", charName ? charName : ".notdef");
488     }
489     writePS((i == 256-8) ? "]\n" : "\n");
490   }
491   writePS("pdfMakeFont\n");
492 }
493
494 void PSOutputDev::setupEmbeddedType1Font(Ref *id) {
495   static char hexChar[17] = "0123456789abcdef";
496   Object refObj, strObj, obj1, obj2;
497   Dict *dict;
498   int length1, length2;
499   int c;
500   int start[4];
501   GBool binMode;
502   int i;
503
504   // check if font is already embedded
505   for (i = 0; i < fontFileIDLen; ++i) {
506     if (fontFileIDs[i].num == id->num &&
507         fontFileIDs[i].gen == id->gen)
508       return;
509   }
510
511   // add entry to fontFileIDs list
512   if (fontFileIDLen >= fontFileIDSize) {
513     fontFileIDSize += 64;
514     fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
515   }
516   fontFileIDs[fontFileIDLen++] = *id;
517
518   // get the font stream and info
519   refObj.initRef(id->num, id->gen);
520   refObj.fetch(&strObj);
521   refObj.free();
522   if (!strObj.isStream()) {
523     error(-1, "Embedded font file object is not a stream");
524     goto err1;
525   }
526   if (!(dict = strObj.streamGetDict())) {
527     error(-1, "Embedded font stream is missing its dictionary");
528     goto err1;
529   }
530   dict->lookup("Length1", &obj1);
531   dict->lookup("Length2", &obj2);
532   if (!obj1.isInt() || !obj2.isInt()) {
533     error(-1, "Missing length fields in embedded font stream dictionary");
534     obj1.free();
535     obj2.free();
536     goto err1;
537   }
538   length1 = obj1.getInt();
539   length2 = obj2.getInt();
540   obj1.free();
541   obj2.free();
542
543   // copy ASCII portion of font
544   strObj.streamReset();
545   for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i)
546     fputc(c, f);
547
548   // figure out if encrypted portion is binary or ASCII
549   binMode = gFalse;
550   for (i = 0; i < 4; ++i) {
551     start[i] = strObj.streamGetChar();
552     if (start[i] == EOF) {
553       error(-1, "Unexpected end of file in embedded font stream");
554       goto err1;
555     }
556     if (!((start[i] >= '0' && start[i] <= '9') ||
557           (start[i] >= 'A' && start[i] <= 'F') ||
558           (start[i] >= 'a' && start[i] <= 'f')))
559       binMode = gTrue;
560   }
561
562   // convert binary data to ASCII
563   if (binMode) {
564     for (i = 0; i < 4; ++i) {
565       fputc(hexChar[(start[i] >> 4) & 0x0f], f);
566       fputc(hexChar[start[i] & 0x0f], f);
567     }
568     while (i < length2) {
569       if ((c = strObj.streamGetChar()) == EOF)
570         break;
571       fputc(hexChar[(c >> 4) & 0x0f], f);
572       fputc(hexChar[c & 0x0f], f);
573       if (++i % 32 == 0)
574         fputc('\n', f);
575     }
576     if (i % 32 > 0)
577       fputc('\n', f);
578
579   // already in ASCII format -- just copy it
580   } else {
581     for (i = 0; i < 4; ++i)
582       fputc(start[i], f);
583     for (i = 4; i < length2; ++i) {
584       if ((c = strObj.streamGetChar()) == EOF)
585         break;
586       fputc(c, f);
587     }
588   }
589
590   // write padding and "cleartomark"
591   for (i = 0; i < 8; ++i)
592     writePS("00000000000000000000000000000000"
593             "00000000000000000000000000000000\n");
594   writePS("cleartomark\n");
595
596  err1:
597   strObj.free();
598 }
599
600 //~ This doesn't handle .pfb files or binary eexec data (which only
601 //~ happens in pfb files?).
602 void PSOutputDev::setupEmbeddedType1Font(char *fileName) {
603   FILE *fontFile;
604   int c;
605   int i;
606
607   // check if font is already embedded
608   for (i = 0; i < fontFileNameLen; ++i) {
609     if (!strcmp(fontFileNames[i], fileName))
610       return;
611   }
612
613   // add entry to fontFileNames list
614   if (fontFileNameLen >= fontFileNameSize) {
615     fontFileNameSize += 64;
616     fontFileNames = (char **)grealloc(fontFileNames,
617                                       fontFileNameSize * sizeof(char *));
618   }
619   fontFileNames[fontFileNameLen++] = fileName;
620
621   // copy the font file
622   if (!(fontFile = fopen(fileName, "rb"))) {
623     error(-1, "Couldn't open external font file");
624     return;
625   }
626   while ((c = fgetc(fontFile)) != EOF)
627     fputc(c, f);
628   fclose(fontFile);
629 }
630
631 void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id) {
632   char *fontBuf;
633   int fontLen;
634   Type1CFontConverter *cvt;
635   int i;
636
637   // check if font is already embedded
638   for (i = 0; i < fontFileIDLen; ++i) {
639     if (fontFileIDs[i].num == id->num &&
640         fontFileIDs[i].gen == id->gen)
641       return;
642   }
643
644   // add entry to fontFileIDs list
645   if (fontFileIDLen >= fontFileIDSize) {
646     fontFileIDSize += 64;
647     fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
648   }
649   fontFileIDs[fontFileIDLen++] = *id;
650
651   // convert it to a Type 1 font
652   fontBuf = font->readEmbFontFile(&fontLen);
653   cvt = new Type1CFontConverter(fontBuf, fontLen, f);
654   cvt->convert();
655   delete cvt;
656   gfree(fontBuf);
657 }
658
659 void PSOutputDev::startPage(int pageNum, GfxState *state) {
660   int x1, y1, x2, y2, width, height, t;
661   double xScale, yScale;
662
663   if (doForm) {
664
665     writePS("/PaintProc {\n");
666     writePS("begin xpdf begin\n");
667     writePS("pdfSetup\n");
668
669   } else {
670
671     writePS("%%%%Page: %d %d\n", pageNum, seqPage);
672     writePS("%%%%BeginPageSetup\n");
673
674     // rotate, translate, and scale page
675     x1 = (int)(state->getX1() + 0.5);
676     y1 = (int)(state->getY1() + 0.5);
677     x2 = (int)(state->getX2() + 0.5);
678     y2 = (int)(state->getY2() + 0.5);
679     width = x2 - x1;
680     height = y2 - y1;
681     if (width > height) {
682       writePS("%%%%PageOrientation: Landscape\n");
683       writePS("pdfStartPage\n");
684       writePS("90 rotate\n");
685       writePS("%d %d translate\n", -x1, -(y1 + paperWidth));
686       t = width;
687       width = height;
688       height = t;
689     } else {
690       writePS("%%%%PageOrientation: Portrait\n");
691       writePS("pdfStartPage\n");
692       if (x1 != 0 || y1 != 0)
693         writePS("%d %d translate\n", -x1, -y1);
694     }
695     if (width > paperWidth || height > paperHeight) {
696       xScale = (double)paperWidth / (double)width;
697       yScale = (double)paperHeight / (double)height;
698       if (yScale < xScale)
699         xScale = yScale;
700       writePS("%0.4f %0.4f scale\n", xScale, xScale);
701     }
702
703     writePS("%%%%EndPageSetup\n");
704     ++seqPage;
705   }
706 }
707
708 void PSOutputDev::endPage() {
709   if (doForm) {
710     writePS("pdfEndPage\n");
711     writePS("end end\n");
712     writePS("} def\n");
713   } else {
714     writePS("showpage\n");
715     writePS("%%%%PageTrailer\n");
716     writePS("pdfEndPage\n");
717   }
718 }
719
720 void PSOutputDev::saveState(GfxState *state) {
721   writePS("q\n");
722 }
723
724 void PSOutputDev::restoreState(GfxState *state) {
725   writePS("Q\n");
726 }
727
728 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
729                             double m21, double m22, double m31, double m32) {
730   writePS("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32);
731 }
732
733 void PSOutputDev::updateLineDash(GfxState *state) {
734   double *dash;
735   double start;
736   int length, i;
737
738   state->getLineDash(&dash, &length, &start);
739   writePS("[");
740   for (i = 0; i < length; ++i)
741     writePS("%g%s", dash[i], (i == length-1) ? "" : " ");
742   writePS("] %g d\n", start);
743 }
744
745 void PSOutputDev::updateFlatness(GfxState *state) {
746   writePS("%d i\n", state->getFlatness());
747 }
748
749 void PSOutputDev::updateLineJoin(GfxState *state) {
750   writePS("%d j\n", state->getLineJoin());
751 }
752
753 void PSOutputDev::updateLineCap(GfxState *state) {
754   writePS("%d J\n", state->getLineCap());
755 }
756
757 void PSOutputDev::updateMiterLimit(GfxState *state) {
758   writePS("%g M\n", state->getMiterLimit());
759 }
760
761 void PSOutputDev::updateLineWidth(GfxState *state) {
762   writePS("%g w\n", state->getLineWidth());
763 }
764
765 void PSOutputDev::updateFillColor(GfxState *state) {
766   GfxColor *color;
767   double r, g, b;
768
769   color = state->getFillColor();
770   r = color->getR();
771   g = color->getG();
772   b = color->getB();
773   if (r == g && g == b)
774     writePS("%g g\n", r);
775   else
776     writePS("%g %g %g rg\n", r, g, b);
777 }
778
779 void PSOutputDev::updateStrokeColor(GfxState *state) {
780   GfxColor *color;
781   double r, g, b;
782
783   color = state->getStrokeColor();
784   r = color->getR();
785   g = color->getG();
786   b = color->getB();
787   if (r == g && g == b)
788     writePS("%g G\n", r);
789   else
790     writePS("%g %g %g RG\n", r, g, b);
791 }
792
793 void PSOutputDev::updateFont(GfxState *state) {
794   if (state->getFont()) {
795     writePS("/F%d_%d %g Tf\n",
796             state->getFont()->getID().num, state->getFont()->getID().gen,
797             state->getFontSize());
798   }
799 }
800
801 void PSOutputDev::updateTextMat(GfxState *state) {
802   double *mat;
803
804   mat = state->getTextMat();
805   writePS("[%g %g %g %g %g %g] Tm\n",
806           mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
807 }
808
809 void PSOutputDev::updateCharSpace(GfxState *state) {
810   writePS("%g Tc\n", state->getCharSpace());
811 }
812
813 void PSOutputDev::updateRender(GfxState *state) {
814   writePS("%d Tr\n", state->getRender());
815 }
816
817 void PSOutputDev::updateRise(GfxState *state) {
818   writePS("%g Ts\n", state->getRise());
819 }
820
821 void PSOutputDev::updateWordSpace(GfxState *state) {
822   writePS("%g Tw\n", state->getWordSpace());
823 }
824
825 void PSOutputDev::updateHorizScaling(GfxState *state) {
826   writePS("%g Tz\n", state->getHorizScaling());
827 }
828
829 void PSOutputDev::updateTextPos(GfxState *state) {
830   writePS("%g %g Td\n", state->getLineX(), state->getLineY());
831 }
832
833 void PSOutputDev::updateTextShift(GfxState *state, double shift) {
834   writePS("%g TJm\n", shift);
835 }
836
837 void PSOutputDev::stroke(GfxState *state) {
838   doPath(state->getPath());
839   writePS("S\n");
840 }
841
842 void PSOutputDev::fill(GfxState *state) {
843   doPath(state->getPath());
844   writePS("f\n");
845 }
846
847 void PSOutputDev::eoFill(GfxState *state) {
848   doPath(state->getPath());
849   writePS("f*\n");
850 }
851
852 void PSOutputDev::clip(GfxState *state) {
853   doPath(state->getPath());
854   writePS("W\n");
855 }
856
857 void PSOutputDev::eoClip(GfxState *state) {
858   doPath(state->getPath());
859   writePS("W*\n");
860 }
861
862 void PSOutputDev::doPath(GfxPath *path) {
863   GfxSubpath *subpath;
864   double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
865   int n, m, i, j;
866
867   n = path->getNumSubpaths();
868
869   if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
870     subpath = path->getSubpath(0);
871     x0 = subpath->getX(0);
872     y0 = subpath->getY(0);
873     x4 = subpath->getX(4);
874     y4 = subpath->getY(4);
875     if (x4 == x0 && y4 == y0) {
876       x1 = subpath->getX(1);
877       y1 = subpath->getY(1);
878       x2 = subpath->getX(2);
879       y2 = subpath->getY(2);
880       x3 = subpath->getX(3);
881       y3 = subpath->getY(3);
882       if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
883         writePS("%g %g %g %g re\n",
884                 x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
885                 fabs(x2 - x0), fabs(y1 - y0));
886         return;
887       } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
888         writePS("%g %g %g %g re\n",
889                 x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
890                 fabs(x1 - x0), fabs(y2 - y0));
891         return;
892       }
893     }
894   }
895
896   for (i = 0; i < n; ++i) {
897     subpath = path->getSubpath(i);
898     m = subpath->getNumPoints();
899     writePS("%g %g m\n", subpath->getX(0), subpath->getY(0));
900     j = 1;
901     while (j < m) {
902       if (subpath->getCurve(j)) {
903         writePS("%g %g %g %g %g %g c\n", subpath->getX(j), subpath->getY(j),
904                 subpath->getX(j+1), subpath->getY(j+1),
905                 subpath->getX(j+2), subpath->getY(j+2));
906         j += 3;
907       } else {
908         writePS("%g %g l\n", subpath->getX(j), subpath->getY(j));
909         ++j;
910       }
911     }
912   }
913 }
914
915 void PSOutputDev::drawString(GfxState *state, GString *s) {
916   // check for invisible text -- this is used by Acrobat Capture
917   if ((state->getRender() & 3) == 3)
918     return;
919
920   writePSString(s);
921   writePS(" %g Tj\n", state->getFont()->getWidth(s));
922 }
923
924 void PSOutputDev::drawImageMask(GfxState *state, Stream *str,
925                                 int width, int height, GBool invert,
926                                 GBool inlineImg) {
927   int len;
928
929   len = height * ((width + 7) / 8);
930   if (psOutLevel1)
931     doImageL1(NULL, invert, inlineImg, str, width, height, len);
932   else
933     doImage(NULL, invert, inlineImg, str, width, height, len);
934 }
935
936 void PSOutputDev::drawImage(GfxState *state, Stream *str, int width,
937                             int height, GfxImageColorMap *colorMap,
938                             GBool inlineImg) {
939   int len;
940
941   len = height * ((width * colorMap->getNumPixelComps() *
942                    colorMap->getBits() + 7) / 8);
943   if (psOutLevel1)
944     doImageL1(colorMap, gFalse, inlineImg, str, width, height, len);
945   else
946     doImage(colorMap, gFalse, inlineImg, str, width, height, len);
947 }
948
949 void PSOutputDev::doImageL1(GfxImageColorMap *colorMap,
950                             GBool invert, GBool inlineImg,
951                             Stream *str, int width, int height, int len) {
952   ImageStream *imgStr;
953   Guchar pixBuf[4];
954   GfxColor color;
955   int x, y, i;
956
957   // width, height, matrix, bits per component
958   if (colorMap) {
959     writePS("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n",
960             width, height,
961             width, -height, height);
962   } else {
963     writePS("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n",
964             width, height, invert ? "true" : "false",
965             width, -height, height);
966   }
967
968   // image
969   if (colorMap) {
970
971     // set up to process the data stream
972     imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
973                              colorMap->getBits());
974     imgStr->reset();
975
976     // process the data stream
977     i = 0;
978     for (y = 0; y < height; ++y) {
979
980       // write the line
981       for (x = 0; x < width; ++x) {
982         imgStr->getPixel(pixBuf);
983         colorMap->getColor(pixBuf, &color);
984         fprintf(f, "%02x", (int)(color.getGray() * 255 + 0.5));
985         if (++i == 32) {
986           fputc('\n', f);
987           i = 0;
988         }
989       }
990     }
991     if (i != 0)
992       fputc('\n', f);
993     delete imgStr;
994
995   // imagemask
996   } else {
997     str->reset();
998     i = 0;
999     for (y = 0; y < height; ++y) {
1000       for (x = 0; x < width; x += 8) {
1001         fprintf(f, "%02x", str->getChar() & 0xff);
1002         if (++i == 32) {
1003           fputc('\n', f);
1004           i = 0;
1005         }
1006       }
1007     }
1008     if (i != 0)
1009       fputc('\n', f);
1010   }
1011 }
1012
1013 void PSOutputDev::doImage(GfxImageColorMap *colorMap,
1014                           GBool invert, GBool inlineImg,
1015                           Stream *str, int width, int height, int len) {
1016   GfxColorSpace *colorSpace;
1017   GString *s;
1018   int n, numComps;
1019   Guchar *color;
1020   GBool useRLE, useA85;
1021   int c;
1022   int i, j, k;
1023
1024   // color space
1025   if (colorMap) {
1026     colorSpace = colorMap->getColorSpace();
1027     if (colorSpace->isIndexed())
1028       writePS("[/Indexed ");
1029     switch (colorSpace->getMode()) {
1030     case colorGray:
1031       writePS("/DeviceGray ");
1032       break;
1033     case colorCMYK:
1034       writePS("/DeviceCMYK ");
1035       break;
1036     case colorRGB:
1037       writePS("/DeviceRGB ");
1038       break;
1039     }
1040     if (colorSpace->isIndexed()) {
1041       n = colorSpace->getIndexHigh();
1042       numComps = colorSpace->getNumColorComps();
1043       writePS("%d <\n", n);
1044       for (i = 0; i <= n; i += 8) {
1045         writePS("  ");
1046         for (j = i; j < i+8 && j <= n; ++j) {
1047           color = colorSpace->getLookupVal(j);
1048           for (k = 0; k < numComps; ++k)
1049             writePS("%02x", color[k]);
1050         }
1051         writePS("\n");
1052       }
1053       writePS("> ] setcolorspace\n");
1054     } else {
1055       writePS("setcolorspace\n");
1056     }
1057   }
1058
1059   // image dictionary
1060   writePS("<<\n  /ImageType 1\n");
1061
1062   // width, height, matrix, bits per component
1063   writePS("  /Width %d\n", width);
1064   writePS("  /Height %d\n", height);
1065   writePS("  /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height);
1066   writePS("  /BitsPerComponent %d\n",
1067           colorMap ? colorMap->getBits() : 1);
1068
1069   // decode 
1070   if (colorMap) {
1071     writePS("  /Decode [");
1072     numComps = colorMap->getNumPixelComps();
1073     for (i = 0; i < numComps; ++i) {
1074       if (i > 0)
1075         writePS(" ");
1076       writePS("%g %g", colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i));
1077     }
1078     writePS("]\n");
1079   } else {
1080     writePS("  /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1);
1081   }
1082
1083   if (doForm) {
1084
1085     // data source
1086     writePS("  /DataSource <~\n");
1087
1088     // write image data stream, using ASCII85 encode filter
1089     str = new ASCII85Encoder(str);
1090     str->reset();
1091     while ((c = str->getChar()) != EOF)
1092       fputc(c, f);
1093     fputc('\n', f);
1094     delete str;
1095
1096     // end of image dictionary
1097     writePS(">>\n%s\n", colorMap ? "image" : "imagemask");
1098
1099   } else {
1100
1101     // data source
1102     writePS("  /DataSource currentfile\n");
1103     s = str->getPSFilter("    ");
1104     if (inlineImg || !s) {
1105       useRLE = gTrue;
1106       useA85 = gTrue;
1107     } else {
1108       useRLE = gFalse;
1109       useA85 = str->isBinary();
1110     }
1111     if (useA85)
1112       writePS("    /ASCII85Decode filter\n");
1113     if (useRLE)
1114       writePS("    /RunLengthDecode filter\n");
1115     else
1116       writePS("%s", s->getCString());
1117     if (s)
1118       delete s;
1119
1120     // end of image dictionary
1121     writePS(">>\n%s\n", colorMap ? "pdfIm" : "pdfImM");
1122
1123     // write image data stream
1124
1125     // cut off inline image streams at appropriate length
1126     if (inlineImg)
1127       str = new FixedLengthEncoder(str, len);
1128     else if (!useRLE)
1129       str = str->getBaseStream();
1130
1131     // add RunLengthEncode and ASCII85 encode filters
1132     if (useRLE)
1133       str = new RunLengthEncoder(str);
1134     if (useA85)
1135       str = new ASCII85Encoder(str);
1136
1137     // copy the stream data
1138     str->reset();
1139     while ((c = str->getChar()) != EOF)
1140       fputc(c, f);
1141
1142     // add newline and trailer to the end
1143     fputc('\n', f);
1144     fputs("%-EOD-\n", f);
1145
1146     // delete encoders
1147     if (useRLE || useA85)
1148       delete str;
1149   }
1150 }
1151
1152 void PSOutputDev::writePS(char *fmt, ...) {
1153   va_list args;
1154
1155   va_start(args, fmt);
1156   vfprintf(f, fmt, args);
1157   va_end(args);
1158 }
1159
1160 void PSOutputDev::writePSString(GString *s) {
1161   Guchar *p;
1162   int n;
1163
1164   fputc('(', f);
1165   for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
1166     if (*p == '(' || *p == ')' || *p == '\\')
1167       fprintf(f, "\\%c", *p);
1168     else if (*p < 0x20 || *p >= 0x80)
1169       fprintf(f, "\\%03o", *p);
1170     else
1171       fputc(*p, f);
1172   }
1173   fputc(')', f);
1174 }