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