1 //========================================================================
2 //
3 // SplashOutputDev.cc
4 //
5 // Copyright 2003-2013 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 <limits.h>
18 #include "gfile.h"
19 #include "GlobalParams.h"
20 #include "Error.h"
21 #include "Object.h"
22 #include "Gfx.h"
23 #include "GfxFont.h"
24 #include "Link.h"
25 #include "CharCodeToUnicode.h"
26 #include "FontEncodingTables.h"
27 #include "BuiltinFont.h"
28 #include "BuiltinFontTables.h"
29 #include "FoFiTrueType.h"
30 #include "JPXStream.h"
31 #include "SplashBitmap.h"
32 #include "SplashGlyphBitmap.h"
33 #include "SplashPattern.h"
34 #include "SplashScreen.h"
35 #include "SplashPath.h"
36 #include "SplashState.h"
37 #include "SplashErrorCodes.h"
38 #include "SplashFontEngine.h"
39 #include "SplashFont.h"
40 #include "SplashFontFile.h"
41 #include "SplashFontFileID.h"
42 #include "Splash.h"
43 #include "SplashOutputDev.h"
44 
45 #ifdef VMS
46 #if (__VMS_VER < 70000000)
47 extern "C" int unlink(char *filename);
48 #endif
49 #endif
50 
51 //------------------------------------------------------------------------
52 
53 // Type 3 font cache size parameters
54 #define type3FontCacheAssoc   8
55 #define type3FontCacheMaxSets 8
56 #define type3FontCacheSize    (128*1024)
57 
58 //------------------------------------------------------------------------
59 
60 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
div255(int x)61 static inline Guchar div255(int x) {
62   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
63 }
64 
65 //------------------------------------------------------------------------
66 // Blend functions
67 //------------------------------------------------------------------------
68 
splashOutBlendMultiply(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)69 static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest,
70 				   SplashColorPtr blend, SplashColorMode cm) {
71   int i;
72 
73   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
74     blend[i] = (dest[i] * src[i]) / 255;
75   }
76 }
77 
splashOutBlendScreen(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)78 static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest,
79 				 SplashColorPtr blend, SplashColorMode cm) {
80   int i;
81 
82   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
83     blend[i] = dest[i] + src[i] - (dest[i] * src[i]) / 255;
84   }
85 }
86 
splashOutBlendOverlay(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)87 static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest,
88 				  SplashColorPtr blend, SplashColorMode cm) {
89   int i;
90 
91   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
92     blend[i] = dest[i] < 0x80
93                  ? (src[i] * 2 * dest[i]) / 255
94                  : 255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255;
95   }
96 }
97 
splashOutBlendDarken(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)98 static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest,
99 				 SplashColorPtr blend, SplashColorMode cm) {
100   int i;
101 
102   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
103     blend[i] = dest[i] < src[i] ? dest[i] : src[i];
104   }
105 }
106 
splashOutBlendLighten(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)107 static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest,
108 				  SplashColorPtr blend, SplashColorMode cm) {
109   int i;
110 
111   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
112     blend[i] = dest[i] > src[i] ? dest[i] : src[i];
113   }
114 }
115 
splashOutBlendColorDodge(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)116 static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest,
117 				     SplashColorPtr blend,
118 				     SplashColorMode cm) {
119   int i, x;
120 
121   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
122     if (dest[i] == 0) {
123       blend[i] = 0;
124     } else if (src[i] == 255) {
125       blend[i] = 255;
126     } else {
127       x = (dest[i] * 255) / (255 - src[i]);
128       blend[i] = x <= 255 ? x : 255;
129     }
130   }
131 }
132 
splashOutBlendColorBurn(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)133 static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest,
134 				    SplashColorPtr blend, SplashColorMode cm) {
135   int i, x;
136 
137   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
138     if (dest[i] == 255) {
139       blend[i] = 255;
140     } else if (src[i] == 0) {
141       blend[i] = 0;
142     } else {
143       x = ((255 - dest[i]) * 255) / src[i];
144       blend[i] = x <= 255 ? 255 - x : 0;
145     }
146   }
147 }
148 
splashOutBlendHardLight(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)149 static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest,
150 				    SplashColorPtr blend, SplashColorMode cm) {
151   int i;
152 
153   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
154     blend[i] = src[i] < 0x80
155                  ? (dest[i] * 2 * src[i]) / 255
156                  : 255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255;
157   }
158 }
159 
splashOutBlendSoftLight(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)160 static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest,
161 				    SplashColorPtr blend, SplashColorMode cm) {
162   int i, x;
163 
164   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
165     if (src[i] < 0x80) {
166       blend[i] = dest[i] - (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) /
167 	         (255 * 255);
168     } else {
169       if (dest[i] < 0x40) {
170 	x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255)
171 	      + 4 * 255) * dest[i]) / 255;
172       } else {
173 	x = (int)sqrt(255.0 * dest[i]);
174       }
175       blend[i] = dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255;
176     }
177   }
178 }
179 
splashOutBlendDifference(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)180 static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest,
181 				     SplashColorPtr blend,
182 				     SplashColorMode cm) {
183   int i;
184 
185   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
186     blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i];
187   }
188 }
189 
splashOutBlendExclusion(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)190 static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest,
191 				    SplashColorPtr blend, SplashColorMode cm) {
192   int i;
193 
194   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
195     blend[i] = dest[i] + src[i] - (2 * dest[i] * src[i]) / 255;
196   }
197 }
198 
getLum(int r,int g,int b)199 static int getLum(int r, int g, int b) {
200   return (int)(0.3 * r + 0.59 * g + 0.11 * b);
201 }
202 
getSat(int r,int g,int b)203 static int getSat(int r, int g, int b) {
204   int rgbMin, rgbMax;
205 
206   rgbMin = rgbMax = r;
207   if (g < rgbMin) {
208     rgbMin = g;
209   } else if (g > rgbMax) {
210     rgbMax = g;
211   }
212   if (b < rgbMin) {
213     rgbMin = b;
214   } else if (b > rgbMax) {
215     rgbMax = b;
216   }
217   return rgbMax - rgbMin;
218 }
219 
clipColor(int rIn,int gIn,int bIn,Guchar * rOut,Guchar * gOut,Guchar * bOut)220 static void clipColor(int rIn, int gIn, int bIn,
221 		      Guchar *rOut, Guchar *gOut, Guchar *bOut) {
222   int lum, rgbMin, rgbMax;
223 
224   lum = getLum(rIn, gIn, bIn);
225   rgbMin = rgbMax = rIn;
226   if (gIn < rgbMin) {
227     rgbMin = gIn;
228   } else if (gIn > rgbMax) {
229     rgbMax = gIn;
230   }
231   if (bIn < rgbMin) {
232     rgbMin = bIn;
233   } else if (bIn > rgbMax) {
234     rgbMax = bIn;
235   }
236   if (rgbMin < 0) {
237     *rOut = (Guchar)(lum + ((rIn - lum) * lum) / (lum - rgbMin));
238     *gOut = (Guchar)(lum + ((gIn - lum) * lum) / (lum - rgbMin));
239     *bOut = (Guchar)(lum + ((bIn - lum) * lum) / (lum - rgbMin));
240   } else if (rgbMax > 255) {
241     *rOut = (Guchar)(lum + ((rIn - lum) * (255 - lum)) / (rgbMax - lum));
242     *gOut = (Guchar)(lum + ((gIn - lum) * (255 - lum)) / (rgbMax - lum));
243     *bOut = (Guchar)(lum + ((bIn - lum) * (255 - lum)) / (rgbMax - lum));
244   } else {
245     *rOut = rIn;
246     *gOut = gIn;
247     *bOut = bIn;
248   }
249 }
250 
setLum(Guchar rIn,Guchar gIn,Guchar bIn,int lum,Guchar * rOut,Guchar * gOut,Guchar * bOut)251 static void setLum(Guchar rIn, Guchar gIn, Guchar bIn, int lum,
252 		   Guchar *rOut, Guchar *gOut, Guchar *bOut) {
253   int d;
254 
255   d = lum - getLum(rIn, gIn, bIn);
256   clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut);
257 }
258 
setSat(Guchar rIn,Guchar gIn,Guchar bIn,int sat,Guchar * rOut,Guchar * gOut,Guchar * bOut)259 static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat,
260 		   Guchar *rOut, Guchar *gOut, Guchar *bOut) {
261   int rgbMin, rgbMid, rgbMax;
262   Guchar *minOut, *midOut, *maxOut;
263 
264   if (rIn < gIn) {
265     rgbMin = rIn;  minOut = rOut;
266     rgbMid = gIn;  midOut = gOut;
267   } else {
268     rgbMin = gIn;  minOut = gOut;
269     rgbMid = rIn;  midOut = rOut;
270   }
271   if (bIn > rgbMid) {
272     rgbMax = bIn;  maxOut = bOut;
273   } else if (bIn > rgbMin) {
274     rgbMax = rgbMid;  maxOut = midOut;
275     rgbMid = bIn;     midOut = bOut;
276   } else {
277     rgbMax = rgbMid;  maxOut = midOut;
278     rgbMid = rgbMin;  midOut = minOut;
279     rgbMin = bIn;     minOut = bOut;
280   }
281   if (rgbMax > rgbMin) {
282     *midOut = (Guchar)((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin);
283     *maxOut = (Guchar)sat;
284   } else {
285     *midOut = *maxOut = 0;
286   }
287   *minOut = 0;
288 }
289 
splashOutBlendHue(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)290 static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
291 			      SplashColorPtr blend, SplashColorMode cm) {
292   Guchar r0, g0, b0;
293 #if SPLASH_CMYK
294   Guchar r1, g1, b1;
295 #endif
296 
297   switch (cm) {
298   case splashModeMono1:
299   case splashModeMono8:
300     blend[0] = dest[0];
301     break;
302   case splashModeRGB8:
303   case splashModeBGR8:
304     setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
305 	   &r0, &g0, &b0);
306     setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
307 	   &blend[0], &blend[1], &blend[2]);
308     break;
309 #if SPLASH_CMYK
310   case splashModeCMYK8:
311     // NB: inputs have already been converted to additive mode
312     setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
313 	   &r0, &g0, &b0);
314     setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
315 	   &r1, &g1, &b1);
316     blend[0] = r1;
317     blend[1] = g1;
318     blend[2] = b1;
319     blend[3] = dest[3];
320     break;
321 #endif
322   }
323 }
324 
splashOutBlendSaturation(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)325 static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
326 				     SplashColorPtr blend,
327 				     SplashColorMode cm) {
328   Guchar r0, g0, b0;
329 #if SPLASH_CMYK
330   Guchar r1, g1, b1;
331 #endif
332 
333   switch (cm) {
334   case splashModeMono1:
335   case splashModeMono8:
336     blend[0] = dest[0];
337     break;
338   case splashModeRGB8:
339   case splashModeBGR8:
340     setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]),
341 	   &r0, &g0, &b0);
342     setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
343 	   &blend[0], &blend[1], &blend[2]);
344     break;
345 #if SPLASH_CMYK
346   case splashModeCMYK8:
347     // NB: inputs have already been converted to additive mode
348     setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]),
349 	   &r0, &g0, &b0);
350     setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
351 	   &r1, &g1, &b1);
352     blend[0] = r1;
353     blend[1] = g1;
354     blend[2] = b1;
355     blend[3] = dest[3];
356     break;
357 #endif
358   }
359 }
360 
splashOutBlendColor(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)361 static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,
362 				SplashColorPtr blend, SplashColorMode cm) {
363 #if SPLASH_CMYK
364   Guchar r, g, b;
365 #endif
366 
367   switch (cm) {
368   case splashModeMono1:
369   case splashModeMono8:
370     blend[0] = dest[0];
371     break;
372   case splashModeRGB8:
373   case splashModeBGR8:
374     setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]),
375 	   &blend[0], &blend[1], &blend[2]);
376     break;
377 #if SPLASH_CMYK
378   case splashModeCMYK8:
379     // NB: inputs have already been converted to additive mode
380     setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]),
381 	   &r, &g, &b);
382     blend[0] = r;
383     blend[1] = g;
384     blend[2] = b;
385     blend[3] = dest[3];
386     break;
387 #endif
388   }
389 }
390 
splashOutBlendLuminosity(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)391 static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,
392 				     SplashColorPtr blend,
393 				     SplashColorMode cm) {
394 #if SPLASH_CMYK
395   Guchar r, g, b;
396 #endif
397 
398   switch (cm) {
399   case splashModeMono1:
400   case splashModeMono8:
401     blend[0] = dest[0];
402     break;
403   case splashModeRGB8:
404   case splashModeBGR8:
405     setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
406 	   &blend[0], &blend[1], &blend[2]);
407     break;
408 #if SPLASH_CMYK
409   case splashModeCMYK8:
410     // NB: inputs have already been converted to additive mode
411     setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
412 	   &r, &g, &b);
413     blend[0] = r;
414     blend[1] = g;
415     blend[2] = b;
416     blend[3] = src[3];
417     break;
418 #endif
419   }
420 }
421 
422 // NB: This must match the GfxBlendMode enum defined in GfxState.h.
423 SplashBlendFunc splashOutBlendFuncs[] = {
424   NULL,
425   &splashOutBlendMultiply,
426   &splashOutBlendScreen,
427   &splashOutBlendOverlay,
428   &splashOutBlendDarken,
429   &splashOutBlendLighten,
430   &splashOutBlendColorDodge,
431   &splashOutBlendColorBurn,
432   &splashOutBlendHardLight,
433   &splashOutBlendSoftLight,
434   &splashOutBlendDifference,
435   &splashOutBlendExclusion,
436   &splashOutBlendHue,
437   &splashOutBlendSaturation,
438   &splashOutBlendColor,
439   &splashOutBlendLuminosity
440 };
441 
442 //------------------------------------------------------------------------
443 // SplashOutFontFileID
444 //------------------------------------------------------------------------
445 
446 class SplashOutFontFileID: public SplashFontFileID {
447 public:
448 
SplashOutFontFileID(Ref * rA)449   SplashOutFontFileID(Ref *rA) {
450     r = *rA;
451     substIdx = -1;
452     oblique = 0;
453   }
454 
~SplashOutFontFileID()455   ~SplashOutFontFileID() {}
456 
matches(SplashFontFileID * id)457   GBool matches(SplashFontFileID *id) {
458     return ((SplashOutFontFileID *)id)->r.num == r.num &&
459            ((SplashOutFontFileID *)id)->r.gen == r.gen;
460   }
461 
setOblique(double obliqueA)462   void setOblique(double obliqueA) { oblique = obliqueA; }
getOblique()463   double getOblique() { return oblique; }
setSubstIdx(int substIdxA)464   void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
getSubstIdx()465   int getSubstIdx() { return substIdx; }
466 
467 private:
468 
469   Ref r;
470   double oblique;
471   int substIdx;
472 };
473 
474 //------------------------------------------------------------------------
475 // T3FontCache
476 //------------------------------------------------------------------------
477 
478 struct T3FontCacheTag {
479   Gushort code;
480   Gushort mru;			// valid bit (0x8000) and MRU index
481 };
482 
483 class T3FontCache {
484 public:
485 
486   T3FontCache(Ref *fontID, double m11A, double m12A,
487 	      double m21A, double m22A,
488 	      int glyphXA, int glyphYA, int glyphWA, int glyphHA,
489 	      GBool aa, GBool validBBoxA);
490   ~T3FontCache();
matches(Ref * idA,double m11A,double m12A,double m21A,double m22A)491   GBool matches(Ref *idA, double m11A, double m12A,
492 		double m21A, double m22A)
493     { return fontID.num == idA->num && fontID.gen == idA->gen &&
494 	     m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
495 
496   Ref fontID;			// PDF font ID
497   double m11, m12, m21, m22;	// transform matrix
498   int glyphX, glyphY;		// pixel offset of glyph bitmaps
499   int glyphW, glyphH;		// size of glyph bitmaps, in pixels
500   GBool validBBox;		// false if the bbox was [0 0 0 0]
501   int glyphSize;		// size of glyph bitmaps, in bytes
502   int cacheSets;		// number of sets in cache
503   int cacheAssoc;		// cache associativity (glyphs per set)
504   Guchar *cacheData;		// glyph pixmap cache
505   T3FontCacheTag *cacheTags;	// cache tags, i.e., char codes
506 };
507 
T3FontCache(Ref * fontIDA,double m11A,double m12A,double m21A,double m22A,int glyphXA,int glyphYA,int glyphWA,int glyphHA,GBool validBBoxA,GBool aa)508 T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
509 			 double m21A, double m22A,
510 			 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
511 			 GBool validBBoxA, GBool aa) {
512   int i;
513 
514   fontID = *fontIDA;
515   m11 = m11A;
516   m12 = m12A;
517   m21 = m21A;
518   m22 = m22A;
519   glyphX = glyphXA;
520   glyphY = glyphYA;
521   glyphW = glyphWA;
522   glyphH = glyphHA;
523   validBBox = validBBoxA;
524   // sanity check for excessively large glyphs (which most likely
525   // indicate an incorrect BBox)
526   i = glyphW * glyphH;
527   if (i > 100000 || glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0) {
528     glyphW = glyphH = 100;
529     validBBox = gFalse;
530   }
531   if (aa) {
532     glyphSize = glyphW * glyphH;
533   } else {
534     glyphSize = ((glyphW + 7) >> 3) * glyphH;
535   }
536   cacheAssoc = type3FontCacheAssoc;
537   for (cacheSets = type3FontCacheMaxSets;
538        cacheSets > 1 &&
539 	 cacheSets * cacheAssoc * glyphSize > type3FontCacheSize;
540        cacheSets >>= 1) ;
541   cacheData = (Guchar *)gmallocn(cacheSets * cacheAssoc, glyphSize);
542   cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc,
543 					 sizeof(T3FontCacheTag));
544   for (i = 0; i < cacheSets * cacheAssoc; ++i) {
545     cacheTags[i].mru = i & (cacheAssoc - 1);
546   }
547 }
548 
~T3FontCache()549 T3FontCache::~T3FontCache() {
550   gfree(cacheData);
551   gfree(cacheTags);
552 }
553 
554 struct T3GlyphStack {
555   Gushort code;			// character code
556 
557   GBool haveDx;			// set after seeing a d0/d1 operator
558   GBool doNotCache;		// set if we see a gsave/grestore before
559 				//   the d0/d1
560 
561   //----- cache info
562   T3FontCache *cache;		// font cache for the current font
563   T3FontCacheTag *cacheTag;	// pointer to cache tag for the glyph
564   Guchar *cacheData;		// pointer to cache data for the glyph
565 
566   //----- saved state
567   SplashBitmap *origBitmap;
568   Splash *origSplash;
569   double origCTM4, origCTM5;
570 
571   T3GlyphStack *next;		// next object on stack
572 };
573 
574 //------------------------------------------------------------------------
575 // SplashTransparencyGroup
576 //------------------------------------------------------------------------
577 
578 struct SplashTransparencyGroup {
579   int tx, ty;			// translation coordinates
580   SplashBitmap *tBitmap;	// bitmap for transparency group
581   GfxColorSpace *blendingColorSpace;
582   GBool isolated;
583 
584   //----- saved state
585   SplashBitmap *origBitmap;
586   Splash *origSplash;
587 
588   SplashTransparencyGroup *next;
589 };
590 
591 //------------------------------------------------------------------------
592 // SplashOutputDev
593 //------------------------------------------------------------------------
594 
SplashOutputDev(SplashColorMode colorModeA,int bitmapRowPadA,GBool reverseVideoA,SplashColorPtr paperColorA,GBool bitmapTopDownA,GBool allowAntialiasA)595 SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
596 				 int bitmapRowPadA,
597 				 GBool reverseVideoA,
598 				 SplashColorPtr paperColorA,
599 				 GBool bitmapTopDownA,
600 				 GBool allowAntialiasA) {
601   colorMode = colorModeA;
602   bitmapRowPad = bitmapRowPadA;
603   bitmapTopDown = bitmapTopDownA;
604   bitmapUpsideDown = gFalse;
605   noComposite = gFalse;
606   allowAntialias = allowAntialiasA;
607   vectorAntialias = allowAntialias &&
608 		      globalParams->getVectorAntialias() &&
609 		      colorMode != splashModeMono1;
610   setupScreenParams(72.0, 72.0);
611   reverseVideo = reverseVideoA;
612   splashColorCopy(paperColor, paperColorA);
613   skipHorizText = gFalse;
614   skipRotatedText = gFalse;
615 
616   xref = NULL;
617 
618   bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
619 			    colorMode != splashModeMono1, bitmapTopDown);
620   splash = new Splash(bitmap, vectorAntialias, &screenParams);
621   splash->setMinLineWidth(globalParams->getMinLineWidth());
622   splash->setStrokeAdjust(globalParams->getStrokeAdjust());
623   splash->clear(paperColor, 0);
624 
625   fontEngine = NULL;
626 
627   nT3Fonts = 0;
628   t3GlyphStack = NULL;
629 
630   font = NULL;
631   needFontUpdate = gFalse;
632   textClipPath = NULL;
633 
634   transpGroupStack = NULL;
635 
636   nestCount = 0;
637 }
638 
setupScreenParams(double hDPI,double vDPI)639 void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) {
640   screenParams.size = globalParams->getScreenSize();
641   screenParams.dotRadius = globalParams->getScreenDotRadius();
642   screenParams.gamma = (SplashCoord)globalParams->getScreenGamma();
643   screenParams.blackThreshold =
644       (SplashCoord)globalParams->getScreenBlackThreshold();
645   screenParams.whiteThreshold =
646       (SplashCoord)globalParams->getScreenWhiteThreshold();
647   switch (globalParams->getScreenType()) {
648   case screenDispersed:
649     screenParams.type = splashScreenDispersed;
650     if (screenParams.size < 0) {
651       screenParams.size = 4;
652     }
653     break;
654   case screenClustered:
655     screenParams.type = splashScreenClustered;
656     if (screenParams.size < 0) {
657       screenParams.size = 10;
658     }
659     break;
660   case screenStochasticClustered:
661     screenParams.type = splashScreenStochasticClustered;
662     if (screenParams.size < 0) {
663       screenParams.size = 64;
664     }
665     if (screenParams.dotRadius < 0) {
666       screenParams.dotRadius = 2;
667     }
668     break;
669   case screenUnset:
670   default:
671     // use clustered dithering for resolution >= 300 dpi
672     // (compare to 299.9 to avoid floating point issues)
673     if (hDPI > 299.9 && vDPI > 299.9) {
674       screenParams.type = splashScreenStochasticClustered;
675       if (screenParams.size < 0) {
676 	screenParams.size = 64;
677       }
678       if (screenParams.dotRadius < 0) {
679 	screenParams.dotRadius = 2;
680       }
681     } else {
682       screenParams.type = splashScreenDispersed;
683       if (screenParams.size < 0) {
684 	screenParams.size = 4;
685       }
686     }
687   }
688 }
689 
~SplashOutputDev()690 SplashOutputDev::~SplashOutputDev() {
691   int i;
692 
693   for (i = 0; i < nT3Fonts; ++i) {
694     delete t3FontCache[i];
695   }
696   if (fontEngine) {
697     delete fontEngine;
698   }
699   if (splash) {
700     delete splash;
701   }
702   if (bitmap) {
703     delete bitmap;
704   }
705 }
706 
startDoc(XRef * xrefA)707 void SplashOutputDev::startDoc(XRef *xrefA) {
708   int i;
709 
710   xref = xrefA;
711   if (fontEngine) {
712     delete fontEngine;
713   }
714   fontEngine = new SplashFontEngine(
715 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
716 				    globalParams->getEnableFreeType(),
717 				    globalParams->getDisableFreeTypeHinting()
718 				      ? splashFTNoHinting : 0,
719 #endif
720 				    allowAntialias &&
721 				      globalParams->getAntialias() &&
722 				      colorMode != splashModeMono1);
723   for (i = 0; i < nT3Fonts; ++i) {
724     delete t3FontCache[i];
725   }
726   nT3Fonts = 0;
727 }
728 
startPage(int pageNum,GfxState * state)729 void SplashOutputDev::startPage(int pageNum, GfxState *state) {
730   int w, h;
731   double *ctm;
732   SplashCoord mat[6];
733   SplashColor color;
734 
735   if (state) {
736     setupScreenParams(state->getHDPI(), state->getVDPI());
737     w = (int)(state->getPageWidth() + 0.5);
738     if (w <= 0) {
739       w = 1;
740     }
741     h = (int)(state->getPageHeight() + 0.5);
742     if (h <= 0) {
743       h = 1;
744     }
745   } else {
746     w = h = 1;
747   }
748   if (splash) {
749     delete splash;
750     splash = NULL;
751   }
752   if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
753     if (bitmap) {
754       delete bitmap;
755       bitmap = NULL;
756     }
757     bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode,
758 			      colorMode != splashModeMono1, bitmapTopDown);
759   }
760   splash = new Splash(bitmap, vectorAntialias, &screenParams);
761   splash->setMinLineWidth(globalParams->getMinLineWidth());
762   if (state) {
763     ctm = state->getCTM();
764     mat[0] = (SplashCoord)ctm[0];
765     mat[1] = (SplashCoord)ctm[1];
766     mat[2] = (SplashCoord)ctm[2];
767     mat[3] = (SplashCoord)ctm[3];
768     mat[4] = (SplashCoord)ctm[4];
769     mat[5] = (SplashCoord)ctm[5];
770     splash->setMatrix(mat);
771   }
772   switch (colorMode) {
773   case splashModeMono1:
774   case splashModeMono8:
775     color[0] = 0;
776     break;
777   case splashModeRGB8:
778   case splashModeBGR8:
779     color[0] = color[1] = color[2] = 0;
780     break;
781 #if SPLASH_CMYK
782   case splashModeCMYK8:
783     color[0] = color[1] = color[2] = color[3] = 0;
784     break;
785 #endif
786   }
787   splash->setStrokePattern(new SplashSolidColor(color));
788   splash->setFillPattern(new SplashSolidColor(color));
789   splash->setLineCap(splashLineCapButt);
790   splash->setLineJoin(splashLineJoinMiter);
791   splash->setLineDash(NULL, 0, 0);
792   splash->setMiterLimit(10);
793   splash->setFlatness(1);
794   // the SA parameter supposedly defaults to false, but Acrobat
795   // apparently hardwires it to true
796   splash->setStrokeAdjust(globalParams->getStrokeAdjust());
797   splash->clear(paperColor, 0);
798 }
799 
endPage()800 void SplashOutputDev::endPage() {
801   if (colorMode != splashModeMono1 && !noComposite) {
802     splash->compositeBackground(paperColor);
803   }
804 }
805 
saveState(GfxState * state)806 void SplashOutputDev::saveState(GfxState *state) {
807   splash->saveState();
808   if (t3GlyphStack && !t3GlyphStack->haveDx) {
809     t3GlyphStack->doNotCache = gTrue;
810     error(errSyntaxWarning, -1,
811 	  "Save (q) operator before d0/d1 in Type 3 glyph");
812   }
813 }
814 
restoreState(GfxState * state)815 void SplashOutputDev::restoreState(GfxState *state) {
816   splash->restoreState();
817   needFontUpdate = gTrue;
818   if (t3GlyphStack && !t3GlyphStack->haveDx) {
819     t3GlyphStack->doNotCache = gTrue;
820     error(errSyntaxWarning, -1,
821 	  "Restore (Q) operator before d0/d1 in Type 3 glyph");
822   }
823 }
824 
updateAll(GfxState * state)825 void SplashOutputDev::updateAll(GfxState *state) {
826   updateLineDash(state);
827   updateLineJoin(state);
828   updateLineCap(state);
829   updateLineWidth(state);
830   updateFlatness(state);
831   updateMiterLimit(state);
832   updateStrokeAdjust(state);
833   updateFillColor(state);
834   updateStrokeColor(state);
835   needFontUpdate = gTrue;
836 }
837 
updateCTM(GfxState * state,double m11,double m12,double m21,double m22,double m31,double m32)838 void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
839 				double m21, double m22,
840 				double m31, double m32) {
841   double *ctm;
842   SplashCoord mat[6];
843 
844   ctm = state->getCTM();
845   mat[0] = (SplashCoord)ctm[0];
846   mat[1] = (SplashCoord)ctm[1];
847   mat[2] = (SplashCoord)ctm[2];
848   mat[3] = (SplashCoord)ctm[3];
849   mat[4] = (SplashCoord)ctm[4];
850   mat[5] = (SplashCoord)ctm[5];
851   splash->setMatrix(mat);
852 }
853 
updateLineDash(GfxState * state)854 void SplashOutputDev::updateLineDash(GfxState *state) {
855   double *dashPattern;
856   int dashLength;
857   double dashStart;
858   SplashCoord dash[20];
859   int i;
860 
861   state->getLineDash(&dashPattern, &dashLength, &dashStart);
862   if (dashLength > 20) {
863     dashLength = 20;
864   }
865   for (i = 0; i < dashLength; ++i) {
866     dash[i] = (SplashCoord)dashPattern[i];
867     if (dash[i] < 0) {
868       dash[i] = 0;
869     }
870   }
871   splash->setLineDash(dash, dashLength, (SplashCoord)dashStart);
872 }
873 
updateFlatness(GfxState * state)874 void SplashOutputDev::updateFlatness(GfxState *state) {
875 #if 0 // Acrobat ignores the flatness setting, and always renders curves
876       // with a fairly small flatness value
877   splash->setFlatness(state->getFlatness());
878 #endif
879 }
880 
updateLineJoin(GfxState * state)881 void SplashOutputDev::updateLineJoin(GfxState *state) {
882   splash->setLineJoin(state->getLineJoin());
883 }
884 
updateLineCap(GfxState * state)885 void SplashOutputDev::updateLineCap(GfxState *state) {
886   splash->setLineCap(state->getLineCap());
887 }
888 
updateMiterLimit(GfxState * state)889 void SplashOutputDev::updateMiterLimit(GfxState *state) {
890   splash->setMiterLimit(state->getMiterLimit());
891 }
892 
updateLineWidth(GfxState * state)893 void SplashOutputDev::updateLineWidth(GfxState *state) {
894   splash->setLineWidth(state->getLineWidth());
895 }
896 
updateStrokeAdjust(GfxState * state)897 void SplashOutputDev::updateStrokeAdjust(GfxState *state) {
898 #if 0 // the SA parameter supposedly defaults to false, but Acrobat
899       // apparently hardwires it to true
900   splash->setStrokeAdjust(state->getStrokeAdjust());
901 #endif
902 }
903 
updateFillColor(GfxState * state)904 void SplashOutputDev::updateFillColor(GfxState *state) {
905   GfxGray gray;
906   GfxRGB rgb;
907 #if SPLASH_CMYK
908   GfxCMYK cmyk;
909 #endif
910 
911   switch (colorMode) {
912   case splashModeMono1:
913   case splashModeMono8:
914     state->getFillGray(&gray);
915     splash->setFillPattern(getColor(gray));
916     break;
917   case splashModeRGB8:
918   case splashModeBGR8:
919     state->getFillRGB(&rgb);
920     splash->setFillPattern(getColor(&rgb));
921     break;
922 #if SPLASH_CMYK
923   case splashModeCMYK8:
924     state->getFillCMYK(&cmyk);
925     splash->setFillPattern(getColor(&cmyk));
926     break;
927 #endif
928   }
929 }
930 
updateStrokeColor(GfxState * state)931 void SplashOutputDev::updateStrokeColor(GfxState *state) {
932   GfxGray gray;
933   GfxRGB rgb;
934 #if SPLASH_CMYK
935   GfxCMYK cmyk;
936 #endif
937 
938   switch (colorMode) {
939   case splashModeMono1:
940   case splashModeMono8:
941     state->getStrokeGray(&gray);
942     splash->setStrokePattern(getColor(gray));
943     break;
944   case splashModeRGB8:
945   case splashModeBGR8:
946     state->getStrokeRGB(&rgb);
947     splash->setStrokePattern(getColor(&rgb));
948     break;
949 #if SPLASH_CMYK
950   case splashModeCMYK8:
951     state->getStrokeCMYK(&cmyk);
952     splash->setStrokePattern(getColor(&cmyk));
953     break;
954 #endif
955   }
956 }
957 
getColor(GfxGray gray)958 SplashPattern *SplashOutputDev::getColor(GfxGray gray) {
959   SplashColor color;
960 
961   if (reverseVideo) {
962     gray = gfxColorComp1 - gray;
963   }
964   color[0] = colToByte(gray);
965   return new SplashSolidColor(color);
966 }
967 
getColor(GfxRGB * rgb)968 SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) {
969   GfxColorComp r, g, b;
970   SplashColor color;
971 
972   if (reverseVideo) {
973     r = gfxColorComp1 - rgb->r;
974     g = gfxColorComp1 - rgb->g;
975     b = gfxColorComp1 - rgb->b;
976   } else {
977     r = rgb->r;
978     g = rgb->g;
979     b = rgb->b;
980   }
981   color[0] = colToByte(r);
982   color[1] = colToByte(g);
983   color[2] = colToByte(b);
984   return new SplashSolidColor(color);
985 }
986 
987 #if SPLASH_CMYK
getColor(GfxCMYK * cmyk)988 SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) {
989   SplashColor color;
990 
991   color[0] = colToByte(cmyk->c);
992   color[1] = colToByte(cmyk->m);
993   color[2] = colToByte(cmyk->y);
994   color[3] = colToByte(cmyk->k);
995   return new SplashSolidColor(color);
996 }
997 #endif
998 
999 
setOverprintMask(GfxColorSpace * colorSpace,GBool overprintFlag,int overprintMode,GfxColor * singleColor)1000 void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace,
1001 				       GBool overprintFlag,
1002 				       int overprintMode,
1003 				       GfxColor *singleColor) {
1004 #if SPLASH_CMYK
1005   Guint mask;
1006   GfxCMYK cmyk;
1007 
1008   if (overprintFlag && globalParams->getOverprintPreview()) {
1009     mask = colorSpace->getOverprintMask();
1010     // The OPM (overprintMode) setting is only relevant when the color
1011     // space is DeviceCMYK or is "implicitly converted to DeviceCMYK".
1012     // Per the PDF spec, this happens with ICCBased color spaces only
1013     // if the profile matches the output device -- Acrobat's output
1014     // preview mode does NOT honor OPM=1 for ICCBased CMYK color
1015     // spaces.  To change the behavior here, use:
1016     //    if (singleColor && overprintMode &&
1017     //        (colorSpace->getMode() == csDeviceCMYK ||
1018     //         (colorSpace->getMode() == csICCBased &&
1019     //          colorSpace->getNComps() == 4 &&
1020     //          <...the profile matches...>)))
1021     if (singleColor && overprintMode &&
1022 	colorSpace->getMode() == csDeviceCMYK) {
1023       colorSpace->getCMYK(singleColor, &cmyk);
1024       if (cmyk.c == 0) {
1025 	mask &= ~1;
1026       }
1027       if (cmyk.m == 0) {
1028 	mask &= ~2;
1029       }
1030       if (cmyk.y == 0) {
1031 	mask &= ~4;
1032       }
1033       if (cmyk.k == 0) {
1034 	mask &= ~8;
1035       }
1036     }
1037   } else {
1038     mask = 0xffffffff;
1039   }
1040   splash->setOverprintMask(mask);
1041 #endif
1042 }
1043 
updateBlendMode(GfxState * state)1044 void SplashOutputDev::updateBlendMode(GfxState *state) {
1045   splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]);
1046 }
1047 
updateFillOpacity(GfxState * state)1048 void SplashOutputDev::updateFillOpacity(GfxState *state) {
1049   splash->setFillAlpha((SplashCoord)state->getFillOpacity());
1050 }
1051 
updateStrokeOpacity(GfxState * state)1052 void SplashOutputDev::updateStrokeOpacity(GfxState *state) {
1053   splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity());
1054 }
1055 
updateTransfer(GfxState * state)1056 void SplashOutputDev::updateTransfer(GfxState *state) {
1057   Function **transfer;
1058   Guchar red[256], green[256], blue[256], gray[256];
1059   double x, y;
1060   int i;
1061 
1062   transfer = state->getTransfer();
1063   if (transfer[0] &&
1064       transfer[0]->getInputSize() == 1 &&
1065       transfer[0]->getOutputSize() == 1) {
1066     if (transfer[1] &&
1067 	transfer[1]->getInputSize() == 1 &&
1068 	transfer[1]->getOutputSize() == 1 &&
1069 	transfer[2] &&
1070 	transfer[2]->getInputSize() == 1 &&
1071 	transfer[2]->getOutputSize() == 1 &&
1072 	transfer[3] &&
1073 	transfer[3]->getInputSize() == 1 &&
1074 	transfer[3]->getOutputSize() == 1) {
1075       for (i = 0; i < 256; ++i) {
1076 	x = i / 255.0;
1077 	transfer[0]->transform(&x, &y);
1078 	red[i] = (Guchar)(y * 255.0 + 0.5);
1079 	transfer[1]->transform(&x, &y);
1080 	green[i] = (Guchar)(y * 255.0 + 0.5);
1081 	transfer[2]->transform(&x, &y);
1082 	blue[i] = (Guchar)(y * 255.0 + 0.5);
1083 	transfer[3]->transform(&x, &y);
1084 	gray[i] = (Guchar)(y * 255.0 + 0.5);
1085       }
1086     } else {
1087       for (i = 0; i < 256; ++i) {
1088 	x = i / 255.0;
1089 	transfer[0]->transform(&x, &y);
1090 	red[i] = green[i] = blue[i] = gray[i] = (Guchar)(y * 255.0 + 0.5);
1091       }
1092     }
1093   } else {
1094     for (i = 0; i < 256; ++i) {
1095       red[i] = green[i] = blue[i] = gray[i] = (Guchar)i;
1096     }
1097   }
1098   splash->setTransfer(red, green, blue, gray);
1099 }
1100 
updateFont(GfxState * state)1101 void SplashOutputDev::updateFont(GfxState *state) {
1102   needFontUpdate = gTrue;
1103 }
1104 
doUpdateFont(GfxState * state)1105 void SplashOutputDev::doUpdateFont(GfxState *state) {
1106   GfxFont *gfxFont;
1107   GfxFontLoc *fontLoc;
1108   GfxFontType fontType;
1109   SplashOutFontFileID *id;
1110   SplashFontFile *fontFile;
1111   int fontNum;
1112   FoFiTrueType *ff;
1113   Ref embRef;
1114   Object refObj, strObj;
1115 #if LOAD_FONTS_FROM_MEM
1116   GString *fontBuf;
1117   FILE *extFontFile;
1118 #else
1119   GString *tmpFileName, *fileName;
1120   FILE *tmpFile;
1121 #endif
1122   char blk[4096];
1123   int *codeToGID;
1124   CharCodeToUnicode *ctu;
1125   double *textMat;
1126   double m11, m12, m21, m22, fontSize, oblique;
1127   double fsx, fsy, w, fontScaleMin, fontScaleAvg, fontScale;
1128   Gushort ww;
1129   SplashCoord mat[4];
1130   char *name;
1131   Unicode uBuf[8];
1132   int substIdx, n, code, cmap, i;
1133 
1134   needFontUpdate = gFalse;
1135   font = NULL;
1136 #if LOAD_FONTS_FROM_MEM
1137   fontBuf = NULL;
1138 #else
1139   tmpFileName = NULL;
1140   fileName = NULL;
1141 #endif
1142   substIdx = -1;
1143 
1144   if (!(gfxFont = state->getFont())) {
1145     goto err1;
1146   }
1147   fontType = gfxFont->getType();
1148   if (fontType == fontType3) {
1149     goto err1;
1150   }
1151 
1152   // sanity-check the font size - skip anything larger than 20 inches
1153   // (this avoids problems allocating memory for the font cache)
1154   state->textTransformDelta(state->getFontSize(), state->getFontSize(),
1155 			    &fsx, &fsy);
1156   state->transformDelta(fsx, fsy, &fsx, &fsy);
1157   if (fabs(fsx) > 20 * state->getHDPI() ||
1158       fabs(fsy) > 20 * state->getVDPI()) {
1159     goto err1;
1160   }
1161 
1162   // check the font file cache
1163   id = new SplashOutFontFileID(gfxFont->getID());
1164   if ((fontFile = fontEngine->getFontFile(id))) {
1165     delete id;
1166 
1167   } else {
1168 
1169     fontNum = 0;
1170 
1171     if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) {
1172       error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'",
1173 	    gfxFont->getName() ? gfxFont->getName()->getCString()
1174 	                       : "(unnamed)");
1175       goto err2;
1176     }
1177 
1178     // embedded font
1179     if (fontLoc->locType == gfxFontLocEmbedded) {
1180       gfxFont->getEmbeddedFontID(&embRef);
1181 #if LOAD_FONTS_FROM_MEM
1182       fontBuf = new GString();
1183       refObj.initRef(embRef.num, embRef.gen);
1184       refObj.fetch(xref, &strObj);
1185       refObj.free();
1186       if (!strObj.isStream()) {
1187 	error(errSyntaxError, -1, "Embedded font object is wrong type");
1188 	strObj.free();
1189 	delete fontLoc;
1190 	goto err2;
1191       }
1192       strObj.streamReset();
1193       while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) {
1194 	fontBuf->append(blk, n);
1195       }
1196       strObj.streamClose();
1197       strObj.free();
1198 #else
1199       if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
1200 	error(errIO, -1, "Couldn't create temporary font file");
1201 	delete fontLoc;
1202 	goto err2;
1203       }
1204       refObj.initRef(embRef.num, embRef.gen);
1205       refObj.fetch(xref, &strObj);
1206       refObj.free();
1207       if (!strObj.isStream()) {
1208 	error(errSyntaxError, -1, "Embedded font object is wrong type");
1209 	strObj.free();
1210 	fclose(tmpFile);
1211 	delete fontLoc;
1212 	goto err2;
1213       }
1214       strObj.streamReset();
1215       while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) {
1216 	fwrite(blk, 1, n, tmpFile);
1217       }
1218       strObj.streamClose();
1219       strObj.free();
1220       fclose(tmpFile);
1221       fileName = tmpFileName;
1222 #endif
1223 
1224     // external font
1225     } else { // gfxFontLocExternal
1226 #if LOAD_FONTS_FROM_MEM
1227       if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) {
1228 	error(errSyntaxError, -1, "Couldn't open external font file '{0:t}'",
1229 	      fontLoc->path);
1230 	delete fontLoc;
1231 	goto err2;
1232       }
1233       fontBuf = new GString();
1234       while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) {
1235 	fontBuf->append(blk, n);
1236       }
1237       fclose(extFontFile);
1238 #else
1239       fileName = fontLoc->path;
1240 #endif
1241       fontNum = fontLoc->fontNum;
1242       if (fontLoc->substIdx >= 0) {
1243 	id->setSubstIdx(fontLoc->substIdx);
1244       }
1245       if (fontLoc->oblique != 0) {
1246 	id->setOblique(fontLoc->oblique);
1247       }
1248     }
1249 
1250     // load the font file
1251     switch (fontLoc->fontType) {
1252     case fontType1:
1253       if (!(fontFile = fontEngine->loadType1Font(
1254 		   id,
1255 #if LOAD_FONTS_FROM_MEM
1256 		   fontBuf,
1257 #else
1258 		   fileName->getCString(),
1259 		   fileName == tmpFileName,
1260 #endif
1261 		   (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1262 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1263 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1264 	                         : "(unnamed)");
1265 	delete fontLoc;
1266 	goto err2;
1267       }
1268       break;
1269     case fontType1C:
1270       if (!(fontFile = fontEngine->loadType1CFont(
1271 		   id,
1272 #if LOAD_FONTS_FROM_MEM
1273 		   fontBuf,
1274 #else
1275 		   fileName->getCString(),
1276 		   fileName == tmpFileName,
1277 #endif
1278 		   (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1279 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1280 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1281 	                         : "(unnamed)");
1282 	delete fontLoc;
1283 	goto err2;
1284       }
1285       break;
1286     case fontType1COT:
1287       if (!(fontFile = fontEngine->loadOpenTypeT1CFont(
1288 		   id,
1289 #if LOAD_FONTS_FROM_MEM
1290 		   fontBuf,
1291 #else
1292 		   fileName->getCString(),
1293 		   fileName == tmpFileName,
1294 #endif
1295 		   (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1296 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1297 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1298 	                         : "(unnamed)");
1299 	delete fontLoc;
1300 	goto err2;
1301       }
1302       break;
1303     case fontTrueType:
1304     case fontTrueTypeOT:
1305 #if LOAD_FONTS_FROM_MEM
1306       if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
1307 				   fontNum))) {
1308 #else
1309       if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
1310 #endif
1311 	codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
1312 	n = 256;
1313 	delete ff;
1314 	// if we're substituting for a non-TrueType font, we need to mark
1315 	// all notdef codes as "do not draw" (rather than drawing TrueType
1316 	// notdef glyphs)
1317 	if (gfxFont->getType() != fontTrueType &&
1318 	    gfxFont->getType() != fontTrueTypeOT) {
1319 	  for (i = 0; i < 256; ++i) {
1320 	    if (codeToGID[i] == 0) {
1321 	      codeToGID[i] = -1;
1322 	    }
1323 	  }
1324 	}
1325       } else {
1326 	codeToGID = NULL;
1327 	n = 0;
1328       }
1329       if (!(fontFile = fontEngine->loadTrueTypeFont(
1330 			   id,
1331 #if LOAD_FONTS_FROM_MEM
1332 			   fontBuf,
1333 #else
1334 			   fileName->getCString(),
1335 			   fileName == tmpFileName,
1336 #endif
1337 			   fontNum, codeToGID, n,
1338 			   gfxFont->getEmbeddedFontName()
1339 			     ? gfxFont->getEmbeddedFontName()->getCString()
1340 			     : (char *)NULL))) {
1341 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1342 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1343 	                         : "(unnamed)");
1344 	delete fontLoc;
1345 	goto err2;
1346       }
1347       break;
1348     case fontCIDType0:
1349     case fontCIDType0C:
1350       if (!(fontFile = fontEngine->loadCIDFont(
1351 			   id,
1352 #if LOAD_FONTS_FROM_MEM
1353 			   fontBuf
1354 #else
1355 			   fileName->getCString(),
1356 			   fileName == tmpFileName
1357 #endif
1358 			))) {
1359 
1360 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1361 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1362 	                         : "(unnamed)");
1363 	delete fontLoc;
1364 	goto err2;
1365       }
1366       break;
1367     case fontCIDType0COT:
1368       if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
1369 	n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
1370 	codeToGID = (int *)gmallocn(n, sizeof(int));
1371 	memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1372 	       n * sizeof(int));
1373       } else {
1374 	codeToGID = NULL;
1375 	n = 0;
1376       }
1377       if (!(fontFile = fontEngine->loadOpenTypeCFFFont(
1378 			   id,
1379 #if LOAD_FONTS_FROM_MEM
1380 			   fontBuf,
1381 #else
1382 			   fileName->getCString(),
1383 			   fileName == tmpFileName,
1384 #endif
1385 			   codeToGID, n))) {
1386 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1387 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1388 	                         : "(unnamed)");
1389 	delete fontLoc;
1390 	goto err2;
1391       }
1392       break;
1393     case fontCIDType2:
1394     case fontCIDType2OT:
1395       codeToGID = NULL;
1396       n = 0;
1397       if (fontLoc->locType == gfxFontLocEmbedded) {
1398 	if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
1399 	  n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
1400 	  codeToGID = (int *)gmallocn(n, sizeof(int));
1401 	  memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1402 		 n * sizeof(int));
1403 	}
1404       } else if (!globalParams->getMapExtTrueTypeFontsViaUnicode()) {
1405 	codeToGID = NULL;
1406 	n = 0;
1407       } else {
1408 	// create a CID-to-GID mapping, via Unicode
1409 	if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
1410 #if LOAD_FONTS_FROM_MEM
1411 	  if ((ff = FoFiTrueType::make(fontBuf->getCString(),
1412 				       fontBuf->getLength(), fontNum))) {
1413 #else
1414 	  if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
1415 #endif
1416 	    // look for a Unicode cmap
1417 	    for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
1418 	      if ((ff->getCmapPlatform(cmap) == 3 &&
1419 		   ff->getCmapEncoding(cmap) == 1) ||
1420 		  ff->getCmapPlatform(cmap) == 0) {
1421 		break;
1422 	      }
1423 	    }
1424 	    if (cmap < ff->getNumCmaps()) {
1425 	      // map CID -> Unicode -> GID
1426 	      if (ctu->isIdentity()) {
1427 		n = 65536;
1428 	      } else {
1429 		n = ctu->getLength();
1430 	      }
1431 	      codeToGID = (int *)gmallocn(n, sizeof(int));
1432 	      for (code = 0; code < n; ++code) {
1433 		if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
1434 		  codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]);
1435 		} else {
1436 		  codeToGID[code] = -1;
1437 		}
1438 	      }
1439 	    }
1440 	    delete ff;
1441 	  }
1442 	  ctu->decRefCnt();
1443 	} else {
1444 	  error(errSyntaxError, -1,
1445 		"Couldn't find a mapping to Unicode for font '{0:s}'",
1446 		gfxFont->getName() ? gfxFont->getName()->getCString()
1447 		                   : "(unnamed)");
1448 	}
1449       }
1450       if (!(fontFile = fontEngine->loadTrueTypeFont(
1451 			   id,
1452 #if LOAD_FONTS_FROM_MEM
1453 			   fontBuf,
1454 #else
1455 			   fileName->getCString(),
1456 			   fileName == tmpFileName,
1457 #endif
1458 			   fontNum, codeToGID, n,
1459 			   gfxFont->getEmbeddedFontName()
1460 			     ? gfxFont->getEmbeddedFontName()->getCString()
1461 			     : (char *)NULL))) {
1462 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1463 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1464 	                         : "(unnamed)");
1465 	delete fontLoc;
1466 	goto err2;
1467       }
1468       break;
1469     default:
1470       // this shouldn't happen
1471       goto err2;
1472     }
1473 
1474     delete fontLoc;
1475   }
1476 
1477   // get the font matrix
1478   textMat = state->getTextMat();
1479   fontSize = state->getFontSize();
1480   oblique = ((SplashOutFontFileID *)fontFile->getID())->getOblique();
1481   m11 = state->getHorizScaling() * textMat[0];
1482   m12 = state->getHorizScaling() * textMat[1];
1483   m21 = oblique * m11 + textMat[2];
1484   m22 = oblique * m12 + textMat[3];
1485   m11 *= fontSize;
1486   m12 *= fontSize;
1487   m21 *= fontSize;
1488   m22 *= fontSize;
1489 
1490   // for substituted fonts: adjust the font matrix -- compare the
1491   // widths of letters and digits (A-Z, a-z, 0-9) in the original font
1492   // and the substituted font
1493   substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx();
1494   if (substIdx >= 0 && substIdx < 12) {
1495     fontScaleMin = 1;
1496     fontScaleAvg = 0;
1497     n = 0;
1498     for (code = 0; code < 256; ++code) {
1499       if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
1500 	  name[0] && !name[1] &&
1501 	  ((name[0] >= 'A' && name[0] <= 'Z') ||
1502 	   (name[0] >= 'a' && name[0] <= 'z') ||
1503 	   (name[0] >= '0' && name[0] <= '9'))) {
1504 	w = ((Gfx8BitFont *)gfxFont)->getWidth(code);
1505 	builtinFontSubst[substIdx]->widths->getWidth(name, &ww);
1506 	if (w > 0.01 && ww > 10) {
1507 	  w /= ww * 0.001;
1508 	  if (w < fontScaleMin) {
1509 	    fontScaleMin = w;
1510 	  }
1511 	  fontScaleAvg += w;
1512 	  ++n;
1513 	}
1514       }
1515     }
1516     // if real font is narrower than substituted font, reduce the font
1517     // size accordingly -- this currently uses a scale factor halfway
1518     // between the minimum and average computed scale factors, which
1519     // is a bit of a kludge, but seems to produce mostly decent
1520     // results
1521     if (n) {
1522       fontScaleAvg /= n;
1523       if (fontScaleAvg < 1) {
1524 	fontScale = 0.5 * (fontScaleMin + fontScaleAvg);
1525 	m11 *= fontScale;
1526 	m12 *= fontScale;
1527       }
1528     }
1529   }
1530 
1531   // create the scaled font
1532   mat[0] = m11;  mat[1] = m12;
1533   mat[2] = m21;  mat[3] = m22;
1534   font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
1535 
1536 #if !LOAD_FONTS_FROM_MEM
1537   if (tmpFileName) {
1538     delete tmpFileName;
1539   }
1540 #endif
1541   return;
1542 
1543  err2:
1544   delete id;
1545  err1:
1546 #if LOAD_FONTS_FROM_MEM
1547   if (fontBuf) {
1548     delete fontBuf;
1549   }
1550 #else
1551   if (tmpFileName) {
1552     unlink(tmpFileName->getCString());
1553     delete tmpFileName;
1554   }
1555 #endif
1556   return;
1557 }
1558 
1559 void SplashOutputDev::stroke(GfxState *state) {
1560   SplashPath *path;
1561 
1562   if (state->getStrokeColorSpace()->isNonMarking()) {
1563     return;
1564   }
1565   setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(),
1566 		   state->getOverprintMode(), state->getStrokeColor());
1567   path = convertPath(state, state->getPath(), gFalse);
1568   splash->stroke(path);
1569   delete path;
1570 }
1571 
1572 void SplashOutputDev::fill(GfxState *state) {
1573   SplashPath *path;
1574 
1575   if (state->getFillColorSpace()->isNonMarking()) {
1576     return;
1577   }
1578   setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
1579 		   state->getOverprintMode(), state->getFillColor());
1580   path = convertPath(state, state->getPath(), gTrue);
1581   splash->fill(path, gFalse);
1582   delete path;
1583 }
1584 
1585 void SplashOutputDev::eoFill(GfxState *state) {
1586   SplashPath *path;
1587 
1588   if (state->getFillColorSpace()->isNonMarking()) {
1589     return;
1590   }
1591   setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
1592 		   state->getOverprintMode(), state->getFillColor());
1593   path = convertPath(state, state->getPath(), gTrue);
1594   splash->fill(path, gTrue);
1595   delete path;
1596 }
1597 
1598 void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
1599 					Object *strRef,
1600 					int paintType, Dict *resDict,
1601 					double *mat, double *bbox,
1602 					int x0, int y0, int x1, int y1,
1603 					double xStep, double yStep) {
1604   double tileXMin, tileYMin, tileXMax, tileYMax, tx, ty;
1605   int tileX0, tileY0, tileW, tileH, tileSize;
1606   SplashBitmap *origBitmap, *tileBitmap;
1607   Splash *origSplash;
1608   SplashColor color;
1609   double mat1[6];
1610   double xa, ya, xb, yb, xc, yc;
1611   int x, y, xx, yy, i;
1612 
1613   // transform the four corners of the bbox from pattern space to
1614   // device space and compute the device space bbox
1615   state->transform(bbox[0] * mat[0] + bbox[1] * mat[2] + mat[4],
1616 		   bbox[0] * mat[1] + bbox[1] * mat[3] + mat[5],
1617 		   &tx, &ty);
1618   tileXMin = tileXMax = tx;
1619   tileYMin = tileYMax = ty;
1620   state->transform(bbox[2] * mat[0] + bbox[1] * mat[2] + mat[4],
1621 		   bbox[2] * mat[1] + bbox[1] * mat[3] + mat[5],
1622 		   &tx, &ty);
1623   if (tx < tileXMin) {
1624     tileXMin = tx;
1625   } else if (tx > tileXMax) {
1626     tileXMax = tx;
1627   }
1628   if (ty < tileYMin) {
1629     tileYMin = ty;
1630   } else if (ty > tileYMax) {
1631     tileYMax = ty;
1632   }
1633   state->transform(bbox[2] * mat[0] + bbox[3] * mat[2] + mat[4],
1634 		   bbox[2] * mat[1] + bbox[3] * mat[3] + mat[5],
1635 		   &tx, &ty);
1636   if (tx < tileXMin) {
1637     tileXMin = tx;
1638   } else if (tx > tileXMax) {
1639     tileXMax = tx;
1640   }
1641   if (ty < tileYMin) {
1642     tileYMin = ty;
1643   } else if (ty > tileYMax) {
1644     tileYMax = ty;
1645   }
1646   state->transform(bbox[0] * mat[0] + bbox[3] * mat[2] + mat[4],
1647 		   bbox[0] * mat[1] + bbox[3] * mat[3] + mat[5],
1648 		   &tx, &ty);
1649   if (tx < tileXMin) {
1650     tileXMin = tx;
1651   } else if (tx > tileXMax) {
1652     tileXMax = tx;
1653   }
1654   if (ty < tileYMin) {
1655     tileYMin = ty;
1656   } else if (ty > tileYMax) {
1657     tileYMax = ty;
1658   }
1659   if (tileXMin == tileXMax || tileYMin == tileYMax) {
1660     return;
1661   }
1662 
1663   tileX0 = (int)floor(tileXMin);
1664   tileY0 = (int)floor(tileYMin);
1665   tileW = (int)ceil(tileXMax) - tileX0;
1666   tileH = (int)ceil(tileYMax) - tileY0;
1667 
1668   // check for an excessively large tile size
1669   tileSize = tileW * tileH;
1670   if (tileSize > 1000000 || tileSize < 0) {
1671     mat1[0] = mat[0];
1672     mat1[1] = mat[1];
1673     mat1[2] = mat[2];
1674     mat1[3] = mat[3];
1675     for (y = y0; y < y1; ++y) {
1676       for (x = x0; x < x1; ++x) {
1677 	xa = x * xStep;
1678 	ya = y * yStep;
1679 	mat1[4] = xa * mat[0] + ya * mat[2] + mat[4];
1680 	mat1[5] = xa * mat[1] + ya * mat[3] + mat[5];
1681 	gfx->drawForm(strRef, resDict, mat1, bbox);
1682       }
1683     }
1684     return;
1685   }
1686 
1687   // create a temporary bitmap
1688   origBitmap = bitmap;
1689   origSplash = splash;
1690   bitmap = tileBitmap = new SplashBitmap(tileW, tileH, bitmapRowPad,
1691 					 colorMode, gTrue, bitmapTopDown);
1692   splash = new Splash(bitmap, vectorAntialias, origSplash->getScreen());
1693   splash->setMinLineWidth(globalParams->getMinLineWidth());
1694   splash->setStrokeAdjust(globalParams->getStrokeAdjust());
1695   for (i = 0; i < splashMaxColorComps; ++i) {
1696     color[i] = 0;
1697   }
1698   splash->clear(color);
1699   ++nestCount;
1700 
1701   // copy the fill color (for uncolored tiling patterns)
1702   // (and stroke color, to handle buggy PDF files)
1703   splash->setFillPattern(origSplash->getFillPattern()->copy());
1704   splash->setStrokePattern(origSplash->getStrokePattern()->copy());
1705 
1706   // render the tile
1707   state->shiftCTM(-tileX0, -tileY0);
1708   updateCTM(state, 0, 0, 0, 0, 0, 0);
1709   gfx->drawForm(strRef, resDict, mat, bbox);
1710   state->shiftCTM(tileX0, tileY0);
1711   updateCTM(state, 0, 0, 0, 0, 0, 0);
1712 
1713   // restore the original bitmap
1714   --nestCount;
1715   delete splash;
1716   bitmap = origBitmap;
1717   splash = origSplash;
1718   splash->setOverprintMask(0xffffffff);
1719 
1720   // draw the tiles
1721   for (y = y0; y < y1; ++y) {
1722     for (x = x0; x < x1; ++x) {
1723       xa = x * xStep;
1724       ya = y * yStep;
1725       xb = xa * mat[0] + ya * mat[2];
1726       yb = xa * mat[1] + ya * mat[3];
1727       state->transformDelta(xb, yb, &xc, &yc);
1728       xx = (int)(xc + tileX0 + 0.5);
1729       yy = (int)(yc + tileY0 + 0.5);
1730       splash->composite(tileBitmap, 0, 0, xx, yy, tileW, tileH,
1731 			gFalse, gFalse);
1732     }
1733   }
1734 
1735   delete tileBitmap;
1736 }
1737 
1738 void SplashOutputDev::clip(GfxState *state) {
1739   SplashPath *path;
1740 
1741   path = convertPath(state, state->getPath(), gTrue);
1742   splash->clipToPath(path, gFalse);
1743   delete path;
1744 }
1745 
1746 void SplashOutputDev::eoClip(GfxState *state) {
1747   SplashPath *path;
1748 
1749   path = convertPath(state, state->getPath(), gTrue);
1750   splash->clipToPath(path, gTrue);
1751   delete path;
1752 }
1753 
1754 void SplashOutputDev::clipToStrokePath(GfxState *state) {
1755   SplashPath *path, *path2;
1756 
1757   path = convertPath(state, state->getPath(), gFalse);
1758   path2 = splash->makeStrokePath(path, state->getLineWidth());
1759   delete path;
1760   splash->clipToPath(path2, gFalse);
1761   delete path2;
1762 }
1763 
1764 SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path,
1765 					 GBool dropEmptySubpaths) {
1766   SplashPath *sPath;
1767   GfxSubpath *subpath;
1768   int n, i, j;
1769 
1770   n = dropEmptySubpaths ? 1 : 0;
1771   sPath = new SplashPath();
1772   for (i = 0; i < path->getNumSubpaths(); ++i) {
1773     subpath = path->getSubpath(i);
1774     if (subpath->getNumPoints() > n) {
1775       sPath->moveTo((SplashCoord)subpath->getX(0),
1776 		    (SplashCoord)subpath->getY(0));
1777       j = 1;
1778       while (j < subpath->getNumPoints()) {
1779 	if (subpath->getCurve(j)) {
1780 	  sPath->curveTo((SplashCoord)subpath->getX(j),
1781 			 (SplashCoord)subpath->getY(j),
1782 			 (SplashCoord)subpath->getX(j+1),
1783 			 (SplashCoord)subpath->getY(j+1),
1784 			 (SplashCoord)subpath->getX(j+2),
1785 			 (SplashCoord)subpath->getY(j+2));
1786 	  j += 3;
1787 	} else {
1788 	  sPath->lineTo((SplashCoord)subpath->getX(j),
1789 			(SplashCoord)subpath->getY(j));
1790 	  ++j;
1791 	}
1792       }
1793       if (subpath->isClosed()) {
1794 	sPath->close();
1795       }
1796     }
1797   }
1798   return sPath;
1799 }
1800 
1801 void SplashOutputDev::drawChar(GfxState *state, double x, double y,
1802 			       double dx, double dy,
1803 			       double originX, double originY,
1804 			       CharCode code, int nBytes,
1805 			       Unicode *u, int uLen) {
1806   SplashPath *path;
1807   int render;
1808   GBool doFill, doStroke, doClip, strokeAdjust;
1809   double m[4];
1810   GBool horiz;
1811 
1812   if (skipHorizText || skipRotatedText) {
1813     state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
1814     horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
1815             fabs(m[2]) < 0.001 && m[3] < 0;
1816     if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
1817       return;
1818     }
1819   }
1820 
1821   // check for invisible text -- this is used by Acrobat Capture
1822   render = state->getRender();
1823   if (render == 3) {
1824     return;
1825   }
1826 
1827   if (needFontUpdate) {
1828     doUpdateFont(state);
1829   }
1830   if (!font) {
1831     return;
1832   }
1833 
1834   x -= originX;
1835   y -= originY;
1836 
1837   doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking();
1838   doStroke = ((render & 3) == 1 || (render & 3) == 2) &&
1839              !state->getStrokeColorSpace()->isNonMarking();
1840   doClip = render & 4;
1841 
1842   path = NULL;
1843   if (doStroke || doClip) {
1844     if ((path = font->getGlyphPath(code))) {
1845       path->offset((SplashCoord)x, (SplashCoord)y);
1846     }
1847   }
1848 
1849   // don't use stroke adjustment when stroking text -- the results
1850   // tend to be ugly (because characters with horizontal upper or
1851   // lower edges get misaligned relative to the other characters)
1852   strokeAdjust = gFalse; // make gcc happy
1853   if (doStroke) {
1854     strokeAdjust = splash->getStrokeAdjust();
1855     splash->setStrokeAdjust(gFalse);
1856   }
1857 
1858   // fill and stroke
1859   if (doFill && doStroke) {
1860     if (path) {
1861       setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
1862 		       state->getOverprintMode(), state->getFillColor());
1863       splash->fill(path, gFalse);
1864       setOverprintMask(state->getStrokeColorSpace(),
1865 		       state->getStrokeOverprint(),
1866 		       state->getOverprintMode(),
1867 		       state->getStrokeColor());
1868       splash->stroke(path);
1869     }
1870 
1871   // fill
1872   } else if (doFill) {
1873     setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
1874 		     state->getOverprintMode(), state->getFillColor());
1875     splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font);
1876 
1877   // stroke
1878   } else if (doStroke) {
1879     if (path) {
1880       setOverprintMask(state->getStrokeColorSpace(),
1881 		       state->getStrokeOverprint(),
1882 		       state->getOverprintMode(),
1883 		       state->getStrokeColor());
1884       splash->stroke(path);
1885     }
1886   }
1887 
1888   // clip
1889   if (doClip) {
1890     if (path) {
1891       if (textClipPath) {
1892 	textClipPath->append(path);
1893       } else {
1894 	textClipPath = path;
1895 	path = NULL;
1896       }
1897     }
1898   }
1899 
1900   if (doStroke) {
1901     splash->setStrokeAdjust(strokeAdjust);
1902   }
1903 
1904   if (path) {
1905     delete path;
1906   }
1907 }
1908 
1909 GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
1910 				      double dx, double dy,
1911 				      CharCode code, Unicode *u, int uLen) {
1912   GfxFont *gfxFont;
1913   Ref *fontID;
1914   double *ctm, *bbox;
1915   T3FontCache *t3Font;
1916   T3GlyphStack *t3gs;
1917   GBool validBBox;
1918   double m[4];
1919   GBool horiz;
1920   double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
1921   int i, j;
1922 
1923   if (skipHorizText || skipRotatedText) {
1924     state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
1925     horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
1926             fabs(m[2]) < 0.001 && m[3] < 0;
1927     if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
1928       return gTrue;
1929     }
1930   }
1931 
1932   if (!(gfxFont = state->getFont())) {
1933     return gFalse;
1934   }
1935   fontID = gfxFont->getID();
1936   ctm = state->getCTM();
1937   state->transform(0, 0, &xt, &yt);
1938 
1939   // is it the first (MRU) font in the cache?
1940   if (!(nT3Fonts > 0 &&
1941 	t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
1942 
1943     // is the font elsewhere in the cache?
1944     for (i = 1; i < nT3Fonts; ++i) {
1945       if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
1946 	t3Font = t3FontCache[i];
1947 	for (j = i; j > 0; --j) {
1948 	  t3FontCache[j] = t3FontCache[j - 1];
1949 	}
1950 	t3FontCache[0] = t3Font;
1951 	break;
1952       }
1953     }
1954     if (i >= nT3Fonts) {
1955 
1956       // create new entry in the font cache
1957       if (nT3Fonts == splashOutT3FontCacheSize) {
1958 	delete t3FontCache[nT3Fonts - 1];
1959 	--nT3Fonts;
1960       }
1961       for (j = nT3Fonts; j > 0; --j) {
1962 	t3FontCache[j] = t3FontCache[j - 1];
1963       }
1964       ++nT3Fonts;
1965       bbox = gfxFont->getFontBBox();
1966       if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
1967 	// unspecified bounding box -- just take a guess
1968 	xMin = xt - 5;
1969 	xMax = xMin + 30;
1970 	yMax = yt + 15;
1971 	yMin = yMax - 45;
1972 	validBBox = gFalse;
1973       } else {
1974 	state->transform(bbox[0], bbox[1], &x1, &y1);
1975 	xMin = xMax = x1;
1976 	yMin = yMax = y1;
1977 	state->transform(bbox[0], bbox[3], &x1, &y1);
1978 	if (x1 < xMin) {
1979 	  xMin = x1;
1980 	} else if (x1 > xMax) {
1981 	  xMax = x1;
1982 	}
1983 	if (y1 < yMin) {
1984 	  yMin = y1;
1985 	} else if (y1 > yMax) {
1986 	  yMax = y1;
1987 	}
1988 	state->transform(bbox[2], bbox[1], &x1, &y1);
1989 	if (x1 < xMin) {
1990 	  xMin = x1;
1991 	} else if (x1 > xMax) {
1992 	  xMax = x1;
1993 	}
1994 	if (y1 < yMin) {
1995 	  yMin = y1;
1996 	} else if (y1 > yMax) {
1997 	  yMax = y1;
1998 	}
1999 	state->transform(bbox[2], bbox[3], &x1, &y1);
2000 	if (x1 < xMin) {
2001 	  xMin = x1;
2002 	} else if (x1 > xMax) {
2003 	  xMax = x1;
2004 	}
2005 	if (y1 < yMin) {
2006 	  yMin = y1;
2007 	} else if (y1 > yMax) {
2008 	  yMax = y1;
2009 	}
2010 	validBBox = gTrue;
2011       }
2012       t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
2013 	                               (int)floor(xMin - xt) - 2,
2014 				       (int)floor(yMin - yt) - 2,
2015 				       (int)ceil(xMax) - (int)floor(xMin) + 4,
2016 				       (int)ceil(yMax) - (int)floor(yMin) + 4,
2017 				       validBBox,
2018 				       colorMode != splashModeMono1);
2019     }
2020   }
2021   t3Font = t3FontCache[0];
2022 
2023   // is the glyph in the cache?
2024   i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2025   for (j = 0; j < t3Font->cacheAssoc; ++j) {
2026     if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
2027 	t3Font->cacheTags[i+j].code == code) {
2028       drawType3Glyph(state, t3Font, &t3Font->cacheTags[i+j],
2029 		     t3Font->cacheData + (i+j) * t3Font->glyphSize);
2030       return gTrue;
2031     }
2032   }
2033 
2034   // push a new Type 3 glyph record
2035   t3gs = new T3GlyphStack();
2036   t3gs->next = t3GlyphStack;
2037   t3GlyphStack = t3gs;
2038   t3GlyphStack->code = code;
2039   t3GlyphStack->cache = t3Font;
2040   t3GlyphStack->cacheTag = NULL;
2041   t3GlyphStack->cacheData = NULL;
2042   t3GlyphStack->haveDx = gFalse;
2043   t3GlyphStack->doNotCache = gFalse;
2044 
2045   return gFalse;
2046 }
2047 
2048 void SplashOutputDev::endType3Char(GfxState *state) {
2049   T3GlyphStack *t3gs;
2050   double *ctm;
2051 
2052   if (t3GlyphStack->cacheTag) {
2053     --nestCount;
2054     memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
2055 	   t3GlyphStack->cache->glyphSize);
2056     delete bitmap;
2057     delete splash;
2058     bitmap = t3GlyphStack->origBitmap;
2059     splash = t3GlyphStack->origSplash;
2060     ctm = state->getCTM();
2061     state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2062 		  t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
2063     updateCTM(state, 0, 0, 0, 0, 0, 0);
2064     drawType3Glyph(state, t3GlyphStack->cache,
2065 		   t3GlyphStack->cacheTag, t3GlyphStack->cacheData);
2066   }
2067   t3gs = t3GlyphStack;
2068   t3GlyphStack = t3gs->next;
2069   delete t3gs;
2070 }
2071 
2072 void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
2073   t3GlyphStack->haveDx = gTrue;
2074 }
2075 
2076 void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
2077 			      double llx, double lly, double urx, double ury) {
2078   double *ctm;
2079   T3FontCache *t3Font;
2080   SplashColor color;
2081   double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
2082   int i, j;
2083 
2084   // ignore multiple d0/d1 operators
2085   if (t3GlyphStack->haveDx) {
2086     return;
2087   }
2088   t3GlyphStack->haveDx = gTrue;
2089   // don't cache if we got a gsave/grestore before the d1
2090   if (t3GlyphStack->doNotCache) {
2091     return;
2092   }
2093 
2094   t3Font = t3GlyphStack->cache;
2095 
2096   // check for a valid bbox
2097   state->transform(0, 0, &xt, &yt);
2098   state->transform(llx, lly, &x1, &y1);
2099   xMin = xMax = x1;
2100   yMin = yMax = y1;
2101   state->transform(llx, ury, &x1, &y1);
2102   if (x1 < xMin) {
2103     xMin = x1;
2104   } else if (x1 > xMax) {
2105     xMax = x1;
2106   }
2107   if (y1 < yMin) {
2108     yMin = y1;
2109   } else if (y1 > yMax) {
2110     yMax = y1;
2111   }
2112   state->transform(urx, lly, &x1, &y1);
2113   if (x1 < xMin) {
2114     xMin = x1;
2115   } else if (x1 > xMax) {
2116     xMax = x1;
2117   }
2118   if (y1 < yMin) {
2119     yMin = y1;
2120   } else if (y1 > yMax) {
2121     yMax = y1;
2122   }
2123   state->transform(urx, ury, &x1, &y1);
2124   if (x1 < xMin) {
2125     xMin = x1;
2126   } else if (x1 > xMax) {
2127     xMax = x1;
2128   }
2129   if (y1 < yMin) {
2130     yMin = y1;
2131   } else if (y1 > yMax) {
2132     yMax = y1;
2133   }
2134   if (xMin - xt < t3Font->glyphX ||
2135       yMin - yt < t3Font->glyphY ||
2136       xMax - xt > t3Font->glyphX + t3Font->glyphW ||
2137       yMax - yt > t3Font->glyphY + t3Font->glyphH) {
2138     if (t3Font->validBBox) {
2139       error(errSyntaxWarning, -1, "Bad bounding box in Type 3 glyph");
2140     }
2141     return;
2142   }
2143 
2144   // allocate a cache entry
2145   i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2146   for (j = 0; j < t3Font->cacheAssoc; ++j) {
2147     if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
2148       t3Font->cacheTags[i+j].mru = 0x8000;
2149       t3Font->cacheTags[i+j].code = t3GlyphStack->code;
2150       t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
2151       t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
2152     } else {
2153       ++t3Font->cacheTags[i+j].mru;
2154     }
2155   }
2156 
2157   // save state
2158   t3GlyphStack->origBitmap = bitmap;
2159   t3GlyphStack->origSplash = splash;
2160   ctm = state->getCTM();
2161   t3GlyphStack->origCTM4 = ctm[4];
2162   t3GlyphStack->origCTM5 = ctm[5];
2163 
2164   // create the temporary bitmap
2165   if (colorMode == splashModeMono1) {
2166     bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
2167 			      splashModeMono1, gFalse);
2168     splash = new Splash(bitmap, gFalse,
2169 			t3GlyphStack->origSplash->getScreen());
2170     color[0] = 0;
2171     splash->clear(color);
2172     color[0] = 0xff;
2173   } else {
2174     bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
2175 			      splashModeMono8, gFalse);
2176     splash = new Splash(bitmap, vectorAntialias,
2177 			t3GlyphStack->origSplash->getScreen());
2178     color[0] = 0x00;
2179     splash->clear(color);
2180     color[0] = 0xff;
2181   }
2182   splash->setMinLineWidth(globalParams->getMinLineWidth());
2183   splash->setStrokeAdjust(t3GlyphStack->origSplash->getStrokeAdjust());
2184   splash->setFillPattern(new SplashSolidColor(color));
2185   splash->setStrokePattern(new SplashSolidColor(color));
2186   //~ this should copy other state from t3GlyphStack->origSplash?
2187   //~ [this is likely the same situation as in beginTransparencyGroup()]
2188   state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2189 		-t3Font->glyphX, -t3Font->glyphY);
2190   updateCTM(state, 0, 0, 0, 0, 0, 0);
2191   ++nestCount;
2192 }
2193 
2194 void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font,
2195 				     T3FontCacheTag *tag, Guchar *data) {
2196   SplashGlyphBitmap glyph;
2197 
2198   setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2199 		   state->getOverprintMode(), state->getFillColor());
2200   glyph.x = -t3Font->glyphX;
2201   glyph.y = -t3Font->glyphY;
2202   glyph.w = t3Font->glyphW;
2203   glyph.h = t3Font->glyphH;
2204   glyph.aa = colorMode != splashModeMono1;
2205   glyph.data = data;
2206   glyph.freeData = gFalse;
2207   splash->fillGlyph(0, 0, &glyph);
2208 }
2209 
2210 void SplashOutputDev::endTextObject(GfxState *state) {
2211   if (textClipPath) {
2212     splash->clipToPath(textClipPath, gFalse);
2213     delete textClipPath;
2214     textClipPath = NULL;
2215   }
2216 }
2217 
2218 struct SplashOutImageMaskData {
2219   ImageStream *imgStr;
2220   GBool invert;
2221   int width, height, y;
2222 };
2223 
2224 GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
2225   SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
2226   Guchar *p;
2227   SplashColorPtr q;
2228   int x;
2229 
2230   if (imgMaskData->y == imgMaskData->height ||
2231       !(p = imgMaskData->imgStr->getLine())) {
2232     memset(line, 0, imgMaskData->width);
2233     return gFalse;
2234   }
2235   for (x = 0, q = line; x < imgMaskData->width; ++x) {
2236     *q++ = *p++ ^ imgMaskData->invert;
2237   }
2238   ++imgMaskData->y;
2239   return gTrue;
2240 }
2241 
2242 void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2243 				    int width, int height, GBool invert,
2244 				    GBool inlineImg, GBool interpolate) {
2245   double *ctm;
2246   SplashCoord mat[6];
2247   SplashOutImageMaskData imgMaskData;
2248 
2249   if (state->getFillColorSpace()->isNonMarking()) {
2250     return;
2251   }
2252   setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2253 		   state->getOverprintMode(), state->getFillColor());
2254 
2255   ctm = state->getCTM();
2256   mat[0] = ctm[0];
2257   mat[1] = ctm[1];
2258   mat[2] = -ctm[2];
2259   mat[3] = -ctm[3];
2260   mat[4] = ctm[2] + ctm[4];
2261   mat[5] = ctm[3] + ctm[5];
2262 
2263   reduceImageResolution(str, ctm, &width, &height);
2264 
2265   imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
2266   imgMaskData.imgStr->reset();
2267   imgMaskData.invert = invert ? 0 : 1;
2268   imgMaskData.width = width;
2269   imgMaskData.height = height;
2270   imgMaskData.y = 0;
2271 
2272   splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat,
2273 			t3GlyphStack != NULL, interpolate);
2274   if (inlineImg) {
2275     while (imgMaskData.y < height) {
2276       imgMaskData.imgStr->getLine();
2277       ++imgMaskData.y;
2278     }
2279   }
2280 
2281   delete imgMaskData.imgStr;
2282   str->close();
2283 }
2284 
2285 void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
2286 					       Object *ref, Stream *str,
2287 					       int width, int height,
2288 					       GBool invert,
2289 					       GBool inlineImg,
2290 					       GBool interpolate) {
2291   double *ctm;
2292   SplashCoord mat[6];
2293   SplashOutImageMaskData imgMaskData;
2294   SplashBitmap *maskBitmap;
2295   Splash *maskSplash;
2296   SplashColor maskColor;
2297 
2298   ctm = state->getCTM();
2299   mat[0] = ctm[0];
2300   mat[1] = ctm[1];
2301   mat[2] = -ctm[2];
2302   mat[3] = -ctm[3];
2303   mat[4] = ctm[2] + ctm[4];
2304   mat[5] = ctm[3] + ctm[5];
2305   reduceImageResolution(str, ctm, &width, &height);
2306   imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
2307   imgMaskData.imgStr->reset();
2308   imgMaskData.invert = invert ? 0 : 1;
2309   imgMaskData.width = width;
2310   imgMaskData.height = height;
2311   imgMaskData.y = 0;
2312   maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
2313 				1, splashModeMono8, gFalse);
2314   maskSplash = new Splash(maskBitmap, gTrue);
2315   maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
2316   clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
2317   maskColor[0] = 0xff;
2318   maskSplash->setFillPattern(new SplashSolidColor(maskColor));
2319   maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
2320 			    width, height, mat, gFalse, interpolate);
2321   delete imgMaskData.imgStr;
2322   str->close();
2323   delete maskSplash;
2324   splash->setSoftMask(maskBitmap);
2325 }
2326 
2327 struct SplashOutImageData {
2328   ImageStream *imgStr;
2329   GfxImageColorMap *colorMap;
2330   SplashColorPtr lookup;
2331   int *maskColors;
2332   SplashColorMode colorMode;
2333   int width, height, y;
2334 };
2335 
2336 GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
2337 				Guchar *alphaLine) {
2338   SplashOutImageData *imgData = (SplashOutImageData *)data;
2339   Guchar *p;
2340   SplashColorPtr q, col;
2341   int nComps, x;
2342 
2343   if (imgData->y == imgData->height ||
2344       !(p = imgData->imgStr->getLine())) {
2345     memset(colorLine, 0,
2346 	   imgData->width * splashColorModeNComps[imgData->colorMode]);
2347     return gFalse;
2348   }
2349 
2350   nComps = imgData->colorMap->getNumPixelComps();
2351 
2352   if (imgData->lookup) {
2353     switch (imgData->colorMode) {
2354     case splashModeMono1:
2355     case splashModeMono8:
2356       for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2357 	*q++ = imgData->lookup[*p];
2358       }
2359       break;
2360     case splashModeRGB8:
2361     case splashModeBGR8:
2362       for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2363 	col = &imgData->lookup[3 * *p];
2364 	*q++ = col[0];
2365 	*q++ = col[1];
2366 	*q++ = col[2];
2367       }
2368       break;
2369 #if SPLASH_CMYK
2370     case splashModeCMYK8:
2371       for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2372 	col = &imgData->lookup[4 * *p];
2373 	*q++ = col[0];
2374 	*q++ = col[1];
2375 	*q++ = col[2];
2376 	*q++ = col[3];
2377       }
2378       break;
2379 #endif
2380     }
2381   } else {
2382     switch (imgData->colorMode) {
2383     case splashModeMono1:
2384     case splashModeMono8:
2385       imgData->colorMap->getGrayByteLine(p, colorLine, imgData->width);
2386       break;
2387     case splashModeRGB8:
2388     case splashModeBGR8:
2389       imgData->colorMap->getRGBByteLine(p, colorLine, imgData->width);
2390       break;
2391 #if SPLASH_CMYK
2392     case splashModeCMYK8:
2393       imgData->colorMap->getCMYKByteLine(p, colorLine, imgData->width);
2394       break;
2395 #endif
2396     }
2397   }
2398 
2399   ++imgData->y;
2400   return gTrue;
2401 }
2402 
2403 GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
2404 				     Guchar *alphaLine) {
2405   SplashOutImageData *imgData = (SplashOutImageData *)data;
2406   Guchar *p, *aq;
2407   SplashColorPtr q, col;
2408   GfxRGB rgb;
2409   GfxGray gray;
2410 #if SPLASH_CMYK
2411   GfxCMYK cmyk;
2412 #endif
2413   Guchar alpha;
2414   int nComps, x, i;
2415 
2416   if (imgData->y == imgData->height ||
2417       !(p = imgData->imgStr->getLine())) {
2418     memset(colorLine, 0,
2419 	   imgData->width * splashColorModeNComps[imgData->colorMode]);
2420     memset(alphaLine, 0, imgData->width);
2421     return gFalse;
2422   }
2423 
2424   nComps = imgData->colorMap->getNumPixelComps();
2425 
2426   for (x = 0, q = colorLine, aq = alphaLine;
2427        x < imgData->width;
2428        ++x, p += nComps) {
2429     alpha = 0;
2430     for (i = 0; i < nComps; ++i) {
2431       if (p[i] < imgData->maskColors[2*i] ||
2432 	  p[i] > imgData->maskColors[2*i+1]) {
2433 	alpha = 0xff;
2434 	break;
2435       }
2436     }
2437     if (imgData->lookup) {
2438       switch (imgData->colorMode) {
2439       case splashModeMono1:
2440       case splashModeMono8:
2441 	*q++ = imgData->lookup[*p];
2442 	break;
2443       case splashModeRGB8:
2444       case splashModeBGR8:
2445 	col = &imgData->lookup[3 * *p];
2446 	*q++ = col[0];
2447 	*q++ = col[1];
2448 	*q++ = col[2];
2449 	break;
2450 #if SPLASH_CMYK
2451       case splashModeCMYK8:
2452 	col = &imgData->lookup[4 * *p];
2453 	*q++ = col[0];
2454 	*q++ = col[1];
2455 	*q++ = col[2];
2456 	*q++ = col[3];
2457 	break;
2458 #endif
2459       }
2460       *aq++ = alpha;
2461     } else {
2462       switch (imgData->colorMode) {
2463       case splashModeMono1:
2464       case splashModeMono8:
2465 	imgData->colorMap->getGray(p, &gray);
2466 	*q++ = colToByte(gray);
2467 	break;
2468       case splashModeRGB8:
2469       case splashModeBGR8:
2470 	imgData->colorMap->getRGB(p, &rgb);
2471 	*q++ = colToByte(rgb.r);
2472 	*q++ = colToByte(rgb.g);
2473 	*q++ = colToByte(rgb.b);
2474 	break;
2475 #if SPLASH_CMYK
2476       case splashModeCMYK8:
2477 	imgData->colorMap->getCMYK(p, &cmyk);
2478 	*q++ = colToByte(cmyk.c);
2479 	*q++ = colToByte(cmyk.m);
2480 	*q++ = colToByte(cmyk.y);
2481 	*q++ = colToByte(cmyk.k);
2482 	break;
2483 #endif
2484       }
2485       *aq++ = alpha;
2486     }
2487   }
2488 
2489   ++imgData->y;
2490   return gTrue;
2491 }
2492 
2493 void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2494 				int width, int height,
2495 				GfxImageColorMap *colorMap,
2496 				int *maskColors, GBool inlineImg,
2497 				GBool interpolate) {
2498   double *ctm;
2499   SplashCoord mat[6];
2500   SplashOutImageData imgData;
2501   SplashColorMode srcMode;
2502   SplashImageSource src;
2503   GfxGray gray;
2504   GfxRGB rgb;
2505 #if SPLASH_CMYK
2506   GfxCMYK cmyk;
2507 #endif
2508   Guchar pix;
2509   int n, i;
2510 
2511   setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
2512 		   state->getOverprintMode(), NULL);
2513 
2514   ctm = state->getCTM();
2515   mat[0] = ctm[0];
2516   mat[1] = ctm[1];
2517   mat[2] = -ctm[2];
2518   mat[3] = -ctm[3];
2519   mat[4] = ctm[2] + ctm[4];
2520   mat[5] = ctm[3] + ctm[5];
2521 
2522   reduceImageResolution(str, ctm, &width, &height);
2523 
2524   imgData.imgStr = new ImageStream(str, width,
2525 				   colorMap->getNumPixelComps(),
2526 				   colorMap->getBits());
2527   imgData.imgStr->reset();
2528   imgData.colorMap = colorMap;
2529   imgData.maskColors = maskColors;
2530   imgData.colorMode = colorMode;
2531   imgData.width = width;
2532   imgData.height = height;
2533   imgData.y = 0;
2534 
2535   // special case for one-channel (monochrome/gray/separation) images:
2536   // build a lookup table here
2537   imgData.lookup = NULL;
2538   if (colorMap->getNumPixelComps() == 1) {
2539     n = 1 << colorMap->getBits();
2540     switch (colorMode) {
2541     case splashModeMono1:
2542     case splashModeMono8:
2543       imgData.lookup = (SplashColorPtr)gmalloc(n);
2544       for (i = 0; i < n; ++i) {
2545 	pix = (Guchar)i;
2546 	colorMap->getGray(&pix, &gray);
2547 	imgData.lookup[i] = colToByte(gray);
2548       }
2549       break;
2550     case splashModeRGB8:
2551     case splashModeBGR8:
2552       imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
2553       for (i = 0; i < n; ++i) {
2554 	pix = (Guchar)i;
2555 	colorMap->getRGB(&pix, &rgb);
2556 	imgData.lookup[3*i] = colToByte(rgb.r);
2557 	imgData.lookup[3*i+1] = colToByte(rgb.g);
2558 	imgData.lookup[3*i+2] = colToByte(rgb.b);
2559       }
2560       break;
2561 #if SPLASH_CMYK
2562     case splashModeCMYK8:
2563       imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
2564       for (i = 0; i < n; ++i) {
2565 	pix = (Guchar)i;
2566 	colorMap->getCMYK(&pix, &cmyk);
2567 	imgData.lookup[4*i] = colToByte(cmyk.c);
2568 	imgData.lookup[4*i+1] = colToByte(cmyk.m);
2569 	imgData.lookup[4*i+2] = colToByte(cmyk.y);
2570 	imgData.lookup[4*i+3] = colToByte(cmyk.k);
2571       }
2572       break;
2573 #endif
2574     }
2575   }
2576 
2577   if (colorMode == splashModeMono1) {
2578     srcMode = splashModeMono8;
2579   } else if (colorMode == splashModeBGR8) {
2580     srcMode = splashModeRGB8;
2581   } else {
2582     srcMode = colorMode;
2583   }
2584   src = maskColors ? &alphaImageSrc : &imageSrc;
2585   splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse,
2586 		    width, height, mat, interpolate);
2587   if (inlineImg) {
2588     while (imgData.y < height) {
2589       imgData.imgStr->getLine();
2590       ++imgData.y;
2591     }
2592   }
2593 
2594   gfree(imgData.lookup);
2595   delete imgData.imgStr;
2596   str->close();
2597 }
2598 
2599 struct SplashOutMaskedImageData {
2600   ImageStream *imgStr;
2601   GfxImageColorMap *colorMap;
2602   SplashBitmap *mask;
2603   SplashColorPtr lookup;
2604   SplashColorMode colorMode;
2605   int width, height, y;
2606 };
2607 
2608 GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
2609 				      Guchar *alphaLine) {
2610   SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;
2611   Guchar *p, *aq;
2612   SplashColorPtr q, col;
2613   GfxRGB rgb;
2614   GfxGray gray;
2615 #if SPLASH_CMYK
2616   GfxCMYK cmyk;
2617 #endif
2618   static Guchar bitToByte[2] = {0x00, 0xff};
2619   Guchar alpha;
2620   Guchar *maskPtr;
2621   int maskShift;
2622   int nComps, x;
2623 
2624   if (imgData->y == imgData->height ||
2625       !(p = imgData->imgStr->getLine())) {
2626     memset(colorLine, 0,
2627 	   imgData->width * splashColorModeNComps[imgData->colorMode]);
2628     memset(alphaLine, 0, imgData->width);
2629     return gFalse;
2630   }
2631 
2632   nComps = imgData->colorMap->getNumPixelComps();
2633 
2634   maskPtr = imgData->mask->getDataPtr() +
2635               imgData->y * imgData->mask->getRowSize();
2636   maskShift = 7;
2637   for (x = 0, q = colorLine, aq = alphaLine;
2638        x < imgData->width;
2639        ++x, p += nComps) {
2640     alpha = bitToByte[(*maskPtr >> maskShift) & 1];
2641     maskPtr += (8 - maskShift) >> 3;
2642     maskShift = (maskShift - 1) & 7;
2643     if (imgData->lookup) {
2644       switch (imgData->colorMode) {
2645       case splashModeMono1:
2646       case splashModeMono8:
2647 	*q++ = imgData->lookup[*p];
2648 	break;
2649       case splashModeRGB8:
2650       case splashModeBGR8:
2651 	col = &imgData->lookup[3 * *p];
2652 	*q++ = col[0];
2653 	*q++ = col[1];
2654 	*q++ = col[2];
2655 	break;
2656 #if SPLASH_CMYK
2657       case splashModeCMYK8:
2658 	col = &imgData->lookup[4 * *p];
2659 	*q++ = col[0];
2660 	*q++ = col[1];
2661 	*q++ = col[2];
2662 	*q++ = col[3];
2663 	break;
2664 #endif
2665       }
2666       *aq++ = alpha;
2667     } else {
2668       switch (imgData->colorMode) {
2669       case splashModeMono1:
2670       case splashModeMono8:
2671 	imgData->colorMap->getGray(p, &gray);
2672 	*q++ = colToByte(gray);
2673 	break;
2674       case splashModeRGB8:
2675       case splashModeBGR8:
2676 	imgData->colorMap->getRGB(p, &rgb);
2677 	*q++ = colToByte(rgb.r);
2678 	*q++ = colToByte(rgb.g);
2679 	*q++ = colToByte(rgb.b);
2680 	break;
2681 #if SPLASH_CMYK
2682       case splashModeCMYK8:
2683 	imgData->colorMap->getCMYK(p, &cmyk);
2684 	*q++ = colToByte(cmyk.c);
2685 	*q++ = colToByte(cmyk.m);
2686 	*q++ = colToByte(cmyk.y);
2687 	*q++ = colToByte(cmyk.k);
2688 	break;
2689 #endif
2690       }
2691       *aq++ = alpha;
2692     }
2693   }
2694 
2695   ++imgData->y;
2696   return gTrue;
2697 }
2698 
2699 void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
2700 				      Stream *str, int width, int height,
2701 				      GfxImageColorMap *colorMap,
2702 				      Stream *maskStr, int maskWidth,
2703 				      int maskHeight, GBool maskInvert,
2704 				      GBool interpolate) {
2705   GfxImageColorMap *maskColorMap;
2706   Object maskDecode, decodeLow, decodeHigh;
2707   double *ctm;
2708   SplashCoord mat[6];
2709   SplashOutMaskedImageData imgData;
2710   SplashOutImageMaskData imgMaskData;
2711   SplashColorMode srcMode;
2712   SplashBitmap *maskBitmap;
2713   Splash *maskSplash;
2714   SplashColor maskColor;
2715   GfxGray gray;
2716   GfxRGB rgb;
2717 #if SPLASH_CMYK
2718   GfxCMYK cmyk;
2719 #endif
2720   Guchar pix;
2721   int n, i;
2722 
2723   setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
2724 		   state->getOverprintMode(), NULL);
2725 
2726   ctm = state->getCTM();
2727   reduceImageResolution(str, ctm, &width, &height);
2728   reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
2729 
2730   // If the mask is higher resolution than the image, use
2731   // drawSoftMaskedImage() instead.
2732   if (maskWidth > width || maskHeight > height) {
2733     decodeLow.initInt(maskInvert ? 0 : 1);
2734     decodeHigh.initInt(maskInvert ? 1 : 0);
2735     maskDecode.initArray(xref);
2736     maskDecode.arrayAdd(&decodeLow);
2737     maskDecode.arrayAdd(&decodeHigh);
2738     maskColorMap = new GfxImageColorMap(1, &maskDecode,
2739 					new GfxDeviceGrayColorSpace());
2740     maskDecode.free();
2741     drawSoftMaskedImage(state, ref, str, width, height, colorMap,
2742 			maskStr, maskWidth, maskHeight, maskColorMap,
2743 			interpolate);
2744     delete maskColorMap;
2745 
2746   } else {
2747 
2748     //----- scale the mask image to the same size as the source image
2749 
2750     mat[0] = (SplashCoord)width;
2751     mat[1] = 0;
2752     mat[2] = 0;
2753     mat[3] = (SplashCoord)height;
2754     mat[4] = 0;
2755     mat[5] = 0;
2756     imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
2757     imgMaskData.imgStr->reset();
2758     imgMaskData.invert = maskInvert ? 0 : 1;
2759     imgMaskData.width = maskWidth;
2760     imgMaskData.height = maskHeight;
2761     imgMaskData.y = 0;
2762     maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse);
2763     maskSplash = new Splash(maskBitmap, gFalse);
2764     maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
2765     maskColor[0] = 0;
2766     maskSplash->clear(maskColor);
2767     maskColor[0] = 0xff;
2768     maskSplash->setFillPattern(new SplashSolidColor(maskColor));
2769     // use "glyph mode" here to get the correct scaled size
2770     maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
2771 			      maskWidth, maskHeight, mat, gTrue, interpolate);
2772     delete imgMaskData.imgStr;
2773     maskStr->close();
2774     delete maskSplash;
2775 
2776     //----- draw the source image
2777 
2778     mat[0] = ctm[0];
2779     mat[1] = ctm[1];
2780     mat[2] = -ctm[2];
2781     mat[3] = -ctm[3];
2782     mat[4] = ctm[2] + ctm[4];
2783     mat[5] = ctm[3] + ctm[5];
2784 
2785     imgData.imgStr = new ImageStream(str, width,
2786 				     colorMap->getNumPixelComps(),
2787 				     colorMap->getBits());
2788     imgData.imgStr->reset();
2789     imgData.colorMap = colorMap;
2790     imgData.mask = maskBitmap;
2791     imgData.colorMode = colorMode;
2792     imgData.width = width;
2793     imgData.height = height;
2794     imgData.y = 0;
2795 
2796     // special case for one-channel (monochrome/gray/separation) images:
2797     // build a lookup table here
2798     imgData.lookup = NULL;
2799     if (colorMap->getNumPixelComps() == 1) {
2800       n = 1 << colorMap->getBits();
2801       switch (colorMode) {
2802       case splashModeMono1:
2803       case splashModeMono8:
2804 	imgData.lookup = (SplashColorPtr)gmalloc(n);
2805 	for (i = 0; i < n; ++i) {
2806 	  pix = (Guchar)i;
2807 	  colorMap->getGray(&pix, &gray);
2808 	  imgData.lookup[i] = colToByte(gray);
2809 	}
2810 	break;
2811       case splashModeRGB8:
2812       case splashModeBGR8:
2813 	imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
2814 	for (i = 0; i < n; ++i) {
2815 	  pix = (Guchar)i;
2816 	  colorMap->getRGB(&pix, &rgb);
2817 	  imgData.lookup[3*i] = colToByte(rgb.r);
2818 	  imgData.lookup[3*i+1] = colToByte(rgb.g);
2819 	  imgData.lookup[3*i+2] = colToByte(rgb.b);
2820 	}
2821 	break;
2822 #if SPLASH_CMYK
2823       case splashModeCMYK8:
2824 	imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
2825 	for (i = 0; i < n; ++i) {
2826 	  pix = (Guchar)i;
2827 	  colorMap->getCMYK(&pix, &cmyk);
2828 	  imgData.lookup[4*i] = colToByte(cmyk.c);
2829 	  imgData.lookup[4*i+1] = colToByte(cmyk.m);
2830 	  imgData.lookup[4*i+2] = colToByte(cmyk.y);
2831 	  imgData.lookup[4*i+3] = colToByte(cmyk.k);
2832 	}
2833 	break;
2834 #endif
2835       }
2836     }
2837 
2838     if (colorMode == splashModeMono1) {
2839       srcMode = splashModeMono8;
2840     } else if (colorMode == splashModeBGR8) {
2841       srcMode = splashModeRGB8;
2842     } else {
2843       srcMode = colorMode;
2844     }
2845     splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue,
2846 		      width, height, mat, interpolate);
2847 
2848     delete maskBitmap;
2849     gfree(imgData.lookup);
2850     delete imgData.imgStr;
2851     str->close();
2852   }
2853 }
2854 
2855 void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
2856 					  Stream *str, int width, int height,
2857 					  GfxImageColorMap *colorMap,
2858 					  Stream *maskStr,
2859 					  int maskWidth, int maskHeight,
2860 					  GfxImageColorMap *maskColorMap,
2861 					  GBool interpolate) {
2862   double *ctm;
2863   SplashCoord mat[6];
2864   SplashOutImageData imgData;
2865   SplashOutImageData imgMaskData;
2866   SplashColorMode srcMode;
2867   SplashBitmap *maskBitmap;
2868   Splash *maskSplash;
2869   GfxGray gray;
2870   GfxRGB rgb;
2871 #if SPLASH_CMYK
2872   GfxCMYK cmyk;
2873 #endif
2874   Guchar pix;
2875   int n, i;
2876 
2877   setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
2878 		   state->getOverprintMode(), NULL);
2879 
2880   ctm = state->getCTM();
2881   mat[0] = ctm[0];
2882   mat[1] = ctm[1];
2883   mat[2] = -ctm[2];
2884   mat[3] = -ctm[3];
2885   mat[4] = ctm[2] + ctm[4];
2886   mat[5] = ctm[3] + ctm[5];
2887 
2888   reduceImageResolution(str, ctm, &width, &height);
2889   reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
2890 
2891   //----- set up the soft mask
2892 
2893   imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
2894 				       maskColorMap->getNumPixelComps(),
2895 				       maskColorMap->getBits());
2896   imgMaskData.imgStr->reset();
2897   imgMaskData.colorMap = maskColorMap;
2898   imgMaskData.maskColors = NULL;
2899   imgMaskData.colorMode = splashModeMono8;
2900   imgMaskData.width = maskWidth;
2901   imgMaskData.height = maskHeight;
2902   imgMaskData.y = 0;
2903   n = 1 << maskColorMap->getBits();
2904   imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
2905   for (i = 0; i < n; ++i) {
2906     pix = (Guchar)i;
2907     maskColorMap->getGray(&pix, &gray);
2908     imgMaskData.lookup[i] = colToByte(gray);
2909   }
2910   maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
2911 				1, splashModeMono8, gFalse);
2912   maskSplash = new Splash(maskBitmap, vectorAntialias);
2913   maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
2914   clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
2915   maskSplash->drawImage(&imageSrc, &imgMaskData, splashModeMono8, gFalse,
2916 			maskWidth, maskHeight, mat, interpolate);
2917   delete imgMaskData.imgStr;
2918   maskStr->close();
2919   gfree(imgMaskData.lookup);
2920   delete maskSplash;
2921   splash->setSoftMask(maskBitmap);
2922 
2923   //----- draw the source image
2924 
2925   imgData.imgStr = new ImageStream(str, width,
2926 				   colorMap->getNumPixelComps(),
2927 				   colorMap->getBits());
2928   imgData.imgStr->reset();
2929   imgData.colorMap = colorMap;
2930   imgData.maskColors = NULL;
2931   imgData.colorMode = colorMode;
2932   imgData.width = width;
2933   imgData.height = height;
2934   imgData.y = 0;
2935 
2936   // special case for one-channel (monochrome/gray/separation) images:
2937   // build a lookup table here
2938   imgData.lookup = NULL;
2939   if (colorMap->getNumPixelComps() == 1) {
2940     n = 1 << colorMap->getBits();
2941     switch (colorMode) {
2942     case splashModeMono1:
2943     case splashModeMono8:
2944       imgData.lookup = (SplashColorPtr)gmalloc(n);
2945       for (i = 0; i < n; ++i) {
2946 	pix = (Guchar)i;
2947 	colorMap->getGray(&pix, &gray);
2948 	imgData.lookup[i] = colToByte(gray);
2949       }
2950       break;
2951     case splashModeRGB8:
2952     case splashModeBGR8:
2953       imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
2954       for (i = 0; i < n; ++i) {
2955 	pix = (Guchar)i;
2956 	colorMap->getRGB(&pix, &rgb);
2957 	imgData.lookup[3*i] = colToByte(rgb.r);
2958 	imgData.lookup[3*i+1] = colToByte(rgb.g);
2959 	imgData.lookup[3*i+2] = colToByte(rgb.b);
2960       }
2961       break;
2962 #if SPLASH_CMYK
2963     case splashModeCMYK8:
2964       imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
2965       for (i = 0; i < n; ++i) {
2966 	pix = (Guchar)i;
2967 	colorMap->getCMYK(&pix, &cmyk);
2968 	imgData.lookup[4*i] = colToByte(cmyk.c);
2969 	imgData.lookup[4*i+1] = colToByte(cmyk.m);
2970 	imgData.lookup[4*i+2] = colToByte(cmyk.y);
2971 	imgData.lookup[4*i+3] = colToByte(cmyk.k);
2972       }
2973       break;
2974 #endif
2975     }
2976   }
2977 
2978   if (colorMode == splashModeMono1) {
2979     srcMode = splashModeMono8;
2980   } else if (colorMode == splashModeBGR8) {
2981     srcMode = splashModeRGB8;
2982   } else {
2983     srcMode = colorMode;
2984   }
2985   splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat,
2986 		    interpolate);
2987 
2988   splash->setSoftMask(NULL);
2989   gfree(imgData.lookup);
2990   delete imgData.imgStr;
2991   str->close();
2992 }
2993 
2994 void SplashOutputDev::reduceImageResolution(Stream *str, double *ctm,
2995 					    int *width, int *height) {
2996   double sw, sh;
2997   int reduction;
2998 
2999   if (str->getKind() == strJPX &&
3000       *width * *height > 10000000) {
3001     sw = (double)*width / (fabs(ctm[2]) + fabs(ctm[3]));
3002     sh = (double)*height / (fabs(ctm[0]) + fabs(ctm[1]));
3003     if (sw > 8 && sh > 8) {
3004       reduction = 3;
3005     } else if (sw > 4 && sh > 4) {
3006       reduction = 2;
3007     } else if (sw > 2 && sh > 2) {
3008       reduction = 1;
3009     } else {
3010       reduction = 0;
3011     }
3012     if (reduction > 0) {
3013       ((JPXStream *)str)->reduceResolution(reduction);
3014       *width >>= reduction;
3015       *height >>= reduction;
3016     }
3017   }
3018 }
3019 
3020 void SplashOutputDev::clearMaskRegion(GfxState *state,
3021 				      Splash *maskSplash,
3022 				      double xMin, double yMin,
3023 				      double xMax, double yMax) {
3024   SplashBitmap *maskBitmap;
3025   double xxMin, yyMin, xxMax, yyMax, xx, yy;
3026   int xxMinI, yyMinI, xxMaxI, yyMaxI, y, n;
3027   Guchar *p;
3028 
3029   maskBitmap = maskSplash->getBitmap();
3030   xxMin = maskBitmap->getWidth();
3031   xxMax = 0;
3032   yyMin = maskBitmap->getHeight();
3033   yyMax = 0;
3034   state->transform(xMin, yMin, &xx, &yy);
3035   if (xx < xxMin) { xxMin = xx; }
3036   if (xx > xxMax) { xxMax = xx; }
3037   if (yy < yyMin) { yyMin = yy; }
3038   if (yy > yyMax) { yyMax = yy; }
3039   state->transform(xMin, yMax, &xx, &yy);
3040   if (xx < xxMin) { xxMin = xx; }
3041   if (xx > xxMax) { xxMax = xx; }
3042   if (yy < yyMin) { yyMin = yy; }
3043   if (yy > yyMax) { yyMax = yy; }
3044   state->transform(xMax, yMin, &xx, &yy);
3045   if (xx < xxMin) { xxMin = xx; }
3046   if (xx > xxMax) { xxMax = xx; }
3047   if (yy < yyMin) { yyMin = yy; }
3048   if (yy > yyMax) { yyMax = yy; }
3049   state->transform(xMax, yMax, &xx, &yy);
3050   if (xx < xxMin) { xxMin = xx; }
3051   if (xx > xxMax) { xxMax = xx; }
3052   if (yy < yyMin) { yyMin = yy; }
3053   if (yy > yyMax) { yyMax = yy; }
3054   xxMinI = (int)floor(xxMin);
3055   if (xxMinI < 0) {
3056     xxMinI = 0;
3057   }
3058   xxMaxI = (int)ceil(xxMax);
3059   if (xxMaxI > maskBitmap->getWidth()) {
3060     xxMaxI = maskBitmap->getWidth();
3061   }
3062   yyMinI = (int)floor(yyMin);
3063   if (yyMinI < 0) {
3064     yyMinI = 0;
3065   }
3066   yyMaxI = (int)ceil(yyMax);
3067   if (yyMaxI > maskBitmap->getHeight()) {
3068     yyMaxI = maskBitmap->getHeight();
3069   }
3070   p = maskBitmap->getDataPtr() + yyMinI * maskBitmap->getRowSize();
3071   if (maskBitmap->getMode() == splashModeMono1) {
3072     n = (xxMaxI + 7) / 8 - xxMinI / 8;
3073     p += xxMinI / 8;
3074   } else {
3075     n = xxMaxI - xxMinI;
3076     p += xxMinI;
3077   }
3078   if (xxMaxI > xxMinI) {
3079     for (y = yyMinI; y < yyMaxI; ++y) {
3080       memset(p, 0, n);
3081       p += maskBitmap->getRowSize();
3082     }
3083   }
3084 }
3085 
3086 void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
3087 					     GfxColorSpace *blendingColorSpace,
3088 					     GBool isolated, GBool knockout,
3089 					     GBool forSoftMask) {
3090   SplashTransparencyGroup *transpGroup;
3091   SplashColor color;
3092   double xMin, yMin, xMax, yMax, x, y;
3093   int tx, ty, w, h, i;
3094 
3095   // transform the bbox
3096   state->transform(bbox[0], bbox[1], &x, &y);
3097   xMin = xMax = x;
3098   yMin = yMax = y;
3099   state->transform(bbox[0], bbox[3], &x, &y);
3100   if (x < xMin) {
3101     xMin = x;
3102   } else if (x > xMax) {
3103     xMax = x;
3104   }
3105   if (y < yMin) {
3106     yMin = y;
3107   } else if (y > yMax) {
3108     yMax = y;
3109   }
3110   state->transform(bbox[2], bbox[1], &x, &y);
3111   if (x < xMin) {
3112     xMin = x;
3113   } else if (x > xMax) {
3114     xMax = x;
3115   }
3116   if (y < yMin) {
3117     yMin = y;
3118   } else if (y > yMax) {
3119     yMax = y;
3120   }
3121   state->transform(bbox[2], bbox[3], &x, &y);
3122   if (x < xMin) {
3123     xMin = x;
3124   } else if (x > xMax) {
3125     xMax = x;
3126   }
3127   if (y < yMin) {
3128     yMin = y;
3129   } else if (y > yMax) {
3130     yMax = y;
3131   }
3132   tx = (int)floor(xMin);
3133   if (tx < 0) {
3134     tx = 0;
3135   } else if (tx >= bitmap->getWidth()) {
3136     tx = bitmap->getWidth() - 1;
3137   }
3138   ty = (int)floor(yMin);
3139   if (ty < 0) {
3140     ty = 0;
3141   } else if (ty >= bitmap->getHeight()) {
3142     ty = bitmap->getHeight() - 1;
3143   }
3144   w = (int)ceil(xMax) - tx + 1;
3145   if (tx + w > bitmap->getWidth()) {
3146     w = bitmap->getWidth() - tx;
3147   }
3148   if (w < 1) {
3149     w = 1;
3150   }
3151   h = (int)ceil(yMax) - ty + 1;
3152   if (ty + h > bitmap->getHeight()) {
3153     h = bitmap->getHeight() - ty;
3154   }
3155   if (h < 1) {
3156     h = 1;
3157   }
3158 
3159   // push a new stack entry
3160   transpGroup = new SplashTransparencyGroup();
3161   transpGroup->tx = tx;
3162   transpGroup->ty = ty;
3163   transpGroup->blendingColorSpace = blendingColorSpace;
3164   transpGroup->isolated = isolated;
3165   transpGroup->next = transpGroupStack;
3166   transpGroupStack = transpGroup;
3167 
3168   // save state
3169   transpGroup->origBitmap = bitmap;
3170   transpGroup->origSplash = splash;
3171 
3172   //~ this handles the blendingColorSpace arg for soft masks, but
3173   //~   not yet for transparency groups
3174 
3175   // switch to the blending color space
3176   if (forSoftMask && isolated && !knockout && blendingColorSpace) {
3177     if (blendingColorSpace->getMode() == csDeviceGray ||
3178 	blendingColorSpace->getMode() == csCalGray ||
3179 	(blendingColorSpace->getMode() == csICCBased &&
3180 	 blendingColorSpace->getNComps() == 1)) {
3181       colorMode = splashModeMono8;
3182     } else if (blendingColorSpace->getMode() == csDeviceRGB ||
3183 	       blendingColorSpace->getMode() == csCalRGB ||
3184 	       (blendingColorSpace->getMode() == csICCBased &&
3185 		blendingColorSpace->getNComps() == 3)) {
3186       //~ does this need to use BGR8?
3187       colorMode = splashModeRGB8;
3188 #if SPLASH_CMYK
3189     } else if (blendingColorSpace->getMode() == csDeviceCMYK ||
3190 	       (blendingColorSpace->getMode() == csICCBased &&
3191 		blendingColorSpace->getNComps() == 4)) {
3192       colorMode = splashModeCMYK8;
3193 #endif
3194     }
3195   }
3196 
3197   // create the temporary bitmap
3198   bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
3199 			    bitmapTopDown);
3200   splash = new Splash(bitmap, vectorAntialias,
3201 		      transpGroup->origSplash->getScreen());
3202   splash->setMinLineWidth(globalParams->getMinLineWidth());
3203   splash->setStrokeAdjust(globalParams->getStrokeAdjust());
3204   //~ Acrobat apparently copies at least the fill and stroke colors, and
3205   //~ maybe other state(?) -- but not the clipping path (and not sure
3206   //~ what else)
3207   //~ [this is likely the same situation as in type3D1()]
3208   splash->setFillPattern(transpGroup->origSplash->getFillPattern()->copy());
3209   splash->setStrokePattern(
3210 		         transpGroup->origSplash->getStrokePattern()->copy());
3211   if (isolated) {
3212     for (i = 0; i < splashMaxColorComps; ++i) {
3213       color[i] = 0;
3214     }
3215     splash->clear(color, 0);
3216   } else {
3217     splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
3218   }
3219   splash->setInTransparencyGroup(transpGroup->origBitmap, tx, ty,
3220 				 !isolated, knockout);
3221   transpGroup->tBitmap = bitmap;
3222   state->shiftCTM(-tx, -ty);
3223   updateCTM(state, 0, 0, 0, 0, 0, 0);
3224   ++nestCount;
3225 }
3226 
3227 void SplashOutputDev::endTransparencyGroup(GfxState *state) {
3228   // restore state
3229   --nestCount;
3230   delete splash;
3231   bitmap = transpGroupStack->origBitmap;
3232   colorMode = bitmap->getMode();
3233   splash = transpGroupStack->origSplash;
3234   state->shiftCTM(transpGroupStack->tx, transpGroupStack->ty);
3235   updateCTM(state, 0, 0, 0, 0, 0, 0);
3236 }
3237 
3238 void SplashOutputDev::paintTransparencyGroup(GfxState *state, double *bbox) {
3239   SplashBitmap *tBitmap;
3240   SplashTransparencyGroup *transpGroup;
3241   GBool isolated;
3242   int tx, ty;
3243 
3244   tx = transpGroupStack->tx;
3245   ty = transpGroupStack->ty;
3246   tBitmap = transpGroupStack->tBitmap;
3247   isolated = transpGroupStack->isolated;
3248 
3249   // paint the transparency group onto the parent bitmap
3250   // - the clip path was set in the parent's state)
3251   if (tx < bitmap->getWidth() && ty < bitmap->getHeight()) {
3252     splash->setOverprintMask(0xffffffff);
3253     splash->composite(tBitmap, 0, 0, tx, ty,
3254 		      tBitmap->getWidth(), tBitmap->getHeight(),
3255 		      gFalse, !isolated);
3256   }
3257 
3258   // pop the stack
3259   transpGroup = transpGroupStack;
3260   transpGroupStack = transpGroup->next;
3261   delete transpGroup;
3262 
3263   delete tBitmap;
3264 }
3265 
3266 void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
3267 				  GBool alpha, Function *transferFunc,
3268 				  GfxColor *backdropColor) {
3269   SplashBitmap *softMask, *tBitmap;
3270   Splash *tSplash;
3271   SplashTransparencyGroup *transpGroup;
3272   SplashColor color;
3273   SplashColorPtr p;
3274   GfxGray gray;
3275   GfxRGB rgb;
3276 #if SPLASH_CMYK
3277   GfxCMYK cmyk;
3278 #endif
3279   double backdrop, backdrop2, lum, lum2;
3280   int tx, ty, x, y;
3281 
3282   tx = transpGroupStack->tx;
3283   ty = transpGroupStack->ty;
3284   tBitmap = transpGroupStack->tBitmap;
3285 
3286   // composite with backdrop color
3287   backdrop = 0;
3288   if (!alpha && tBitmap->getMode() != splashModeMono1) {
3289     //~ need to correctly handle the case where no blending color
3290     //~ space is given
3291     tSplash = new Splash(tBitmap, vectorAntialias,
3292 			 transpGroupStack->origSplash->getScreen());
3293     tSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
3294     if (transpGroupStack->blendingColorSpace) {
3295       switch (tBitmap->getMode()) {
3296       case splashModeMono1:
3297 	// transparency is not supported in mono1 mode
3298 	break;
3299       case splashModeMono8:
3300 	transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
3301 	backdrop = colToDbl(gray);
3302 	color[0] = colToByte(gray);
3303 	tSplash->compositeBackground(color);
3304 	break;
3305       case splashModeRGB8:
3306       case splashModeBGR8:
3307 	transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb);
3308 	backdrop = 0.3 * colToDbl(rgb.r) +
3309 	           0.59 * colToDbl(rgb.g) +
3310 	           0.11 * colToDbl(rgb.b);
3311 	color[0] = colToByte(rgb.r);
3312 	color[1] = colToByte(rgb.g);
3313 	color[2] = colToByte(rgb.b);
3314 	tSplash->compositeBackground(color);
3315 	break;
3316 #if SPLASH_CMYK
3317       case splashModeCMYK8:
3318 	transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk);
3319 	backdrop = (1 - colToDbl(cmyk.k))
3320 	           - 0.3 * colToDbl(cmyk.c)
3321 	           - 0.59 * colToDbl(cmyk.m)
3322 	           - 0.11 * colToDbl(cmyk.y);
3323 	if (backdrop < 0) {
3324 	  backdrop = 0;
3325 	}
3326 	color[0] = colToByte(cmyk.c);
3327 	color[1] = colToByte(cmyk.m);
3328 	color[2] = colToByte(cmyk.y);
3329 	color[3] = colToByte(cmyk.k);
3330 	tSplash->compositeBackground(color);
3331 	break;
3332 #endif
3333       }
3334       delete tSplash;
3335     }
3336   }
3337   if (transferFunc) {
3338     transferFunc->transform(&backdrop, &backdrop2);
3339   } else {
3340     backdrop2 = backdrop;
3341   }
3342 
3343   softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
3344 			      1, splashModeMono8, gFalse);
3345   memset(softMask->getDataPtr(), (int)(backdrop2 * 255.0 + 0.5),
3346 	 softMask->getRowSize() * softMask->getHeight());
3347   if (tx < softMask->getWidth() && ty < softMask->getHeight()) {
3348     p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;
3349     for (y = 0; y < tBitmap->getHeight(); ++y) {
3350       for (x = 0; x < tBitmap->getWidth(); ++x) {
3351 	if (alpha) {
3352 	  lum = tBitmap->getAlpha(x, y) / 255.0;
3353 	} else {
3354 	  tBitmap->getPixel(x, y, color);
3355 	  // convert to luminosity
3356 	  switch (tBitmap->getMode()) {
3357 	  case splashModeMono1:
3358 	  case splashModeMono8:
3359 	    lum = color[0] / 255.0;
3360 	    break;
3361 	  case splashModeRGB8:
3362 	  case splashModeBGR8:
3363 	    lum = (0.3 / 255.0) * color[0] +
3364 	          (0.59 / 255.0) * color[1] +
3365 	          (0.11 / 255.0) * color[2];
3366 	    break;
3367 #if SPLASH_CMYK
3368 	  case splashModeCMYK8:
3369 	    lum = (1 - color[3] / 255.0)
3370 	          - (0.3 / 255.0) * color[0]
3371 	          - (0.59 / 255.0) * color[1]
3372 	          - (0.11 / 255.0) * color[2];
3373 	    if (lum < 0) {
3374 	      lum = 0;
3375 	    }
3376 	    break;
3377 #endif
3378 	  }
3379 	}
3380 	if (transferFunc) {
3381 	  transferFunc->transform(&lum, &lum2);
3382 	} else {
3383 	  lum2 = lum;
3384 	}
3385 	p[x] = (int)(lum2 * 255.0 + 0.5);
3386       }
3387       p += softMask->getRowSize();
3388     }
3389   }
3390   splash->setSoftMask(softMask);
3391 
3392   // pop the stack
3393   transpGroup = transpGroupStack;
3394   transpGroupStack = transpGroup->next;
3395   delete transpGroup;
3396 
3397   delete tBitmap;
3398 }
3399 
3400 void SplashOutputDev::clearSoftMask(GfxState *state) {
3401   splash->setSoftMask(NULL);
3402 }
3403 
3404 void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) {
3405   splashColorCopy(paperColor, paperColorA);
3406 }
3407 
3408 int SplashOutputDev::getBitmapWidth() {
3409   return bitmap->getWidth();
3410 }
3411 
3412 int SplashOutputDev::getBitmapHeight() {
3413   return bitmap->getHeight();
3414 }
3415 
3416 SplashBitmap *SplashOutputDev::takeBitmap() {
3417   SplashBitmap *ret;
3418 
3419   ret = bitmap;
3420   bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
3421 			    colorMode != splashModeMono1, bitmapTopDown);
3422   return ret;
3423 }
3424 
3425 void SplashOutputDev::getModRegion(int *xMin, int *yMin,
3426 				   int *xMax, int *yMax) {
3427   splash->getModRegion(xMin, yMin, xMax, yMax);
3428 }
3429 
3430 void SplashOutputDev::clearModRegion() {
3431   splash->clearModRegion();
3432 }
3433 
3434 void SplashOutputDev::setFillColor(int r, int g, int b) {
3435   GfxRGB rgb;
3436   GfxGray gray;
3437 #if SPLASH_CMYK
3438   GfxCMYK cmyk;
3439 #endif
3440 
3441   rgb.r = byteToCol(r);
3442   rgb.g = byteToCol(g);
3443   rgb.b = byteToCol(b);
3444   switch (colorMode) {
3445   case splashModeMono1:
3446   case splashModeMono8:
3447     gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5);
3448     if (gray > gfxColorComp1) {
3449       gray = gfxColorComp1;
3450     }
3451     splash->setFillPattern(getColor(gray));
3452     break;
3453   case splashModeRGB8:
3454   case splashModeBGR8:
3455     splash->setFillPattern(getColor(&rgb));
3456     break;
3457 #if SPLASH_CMYK
3458   case splashModeCMYK8:
3459     cmyk.c = gfxColorComp1 - rgb.r;
3460     cmyk.m = gfxColorComp1 - rgb.g;
3461     cmyk.y = gfxColorComp1 - rgb.b;
3462     cmyk.k = 0;
3463     splash->setFillPattern(getColor(&cmyk));
3464     break;
3465 #endif
3466   }
3467 }
3468 
3469 SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) {
3470   Ref ref;
3471   SplashOutFontFileID *id;
3472   GfxFontLoc *fontLoc;
3473 #if LOAD_FONTS_FROM_MEM
3474   GString *fontBuf;
3475   FILE *extFontFile;
3476   char blk[4096];
3477   int n;
3478 #endif
3479   SplashFontFile *fontFile;
3480   SplashFont *fontObj;
3481   FoFiTrueType *ff;
3482   int *codeToGID;
3483   Unicode u;
3484   SplashCoord textMat[4];
3485   SplashCoord oblique;
3486   int cmap, i;
3487 
3488   for (i = 0; i < nBuiltinFonts; ++i) {
3489     if (!name->cmp(builtinFonts[i].name)) {
3490       break;
3491     }
3492   }
3493   if (i == nBuiltinFonts) {
3494     return NULL;
3495   }
3496   ref.num = i;
3497   ref.gen = -1;
3498   id = new SplashOutFontFileID(&ref);
3499 
3500   // check the font file cache
3501   if ((fontFile = fontEngine->getFontFile(id))) {
3502     delete id;
3503 
3504   // load the font file
3505   } else {
3506     if (!(fontLoc = GfxFont::locateBase14Font(name))) {
3507       return NULL;
3508     }
3509 #if LOAD_FONTS_FROM_MEM
3510     fontBuf = NULL;
3511     if (fontLoc->fontType == fontType1 ||
3512 	fontLoc->fontType == fontTrueType) {
3513       if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) {
3514 	delete fontLoc;
3515 	delete id;
3516 	return NULL;
3517       }
3518       fontBuf = new GString();
3519       while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) {
3520 	fontBuf->append(blk, n);
3521       }
3522       fclose(extFontFile);
3523     }
3524 #endif
3525     if (fontLoc->fontType == fontType1) {
3526       fontFile = fontEngine->loadType1Font(id,
3527 #if LOAD_FONTS_FROM_MEM
3528 					   fontBuf,
3529 #else
3530 					   fontLoc->path->getCString(),
3531 					   gFalse,
3532 #endif
3533 					   winAnsiEncoding);
3534     } else if (fontLoc->fontType == fontTrueType) {
3535 #if LOAD_FONTS_FROM_MEM
3536       if (!(ff = FoFiTrueType::make(fontBuf->getCString(),
3537 				    fontBuf->getLength(),
3538 				    fontLoc->fontNum))) {
3539 #else
3540       if (!(ff = FoFiTrueType::load(fontLoc->path->getCString(),
3541 				    fontLoc->fontNum))) {
3542 #endif
3543 	delete fontLoc;
3544 	delete id;
3545 	return NULL;
3546       }
3547       for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
3548 	if ((ff->getCmapPlatform(cmap) == 3 &&
3549 	     ff->getCmapEncoding(cmap) == 1) ||
3550 	    ff->getCmapPlatform(cmap) == 0) {
3551 	  break;
3552 	}
3553       }
3554       if (cmap == ff->getNumCmaps()) {
3555 	delete ff;
3556 	delete fontLoc;
3557 	delete id;
3558 	return NULL;
3559       }
3560       codeToGID = (int *)gmallocn(256, sizeof(int));
3561       for (i = 0; i < 256; ++i) {
3562 	codeToGID[i] = 0;
3563 	if (winAnsiEncoding[i] &&
3564 	    (u = globalParams->mapNameToUnicode(winAnsiEncoding[i]))) {
3565 	  codeToGID[i] = ff->mapCodeToGID(cmap, u);
3566 	}
3567       }
3568       delete ff;
3569       fontFile = fontEngine->loadTrueTypeFont(id,
3570 #if LOAD_FONTS_FROM_MEM
3571 					      fontBuf,
3572 #else
3573 					      fontLoc->path->getCString(),
3574 					      gFalse,
3575 #endif
3576 					      fontLoc->fontNum,
3577 					      codeToGID, 256, NULL);
3578     } else {
3579       delete fontLoc;
3580       delete id;
3581       return NULL;
3582     }
3583     delete fontLoc;
3584   }
3585   if (!fontFile) {
3586     return NULL;
3587   }
3588 
3589   // create the scaled font
3590   oblique = (SplashCoord)
3591               ((SplashOutFontFileID *)fontFile->getID())->getOblique();
3592   textMat[0] = (SplashCoord)textMatA[0];
3593   textMat[1] = (SplashCoord)textMatA[1];
3594   textMat[2] = oblique * textMatA[0] + textMatA[2];
3595   textMat[3] = oblique * textMatA[1] + textMatA[3];
3596   fontObj = fontEngine->getFont(fontFile, textMat, splash->getMatrix());
3597 
3598   return fontObj;
3599 }
3600 
3601 #if 1 //~tmp: turn off anti-aliasing temporarily
3602 void SplashOutputDev::setInShading(GBool sh) {
3603   splash->setInShading(sh);
3604 }
3605 #endif
3606