]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/SplashOutputDev.cc
implement get/set properties
[evince.git] / pdf / xpdf / SplashOutputDev.cc
1 //========================================================================
2 //
3 // SplashOutputDev.cc
4 //
5 // Copyright 2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <aconf.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include <string.h>
16 #include <math.h>
17 #include "gfile.h"
18 #include "GlobalParams.h"
19 #include "Error.h"
20 #include "Object.h"
21 #include "GfxState.h"
22 #include "GfxFont.h"
23 #include "Link.h"
24 #include "CharCodeToUnicode.h"
25 #include "FontEncodingTables.h"
26 #include "FoFiTrueType.h"
27 #include "SplashBitmap.h"
28 #include "SplashGlyphBitmap.h"
29 #include "SplashPattern.h"
30 #include "SplashScreen.h"
31 #include "SplashPath.h"
32 #include "SplashState.h"
33 #include "SplashErrorCodes.h"
34 #include "SplashFontEngine.h"
35 #include "SplashFont.h"
36 #include "SplashFontFile.h"
37 #include "SplashFontFileID.h"
38 #include "Splash.h"
39 #include "SplashOutputDev.h"
40
41 //------------------------------------------------------------------------
42 // Font substitutions
43 //------------------------------------------------------------------------
44
45 struct SplashOutFontSubst {
46   char *name;
47   double mWidth;
48 };
49
50 // index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
51 static SplashOutFontSubst splashOutSubstFonts[16] = {
52   {"Helvetica",             0.833},
53   {"Helvetica-Oblique",     0.833},
54   {"Helvetica-Bold",        0.889},
55   {"Helvetica-BoldOblique", 0.889},
56   {"Times-Roman",           0.788},
57   {"Times-Italic",          0.722},
58   {"Times-Bold",            0.833},
59   {"Times-BoldItalic",      0.778},
60   {"Courier",               0.600},
61   {"Courier-Oblique",       0.600},
62   {"Courier-Bold",          0.600},
63   {"Courier-BoldOblique",   0.600},
64   {"Symbol",                0.576},
65   {"Symbol",                0.576},
66   {"Symbol",                0.576},
67   {"Symbol",                0.576}
68 };
69
70 //------------------------------------------------------------------------
71
72 #define soutRound(x) ((int)(x + 0.5))
73
74 //------------------------------------------------------------------------
75 // SplashOutFontFileID
76 //------------------------------------------------------------------------
77
78 class SplashOutFontFileID: public SplashFontFileID {
79 public:
80
81   SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; }
82
83   ~SplashOutFontFileID() {}
84
85   GBool matches(SplashFontFileID *id) {
86     return ((SplashOutFontFileID *)id)->r.num == r.num &&
87            ((SplashOutFontFileID *)id)->r.gen == r.gen;
88   }
89
90   void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
91   int getSubstIdx() { return substIdx; }
92
93 private:
94
95   Ref r;
96   int substIdx;
97 };
98
99 //------------------------------------------------------------------------
100 // T3FontCache
101 //------------------------------------------------------------------------
102
103 struct T3FontCacheTag {
104   Gushort code;
105   Gushort mru;                  // valid bit (0x8000) and MRU index
106 };
107
108 class T3FontCache {
109 public:
110
111   T3FontCache(Ref *fontID, double m11A, double m12A,
112               double m21A, double m22A,
113               int glyphXA, int glyphYA, int glyphWA, int glyphHA,
114               GBool aa);
115   ~T3FontCache();
116   GBool matches(Ref *idA, double m11A, double m12A,
117                 double m21A, double m22A)
118     { return fontID.num == idA->num && fontID.gen == idA->gen &&
119              m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
120
121   Ref fontID;                   // PDF font ID
122   double m11, m12, m21, m22;    // transform matrix
123   int glyphX, glyphY;           // pixel offset of glyph bitmaps
124   int glyphW, glyphH;           // size of glyph bitmaps, in pixels
125   int glyphSize;                // size of glyph bitmaps, in bytes
126   int cacheSets;                // number of sets in cache
127   int cacheAssoc;               // cache associativity (glyphs per set)
128   Guchar *cacheData;            // glyph pixmap cache
129   T3FontCacheTag *cacheTags;    // cache tags, i.e., char codes
130 };
131
132 T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
133                          double m21A, double m22A,
134                          int glyphXA, int glyphYA, int glyphWA, int glyphHA,
135                          GBool aa) {
136   int i;
137
138   fontID = *fontIDA;
139   m11 = m11A;
140   m12 = m12A;
141   m21 = m21A;
142   m22 = m22A;
143   glyphX = glyphXA;
144   glyphY = glyphYA;
145   glyphW = glyphWA;
146   glyphH = glyphHA;
147   if (aa) {
148     glyphSize = glyphW * glyphH;
149   } else {
150     glyphSize = ((glyphW + 7) >> 3) * glyphH;
151   }
152   cacheAssoc = 8;
153   if (glyphSize <= 256) {
154     cacheSets = 8;
155   } else if (glyphSize <= 512) {
156     cacheSets = 4;
157   } else if (glyphSize <= 1024) {
158     cacheSets = 2;
159   } else {
160     cacheSets = 1;
161   }
162   cacheData = (Guchar *)gmalloc(cacheSets * cacheAssoc * glyphSize);
163   cacheTags = (T3FontCacheTag *)gmalloc(cacheSets * cacheAssoc *
164                                         sizeof(T3FontCacheTag));
165   for (i = 0; i < cacheSets * cacheAssoc; ++i) {
166     cacheTags[i].mru = i & (cacheAssoc - 1);
167   }
168 }
169
170 T3FontCache::~T3FontCache() {
171   gfree(cacheData);
172   gfree(cacheTags);
173 }
174
175 struct T3GlyphStack {
176   Gushort code;                 // character code
177   double x, y;                  // position to draw the glyph
178
179   //----- cache info
180   T3FontCache *cache;           // font cache for the current font
181   T3FontCacheTag *cacheTag;     // pointer to cache tag for the glyph
182   Guchar *cacheData;            // pointer to cache data for the glyph
183
184   //----- saved state
185   SplashBitmap *origBitmap;
186   Splash *origSplash;
187   double origCTM4, origCTM5;
188
189   T3GlyphStack *next;           // next object on stack
190 };
191
192 //------------------------------------------------------------------------
193 // SplashOutputDev
194 //------------------------------------------------------------------------
195
196 SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
197                                  GBool reverseVideoA,
198                                  SplashColor paperColorA) {
199   colorMode = colorModeA;
200   reverseVideo = reverseVideoA;
201   paperColor = paperColorA;
202
203   xref = NULL;
204
205   bitmap = new SplashBitmap(1, 1, colorMode);
206   splash = new Splash(bitmap);
207   splash->clear(paperColor);
208
209   fontEngine = NULL;
210
211   nT3Fonts = 0;
212   t3GlyphStack = NULL;
213
214   font = NULL;
215   needFontUpdate = gFalse;
216   textClipPath = NULL;
217
218   underlayCbk = NULL;
219   underlayCbkData = NULL;
220 }
221
222 SplashOutputDev::~SplashOutputDev() {
223   int i;
224
225   for (i = 0; i < nT3Fonts; ++i) {
226     delete t3FontCache[i];
227   }
228   if (fontEngine) {
229     delete fontEngine;
230   }
231   if (splash) {
232     delete splash;
233   }
234   if (bitmap) {
235     delete bitmap;
236   }
237 }
238
239 void SplashOutputDev::startDoc(XRef *xrefA) {
240   int i;
241
242   xref = xrefA;
243   if (fontEngine) {
244     delete fontEngine;
245   }
246   fontEngine = new SplashFontEngine(
247 #if HAVE_T1LIB_H
248                                     globalParams->getEnableT1lib(),
249 #endif
250 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
251                                     globalParams->getEnableFreeType(),
252 #endif
253                                     globalParams->getAntialias());
254   for (i = 0; i < nT3Fonts; ++i) {
255     delete t3FontCache[i];
256   }
257   nT3Fonts = 0;
258 }
259
260 void SplashOutputDev::startPage(int pageNum, GfxState *state) {
261   int w, h;
262   SplashColor color;
263
264   w = state ? (int)(state->getPageWidth() + 0.5) : 1;
265   h = state ? (int)(state->getPageHeight() + 0.5) : 1;
266   if (splash) {
267     delete splash;
268   }
269   if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
270     if (bitmap) {
271       delete bitmap;
272     }
273     bitmap = new SplashBitmap(w, h, colorMode);
274   }
275   splash = new Splash(bitmap);
276   switch (colorMode) {
277   case splashModeMono1: color.mono1 = 0; break;
278   case splashModeMono8: color.mono8 = 0; break;
279   case splashModeRGB8:
280   case splashModeRGB8Packed: color.rgb8 = splashMakeRGB8(0, 0, 0); break;
281   case splashModeBGR8Packed: color.bgr8 = splashMakeBGR8(0, 0, 0); break;
282   }
283   splash->setStrokePattern(new SplashSolidColor(color));
284   splash->setFillPattern(new SplashSolidColor(color));
285   splash->setLineCap(splashLineCapButt);
286   splash->setLineJoin(splashLineJoinMiter);
287   splash->setLineDash(NULL, 0, 0);
288   splash->setMiterLimit(10);
289   splash->setFlatness(1);
290   splash->clear(paperColor);
291
292   if (underlayCbk) {
293     (*underlayCbk)(underlayCbkData);
294   }
295 }
296
297 void SplashOutputDev::endPage() {
298 }
299
300 void SplashOutputDev::drawLink(Link *link, Catalog *catalog) {
301   double x1, y1, x2, y2;
302   LinkBorderStyle *borderStyle;
303   GfxRGB rgb;
304   double gray;
305   double *dash;
306   int dashLength;
307   SplashCoord dashList[20];
308   SplashPath *path;
309   int x, y, i;
310
311   link->getRect(&x1, &y1, &x2, &y2);
312   borderStyle = link->getBorderStyle();
313   if (borderStyle->getWidth() > 0) {
314     borderStyle->getColor(&rgb.r, &rgb.g, &rgb.b);
315     gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b;
316     if (gray > 1) {
317       gray = 1;
318     }
319     splash->setStrokePattern(getColor(gray, &rgb));
320     splash->setLineWidth((SplashCoord)borderStyle->getWidth());
321     borderStyle->getDash(&dash, &dashLength);
322     if (borderStyle->getType() == linkBorderDashed && dashLength > 0) {
323       if (dashLength > 20) {
324         dashLength = 20;
325       }
326       for (i = 0; i < dashLength; ++i) {
327         dashList[i] = (SplashCoord)dash[i];
328       }
329       splash->setLineDash(dashList, dashLength, 0);
330     }
331     path = new SplashPath();
332     if (borderStyle->getType() == linkBorderUnderlined) {
333       cvtUserToDev(x1, y1, &x, &y);
334       path->moveTo((SplashCoord)x, (SplashCoord)y);
335       cvtUserToDev(x2, y1, &x, &y);
336       path->lineTo((SplashCoord)x, (SplashCoord)y);
337     } else {
338       cvtUserToDev(x1, y1, &x, &y);
339       path->moveTo((SplashCoord)x, (SplashCoord)y);
340       cvtUserToDev(x2, y1, &x, &y);
341       path->lineTo((SplashCoord)x, (SplashCoord)y);
342       cvtUserToDev(x2, y2, &x, &y);
343       path->lineTo((SplashCoord)x, (SplashCoord)y);
344       cvtUserToDev(x1, y2, &x, &y);
345       path->lineTo((SplashCoord)x, (SplashCoord)y);
346       path->close();
347     }
348     splash->stroke(path);
349     delete path;
350   }
351 }
352
353 void SplashOutputDev::saveState(GfxState *state) {
354   splash->saveState();
355 }
356
357 void SplashOutputDev::restoreState(GfxState *state) {
358   splash->restoreState();
359   needFontUpdate = gTrue;
360 }
361
362 void SplashOutputDev::updateAll(GfxState *state) {
363   updateLineDash(state);
364   updateLineJoin(state);
365   updateLineCap(state);
366   updateLineWidth(state);
367   updateFlatness(state);
368   updateMiterLimit(state);
369   updateFillColor(state);
370   updateStrokeColor(state);
371   needFontUpdate = gTrue;
372 }
373
374 void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
375                                 double m21, double m22,
376                                 double m31, double m32) {
377   updateLineDash(state);
378   updateLineJoin(state);
379   updateLineCap(state);
380   updateLineWidth(state);
381 }
382
383 void SplashOutputDev::updateLineDash(GfxState *state) {
384   double *dashPattern;
385   int dashLength;
386   double dashStart;
387   SplashCoord dash[20];
388   SplashCoord phase;
389   int i;
390
391   state->getLineDash(&dashPattern, &dashLength, &dashStart);
392   if (dashLength > 20) {
393     dashLength = 20;
394   }
395   for (i = 0; i < dashLength; ++i) {
396     dash[i] =  (SplashCoord)state->transformWidth(dashPattern[i]);
397     if (dash[i] < 1) {
398       dash[i] = 1;
399     }
400   }
401   phase = (SplashCoord)state->transformWidth(dashStart);
402   splash->setLineDash(dash, dashLength, phase);
403 }
404
405 void SplashOutputDev::updateFlatness(GfxState *state) {
406   splash->setFlatness(state->getFlatness());
407 }
408
409 void SplashOutputDev::updateLineJoin(GfxState *state) {
410   splash->setLineJoin(state->getLineJoin());
411 }
412
413 void SplashOutputDev::updateLineCap(GfxState *state) {
414   splash->setLineCap(state->getLineCap());
415 }
416
417 void SplashOutputDev::updateMiterLimit(GfxState *state) {
418   splash->setMiterLimit(state->getMiterLimit());
419 }
420
421 void SplashOutputDev::updateLineWidth(GfxState *state) {
422   splash->setLineWidth(state->getTransformedLineWidth());
423 }
424
425 void SplashOutputDev::updateFillColor(GfxState *state) {
426   double gray;
427   GfxRGB rgb;
428
429   state->getFillGray(&gray);
430   state->getFillRGB(&rgb);
431   splash->setFillPattern(getColor(gray, &rgb));
432 }
433
434 void SplashOutputDev::updateStrokeColor(GfxState *state) {
435   double gray;
436   GfxRGB rgb;
437
438   state->getStrokeGray(&gray);
439   state->getStrokeRGB(&rgb);
440   splash->setStrokePattern(getColor(gray, &rgb));
441 }
442
443 SplashPattern *SplashOutputDev::getColor(double gray, GfxRGB *rgb) {
444   SplashPattern *pattern;
445   SplashColor color0, color1;
446   double r, g, b;
447
448   if (reverseVideo) {
449     gray = 1 - gray;
450     r = 1 - rgb->r;
451     g = 1 - rgb->g;
452     b = 1 - rgb->b;
453   } else {
454     r = rgb->r;
455     g = rgb->g;
456     b = rgb->b;
457   }
458
459   pattern = NULL; // make gcc happy
460   switch (colorMode) {
461   case splashModeMono1:
462     color0.mono1 = 0;
463     color1.mono1 = 1;
464     pattern = new SplashHalftone(color0, color1,
465                                  splash->getScreen()->copy(),
466                                  (SplashCoord)gray);
467     break;
468   case splashModeMono8:
469     color1.mono8 = soutRound(255 * gray);
470     pattern = new SplashSolidColor(color1);
471     break;
472   case splashModeRGB8:
473   case splashModeRGB8Packed:
474     color1.rgb8 = splashMakeRGB8(soutRound(255 * r),
475                                  soutRound(255 * g),
476                                  soutRound(255 * b));
477     pattern = new SplashSolidColor(color1);
478     break;
479   case splashModeBGR8Packed:
480     color1.bgr8 = splashMakeBGR8(soutRound(255 * r),
481                                  soutRound(255 * g),
482                                  soutRound(255 * b));
483     pattern = new SplashSolidColor(color1);
484     break;
485   }
486
487   return pattern;
488 }
489
490 void SplashOutputDev::updateFont(GfxState *state) {
491   GfxFont *gfxFont;
492   GfxFontType fontType;
493   SplashOutFontFileID *id;
494   SplashFontFile *fontFile;
495   FoFiTrueType *ff;
496   Ref embRef;
497   Object refObj, strObj;
498   GString *tmpFileName, *fileName, *substName;
499   FILE *tmpFile;
500   Gushort *codeToGID;
501   DisplayFontParam *dfp;
502   double m11, m12, m21, m22, w1, w2;
503   SplashCoord mat[4];
504   char *name;
505   int c, substIdx, n, code;
506
507   needFontUpdate = gFalse;
508   font = NULL;
509   tmpFileName = NULL;
510   substIdx = -1;
511
512   if (!(gfxFont = state->getFont())) {
513     goto err1;
514   }
515   fontType = gfxFont->getType();
516   if (fontType == fontType3) {
517     goto err1;
518   }
519
520   // check the font file cache
521   id = new SplashOutFontFileID(gfxFont->getID());
522   if ((fontFile = fontEngine->getFontFile(id))) {
523     delete id;
524
525   } else {
526
527     // if there is an embedded font, write it to disk
528     if (gfxFont->getEmbeddedFontID(&embRef)) {
529       if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
530         error(-1, "Couldn't create temporary font file");
531         goto err2;
532       }
533       refObj.initRef(embRef.num, embRef.gen);
534       refObj.fetch(xref, &strObj);
535       refObj.free();
536       strObj.streamReset();
537       while ((c = strObj.streamGetChar()) != EOF) {
538         fputc(c, tmpFile);
539       }
540       strObj.streamClose();
541       strObj.free();
542       fclose(tmpFile);
543       fileName = tmpFileName;
544
545     // if there is an external font file, use it
546     } else if (!(fileName = gfxFont->getExtFontFile())) {
547
548       // look for a display font mapping or a substitute font
549       dfp = NULL;
550       if (gfxFont->isCIDFont()) {
551         if (((GfxCIDFont *)gfxFont)->getCollection()) {
552           dfp = globalParams->
553                   getDisplayCIDFont(gfxFont->getName(),
554                                     ((GfxCIDFont *)gfxFont)->getCollection());
555         }
556       } else {
557         if (gfxFont->getName()) {
558           dfp = globalParams->getDisplayFont(gfxFont->getName());
559         }
560         if (!dfp) {
561           // 8-bit font substitution
562           if (gfxFont->isFixedWidth()) {
563             substIdx = 8;
564           } else if (gfxFont->isSerif()) {
565             substIdx = 4;
566           } else {
567             substIdx = 0;
568           }
569           if (gfxFont->isBold()) {
570             substIdx += 2;
571           }
572           if (gfxFont->isItalic()) {
573             substIdx += 1;
574           }
575           substName = new GString(splashOutSubstFonts[substIdx].name);
576           dfp = globalParams->getDisplayFont(substName);
577           delete substName;
578           id->setSubstIdx(substIdx);
579         }
580       }
581       if (!dfp) {
582         error(-1, "Couldn't find a font for '%s'",
583               gfxFont->getName() ? gfxFont->getName()->getCString()
584                                  : "(unnamed)");
585         goto err2;
586       }
587       switch (dfp->kind) {
588       case displayFontT1:
589         fileName = dfp->t1.fileName;
590         fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1;
591         break;
592       case displayFontTT:
593         fileName = dfp->tt.fileName;
594         fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType;
595         break;
596       }
597     }
598
599     // load the font file
600     switch (fontType) {
601     case fontType1:
602       if (!(fontFile = fontEngine->loadType1Font(
603                            id,
604                            fileName->getCString(),
605                            fileName == tmpFileName,
606                            ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
607         error(-1, "Couldn't create a font for '%s'",
608               gfxFont->getName() ? gfxFont->getName()->getCString()
609                                  : "(unnamed)");
610         goto err2;
611       }
612       break;
613     case fontType1C:
614       if (!(fontFile = fontEngine->loadType1CFont(
615                            id,
616                            fileName->getCString(),
617                            fileName == tmpFileName,
618                            ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
619         error(-1, "Couldn't create a font for '%s'",
620               gfxFont->getName() ? gfxFont->getName()->getCString()
621                                  : "(unnamed)");
622         goto err2;
623       }
624       break;
625     case fontTrueType:
626       if (!(ff = FoFiTrueType::load(fileName->getCString()))) {
627         goto err2;
628       }
629       codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
630       delete ff;
631       if (!(fontFile = fontEngine->loadTrueTypeFont(
632                            id,
633                            fileName->getCString(),
634                            fileName == tmpFileName,
635                            codeToGID, 256))) {
636         error(-1, "Couldn't create a font for '%s'",
637               gfxFont->getName() ? gfxFont->getName()->getCString()
638                                  : "(unnamed)");
639         goto err2;
640       }
641       break;
642     case fontCIDType0:
643     case fontCIDType0C:
644       if (!(fontFile = fontEngine->loadCIDFont(
645                            id,
646                            fileName->getCString(),
647                            fileName == tmpFileName))) {
648         error(-1, "Couldn't create a font for '%s'",
649               gfxFont->getName() ? gfxFont->getName()->getCString()
650                                  : "(unnamed)");
651         goto err2;
652       }
653       break;
654     case fontCIDType2:
655       n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
656       codeToGID = (Gushort *)gmalloc(n * sizeof(Gushort));
657       memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
658              n * sizeof(Gushort));
659       if (!(fontFile = fontEngine->loadTrueTypeFont(
660                            id,
661                            fileName->getCString(),
662                            fileName == tmpFileName,
663                            codeToGID, n))) {
664         error(-1, "Couldn't create a font for '%s'",
665               gfxFont->getName() ? gfxFont->getName()->getCString()
666                                  : "(unnamed)");
667         goto err2;
668       }
669       break;
670     default:
671       // this shouldn't happen
672       goto err2;
673     }
674   }
675
676   // get the font matrix
677   state->getFontTransMat(&m11, &m12, &m21, &m22);
678   m11 *= state->getHorizScaling();
679   m12 *= state->getHorizScaling();
680
681   // for substituted fonts: adjust the font matrix -- compare the
682   // width of 'm' in the original font and the substituted font
683   substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx();
684   if (substIdx >= 0) {
685     for (code = 0; code < 256; ++code) {
686       if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
687           name[0] == 'm' && name[1] == '\0') {
688         break;
689       }
690     }
691     if (code < 256) {
692       w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
693       w2 = splashOutSubstFonts[substIdx].mWidth;
694       if (!gfxFont->isSymbolic()) {
695         // if real font is substantially narrower than substituted
696         // font, reduce the font size accordingly
697         if (w1 > 0.01 && w1 < 0.9 * w2) {
698           w1 /= w2;
699           m11 *= w1;
700           m21 *= w1;
701         }
702       }
703     }
704   }
705
706   // create the scaled font
707   mat[0] = m11;  mat[1] = -m12;
708   mat[2] = m21;  mat[3] = -m22;
709   font = fontEngine->getFont(fontFile, mat);
710
711   if (tmpFileName) {
712     delete tmpFileName;
713   }
714   return;
715
716  err2:
717   delete id;
718  err1:
719   if (tmpFileName) {
720     delete tmpFileName;
721   }
722   return;
723 }
724
725 void SplashOutputDev::stroke(GfxState *state) {
726   SplashPath *path;
727
728   path = convertPath(state, state->getPath());
729   splash->stroke(path);
730   delete path;
731 }
732
733 void SplashOutputDev::fill(GfxState *state) {
734   SplashPath *path;
735
736   path = convertPath(state, state->getPath());
737   splash->fill(path, gFalse);
738   delete path;
739 }
740
741 void SplashOutputDev::eoFill(GfxState *state) {
742   SplashPath *path;
743
744   path = convertPath(state, state->getPath());
745   splash->fill(path, gTrue);
746   delete path;
747 }
748
749 void SplashOutputDev::clip(GfxState *state) {
750   SplashPath *path;
751
752   path = convertPath(state, state->getPath());
753   splash->clipToPath(path, gFalse);
754   delete path;
755 }
756
757 void SplashOutputDev::eoClip(GfxState *state) {
758   SplashPath *path;
759
760   path = convertPath(state, state->getPath());
761   splash->clipToPath(path, gTrue);
762   delete path;
763 }
764
765 SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path) {
766   SplashPath *sPath;
767   GfxSubpath *subpath;
768   double x1, y1, x2, y2, x3, y3;
769   int i, j;
770
771   sPath = new SplashPath();
772   for (i = 0; i < path->getNumSubpaths(); ++i) {
773     subpath = path->getSubpath(i);
774     if (subpath->getNumPoints() > 0) {
775       state->transform(subpath->getX(0), subpath->getY(0), &x1, &y1);
776       sPath->moveTo((SplashCoord)x1, (SplashCoord)y1);
777       j = 1;
778       while (j < subpath->getNumPoints()) {
779         if (subpath->getCurve(j)) {
780           state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
781           state->transform(subpath->getX(j+1), subpath->getY(j+1), &x2, &y2);
782           state->transform(subpath->getX(j+2), subpath->getY(j+2), &x3, &y3);
783           sPath->curveTo((SplashCoord)x1, (SplashCoord)y1,
784                          (SplashCoord)x2, (SplashCoord)y2,
785                          (SplashCoord)x3, (SplashCoord)y3);
786           j += 3;
787         } else {
788           state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
789           sPath->lineTo((SplashCoord)x1, (SplashCoord)y1);
790           ++j;
791         }
792       }
793       if (subpath->isClosed()) {
794         sPath->close();
795       }
796     }
797   }
798   return sPath;
799 }
800
801 void SplashOutputDev::drawChar(GfxState *state, double x, double y,
802                                double dx, double dy,
803                                double originX, double originY,
804                                CharCode code, Unicode *u, int uLen) {
805   double x1, y1;
806   SplashPath *path;
807   int render;
808
809   if (needFontUpdate) {
810     updateFont(state);
811   }
812   if (!font) {
813     return;
814   }
815
816   // check for invisible text -- this is used by Acrobat Capture
817   render = state->getRender();
818   if (render == 3) {
819     return;
820   }
821
822   x -= originX;
823   y -= originY;
824   state->transform(x, y, &x1, &y1);
825
826   // fill
827   if (!(render & 1)) {
828     splash->fillChar((SplashCoord)x1, (SplashCoord)y1, code, font);
829   }
830
831   // stroke
832   if ((render & 3) == 1 || (render & 3) == 2) {
833     if ((path = font->getGlyphPath(code))) {
834       path->offset((SplashCoord)x1, (SplashCoord)y1);
835       splash->stroke(path);
836       delete path;
837     }
838   }
839
840   // clip
841   if (render & 4) {
842     path = font->getGlyphPath(code);
843     path->offset((SplashCoord)x1, (SplashCoord)y1);
844     if (textClipPath) {
845       textClipPath->append(path);
846       delete path;
847     } else {
848       textClipPath = path;
849     }
850   }
851 }
852
853 GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
854                                       double dx, double dy,
855                                       CharCode code, Unicode *u, int uLen) {
856   GfxFont *gfxFont;
857   Ref *fontID;
858   double *ctm, *bbox;
859   T3FontCache *t3Font;
860   T3GlyphStack *t3gs;
861   double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
862   int i, j;
863
864   if (!(gfxFont = state->getFont())) {
865     return gFalse;
866   }
867   fontID = gfxFont->getID();
868   ctm = state->getCTM();
869   state->transform(0, 0, &xt, &yt);
870
871   // is it the first (MRU) font in the cache?
872   if (!(nT3Fonts > 0 &&
873         t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
874
875     // is the font elsewhere in the cache?
876     for (i = 1; i < nT3Fonts; ++i) {
877       if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
878         t3Font = t3FontCache[i];
879         for (j = i; j > 0; --j) {
880           t3FontCache[j] = t3FontCache[j - 1];
881         }
882         t3FontCache[0] = t3Font;
883         break;
884       }
885     }
886     if (i >= nT3Fonts) {
887
888       // create new entry in the font cache
889       if (nT3Fonts == splashOutT3FontCacheSize) {
890         delete t3FontCache[nT3Fonts - 1];
891         --nT3Fonts;
892       }
893       for (j = nT3Fonts; j > 0; --j) {
894         t3FontCache[j] = t3FontCache[j - 1];
895       }
896       ++nT3Fonts;
897       bbox = gfxFont->getFontBBox();
898       if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
899         // broken bounding box -- just take a guess
900         xMin = xt - 5;
901         xMax = xMin + 30;
902         yMax = yt + 15;
903         yMin = yMax - 45;
904       } else {
905         state->transform(bbox[0], bbox[1], &x1, &y1);
906         xMin = xMax = x1;
907         yMin = yMax = y1;
908         state->transform(bbox[0], bbox[3], &x1, &y1);
909         if (x1 < xMin) {
910           xMin = x1;
911         } else if (x1 > xMax) {
912           xMax = x1;
913         }
914         if (y1 < yMin) {
915           yMin = y1;
916         } else if (y1 > yMax) {
917           yMax = y1;
918         }
919         state->transform(bbox[2], bbox[1], &x1, &y1);
920         if (x1 < xMin) {
921           xMin = x1;
922         } else if (x1 > xMax) {
923           xMax = x1;
924         }
925         if (y1 < yMin) {
926           yMin = y1;
927         } else if (y1 > yMax) {
928           yMax = y1;
929         }
930         state->transform(bbox[2], bbox[3], &x1, &y1);
931         if (x1 < xMin) {
932           xMin = x1;
933         } else if (x1 > xMax) {
934           xMax = x1;
935         }
936         if (y1 < yMin) {
937           yMin = y1;
938         } else if (y1 > yMax) {
939           yMax = y1;
940         }
941       }
942       t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
943                                        (int)floor(xMin - xt),
944                                        (int)floor(yMin - yt),
945                                        (int)ceil(xMax) - (int)floor(xMin) + 3,
946                                        (int)ceil(yMax) - (int)floor(yMin) + 3,
947                                        colorMode != splashModeMono1);
948     }
949   }
950   t3Font = t3FontCache[0];
951
952   // is the glyph in the cache?
953   i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
954   for (j = 0; j < t3Font->cacheAssoc; ++j) {
955     if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
956         t3Font->cacheTags[i+j].code == code) {
957       drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
958                      t3Font->cacheData + (i+j) * t3Font->glyphSize,
959                      xt, yt);
960       return gTrue;
961     }
962   }
963
964   // push a new Type 3 glyph record
965   t3gs = new T3GlyphStack();
966   t3gs->next = t3GlyphStack;
967   t3GlyphStack = t3gs;
968   t3GlyphStack->code = code;
969   t3GlyphStack->x = xt;
970   t3GlyphStack->y = yt;
971   t3GlyphStack->cache = t3Font;
972   t3GlyphStack->cacheTag = NULL;
973   t3GlyphStack->cacheData = NULL;
974
975   return gFalse;
976 }
977
978 void SplashOutputDev::endType3Char(GfxState *state) {
979   T3GlyphStack *t3gs;
980   double *ctm;
981
982   if (t3GlyphStack->cacheTag) {
983     memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr().mono8,
984            t3GlyphStack->cache->glyphSize);
985     delete bitmap;
986     delete splash;
987     bitmap = t3GlyphStack->origBitmap;
988     splash = t3GlyphStack->origSplash;
989     ctm = state->getCTM();
990     state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
991                   t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
992     drawType3Glyph(t3GlyphStack->cache,
993                    t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
994                    t3GlyphStack->x, t3GlyphStack->y);
995   }
996   t3gs = t3GlyphStack;
997   t3GlyphStack = t3gs->next;
998   delete t3gs;
999 }
1000
1001 void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
1002 }
1003
1004 void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
1005                               double llx, double lly, double urx, double ury) {
1006   double *ctm;
1007   T3FontCache *t3Font;
1008   SplashColor color;
1009   double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
1010   int i, j;
1011
1012   t3Font = t3GlyphStack->cache;
1013
1014   // check for a valid bbox
1015   state->transform(0, 0, &xt, &yt);
1016   state->transform(llx, lly, &x1, &y1);
1017   xMin = xMax = x1;
1018   yMin = yMax = y1;
1019   state->transform(llx, ury, &x1, &y1);
1020   if (x1 < xMin) {
1021     xMin = x1;
1022   } else if (x1 > xMax) {
1023     xMax = x1;
1024   }
1025   if (y1 < yMin) {
1026     yMin = y1;
1027   } else if (y1 > yMax) {
1028     yMax = y1;
1029   }
1030   state->transform(urx, lly, &x1, &y1);
1031   if (x1 < xMin) {
1032     xMin = x1;
1033   } else if (x1 > xMax) {
1034     xMax = x1;
1035   }
1036   if (y1 < yMin) {
1037     yMin = y1;
1038   } else if (y1 > yMax) {
1039     yMax = y1;
1040   }
1041   state->transform(urx, ury, &x1, &y1);
1042   if (x1 < xMin) {
1043     xMin = x1;
1044   } else if (x1 > xMax) {
1045     xMax = x1;
1046   }
1047   if (y1 < yMin) {
1048     yMin = y1;
1049   } else if (y1 > yMax) {
1050     yMax = y1;
1051   }
1052   if (xMin - xt < t3Font->glyphX ||
1053       yMin - yt < t3Font->glyphY ||
1054       xMax - xt > t3Font->glyphX + t3Font->glyphW ||
1055       yMax - yt > t3Font->glyphY + t3Font->glyphH) {
1056     error(-1, "Bad bounding box in Type 3 glyph");
1057     return;
1058   }
1059
1060   // allocate a cache entry
1061   i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
1062   for (j = 0; j < t3Font->cacheAssoc; ++j) {
1063     if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
1064       t3Font->cacheTags[i+j].mru = 0x8000;
1065       t3Font->cacheTags[i+j].code = t3GlyphStack->code;
1066       t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
1067       t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
1068     } else {
1069       ++t3Font->cacheTags[i+j].mru;
1070     }
1071   }
1072
1073   // save state
1074   t3GlyphStack->origBitmap = bitmap;
1075   t3GlyphStack->origSplash = splash;
1076   ctm = state->getCTM();
1077   t3GlyphStack->origCTM4 = ctm[4];
1078   t3GlyphStack->origCTM5 = ctm[5];
1079
1080   // create the temporary bitmap
1081   if (colorMode == splashModeMono1) {
1082     bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, splashModeMono1);
1083     splash = new Splash(bitmap);
1084     color.mono1 = 0;
1085     splash->clear(color);
1086     color.mono1 = 1;
1087   } else {
1088     bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, splashModeMono8);
1089     splash = new Splash(bitmap);
1090     color.mono8 = 0x00;
1091     splash->clear(color);
1092     color.mono8 = 0xff;
1093   }
1094   splash->setFillPattern(new SplashSolidColor(color));
1095   splash->setStrokePattern(new SplashSolidColor(color));
1096   //~ this should copy other state from t3GlyphStack->origSplash?
1097   state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
1098                 -t3Font->glyphX, -t3Font->glyphY);
1099 }
1100
1101 void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font,
1102                                      T3FontCacheTag *tag, Guchar *data,
1103                                      double x, double y) {
1104   SplashGlyphBitmap glyph;
1105
1106   glyph.x = -t3Font->glyphX;
1107   glyph.y = -t3Font->glyphY;
1108   glyph.w = t3Font->glyphW;
1109   glyph.h = t3Font->glyphH;
1110   glyph.aa = colorMode != splashModeMono1;
1111   glyph.data = data;
1112   glyph.freeData = gFalse;
1113   splash->fillGlyph((SplashCoord)x, (SplashCoord)y, &glyph);
1114 }
1115
1116 void SplashOutputDev::endTextObject(GfxState *state) {
1117   if (textClipPath) {
1118     splash->clipToPath(textClipPath, gFalse);
1119     delete textClipPath;
1120     textClipPath = NULL;
1121   }
1122 }
1123
1124 struct SplashOutImageMaskData {
1125   ImageStream *imgStr;
1126   int nPixels, idx;
1127   GBool invert;
1128 };
1129
1130 GBool SplashOutputDev::imageMaskSrc(void *data, SplashMono1 *pixel) {
1131   SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
1132   Guchar pix;
1133
1134   if (imgMaskData->idx >= imgMaskData->nPixels) {
1135     return gFalse;
1136   }
1137   //~ use getLine
1138   imgMaskData->imgStr->getPixel(&pix);
1139   if (!imgMaskData->invert) {
1140     pix ^= 1;
1141   }
1142   *pixel = pix;
1143   ++imgMaskData->idx;
1144   return gTrue;
1145 }
1146
1147 void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1148                                     int width, int height, GBool invert,
1149                                     GBool inlineImg) {
1150   double *ctm;
1151   SplashCoord mat[6];
1152   SplashOutImageMaskData imgMaskData;
1153   Guchar pix;
1154
1155   ctm = state->getCTM();
1156   mat[0] = ctm[0];
1157   mat[1] = ctm[1];
1158   mat[2] = -ctm[2];
1159   mat[3] = -ctm[3];
1160   mat[4] = ctm[2] + ctm[4];
1161   mat[5] = ctm[3] + ctm[5];
1162
1163   imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
1164   imgMaskData.imgStr->reset();
1165   imgMaskData.nPixels = width * height;
1166   imgMaskData.idx = 0;
1167   imgMaskData.invert = invert;
1168
1169   splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat);
1170   if (inlineImg) {
1171     while (imageMaskSrc(&imgMaskData, &pix)) ;
1172   }
1173
1174   delete imgMaskData.imgStr;
1175 }
1176
1177 struct SplashOutImageData {
1178   ImageStream *imgStr;
1179   GfxImageColorMap *colorMap;
1180   int *maskColors;
1181   SplashOutputDev *out;
1182   int nPixels, idx;
1183 };
1184
1185 GBool SplashOutputDev::imageSrc(void *data, SplashColor *pixel,
1186                                 Guchar *alpha) {
1187   SplashOutImageData *imgData = (SplashOutImageData *)data;
1188   Guchar pix[gfxColorMaxComps];
1189   GfxRGB rgb;
1190   double gray;
1191   int i;
1192
1193   if (imgData->idx >= imgData->nPixels) {
1194     return gFalse;
1195   }
1196
1197   //~ use getLine
1198   imgData->imgStr->getPixel(pix);
1199   switch (imgData->out->colorMode) {
1200   case splashModeMono1:
1201   case splashModeMono8:
1202     imgData->colorMap->getGray(pix, &gray);
1203     pixel->mono8 = soutRound(255 * gray);
1204     break;
1205   case splashModeRGB8:
1206   case splashModeRGB8Packed:
1207     imgData->colorMap->getRGB(pix, &rgb);
1208     pixel->rgb8 = splashMakeRGB8(soutRound(255 * rgb.r),
1209                                  soutRound(255 * rgb.g),
1210                                  soutRound(255 * rgb.b));
1211     break;
1212   case splashModeBGR8Packed:
1213     imgData->colorMap->getRGB(pix, &rgb);
1214     pixel->bgr8 = splashMakeBGR8(soutRound(255 * rgb.r),
1215                                  soutRound(255 * rgb.g),
1216                                  soutRound(255 * rgb.b));
1217     break;
1218   }
1219
1220   if (imgData->maskColors) {
1221     *alpha = 0;
1222     for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1223       if (pix[i] < imgData->maskColors[2*i] ||
1224           pix[i] > imgData->maskColors[2*i+1]) {
1225         *alpha = 1;
1226         break;
1227       }
1228     }
1229   } else {
1230     *alpha = 1;
1231   }
1232
1233   ++imgData->idx;
1234   return gTrue;
1235 }
1236
1237 void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1238                                 int width, int height,
1239                                 GfxImageColorMap *colorMap,
1240                                 int *maskColors, GBool inlineImg) {
1241   double *ctm;
1242   SplashCoord mat[6];
1243   SplashOutImageData imgData;
1244   SplashColor pix;
1245   Guchar alpha;
1246
1247   ctm = state->getCTM();
1248   mat[0] = ctm[0];
1249   mat[1] = ctm[1];
1250   mat[2] = -ctm[2];
1251   mat[3] = -ctm[3];
1252   mat[4] = ctm[2] + ctm[4];
1253   mat[5] = ctm[3] + ctm[5];
1254
1255   imgData.imgStr = new ImageStream(str, width,
1256                                    colorMap->getNumPixelComps(),
1257                                    colorMap->getBits());
1258   imgData.imgStr->reset();
1259   imgData.colorMap = colorMap;
1260   imgData.maskColors = maskColors;
1261   imgData.out = this;
1262   imgData.nPixels = width * height;
1263   imgData.idx = 0;
1264
1265   splash->drawImage(&imageSrc, &imgData,
1266                     (colorMode == splashModeMono1) ? splashModeMono8
1267                                                    : colorMode,
1268                     width, height, mat);
1269   if (inlineImg) {
1270     while (imageSrc(&imgData, &pix, &alpha)) ;
1271   }
1272
1273   delete imgData.imgStr;
1274 }
1275
1276 int SplashOutputDev::getBitmapWidth() {
1277   return bitmap->getWidth();
1278 }
1279
1280 int SplashOutputDev::getBitmapHeight() {
1281   return bitmap->getHeight();
1282 }
1283
1284 void SplashOutputDev::xorRectangle(int x0, int y0, int x1, int y1,
1285                                    SplashPattern *pattern) {
1286   SplashPath *path;
1287
1288   path = new SplashPath();
1289   path->moveTo((SplashCoord)x0, (SplashCoord)y0);
1290   path->lineTo((SplashCoord)x1, (SplashCoord)y0);
1291   path->lineTo((SplashCoord)x1, (SplashCoord)y1);
1292   path->lineTo((SplashCoord)x0, (SplashCoord)y1);
1293   path->close();
1294   splash->setFillPattern(pattern);
1295   splash->xorFill(path, gTrue);
1296   delete path;
1297 }
1298
1299 void SplashOutputDev::setFillColor(int r, int g, int b) {
1300   GfxRGB rgb;
1301   double gray;
1302
1303   rgb.r = r / 255.0;
1304   rgb.g = g / 255.0;
1305   rgb.b = b / 255.0;
1306   gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g;
1307   splash->setFillPattern(getColor(gray, &rgb));
1308 }
1309
1310 SplashFont *SplashOutputDev::getFont(GString *name, double *mat) {
1311   DisplayFontParam *dfp;
1312   Ref ref;
1313   SplashOutFontFileID *id;
1314   SplashFontFile *fontFile;
1315   SplashFont *fontObj;
1316   int i;
1317
1318   for (i = 0; i < 16; ++i) {
1319     if (!name->cmp(splashOutSubstFonts[i].name)) {
1320       break;
1321     }
1322   }
1323   if (i == 16) {
1324     return NULL;
1325   }
1326   ref.num = i;
1327   ref.gen = -1;
1328   id = new SplashOutFontFileID(&ref);
1329
1330   // check the font file cache
1331   if ((fontFile = fontEngine->getFontFile(id))) {
1332     delete id;
1333
1334   // load the font file
1335   } else {
1336     dfp = globalParams->getDisplayFont(name);
1337     if (dfp->kind != displayFontT1) {
1338       return NULL;
1339     }
1340     fontFile = fontEngine->loadType1Font(id, dfp->t1.fileName->getCString(),
1341                                          gFalse, winAnsiEncoding);
1342   }
1343
1344   // create the scaled font
1345   fontObj = fontEngine->getFont(fontFile, (SplashCoord *)mat);
1346
1347   return fontObj;
1348 }