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