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 "gmempp.h"
19 #include "gfile.h"
20 #include "Trace.h"
21 #include "GlobalParams.h"
22 #include "Error.h"
23 #include "Object.h"
24 #include "Gfx.h"
25 #include "GfxFont.h"
26 #include "ShadingImage.h"
27 #include "Link.h"
28 #include "CharCodeToUnicode.h"
29 #include "FontEncodingTables.h"
30 #include "BuiltinFont.h"
31 #include "BuiltinFontTables.h"
32 #include "FoFiTrueType.h"
33 #include "FoFiType1C.h"
34 #include "JPXStream.h"
35 #include "SplashBitmap.h"
36 #include "SplashGlyphBitmap.h"
37 #include "SplashPattern.h"
38 #include "SplashScreen.h"
39 #include "SplashPath.h"
40 #include "SplashState.h"
41 #include "SplashErrorCodes.h"
42 #include "SplashFontEngine.h"
43 #include "SplashFont.h"
44 #include "SplashFontFile.h"
45 #include "SplashFontFileID.h"
46 #include "Splash.h"
47 #include "SplashOutputDev.h"
48 
49 #ifdef VMS
50 #if (__VMS_VER < 70000000)
51 extern "C" int unlink(char *filename);
52 #endif
53 #endif
54 
55 //------------------------------------------------------------------------
56 
57 // max tile size (used in tilingPatternFill())
58 // - Adobe uses a resolution-independent threshold here, of 6M sq pts
59 // - xpdf uses a resolution-dependent max, but with different values
60 //   on 32-bit and 64-bit systems
61 #if SplashBitmapRowSizeMax == INT_MAX
62 #  define maxTileSize 200000000
63 #else
64 #  define maxTileSize 2000000000
65 #endif
66 
67 //------------------------------------------------------------------------
68 
69 // Type 3 font cache size parameters
70 #define type3FontCacheAssoc   8
71 #define type3FontCacheMaxSets 8
72 #define type3FontCacheSize    (128*1024)
73 
74 // Map StrokeAdjustMode (from GlobalParams) to SplashStrokeAdjustMode
75 // (for Splash).
76 static SplashStrokeAdjustMode mapStrokeAdjustMode[3] = {
77   splashStrokeAdjustOff,
78   splashStrokeAdjustNormal,
79   splashStrokeAdjustCAD
80 };
81 
82 //------------------------------------------------------------------------
83 
84 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
div255(int x)85 static inline Guchar div255(int x) {
86   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
87 }
88 
89 // Clip x to lie in [0, 255].
clip255(int x)90 static inline Guchar clip255(int x) {
91   return x < 0 ? 0 : x > 255 ? 255 : (Guchar)x;
92 }
93 
94 
95 //------------------------------------------------------------------------
96 // Blend functions
97 //------------------------------------------------------------------------
98 
splashOutBlendMultiply(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)99 static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest,
100 				   SplashColorPtr blend, SplashColorMode cm) {
101   int i;
102 
103   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
104     blend[i] = (Guchar)((dest[i] * src[i]) / 255);
105   }
106 }
107 
splashOutBlendScreen(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)108 static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest,
109 				 SplashColorPtr blend, SplashColorMode cm) {
110   int i;
111 
112   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
113     blend[i] = (Guchar)(dest[i] + src[i] - (dest[i] * src[i]) / 255);
114   }
115 }
116 
117 // note: this is the same as HardLight, with src/dest reversed
splashOutBlendOverlay(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)118 static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest,
119 				  SplashColorPtr blend, SplashColorMode cm) {
120   int i;
121 
122   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
123     // the spec says "if Cs <= 0.5" -- note that 0x80 is 128/255=0.5020
124     blend[i] = dest[i] < 0x80
125                  ? (Guchar)((src[i] * 2 * dest[i]) / 255)
126                  : (Guchar)(255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255);
127   }
128 }
129 
splashOutBlendDarken(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)130 static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest,
131 				 SplashColorPtr blend, SplashColorMode cm) {
132   int i;
133 
134   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
135     blend[i] = dest[i] < src[i] ? dest[i] : src[i];
136   }
137 }
138 
splashOutBlendLighten(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)139 static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest,
140 				  SplashColorPtr blend, SplashColorMode cm) {
141   int i;
142 
143   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
144     blend[i] = dest[i] > src[i] ? dest[i] : src[i];
145   }
146 }
147 
splashOutBlendColorDodge(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)148 static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest,
149 				     SplashColorPtr blend,
150 				     SplashColorMode cm) {
151   int i;
152 
153   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
154     if (dest[i] == 0) {
155       blend[i] = 0;
156     } else if (dest[i] >= 255 - src[i]) {
157       blend[i] = 255;
158     } else {
159       blend[i] = (Guchar)((dest[i] * 255) / (255 - src[i]));
160     }
161   }
162 }
163 
splashOutBlendColorBurn(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)164 static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest,
165 				    SplashColorPtr blend, SplashColorMode cm) {
166   int i;
167 
168   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
169     if (dest[i] == 255) {
170       blend[i] = 255;
171     } else if (255 - dest[i] >= src[i]) {
172       blend[i] = 0;
173     } else {
174       blend[i] = (Guchar)(255 - (((255 - dest[i]) * 255) / src[i]));
175     }
176   }
177 }
178 
splashOutBlendHardLight(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)179 static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest,
180 				    SplashColorPtr blend, SplashColorMode cm) {
181   int i;
182 
183   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
184     // the spec says "if Cs <= 0.5" -- note that 0x80 is 128/255=0.5020
185     blend[i] = src[i] < 0x80
186                  ? (Guchar)((dest[i] * 2 * src[i]) / 255)
187                  : (Guchar)(255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255);
188   }
189 }
190 
splashOutBlendSoftLight(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)191 static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest,
192 				    SplashColorPtr blend, SplashColorMode cm) {
193   int i, x;
194 
195   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
196     // the spec says "if Cs <= 0.5" -- note that 0x80 is 128/255=0.5020
197     if (src[i] < 0x80) {
198       blend[i] = (Guchar)(dest[i] -
199 			  (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) /
200 			    (255 * 255));
201     } else {
202       // the spec says "if Cb <= 0.25" -- note that 0x40 is 64/255=0.2510
203       if (dest[i] < 0x40) {
204 	x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255)
205 	      + 4 * 255) * dest[i]) / 255;
206       } else {
207 	x = (int)sqrt(255.0 * dest[i]);
208       }
209       blend[i] = (Guchar)(dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255);
210     }
211   }
212 }
213 
splashOutBlendDifference(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)214 static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest,
215 				     SplashColorPtr blend,
216 				     SplashColorMode cm) {
217   int i;
218 
219   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
220     blend[i] = dest[i] < src[i] ? (Guchar)(src[i] - dest[i])
221 			        : (Guchar)(dest[i] - src[i]);
222   }
223 }
224 
splashOutBlendExclusion(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)225 static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest,
226 				    SplashColorPtr blend, SplashColorMode cm) {
227   int i;
228 
229   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
230     blend[i] = (Guchar)(dest[i] + src[i] - (2 * dest[i] * src[i]) / 255);
231   }
232 }
233 
getLum(int r,int g,int b)234 static int getLum(int r, int g, int b) {
235   return (int)(0.3 * r + 0.59 * g + 0.11 * b);
236 }
237 
getSat(int r,int g,int b)238 static int getSat(int r, int g, int b) {
239   int rgbMin, rgbMax;
240 
241   rgbMin = rgbMax = r;
242   if (g < rgbMin) {
243     rgbMin = g;
244   } else if (g > rgbMax) {
245     rgbMax = g;
246   }
247   if (b < rgbMin) {
248     rgbMin = b;
249   } else if (b > rgbMax) {
250     rgbMax = b;
251   }
252   return rgbMax - rgbMin;
253 }
254 
clipColor(int rIn,int gIn,int bIn,Guchar * rOut,Guchar * gOut,Guchar * bOut)255 static void clipColor(int rIn, int gIn, int bIn,
256 		      Guchar *rOut, Guchar *gOut, Guchar *bOut) {
257   int lum, rgbMin, rgbMax, r, g, b;
258 
259   lum = getLum(rIn, gIn, bIn);
260   rgbMin = rgbMax = rIn;
261   if (gIn < rgbMin) {
262     rgbMin = gIn;
263   } else if (gIn > rgbMax) {
264     rgbMax = gIn;
265   }
266   if (bIn < rgbMin) {
267     rgbMin = bIn;
268   } else if (bIn > rgbMax) {
269     rgbMax = bIn;
270   }
271   r = rIn;
272   g = gIn;
273   b = bIn;
274   if (rgbMin < 0) {
275     r = lum + ((r - lum) * lum) / (lum - rgbMin);
276     g = lum + ((g - lum) * lum) / (lum - rgbMin);
277     b = lum + ((b - lum) * lum) / (lum - rgbMin);
278   }
279   if (rgbMax > 255) {
280     r = lum + ((r - lum) * (255 - lum)) / (rgbMax - lum);
281     g = lum + ((g - lum) * (255 - lum)) / (rgbMax - lum);
282     b = lum + ((b - lum) * (255 - lum)) / (rgbMax - lum);
283   }
284   *rOut = (Guchar)r;
285   *gOut = (Guchar)g;
286   *bOut = (Guchar)b;
287 }
288 
setLum(Guchar rIn,Guchar gIn,Guchar bIn,int lum,Guchar * rOut,Guchar * gOut,Guchar * bOut)289 static void setLum(Guchar rIn, Guchar gIn, Guchar bIn, int lum,
290 		   Guchar *rOut, Guchar *gOut, Guchar *bOut) {
291   int d;
292 
293   d = lum - getLum(rIn, gIn, bIn);
294   clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut);
295 }
296 
setSat(Guchar rIn,Guchar gIn,Guchar bIn,int sat,Guchar * rOut,Guchar * gOut,Guchar * bOut)297 static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat,
298 		   Guchar *rOut, Guchar *gOut, Guchar *bOut) {
299   int rgbMin, rgbMid, rgbMax;
300   Guchar *minOut, *midOut, *maxOut;
301 
302   if (rIn < gIn) {
303     rgbMin = rIn;  minOut = rOut;
304     rgbMid = gIn;  midOut = gOut;
305   } else {
306     rgbMin = gIn;  minOut = gOut;
307     rgbMid = rIn;  midOut = rOut;
308   }
309   if (bIn > rgbMid) {
310     rgbMax = bIn;  maxOut = bOut;
311   } else if (bIn > rgbMin) {
312     rgbMax = rgbMid;  maxOut = midOut;
313     rgbMid = bIn;     midOut = bOut;
314   } else {
315     rgbMax = rgbMid;  maxOut = midOut;
316     rgbMid = rgbMin;  midOut = minOut;
317     rgbMin = bIn;     minOut = bOut;
318   }
319   if (rgbMax > rgbMin) {
320     *midOut = (Guchar)(((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin));
321     *maxOut = (Guchar)sat;
322   } else {
323     *midOut = *maxOut = 0;
324   }
325   *minOut = 0;
326 }
327 
splashOutBlendHue(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)328 static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
329 			      SplashColorPtr blend, SplashColorMode cm) {
330   Guchar r0, g0, b0;
331 
332   switch (cm) {
333   case splashModeMono1:
334   case splashModeMono8:
335     blend[0] = dest[0];
336     break;
337   case splashModeRGB8:
338   case splashModeBGR8:
339     setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
340 	   &r0, &g0, &b0);
341     setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
342 	   &blend[0], &blend[1], &blend[2]);
343     break;
344 #if SPLASH_CMYK
345   case splashModeCMYK8:
346     // NB: inputs have already been converted to additive mode
347     setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
348 	   &r0, &g0, &b0);
349     setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
350 	   &blend[0], &blend[1], &blend[2]);
351     blend[3] = dest[3];
352     break;
353 #endif
354   }
355 }
356 
splashOutBlendSaturation(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)357 static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
358 				     SplashColorPtr blend,
359 				     SplashColorMode cm) {
360   Guchar r0, g0, b0;
361 
362   switch (cm) {
363   case splashModeMono1:
364   case splashModeMono8:
365     blend[0] = dest[0];
366     break;
367   case splashModeRGB8:
368   case splashModeBGR8:
369     setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]),
370 	   &r0, &g0, &b0);
371     setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
372 	   &blend[0], &blend[1], &blend[2]);
373     break;
374 #if SPLASH_CMYK
375   case splashModeCMYK8:
376     // NB: inputs have already been converted to additive mode
377     setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]),
378 	   &r0, &g0, &b0);
379     setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
380 	   &blend[0], &blend[1], &blend[2]);
381     blend[3] = dest[3];
382     break;
383 #endif
384   }
385 }
386 
splashOutBlendColor(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)387 static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,
388 				SplashColorPtr blend, SplashColorMode cm) {
389 
390   switch (cm) {
391   case splashModeMono1:
392   case splashModeMono8:
393     blend[0] = dest[0];
394     break;
395   case splashModeRGB8:
396   case splashModeBGR8:
397     setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]),
398 	   &blend[0], &blend[1], &blend[2]);
399     break;
400 #if SPLASH_CMYK
401   case splashModeCMYK8:
402     // NB: inputs have already been converted to additive mode
403     setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]),
404 	   &blend[0], &blend[1], &blend[2]);
405     blend[3] = dest[3];
406     break;
407 #endif
408   }
409 }
410 
splashOutBlendLuminosity(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)411 static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,
412 				     SplashColorPtr blend,
413 				     SplashColorMode cm) {
414 
415   switch (cm) {
416   case splashModeMono1:
417   case splashModeMono8:
418     blend[0] = dest[0];
419     break;
420   case splashModeRGB8:
421   case splashModeBGR8:
422     setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
423 	   &blend[0], &blend[1], &blend[2]);
424     break;
425 #if SPLASH_CMYK
426   case splashModeCMYK8:
427     // NB: inputs have already been converted to additive mode
428     setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
429 	   &blend[0], &blend[1], &blend[2]);
430     blend[3] = src[3];
431     break;
432 #endif
433   }
434 }
435 
436 // NB: This must match the GfxBlendMode enum defined in GfxState.h.
437 SplashBlendFunc splashOutBlendFuncs[] = {
438   NULL,
439   &splashOutBlendMultiply,
440   &splashOutBlendScreen,
441   &splashOutBlendOverlay,
442   &splashOutBlendDarken,
443   &splashOutBlendLighten,
444   &splashOutBlendColorDodge,
445   &splashOutBlendColorBurn,
446   &splashOutBlendHardLight,
447   &splashOutBlendSoftLight,
448   &splashOutBlendDifference,
449   &splashOutBlendExclusion,
450   &splashOutBlendHue,
451   &splashOutBlendSaturation,
452   &splashOutBlendColor,
453   &splashOutBlendLuminosity
454 };
455 
456 
457 //------------------------------------------------------------------------
458 // SplashOutFontFileID
459 //------------------------------------------------------------------------
460 
461 class SplashOutFontFileID: public SplashFontFileID {
462 public:
463 
SplashOutFontFileID(Ref * rA)464   SplashOutFontFileID(Ref *rA) {
465     r = *rA;
466     substIdx = -1;
467     oblique = 0;
468   }
469 
~SplashOutFontFileID()470   ~SplashOutFontFileID() {}
471 
matches(SplashFontFileID * id)472   GBool matches(SplashFontFileID *id) {
473     return ((SplashOutFontFileID *)id)->r.num == r.num &&
474            ((SplashOutFontFileID *)id)->r.gen == r.gen;
475   }
476 
setOblique(double obliqueA)477   void setOblique(double obliqueA) { oblique = obliqueA; }
getOblique()478   double getOblique() { return oblique; }
setSubstIdx(int substIdxA)479   void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
getSubstIdx()480   int getSubstIdx() { return substIdx; }
481 
482 private:
483 
484   Ref r;
485   double oblique;
486   int substIdx;
487 };
488 
489 //------------------------------------------------------------------------
490 // T3FontCache
491 //------------------------------------------------------------------------
492 
493 struct T3FontCacheTag {
494   Gushort code;
495   Gushort mru;			// valid bit (0x8000) and MRU index
496 };
497 
498 class T3FontCache {
499 public:
500 
501   T3FontCache(Ref *fontID, double m11A, double m12A,
502 	      double m21A, double m22A,
503 	      int glyphXA, int glyphYA, int glyphWA, int glyphHA,
504 	      GBool validBBoxA, GBool aa);
505   ~T3FontCache();
matches(Ref * idA,double m11A,double m12A,double m21A,double m22A)506   GBool matches(Ref *idA, double m11A, double m12A,
507 		double m21A, double m22A)
508     { return fontID.num == idA->num && fontID.gen == idA->gen &&
509 	     m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
510 
511   Ref fontID;			// PDF font ID
512   double m11, m12, m21, m22;	// transform matrix
513   int glyphX, glyphY;		// pixel offset of glyph bitmaps
514   int glyphW, glyphH;		// size of glyph bitmaps, in pixels
515   GBool validBBox;		// false if the bbox was [0 0 0 0]
516   int glyphSize;		// size of glyph bitmaps, in bytes
517   int cacheSets;		// number of sets in cache
518   int cacheAssoc;		// cache associativity (glyphs per set)
519   Guchar *cacheData;		// glyph pixmap cache
520   T3FontCacheTag *cacheTags;	// cache tags, i.e., char codes
521   int refCount;			// active reference count for this T3 font
522 };
523 
T3FontCache(Ref * fontIDA,double m11A,double m12A,double m21A,double m22A,int glyphXA,int glyphYA,int glyphWA,int glyphHA,GBool validBBoxA,GBool aa)524 T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
525 			 double m21A, double m22A,
526 			 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
527 			 GBool validBBoxA, GBool aa) {
528   int i;
529 
530   fontID = *fontIDA;
531   m11 = m11A;
532   m12 = m12A;
533   m21 = m21A;
534   m22 = m22A;
535   glyphX = glyphXA;
536   glyphY = glyphYA;
537   glyphW = glyphWA;
538   glyphH = glyphHA;
539   validBBox = validBBoxA;
540   // sanity check for excessively large glyphs (which most likely
541   // indicate an incorrect BBox)
542   i = glyphW * glyphH;
543   if (i > 100000 || glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0) {
544     glyphW = glyphH = 100;
545     validBBox = gFalse;
546   }
547   if (aa) {
548     glyphSize = glyphW * glyphH;
549   } else {
550     glyphSize = ((glyphW + 7) >> 3) * glyphH;
551   }
552   cacheAssoc = type3FontCacheAssoc;
553   for (cacheSets = type3FontCacheMaxSets;
554        cacheSets > 1 &&
555 	 cacheSets * cacheAssoc * glyphSize > type3FontCacheSize;
556        cacheSets >>= 1) ;
557   cacheData = (Guchar *)gmallocn(cacheSets * cacheAssoc, glyphSize);
558   cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc,
559 					 sizeof(T3FontCacheTag));
560   for (i = 0; i < cacheSets * cacheAssoc; ++i) {
561     cacheTags[i].mru = (Gushort)(i & (cacheAssoc - 1));
562   }
563   refCount = 0;
564 }
565 
~T3FontCache()566 T3FontCache::~T3FontCache() {
567   gfree(cacheData);
568   gfree(cacheTags);
569 }
570 
571 struct T3GlyphStack {
572   Gushort code;			// character code
573 
574   GBool haveDx;			// set after seeing a d0/d1 operator
575   GBool doNotCache;		// set if we see a gsave/grestore before
576 				//   the d0/d1
577 
578   //----- cache info
579   T3FontCache *cache;		// font cache for the current font
580   T3FontCacheTag *cacheTag;	// pointer to cache tag for the glyph
581   Guchar *cacheData;		// pointer to cache data for the glyph
582 
583   //----- saved state
584   SplashBitmap *origBitmap;
585   Splash *origSplash;
586   double origCTM4, origCTM5;
587   SplashStrokeAdjustMode savedStrokeAdjust;
588 
589   T3GlyphStack *next;		// next object on stack
590 };
591 
592 //------------------------------------------------------------------------
593 // SplashTransparencyGroup
594 //------------------------------------------------------------------------
595 
596 struct SplashTransparencyGroup {
597   int tx, ty;			// translation coordinates
598   SplashBitmap *tBitmap;	// bitmap for transparency group
599   GfxColorSpace *blendingColorSpace;
600   GBool isolated;
601 
602   //----- modified region in tBitmap
603   int modXMin, modYMin, modXMax, modYMax;
604 
605   //----- saved state
606   SplashBitmap *origBitmap;
607   Splash *origSplash;
608   SplashBitmap *backdropBitmap;
609 
610   SplashTransparencyGroup *next;
611 };
612 
613 //------------------------------------------------------------------------
614 // SplashOutputDev
615 //------------------------------------------------------------------------
616 
SplashOutputDev(SplashColorMode colorModeA,int bitmapRowPadA,GBool reverseVideoA,SplashColorPtr paperColorA,GBool bitmapTopDownA,GBool allowAntialiasA)617 SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
618 				 int bitmapRowPadA,
619 				 GBool reverseVideoA,
620 				 SplashColorPtr paperColorA,
621 				 GBool bitmapTopDownA,
622 				 GBool allowAntialiasA) {
623   colorMode = colorModeA;
624   bitmapRowPad = bitmapRowPadA;
625   bitmapTopDown = bitmapTopDownA;
626   bitmapUpsideDown = gFalse;
627   noComposite = gFalse;
628   allowAntialias = allowAntialiasA;
629   vectorAntialias = allowAntialias &&
630 		      globalParams->getVectorAntialias() &&
631 		      colorMode != splashModeMono1;
632   setupScreenParams(72.0, 72.0);
633   reverseVideo = reverseVideoA;
634   splashColorCopy(paperColor, paperColorA);
635   skipHorizText = gFalse;
636   skipRotatedText = gFalse;
637 
638   xref = NULL;
639 
640   bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
641 			    colorMode != splashModeMono1, bitmapTopDown, NULL);
642   splash = new Splash(bitmap, vectorAntialias, NULL, &screenParams);
643   splash->setMinLineWidth(globalParams->getMinLineWidth());
644   splash->setStrokeAdjust(
645 		 mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
646   splash->setEnablePathSimplification(
647 		 globalParams->getEnablePathSimplification());
648   splash->clear(paperColor, 0);
649 
650   fontEngine = NULL;
651 
652   nT3Fonts = 0;
653   t3GlyphStack = NULL;
654 
655   font = NULL;
656   needFontUpdate = gFalse;
657   textClipPath = NULL;
658 
659   transpGroupStack = NULL;
660 
661   nestCount = 0;
662 
663   startPageCbk = NULL;
664   startPageCbkData = NULL;
665 }
666 
667 
setupScreenParams(double hDPI,double vDPI)668 void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) {
669   screenParams.size = globalParams->getScreenSize();
670   screenParams.dotRadius = globalParams->getScreenDotRadius();
671   screenParams.gamma = (SplashCoord)globalParams->getScreenGamma();
672   screenParams.blackThreshold =
673       (SplashCoord)globalParams->getScreenBlackThreshold();
674   screenParams.whiteThreshold =
675       (SplashCoord)globalParams->getScreenWhiteThreshold();
676   switch (globalParams->getScreenType()) {
677   case screenDispersed:
678     screenParams.type = splashScreenDispersed;
679     if (screenParams.size < 0) {
680       screenParams.size = 4;
681     }
682     break;
683   case screenClustered:
684     screenParams.type = splashScreenClustered;
685     if (screenParams.size < 0) {
686       screenParams.size = 10;
687     }
688     break;
689   case screenStochasticClustered:
690     screenParams.type = splashScreenStochasticClustered;
691     if (screenParams.size < 0) {
692       screenParams.size = 64;
693     }
694     if (screenParams.dotRadius < 0) {
695       screenParams.dotRadius = 2;
696     }
697     break;
698   case screenUnset:
699   default:
700     // use clustered dithering for resolution >= 300 dpi
701     // (compare to 299.9 to avoid floating point issues)
702     if (hDPI > 299.9 && vDPI > 299.9) {
703       screenParams.type = splashScreenStochasticClustered;
704       if (screenParams.size < 0) {
705 	screenParams.size = 64;
706       }
707       if (screenParams.dotRadius < 0) {
708 	screenParams.dotRadius = 2;
709       }
710     } else {
711       screenParams.type = splashScreenDispersed;
712       if (screenParams.size < 0) {
713 	screenParams.size = 4;
714       }
715     }
716   }
717 }
718 
~SplashOutputDev()719 SplashOutputDev::~SplashOutputDev() {
720   int i;
721 
722   for (i = 0; i < nT3Fonts; ++i) {
723     delete t3FontCache[i];
724   }
725   if (fontEngine) {
726     delete fontEngine;
727   }
728   if (splash) {
729     delete splash;
730   }
731   if (bitmap) {
732     delete bitmap;
733   }
734   if (textClipPath) {
735     delete textClipPath;
736   }
737 }
738 
startDoc(XRef * xrefA)739 void SplashOutputDev::startDoc(XRef *xrefA) {
740   int i;
741 
742   xref = xrefA;
743   if (fontEngine) {
744     delete fontEngine;
745   }
746   fontEngine = new SplashFontEngine(
747 #if HAVE_FREETYPE_H
748 				    globalParams->getEnableFreeType(),
749 				    globalParams->getDisableFreeTypeHinting()
750 				      ? splashFTNoHinting : 0,
751 #endif
752 				    allowAntialias &&
753 				      globalParams->getAntialias() &&
754 				      colorMode != splashModeMono1);
755   for (i = 0; i < nT3Fonts; ++i) {
756     delete t3FontCache[i];
757   }
758   nT3Fonts = 0;
759 }
760 
startPage(int pageNum,GfxState * state)761 void SplashOutputDev::startPage(int pageNum, GfxState *state) {
762   int w, h;
763   double *ctm;
764   SplashCoord mat[6];
765   SplashColor color;
766 
767   if (state) {
768     setupScreenParams(state->getHDPI(), state->getVDPI());
769     w = (int)(state->getPageWidth() + 0.5);
770     if (w <= 0) {
771       w = 1;
772     }
773     h = (int)(state->getPageHeight() + 0.5);
774     if (h <= 0) {
775       h = 1;
776     }
777   } else {
778     w = h = 1;
779   }
780   if (splash) {
781     delete splash;
782     splash = NULL;
783   }
784   if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
785     if (bitmap) {
786       delete bitmap;
787       bitmap = NULL;
788     }
789     traceMessage("page bitmap");
790     bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode,
791 			      colorMode != splashModeMono1, bitmapTopDown,
792 			      NULL);
793   }
794   splash = new Splash(bitmap, vectorAntialias, NULL, &screenParams);
795   splash->setMinLineWidth(globalParams->getMinLineWidth());
796   splash->setEnablePathSimplification(
797 		 globalParams->getEnablePathSimplification());
798   if (state) {
799     ctm = state->getCTM();
800     mat[0] = (SplashCoord)ctm[0];
801     mat[1] = (SplashCoord)ctm[1];
802     mat[2] = (SplashCoord)ctm[2];
803     mat[3] = (SplashCoord)ctm[3];
804     mat[4] = (SplashCoord)ctm[4];
805     mat[5] = (SplashCoord)ctm[5];
806     splash->setMatrix(mat);
807   }
808   switch (colorMode) {
809   case splashModeMono1:
810   case splashModeMono8:
811     color[0] = 0;
812     break;
813   case splashModeRGB8:
814   case splashModeBGR8:
815     color[0] = color[1] = color[2] = 0;
816     break;
817 #if SPLASH_CMYK
818   case splashModeCMYK8:
819     color[0] = color[1] = color[2] = color[3] = 0;
820     break;
821 #endif
822   }
823   splash->setStrokePattern(new SplashSolidColor(color));
824   splash->setFillPattern(new SplashSolidColor(color));
825   splash->setLineCap(splashLineCapButt);
826   splash->setLineJoin(splashLineJoinMiter);
827   splash->setLineDash(NULL, 0, 0);
828   splash->setMiterLimit(10);
829   splash->setFlatness(1);
830   // the SA parameter supposedly defaults to false, but Acrobat
831   // apparently hardwires it to true
832   splash->setStrokeAdjust(
833 	      mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
834   splash->clear(paperColor, 0);
835   reverseVideoInvertImages = globalParams->getReverseVideoInvertImages();
836   if (startPageCbk) {
837     (*startPageCbk)(startPageCbkData);
838   }
839 }
840 
endPage()841 void SplashOutputDev::endPage() {
842   if (colorMode != splashModeMono1 && !noComposite) {
843     splash->compositeBackground(paperColor);
844   }
845 }
846 
saveState(GfxState * state)847 void SplashOutputDev::saveState(GfxState *state) {
848   splash->saveState();
849   if (t3GlyphStack && !t3GlyphStack->haveDx) {
850     t3GlyphStack->doNotCache = gTrue;
851     error(errSyntaxWarning, -1,
852 	  "Save (q) operator before d0/d1 in Type 3 glyph");
853   }
854 }
855 
restoreState(GfxState * state)856 void SplashOutputDev::restoreState(GfxState *state) {
857   splash->restoreState();
858   needFontUpdate = gTrue;
859   if (t3GlyphStack && !t3GlyphStack->haveDx) {
860     t3GlyphStack->doNotCache = gTrue;
861     error(errSyntaxWarning, -1,
862 	  "Restore (Q) operator before d0/d1 in Type 3 glyph");
863   }
864 }
865 
updateAll(GfxState * state)866 void SplashOutputDev::updateAll(GfxState *state) {
867   updateLineDash(state);
868   updateLineJoin(state);
869   updateLineCap(state);
870   updateLineWidth(state);
871   updateFlatness(state);
872   updateMiterLimit(state);
873   updateStrokeAdjust(state);
874   updateFillColor(state);
875   updateStrokeColor(state);
876   needFontUpdate = gTrue;
877 }
878 
updateCTM(GfxState * state,double m11,double m12,double m21,double m22,double m31,double m32)879 void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
880 				double m21, double m22,
881 				double m31, double m32) {
882   double *ctm;
883   SplashCoord mat[6];
884 
885   ctm = state->getCTM();
886   mat[0] = (SplashCoord)ctm[0];
887   mat[1] = (SplashCoord)ctm[1];
888   mat[2] = (SplashCoord)ctm[2];
889   mat[3] = (SplashCoord)ctm[3];
890   mat[4] = (SplashCoord)ctm[4];
891   mat[5] = (SplashCoord)ctm[5];
892   splash->setMatrix(mat);
893 }
894 
updateLineDash(GfxState * state)895 void SplashOutputDev::updateLineDash(GfxState *state) {
896   double *dashPattern;
897   int dashLength;
898   double dashStart;
899   SplashCoord dash[20];
900   int i;
901 
902   state->getLineDash(&dashPattern, &dashLength, &dashStart);
903   if (dashLength > 20) {
904     dashLength = 20;
905   }
906   for (i = 0; i < dashLength; ++i) {
907     dash[i] = (SplashCoord)dashPattern[i];
908     if (dash[i] < 0) {
909       dash[i] = 0;
910     }
911   }
912   splash->setLineDash(dash, dashLength, (SplashCoord)dashStart);
913 }
914 
updateFlatness(GfxState * state)915 void SplashOutputDev::updateFlatness(GfxState *state) {
916 #if 0 // Acrobat ignores the flatness setting, and always renders curves
917       // with a fairly small flatness value
918   splash->setFlatness(state->getFlatness());
919 #endif
920 }
921 
updateLineJoin(GfxState * state)922 void SplashOutputDev::updateLineJoin(GfxState *state) {
923   splash->setLineJoin(state->getLineJoin());
924 }
925 
updateLineCap(GfxState * state)926 void SplashOutputDev::updateLineCap(GfxState *state) {
927   splash->setLineCap(state->getLineCap());
928 }
929 
updateMiterLimit(GfxState * state)930 void SplashOutputDev::updateMiterLimit(GfxState *state) {
931   splash->setMiterLimit(state->getMiterLimit());
932 }
933 
updateLineWidth(GfxState * state)934 void SplashOutputDev::updateLineWidth(GfxState *state) {
935   splash->setLineWidth(state->getLineWidth());
936 }
937 
updateStrokeAdjust(GfxState * state)938 void SplashOutputDev::updateStrokeAdjust(GfxState *state) {
939 #if 0 // the SA parameter supposedly defaults to false, but Acrobat
940       // apparently hardwires it to true
941   if (state->getStrokeAdjust()) {
942     if (globalParams->getStrokeAdjustMode() == strokeAdjustCAD) {
943       splash->setStrokeAdjust(splashStrokeAdjustCAD);
944     } else {
945       splash->setStrokeAdjust(splashStrokeAdjustNormal);
946     }
947   } else {
948     splash->setStrokeAdjust(splashStrokeAdjustOff);
949   }
950 #endif
951 }
952 
953 
updateFillColor(GfxState * state)954 void SplashOutputDev::updateFillColor(GfxState *state) {
955   GfxGray gray;
956   GfxRGB rgb;
957 #if SPLASH_CMYK
958   GfxCMYK cmyk;
959 #endif
960 
961   switch (colorMode) {
962   case splashModeMono1:
963   case splashModeMono8:
964     state->getFillGray(&gray);
965     splash->setFillPattern(getColor(gray));
966     break;
967   case splashModeRGB8:
968   case splashModeBGR8:
969     state->getFillRGB(&rgb);
970     splash->setFillPattern(getColor(&rgb));
971     break;
972 #if SPLASH_CMYK
973   case splashModeCMYK8:
974     state->getFillCMYK(&cmyk);
975     splash->setFillPattern(getColor(&cmyk));
976     break;
977 #endif
978   }
979 }
980 
updateStrokeColor(GfxState * state)981 void SplashOutputDev::updateStrokeColor(GfxState *state) {
982   GfxGray gray;
983   GfxRGB rgb;
984 #if SPLASH_CMYK
985   GfxCMYK cmyk;
986 #endif
987 
988   switch (colorMode) {
989   case splashModeMono1:
990   case splashModeMono8:
991     state->getStrokeGray(&gray);
992     splash->setStrokePattern(getColor(gray));
993     break;
994   case splashModeRGB8:
995   case splashModeBGR8:
996     state->getStrokeRGB(&rgb);
997     splash->setStrokePattern(getColor(&rgb));
998     break;
999 #if SPLASH_CMYK
1000   case splashModeCMYK8:
1001     state->getStrokeCMYK(&cmyk);
1002     splash->setStrokePattern(getColor(&cmyk));
1003     break;
1004 #endif
1005   }
1006 }
1007 
1008 
getColor(GfxGray gray)1009 SplashPattern *SplashOutputDev::getColor(GfxGray gray) {
1010   SplashColor color;
1011 
1012   getColor(gray, color);
1013   return new SplashSolidColor(color);
1014 }
1015 
getColor(GfxRGB * rgb)1016 SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) {
1017   SplashColor color;
1018 
1019   getColor(rgb, color);
1020   return new SplashSolidColor(color);
1021 }
1022 
1023 #if SPLASH_CMYK
getColor(GfxCMYK * cmyk)1024 SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) {
1025   SplashColor color;
1026 
1027   getColor(cmyk, color);
1028   return new SplashSolidColor(color);
1029 }
1030 #endif
1031 
1032 
getColor(GfxGray gray,SplashColorPtr color)1033 void SplashOutputDev::getColor(GfxGray gray, SplashColorPtr color) {
1034   if (reverseVideo) {
1035     gray = gfxColorComp1 - gray;
1036   }
1037   color[0] = colToByte(gray);
1038 }
1039 
getColor(GfxRGB * rgb,SplashColorPtr color)1040 void SplashOutputDev::getColor(GfxRGB *rgb, SplashColorPtr color) {
1041   GfxColorComp r, g, b;
1042 
1043   if (reverseVideo) {
1044     r = gfxColorComp1 - rgb->r;
1045     g = gfxColorComp1 - rgb->g;
1046     b = gfxColorComp1 - rgb->b;
1047   } else {
1048     r = rgb->r;
1049     g = rgb->g;
1050     b = rgb->b;
1051   }
1052   color[0] = colToByte(r);
1053   color[1] = colToByte(g);
1054   color[2] = colToByte(b);
1055 }
1056 
1057 #if SPLASH_CMYK
getColor(GfxCMYK * cmyk,SplashColorPtr color)1058 void SplashOutputDev::getColor(GfxCMYK *cmyk, SplashColorPtr color) {
1059   color[0] = colToByte(cmyk->c);
1060   color[1] = colToByte(cmyk->m);
1061   color[2] = colToByte(cmyk->y);
1062   color[3] = colToByte(cmyk->k);
1063 }
1064 #endif
1065 
1066 
1067 
setOverprintMask(GfxState * state,GfxColorSpace * colorSpace,GBool overprintFlag,int overprintMode,GfxColor * singleColor)1068 void SplashOutputDev::setOverprintMask(GfxState *state,
1069 				       GfxColorSpace *colorSpace,
1070 				       GBool overprintFlag,
1071 				       int overprintMode,
1072 				       GfxColor *singleColor) {
1073 #if SPLASH_CMYK
1074   Guint mask;
1075   GfxCMYK cmyk;
1076 
1077   if (overprintFlag && globalParams->getOverprintPreview()) {
1078     mask = colorSpace->getOverprintMask();
1079     // The OPM (overprintMode) setting is only relevant when the color
1080     // space is DeviceCMYK or is "implicitly converted to DeviceCMYK".
1081     // Per the PDF spec, this happens with ICCBased color spaces only
1082     // if the profile matches the output device.
1083     if (singleColor && overprintMode &&
1084 	colorSpace->getMode() == csDeviceCMYK) {
1085       colorSpace->getCMYK(singleColor, &cmyk, state->getRenderingIntent());
1086       if (cmyk.c == 0) {
1087 	mask &= ~1;
1088       }
1089       if (cmyk.m == 0) {
1090 	mask &= ~2;
1091       }
1092       if (cmyk.y == 0) {
1093 	mask &= ~4;
1094       }
1095       if (cmyk.k == 0) {
1096 	mask &= ~8;
1097       }
1098     }
1099   } else {
1100     mask = 0xffffffff;
1101   }
1102   splash->setOverprintMask(mask);
1103 #endif
1104 
1105 }
1106 
updateBlendMode(GfxState * state)1107 void SplashOutputDev::updateBlendMode(GfxState *state) {
1108   splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]);
1109 }
1110 
updateFillOpacity(GfxState * state)1111 void SplashOutputDev::updateFillOpacity(GfxState *state) {
1112   splash->setFillAlpha((SplashCoord)state->getFillOpacity());
1113 }
1114 
updateStrokeOpacity(GfxState * state)1115 void SplashOutputDev::updateStrokeOpacity(GfxState *state) {
1116   splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity());
1117 }
1118 
updateRenderingIntent(GfxState * state)1119 void SplashOutputDev::updateRenderingIntent(GfxState *state) {
1120   updateFillColor(state);
1121   updateStrokeColor(state);
1122 }
1123 
updateTransfer(GfxState * state)1124 void SplashOutputDev::updateTransfer(GfxState *state) {
1125   Function **transfer;
1126   Guchar red[256], green[256], blue[256], gray[256];
1127   double x, y;
1128   int i;
1129 
1130   transfer = state->getTransfer();
1131   if (transfer[0] &&
1132       transfer[0]->getInputSize() == 1 &&
1133       transfer[0]->getOutputSize() == 1) {
1134     if (transfer[1] &&
1135 	transfer[1]->getInputSize() == 1 &&
1136 	transfer[1]->getOutputSize() == 1 &&
1137 	transfer[2] &&
1138 	transfer[2]->getInputSize() == 1 &&
1139 	transfer[2]->getOutputSize() == 1 &&
1140 	transfer[3] &&
1141 	transfer[3]->getInputSize() == 1 &&
1142 	transfer[3]->getOutputSize() == 1) {
1143       for (i = 0; i < 256; ++i) {
1144 	x = i / 255.0;
1145 	transfer[0]->transform(&x, &y);
1146 	red[i] = (Guchar)(y * 255.0 + 0.5);
1147 	transfer[1]->transform(&x, &y);
1148 	green[i] = (Guchar)(y * 255.0 + 0.5);
1149 	transfer[2]->transform(&x, &y);
1150 	blue[i] = (Guchar)(y * 255.0 + 0.5);
1151 	transfer[3]->transform(&x, &y);
1152 	gray[i] = (Guchar)(y * 255.0 + 0.5);
1153       }
1154     } else {
1155       for (i = 0; i < 256; ++i) {
1156 	x = i / 255.0;
1157 	transfer[0]->transform(&x, &y);
1158 	red[i] = green[i] = blue[i] = gray[i] = (Guchar)(y * 255.0 + 0.5);
1159       }
1160     }
1161   } else {
1162     for (i = 0; i < 256; ++i) {
1163       red[i] = green[i] = blue[i] = gray[i] = (Guchar)i;
1164     }
1165   }
1166   splash->setTransfer(red, green, blue, gray);
1167 }
1168 
updateFont(GfxState * state)1169 void SplashOutputDev::updateFont(GfxState *state) {
1170   needFontUpdate = gTrue;
1171 }
1172 
doUpdateFont(GfxState * state)1173 void SplashOutputDev::doUpdateFont(GfxState *state) {
1174   GfxFont *gfxFont;
1175   GfxFontLoc *fontLoc;
1176   GfxFontType fontType;
1177   SplashOutFontFileID *id;
1178   SplashFontFile *fontFile;
1179   int fontNum;
1180   FoFiTrueType *ff;
1181   FoFiType1C *ffT1C;
1182   Ref embRef;
1183   Object refObj, strObj;
1184 #if LOAD_FONTS_FROM_MEM
1185   GString *fontBuf;
1186   FILE *extFontFile;
1187 #else
1188   GString *tmpFileName, *fileName;
1189   FILE *tmpFile;
1190 #endif
1191   char blk[4096];
1192   int *codeToGID;
1193   CharCodeToUnicode *ctu;
1194   double *textMat;
1195   double m11, m12, m21, m22, fontSize, oblique;
1196   double fsx, fsy, w, fontScaleMin, fontScaleAvg, fontScale;
1197   Gushort ww;
1198   SplashCoord mat[4];
1199   char *name, *start;
1200   Unicode uBuf[8];
1201   int substIdx, n, code, cmap, cmapPlatform, cmapEncoding, length, i;
1202 
1203   needFontUpdate = gFalse;
1204   font = NULL;
1205 #if LOAD_FONTS_FROM_MEM
1206   fontBuf = NULL;
1207 #else
1208   tmpFileName = NULL;
1209   fileName = NULL;
1210 #endif
1211   substIdx = -1;
1212 
1213   if (!(gfxFont = state->getFont())) {
1214     goto err1;
1215   }
1216   fontType = gfxFont->getType();
1217   if (fontType == fontType3) {
1218     goto err1;
1219   }
1220 
1221   // sanity-check the font size: skip anything larger than 10k x 10k,
1222   // to avoid problems allocating a bitmap (note that code in
1223   // SplashFont disables caching at a smaller size than this)
1224   state->textTransformDelta(state->getFontSize(), state->getFontSize(),
1225 			    &fsx, &fsy);
1226   state->transformDelta(fsx, fsy, &fsx, &fsy);
1227   if (fabs(fsx) > 20000 || fabs(fsy) > 20000) {
1228     goto err1;
1229   }
1230 
1231   // check the font file cache
1232   id = new SplashOutFontFileID(gfxFont->getID());
1233   if (fontEngine->checkForBadFontFile(id)) {
1234     goto err2;
1235   }
1236   if ((fontFile = fontEngine->getFontFile(id))) {
1237     delete id;
1238 
1239   } else {
1240 
1241     fontNum = 0;
1242 
1243     if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) {
1244       error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'",
1245 	    gfxFont->getName() ? gfxFont->getName()->getCString()
1246 	                       : "(unnamed)");
1247       goto err2;
1248     }
1249 
1250     // embedded font
1251     if (fontLoc->locType == gfxFontLocEmbedded) {
1252       gfxFont->getEmbeddedFontID(&embRef);
1253 #if LOAD_FONTS_FROM_MEM
1254       fontBuf = new GString();
1255       refObj.initRef(embRef.num, embRef.gen);
1256       refObj.fetch(xref, &strObj);
1257       refObj.free();
1258       if (!strObj.isStream()) {
1259 	error(errSyntaxError, -1, "Embedded font object is wrong type");
1260 	strObj.free();
1261 	delete fontLoc;
1262 	goto err2;
1263       }
1264       strObj.streamReset();
1265       while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) {
1266 	fontBuf->append(blk, n);
1267       }
1268       strObj.streamClose();
1269       strObj.free();
1270 #else
1271       if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
1272 	error(errIO, -1, "Couldn't create temporary font file");
1273 	delete fontLoc;
1274 	goto err2;
1275       }
1276       refObj.initRef(embRef.num, embRef.gen);
1277       refObj.fetch(xref, &strObj);
1278       refObj.free();
1279       if (!strObj.isStream()) {
1280 	error(errSyntaxError, -1, "Embedded font object is wrong type");
1281 	strObj.free();
1282 	fclose(tmpFile);
1283 	delete fontLoc;
1284 	goto err2;
1285       }
1286       strObj.streamReset();
1287       while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) {
1288 	fwrite(blk, 1, n, tmpFile);
1289       }
1290       strObj.streamClose();
1291       strObj.free();
1292       fclose(tmpFile);
1293       fileName = tmpFileName;
1294 #endif
1295 
1296     // external font
1297     } else { // gfxFontLocExternal
1298 #if LOAD_FONTS_FROM_MEM
1299       if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) {
1300 	error(errSyntaxError, -1, "Couldn't open external font file '{0:t}'",
1301 	      fontLoc->path);
1302 	delete fontLoc;
1303 	goto err2;
1304       }
1305       fontBuf = new GString();
1306       while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) {
1307 	fontBuf->append(blk, n);
1308       }
1309       fclose(extFontFile);
1310 #else
1311       fileName = fontLoc->path;
1312 #endif
1313       fontNum = fontLoc->fontNum;
1314       if (fontLoc->substIdx >= 0) {
1315 	id->setSubstIdx(fontLoc->substIdx);
1316       }
1317       if (fontLoc->oblique != 0) {
1318 	id->setOblique(fontLoc->oblique);
1319       }
1320     }
1321 
1322     // load the font file
1323     switch (fontLoc->fontType) {
1324     case fontType1:
1325       if (!(fontFile = fontEngine->loadType1Font(
1326 		   id,
1327 #if LOAD_FONTS_FROM_MEM
1328 		   fontBuf,
1329 #else
1330 		   fileName->getCString(),
1331 		   fileName == tmpFileName,
1332 #endif
1333 		   (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1334 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1335 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1336 	                         : "(unnamed)");
1337 	delete fontLoc;
1338 	goto err1;
1339       }
1340       break;
1341     case fontType1C:
1342 #if LOAD_FONTS_FROM_MEM
1343       if ((ffT1C = FoFiType1C::make(fontBuf->getCString(),
1344 				    fontBuf->getLength()))) {
1345 #else
1346       if ((ffT1C = FoFiType1C::load(fileName->getCString()))) {
1347 #endif
1348 	codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffT1C);
1349 	delete ffT1C;
1350       } else {
1351 	codeToGID = NULL;
1352       }
1353       if (!(fontFile = fontEngine->loadType1CFont(
1354 		   id,
1355 #if LOAD_FONTS_FROM_MEM
1356 		   fontBuf,
1357 #else
1358 		   fileName->getCString(),
1359 		   fileName == tmpFileName,
1360 #endif
1361 		   codeToGID,
1362 		   (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1363 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1364 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1365 	                         : "(unnamed)");
1366 	delete fontLoc;
1367 	goto err1;
1368       }
1369       break;
1370     case fontType1COT:
1371       codeToGID = NULL;
1372 #if LOAD_FONTS_FROM_MEM
1373       if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
1374 				   fontNum, gTrue))) {
1375 #else
1376 	if ((ff = FoFiTrueType::load(fileName->getCString(),
1377 				     fontNum, gTrue))) {
1378 #endif
1379 	if (ff->getCFFBlock(&start, &length) &&
1380 	    (ffT1C = FoFiType1C::make(start, length))) {
1381 	  codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffT1C);
1382 	  delete ffT1C;
1383 	}
1384 	delete ff;
1385       }
1386       if (!(fontFile = fontEngine->loadOpenTypeT1CFont(
1387 		   id,
1388 #if LOAD_FONTS_FROM_MEM
1389 		   fontBuf,
1390 #else
1391 		   fileName->getCString(),
1392 		   fileName == tmpFileName,
1393 #endif
1394 		   codeToGID,
1395 		   (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1396 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1397 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1398 	                         : "(unnamed)");
1399 	delete fontLoc;
1400 	goto err1;
1401       }
1402       break;
1403     case fontTrueType:
1404     case fontTrueTypeOT:
1405 #if LOAD_FONTS_FROM_MEM
1406       if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
1407 				   fontNum))) {
1408 #else
1409       if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
1410 #endif
1411 	codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
1412 	n = 256;
1413 	delete ff;
1414 	// if we're substituting for a non-TrueType font, we need to mark
1415 	// all notdef codes as "do not draw" (rather than drawing TrueType
1416 	// notdef glyphs)
1417 	if (gfxFont->getType() != fontTrueType &&
1418 	    gfxFont->getType() != fontTrueTypeOT) {
1419 	  for (i = 0; i < 256; ++i) {
1420 	    if (codeToGID[i] == 0) {
1421 	      codeToGID[i] = -1;
1422 	    }
1423 	  }
1424 	}
1425       } else {
1426 	codeToGID = NULL;
1427 	n = 0;
1428       }
1429       if (!(fontFile = fontEngine->loadTrueTypeFont(
1430 			   id,
1431 #if LOAD_FONTS_FROM_MEM
1432 			   fontBuf,
1433 #else
1434 			   fileName->getCString(),
1435 			   fileName == tmpFileName,
1436 #endif
1437 			   fontNum, codeToGID, n,
1438 			   gfxFont->getEmbeddedFontName()
1439 			     ? gfxFont->getEmbeddedFontName()->getCString()
1440 			     : (char *)NULL))) {
1441 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1442 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1443 	                         : "(unnamed)");
1444 	delete fontLoc;
1445 	goto err1;
1446       }
1447       break;
1448     case fontCIDType0:
1449     case fontCIDType0C:
1450       if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
1451 	n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
1452 	codeToGID = (int *)gmallocn(n, sizeof(int));
1453 	memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1454 	       n * sizeof(int));
1455       } else {
1456 	codeToGID = NULL;
1457 	n = 0;
1458       }
1459       if (!(fontFile = fontEngine->loadCIDFont(
1460 			   id,
1461 #if LOAD_FONTS_FROM_MEM
1462 			   fontBuf,
1463 #else
1464 			   fileName->getCString(),
1465 			   fileName == tmpFileName,
1466 #endif
1467 			   codeToGID, n))) {
1468 
1469 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1470 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1471 	                         : "(unnamed)");
1472 	delete fontLoc;
1473 	goto err1;
1474       }
1475       break;
1476     case fontCIDType0COT:
1477       codeToGID = NULL;
1478       n = 0;
1479       if (fontLoc->locType == gfxFontLocEmbedded) {
1480 	if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
1481 	  n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
1482 	  codeToGID = (int *)gmallocn(n, sizeof(int));
1483 	  memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1484 		 n * sizeof(int));
1485 	}
1486       } else if (globalParams->getMapExtTrueTypeFontsViaUnicode()) {
1487 	// create a CID-to-GID mapping, via Unicode
1488 	if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
1489 #if LOAD_FONTS_FROM_MEM
1490 	  if ((ff = FoFiTrueType::make(fontBuf->getCString(),
1491 				       fontBuf->getLength(), fontNum))) {
1492 #else
1493 	  if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
1494 #endif
1495 	    // look for a Unicode cmap
1496 	    for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
1497 	      cmapPlatform = ff->getCmapPlatform(cmap);
1498 	      cmapEncoding = ff->getCmapEncoding(cmap);
1499 	      if ((cmapPlatform == 3 && cmapEncoding == 1) ||
1500 		  (cmapPlatform == 0 && cmapEncoding <= 4)) {
1501 		break;
1502 	      }
1503 	    }
1504 	    if (cmap < ff->getNumCmaps()) {
1505 	      // map CID -> Unicode -> GID
1506 	      if (ctu->isIdentity()) {
1507 		n = 65536;
1508 	      } else {
1509 		n = ctu->getLength();
1510 	      }
1511 	      codeToGID = (int *)gmallocn(n, sizeof(int));
1512 	      for (code = 0; code < n; ++code) {
1513 		if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
1514 		  codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]);
1515 		} else {
1516 		  codeToGID[code] = -1;
1517 		}
1518 	      }
1519 	    }
1520 	    delete ff;
1521 	  }
1522 	  ctu->decRefCnt();
1523 	} else {
1524 	  error(errSyntaxError, -1,
1525 		"Couldn't find a mapping to Unicode for font '{0:s}'",
1526 		gfxFont->getName() ? gfxFont->getName()->getCString()
1527 		                   : "(unnamed)");
1528 	}
1529       }
1530       if (!(fontFile = fontEngine->loadOpenTypeCFFFont(
1531 			   id,
1532 #if LOAD_FONTS_FROM_MEM
1533 			   fontBuf,
1534 #else
1535 			   fileName->getCString(),
1536 			   fileName == tmpFileName,
1537 #endif
1538 			   codeToGID, n))) {
1539 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1540 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1541 	                         : "(unnamed)");
1542 	delete fontLoc;
1543 	goto err1;
1544       }
1545       break;
1546     case fontCIDType2:
1547     case fontCIDType2OT:
1548       codeToGID = NULL;
1549       n = 0;
1550       if (fontLoc->locType == gfxFontLocEmbedded) {
1551 	if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
1552 	  n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
1553 	  codeToGID = (int *)gmallocn(n, sizeof(int));
1554 	  memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1555 		 n * sizeof(int));
1556 	}
1557       } else if (globalParams->getMapExtTrueTypeFontsViaUnicode() &&
1558 		 !((GfxCIDFont *)gfxFont)->usesIdentityEncoding()) {
1559 	// create a CID-to-GID mapping, via Unicode
1560 	if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
1561 #if LOAD_FONTS_FROM_MEM
1562 	  if ((ff = FoFiTrueType::make(fontBuf->getCString(),
1563 				       fontBuf->getLength(), fontNum))) {
1564 #else
1565 	  if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
1566 #endif
1567 	    // look for a Unicode cmap
1568 	    for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
1569 	      cmapPlatform = ff->getCmapPlatform(cmap);
1570 	      cmapEncoding = ff->getCmapEncoding(cmap);
1571 	      if ((cmapPlatform == 3 && cmapEncoding == 1) ||
1572 		  (cmapPlatform == 0 && cmapEncoding <= 4)) {
1573 		break;
1574 	      }
1575 	    }
1576 	    if (cmap < ff->getNumCmaps()) {
1577 	      // map CID -> Unicode -> GID
1578 	      if (ctu->isIdentity()) {
1579 		n = 65536;
1580 	      } else {
1581 		n = ctu->getLength();
1582 	      }
1583 	      codeToGID = (int *)gmallocn(n, sizeof(int));
1584 	      for (code = 0; code < n; ++code) {
1585 		if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
1586 		  codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]);
1587 		} else {
1588 		  codeToGID[code] = -1;
1589 		}
1590 	      }
1591 	    }
1592 	    delete ff;
1593 	  }
1594 	  ctu->decRefCnt();
1595 	} else {
1596 	  error(errSyntaxError, -1,
1597 		"Couldn't find a mapping to Unicode for font '{0:s}'",
1598 		gfxFont->getName() ? gfxFont->getName()->getCString()
1599 		                   : "(unnamed)");
1600 	}
1601       }
1602       if (!(fontFile = fontEngine->loadTrueTypeFont(
1603 			   id,
1604 #if LOAD_FONTS_FROM_MEM
1605 			   fontBuf,
1606 #else
1607 			   fileName->getCString(),
1608 			   fileName == tmpFileName,
1609 #endif
1610 			   fontNum, codeToGID, n,
1611 			   gfxFont->getEmbeddedFontName()
1612 			     ? gfxFont->getEmbeddedFontName()->getCString()
1613 			     : (char *)NULL))) {
1614 	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1615 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1616 	                         : "(unnamed)");
1617 	delete fontLoc;
1618 	goto err1;
1619       }
1620       break;
1621     default:
1622       // this shouldn't happen
1623       delete fontLoc;
1624       goto err2;
1625     }
1626 
1627     delete fontLoc;
1628   }
1629 
1630   // get the font matrix
1631   textMat = state->getTextMat();
1632   fontSize = state->getFontSize();
1633   oblique = ((SplashOutFontFileID *)fontFile->getID())->getOblique();
1634   m11 = state->getHorizScaling() * textMat[0];
1635   m12 = state->getHorizScaling() * textMat[1];
1636   m21 = oblique * m11 + textMat[2];
1637   m22 = oblique * m12 + textMat[3];
1638   m11 *= fontSize;
1639   m12 *= fontSize;
1640   m21 *= fontSize;
1641   m22 *= fontSize;
1642 
1643   // for substituted fonts: adjust the font matrix -- compare the
1644   // widths of letters and digits (A-Z, a-z, 0-9) in the original font
1645   // and the substituted font
1646   substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx();
1647   if (substIdx >= 0 && substIdx < 12) {
1648     fontScaleMin = 1;
1649     fontScaleAvg = 0;
1650     n = 0;
1651     for (code = 0; code < 256; ++code) {
1652       if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
1653 	  name[0] && !name[1] &&
1654 	  ((name[0] >= 'A' && name[0] <= 'Z') ||
1655 	   (name[0] >= 'a' && name[0] <= 'z') ||
1656 	   (name[0] >= '0' && name[0] <= '9'))) {
1657 	w = ((Gfx8BitFont *)gfxFont)->getWidth((Guchar)code);
1658 	if (builtinFontSubst[substIdx]->widths->getWidth(name, &ww) &&
1659 	    w > 0.01 && ww > 10) {
1660 	  w /= ww * 0.001;
1661 	  if (w < fontScaleMin) {
1662 	    fontScaleMin = w;
1663 	  }
1664 	  fontScaleAvg += w;
1665 	  ++n;
1666 	}
1667       }
1668     }
1669     // if real font is narrower than substituted font, reduce the font
1670     // size accordingly -- this currently uses a scale factor halfway
1671     // between the minimum and average computed scale factors, which
1672     // is a bit of a kludge, but seems to produce mostly decent
1673     // results
1674     if (n) {
1675       fontScaleAvg /= n;
1676       if (fontScaleAvg < 1) {
1677 	fontScale = 0.5 * (fontScaleMin + fontScaleAvg);
1678 	m11 *= fontScale;
1679 	m12 *= fontScale;
1680       }
1681     }
1682   }
1683 
1684   // create the scaled font
1685   mat[0] = m11;  mat[1] = m12;
1686   mat[2] = m21;  mat[3] = m22;
1687   font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
1688 
1689 #if !LOAD_FONTS_FROM_MEM
1690   if (tmpFileName) {
1691     delete tmpFileName;
1692   }
1693 #endif
1694   return;
1695 
1696  err2:
1697   delete id;
1698  err1:
1699 #if LOAD_FONTS_FROM_MEM
1700   if (fontBuf) {
1701     delete fontBuf;
1702   }
1703 #else
1704   if (tmpFileName) {
1705     unlink(tmpFileName->getCString());
1706     delete tmpFileName;
1707   }
1708 #endif
1709   return;
1710 }
1711 
1712 void SplashOutputDev::stroke(GfxState *state) {
1713   SplashPath *path;
1714 
1715   if (state->getStrokeColorSpace()->isNonMarking()) {
1716     return;
1717   }
1718   setOverprintMask(state, state->getStrokeColorSpace(),
1719 		   state->getStrokeOverprint(), state->getOverprintMode(),
1720 		   state->getStrokeColor());
1721   path = convertPath(state, state->getPath(), gFalse);
1722   splash->stroke(path);
1723   delete path;
1724 }
1725 
1726 void SplashOutputDev::fill(GfxState *state) {
1727   SplashPath *path;
1728 
1729   if (state->getFillColorSpace()->isNonMarking()) {
1730     return;
1731   }
1732   setOverprintMask(state, state->getFillColorSpace(),
1733 		   state->getFillOverprint(), state->getOverprintMode(),
1734 		   state->getFillColor());
1735   path = convertPath(state, state->getPath(), gTrue);
1736   splash->fill(path, gFalse);
1737   delete path;
1738 }
1739 
1740 void SplashOutputDev::eoFill(GfxState *state) {
1741   SplashPath *path;
1742 
1743   if (state->getFillColorSpace()->isNonMarking()) {
1744     return;
1745   }
1746   setOverprintMask(state, state->getFillColorSpace(),
1747 		   state->getFillOverprint(), state->getOverprintMode(),
1748 		   state->getFillColor());
1749   path = convertPath(state, state->getPath(), gTrue);
1750   splash->fill(path, gTrue);
1751   delete path;
1752 }
1753 
1754 void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
1755 					Object *strRef,
1756 					int paintType, int tilingType,
1757 					Dict *resDict,
1758 					double *mat, double *bbox,
1759 					int x0, int y0, int x1, int y1,
1760 					double xStep, double yStep) {
1761   SplashBitmap *origBitmap, *tileBitmap;
1762   Splash *origSplash;
1763   SplashColor color;
1764   Guint *overprintMaskBitmap;
1765   double *ctm;
1766   double ictm[6], tileMat[6], mat1[6], mat2[6];
1767   double tileXMin, tileYMin, tileXMax, tileYMax;
1768   double xStepX, xStepY, yStepX, yStepY;
1769   double adjXMin, adjYMin;
1770   double sx, sy;
1771   double clipXMin, clipYMin, clipXMax, clipYMax, clipXC, clipYC;
1772   double tx, ty, idet, txMin, tyMin, txMax, tyMax;
1773   int tileW, tileH, tileSize;
1774   int ixMin, ixMax, iyMin, iyMax, ix, iy, x, y;
1775   int i;
1776 
1777   // Notes:
1778   // - PTM = pattern matrix = transform from pattern space to default
1779   //         user space (default for most recent page or form)
1780   // - BTM = transform from default user space to device space
1781   //
1782   // This function is called with:
1783   // - mat = PTM * BTM * iCTM = transform from pattern space to
1784   //         current user space
1785 
1786   // transform the four corners of the pattern bbox from pattern space
1787   // to device space and compute the device space bbox
1788   state->transform(bbox[0] * mat[0] + bbox[1] * mat[2] + mat[4],
1789 		   bbox[0] * mat[1] + bbox[1] * mat[3] + mat[5],
1790 		   &tx, &ty);
1791   tileXMin = tileXMax = tx;
1792   tileYMin = tileYMax = ty;
1793   state->transform(bbox[2] * mat[0] + bbox[1] * mat[2] + mat[4],
1794 		   bbox[2] * mat[1] + bbox[1] * mat[3] + mat[5],
1795 		   &tx, &ty);
1796   if (tx < tileXMin) {
1797     tileXMin = tx;
1798   } else if (tx > tileXMax) {
1799     tileXMax = tx;
1800   }
1801   if (ty < tileYMin) {
1802     tileYMin = ty;
1803   } else if (ty > tileYMax) {
1804     tileYMax = ty;
1805   }
1806   state->transform(bbox[2] * mat[0] + bbox[3] * mat[2] + mat[4],
1807 		   bbox[2] * mat[1] + bbox[3] * mat[3] + mat[5],
1808 		   &tx, &ty);
1809   if (tx < tileXMin) {
1810     tileXMin = tx;
1811   } else if (tx > tileXMax) {
1812     tileXMax = tx;
1813   }
1814   if (ty < tileYMin) {
1815     tileYMin = ty;
1816   } else if (ty > tileYMax) {
1817     tileYMax = ty;
1818   }
1819   state->transform(bbox[0] * mat[0] + bbox[3] * mat[2] + mat[4],
1820 		   bbox[0] * mat[1] + bbox[3] * mat[3] + mat[5],
1821 		   &tx, &ty);
1822   if (tx < tileXMin) {
1823     tileXMin = tx;
1824   } else if (tx > tileXMax) {
1825     tileXMax = tx;
1826   }
1827   if (ty < tileYMin) {
1828     tileYMin = ty;
1829   } else if (ty > tileYMax) {
1830     tileYMax = ty;
1831   }
1832   if (tileXMin == tileXMax || tileYMin == tileYMax) {
1833     return;
1834   }
1835   tileW = (int)(tileXMax - tileXMin + 0.5);
1836   tileH = (int)(tileYMax - tileYMin + 0.5);
1837   if (tileW < 1) {
1838     tileW = 1;
1839   }
1840   if (tileH < 1) {
1841     tileH = 1;
1842   }
1843 
1844   // check for an excessively large tile size
1845   tileSize = tileW * tileH;
1846   if (tileXMax - tileXMin + 0.5 > (double)INT_MAX ||
1847       tileYMax - tileYMin + 0.5 > (double)INT_MAX ||
1848       tileW > INT_MAX / tileH ||
1849       tileSize > maxTileSize) {
1850     mat1[0] = mat[0];
1851     mat1[1] = mat[1];
1852     mat1[2] = mat[2];
1853     mat1[3] = mat[3];
1854     for (iy = y0; iy < y1; ++iy) {
1855       for (ix = x0; ix < x1; ++ix) {
1856 	tx = ix * xStep;
1857 	ty = iy * yStep;
1858 	mat1[4] = tx * mat[0] + ty * mat[2] + mat[4];
1859 	mat1[5] = tx * mat[1] + ty * mat[3] + mat[5];
1860 	gfx->drawForm(strRef, resDict, mat1, bbox);
1861       }
1862     }
1863     return;
1864   }
1865 
1866   // transform XStep and YStep to device space
1867   state->transformDelta(xStep * mat[0], xStep * mat[1], &xStepX, &xStepY);
1868   state->transformDelta(yStep * mat[2], yStep * mat[3], &yStepX, &yStepY);
1869 
1870   // get the clipping bbox (in device space)
1871   state->getClipBBox(&clipXMin, &clipYMin, &clipXMax, &clipYMax);
1872 
1873   // compute tiling parameters
1874   idet = xStepX * yStepY - yStepX * xStepY;
1875   if (tilingType == 2 || idet == 0) {
1876     adjXMin = tileXMin;
1877     adjYMin = tileYMin;
1878     sx = 1;
1879     sy = 1;
1880   } else {
1881     // reposition the pattern origin to the center of the clipping bbox
1882     idet = 1 / idet;
1883     clipXC = 0.5 * (clipXMin + clipXMax);
1884     clipYC = 0.5 * (clipYMin + clipYMax);
1885     ix = (int)((yStepX * (tileYMin - clipYC) - (tileXMin - clipXC) * yStepY)
1886 	       * idet + 0.5);
1887     iy = (int)((xStepX * (clipYC - tileYMin) - (clipXC - tileXMin) * xStepY)
1888 	       * idet + 0.5);
1889     adjXMin = (int)floor(tileXMin + ix * xStepX + iy * yStepX + 0.5);
1890     adjYMin = (int)floor(tileYMin + ix * xStepY + iy * yStepY + 0.5);
1891     sx = tileW / (tileXMax - tileXMin);
1892     sy = tileH / (tileYMax - tileYMin);
1893     xStepX = (int)floor(sx * xStepX + 0.5);
1894     xStepY = (int)floor(sy * xStepY + 0.5);
1895     yStepX = (int)floor(sx * yStepX + 0.5);
1896     yStepY = (int)floor(sy * yStepY + 0.5);
1897   }
1898 
1899   // compute tile matrix = PTM * BTM * Mtranslate * Mscale * iCTM
1900   //                     = mat * CTM * Mtranslate * Mscale * iCTM
1901   ctm = state->getCTM();
1902   idet = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1903   ictm[0] = ctm[3] * idet;
1904   ictm[1] = -ctm[1] * idet;
1905   ictm[2] = -ctm[2] * idet;
1906   ictm[3] = ctm[0] * idet;
1907   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * idet;
1908   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * idet;
1909   // mat * CTM
1910   mat1[0] = mat[0] * ctm[0] + mat[1] * ctm[2];
1911   mat1[1] = mat[0] * ctm[1] + mat[1] * ctm[3];
1912   mat1[2] = mat[2] * ctm[0] + mat[3] * ctm[2];
1913   mat1[3] = mat[2] * ctm[1] + mat[3] * ctm[3];
1914   mat1[4] = mat[4] * ctm[0] + mat[5] * ctm[2] + ctm[4];
1915   mat1[5] = mat[4] * ctm[1] + mat[5] * ctm[3] + ctm[5];
1916   // mat * CTM * (Mtranslate * Mscale)
1917   mat2[0] = mat1[0] * sx;
1918   mat2[1] = mat1[1] * sy;
1919   mat2[2] = mat1[2] * sx;
1920   mat2[3] = mat1[3] * sy;
1921   mat2[4] = mat1[4] * sx - sx * tileXMin;
1922   mat2[5] = mat1[5] * sy - sy * tileYMin;
1923   // mat * CTM * (Mtranslate * Mscale) * iCTM
1924   tileMat[0] = mat2[0] * ictm[0] + mat2[1] * ictm[2];
1925   tileMat[1] = mat2[0] * ictm[1] + mat2[1] * ictm[3];
1926   tileMat[2] = mat2[2] * ictm[0] + mat2[3] * ictm[2];
1927   tileMat[3] = mat2[2] * ictm[1] + mat2[3] * ictm[3];
1928   tileMat[4] = mat2[4] * ictm[0] + mat2[5] * ictm[2] + ictm[4];
1929   tileMat[5] = mat2[4] * ictm[1] + mat2[5] * ictm[3] + ictm[5];
1930 
1931   // compute tiling range:
1932   // - look at the four corners of the clipping bbox
1933   // - solve for the (ix,iy) tile position at each corner
1934   // - take the min and max values for ix, iy
1935   idet = xStepX * yStepY - xStepY * yStepX;
1936   if (idet == 0) {
1937     return;
1938   }
1939   idet = 1 / idet;
1940   // LL corner
1941   tx = idet * (yStepY * (clipXMin - tileW - 1 - adjXMin)
1942 	       - yStepX * (clipYMax + 1 - adjYMin));
1943   ty = idet * (xStepX * (clipYMax + 1 - adjYMin)
1944 	       - xStepY * (clipXMin - tileW - 1 - adjXMin));
1945   txMin = txMax = tx;
1946   tyMin = tyMax = ty;
1947   // LR corner
1948   tx = idet * (yStepY * (clipXMax + 1 - adjXMin)
1949 	       - yStepX * (clipYMax + 1 - adjYMin));
1950   ty = idet * (xStepX * (clipYMax + 1 - adjYMin)
1951 	       - xStepY * (clipXMax + 1 - adjXMin));
1952   if (tx < txMin) {
1953     txMin = tx;
1954   } else if (tx > txMax) {
1955     txMax = tx;
1956   }
1957   if (ty < tyMin) {
1958     tyMin = ty;
1959   } else if (ty > tyMax) {
1960     tyMax = ty;
1961   }
1962   // UL corner
1963   tx = idet * (yStepY * (clipXMin - tileW - 1 - adjXMin)
1964 	       - yStepX * (clipYMin - tileH - 1 - adjYMin));
1965   ty = idet * (xStepX * (clipYMin - tileH - 1 - adjYMin)
1966 	       - xStepY * (clipXMin - tileW - 1 - adjXMin));
1967   if (tx < txMin) {
1968     txMin = tx;
1969   } else if (tx > txMax) {
1970     txMax = tx;
1971   }
1972   if (ty < tyMin) {
1973     tyMin = ty;
1974   } else if (ty > tyMax) {
1975     tyMax = ty;
1976   }
1977   // UR corner
1978   tx = idet * (yStepY * (clipXMax + 1 - adjXMin)
1979 	       - yStepX * (clipYMin - tileH - 1 - adjYMin));
1980   ty = idet * (xStepX * (clipYMin - tileH - 1 - adjYMin)
1981 	       - xStepY * (clipXMax + 1 - adjXMin));
1982   if (tx < txMin) {
1983     txMin = tx;
1984   } else if (tx > txMax) {
1985     txMax = tx;
1986   }
1987   if (ty < tyMin) {
1988     tyMin = ty;
1989   } else if (ty > tyMax) {
1990     tyMax = ty;
1991   }
1992   ixMin = (int)floor(txMin);
1993   ixMax = (int)ceil(txMax);
1994   iyMin = (int)floor(tyMin);
1995   iyMax = (int)ceil(tyMax);
1996 
1997   // create a temporary bitmap
1998   origBitmap = bitmap;
1999   origSplash = splash;
2000   traceMessage("tiling pattern bitmap");
2001   bitmap = tileBitmap = new SplashBitmap(tileW, tileH, bitmapRowPad,
2002 					 colorMode, gTrue, bitmapTopDown,
2003 					 origBitmap);
2004   splash = new Splash(bitmap, vectorAntialias,
2005 		      origSplash->getImageCache(), origSplash->getScreen());
2006   for (i = 0; i < splashMaxColorComps; ++i) {
2007     color[i] = 0;
2008   }
2009   splash->clear(color);
2010 #if SPLASH_CMYK
2011   // if we're doing overprint preview, we need to track the overprint
2012   // mask at each pixel in the tile bitmap
2013   if (globalParams->getOverprintPreview() &&
2014       colorMode == splashModeCMYK8) {
2015     overprintMaskBitmap = (Guint *)gmallocn(tileH, tileW * (int)sizeof(Guint));
2016     memset(overprintMaskBitmap, 0, tileH * tileW * sizeof(Guint));
2017     splash->setOverprintMaskBitmap(overprintMaskBitmap);
2018   } else {
2019     overprintMaskBitmap = NULL;
2020   }
2021 #else // SPLASH_CMYK
2022   overprintMaskBitmap = NULL;
2023 #endif // SPLASH_CMYK
2024   splash->setMinLineWidth(globalParams->getMinLineWidth());
2025   splash->setStrokeAdjust(
2026 		 mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
2027   splash->setEnablePathSimplification(
2028 		 globalParams->getEnablePathSimplification());
2029   ++nestCount;
2030 
2031   // copy the fill color (for uncolored tiling patterns)
2032   // (and stroke color, to handle buggy PDF files)
2033   // -- Acrobat apparently doesn't copy the full state here
2034   splash->setFillPattern(origSplash->getFillPattern()->copy());
2035   splash->setStrokePattern(origSplash->getStrokePattern()->copy());
2036 
2037   // reset the clip rectangle
2038   state->resetDevClipRect(0, 0, tileW, tileH);
2039 
2040   // render the tile
2041   gfx->drawForm(strRef, resDict, tileMat, bbox);
2042 
2043   // restore the original bitmap
2044   --nestCount;
2045   delete splash;
2046   bitmap = origBitmap;
2047   splash = origSplash;
2048   splash->setOverprintMask(0xffffffff);
2049 
2050   // draw the tiles
2051   if (tileW == 1 && tileH == 1 &&
2052       fabs(xStepX * yStepY - xStepY * yStepX) < 0.9) {
2053     // if the tile is 1x1 pixel, and the stepping completely fills the
2054     // area, just composite the 1x1 image across the clip region
2055     // (this avoids performance problems in cases where the step size
2056     // is very small) (we compare to 0.9 instead of 1.0 to avoid fp
2057     // jitter issues)
2058     ixMin = (int)floor(clipXMin);
2059     ixMax = (int)floor(clipXMax) + 1;
2060     iyMin = (int)floor(clipYMin);
2061     iyMax = (int)floor(clipYMax) + 1;
2062     for (iy = iyMin; iy < iyMax; ++iy) {
2063       for (ix = ixMin; ix < ixMax; ++ix) {
2064 	splash->composite(tileBitmap, 0, 0, ix, iy, tileW, tileH,
2065 			  gFalse, gFalse);
2066       }
2067     }
2068   } else {
2069     for (iy = iyMin; iy < iyMax; ++iy) {
2070       for (ix = ixMin; ix < ixMax; ++ix) {
2071 	x = (int)(adjXMin + ix * xStepX + iy * yStepX + 0.5);
2072 	y = (int)(adjYMin + ix * xStepY + iy * yStepY + 0.5);
2073 	if (overprintMaskBitmap) {
2074 	  splash->compositeWithOverprint(tileBitmap, overprintMaskBitmap,
2075 					 0, 0, x, y, tileW, tileH,
2076 					 gFalse, gFalse);
2077 	} else {
2078 	  splash->composite(tileBitmap, 0, 0, x, y, tileW, tileH,
2079 			    gFalse, gFalse);
2080 	}
2081       }
2082     }
2083   }
2084 
2085   gfree(overprintMaskBitmap);
2086   delete tileBitmap;
2087 }
2088 
2089 GBool SplashOutputDev::shadedFill(GfxState *state, GfxShading *shading) {
2090 
2091   // generate the bitmap
2092   SplashColorMode srcMode;
2093   if (colorMode == splashModeMono1) {
2094     srcMode = splashModeMono8;
2095   } else if (colorMode == splashModeBGR8) {
2096     srcMode = splashModeRGB8;
2097   } else {
2098     srcMode = colorMode;
2099   }
2100   int x, y;
2101   SplashBitmap *tBitmap = ShadingImage::generateBitmap(state, shading, srcMode,
2102 						       reverseVideo,
2103 						       splash, bitmap, &x, &y);
2104   if (!tBitmap) {
2105     // clip region is empty - nothing to draw
2106     return gTrue;
2107   }
2108 
2109   // check clipping and composite the bitmap
2110   int xMin = x;
2111   int yMin = y;
2112   int xMax = x + tBitmap->getWidth();
2113   int yMax = y + tBitmap->getHeight();
2114   SplashClipResult clipRes = splash->limitRectToClipRect(&xMin, &yMin,
2115 							 &xMax, &yMax);
2116   if (clipRes != splashClipAllOutside) {
2117     setOverprintMask(state, state->getFillColorSpace(),
2118 		     state->getFillOverprint(), state->getOverprintMode(),
2119 		     NULL);
2120     splash->composite(tBitmap, xMin - x, yMin - y, xMin, yMin,
2121 		      xMax - xMin, yMax - yMin,
2122 		      clipRes == splashClipAllInside, gFalse);
2123   }
2124 
2125   delete tBitmap;
2126 
2127   return gTrue;
2128 }
2129 
2130 void SplashOutputDev::clip(GfxState *state) {
2131   SplashPath *path;
2132 
2133   path = convertPath(state, state->getPath(), gTrue);
2134   splash->clipToPath(path, gFalse);
2135   delete path;
2136 }
2137 
2138 void SplashOutputDev::eoClip(GfxState *state) {
2139   SplashPath *path;
2140 
2141   path = convertPath(state, state->getPath(), gTrue);
2142   splash->clipToPath(path, gTrue);
2143   delete path;
2144 }
2145 
2146 void SplashOutputDev::clipToStrokePath(GfxState *state) {
2147   SplashPath *path, *path2;
2148 
2149   path = convertPath(state, state->getPath(), gFalse);
2150   path2 = splash->makeStrokePath(path, state->getLineWidth(),
2151 				 state->getLineCap(), state->getLineJoin());
2152   delete path;
2153   splash->clipToPath(path2, gFalse);
2154   delete path2;
2155 }
2156 
2157 SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path,
2158 					 GBool dropEmptySubpaths) {
2159   SplashPath *sPath;
2160   GfxSubpath *subpath;
2161   int n, i, j;
2162 
2163   n = dropEmptySubpaths ? 1 : 0;
2164   sPath = new SplashPath();
2165   for (i = 0; i < path->getNumSubpaths(); ++i) {
2166     subpath = path->getSubpath(i);
2167     if (subpath->getNumPoints() > n) {
2168       sPath->moveTo((SplashCoord)subpath->getX(0),
2169 		    (SplashCoord)subpath->getY(0));
2170       j = 1;
2171       while (j < subpath->getNumPoints()) {
2172 	if (subpath->getCurve(j)) {
2173 	  sPath->curveTo((SplashCoord)subpath->getX(j),
2174 			 (SplashCoord)subpath->getY(j),
2175 			 (SplashCoord)subpath->getX(j+1),
2176 			 (SplashCoord)subpath->getY(j+1),
2177 			 (SplashCoord)subpath->getX(j+2),
2178 			 (SplashCoord)subpath->getY(j+2));
2179 	  j += 3;
2180 	} else {
2181 	  sPath->lineTo((SplashCoord)subpath->getX(j),
2182 			(SplashCoord)subpath->getY(j));
2183 	  ++j;
2184 	}
2185       }
2186       if (subpath->isClosed()) {
2187 	sPath->close();
2188       }
2189     }
2190   }
2191   return sPath;
2192 }
2193 
2194 void SplashOutputDev::drawChar(GfxState *state, double x, double y,
2195 			       double dx, double dy,
2196 			       double originX, double originY,
2197 			       CharCode code, int nBytes,
2198 			       Unicode *u, int uLen) {
2199   SplashPath *path;
2200   int render;
2201   GBool doFill, doStroke, doClip;
2202   SplashStrokeAdjustMode strokeAdjust;
2203   double m[4];
2204   GBool horiz;
2205 
2206   if (skipHorizText || skipRotatedText) {
2207     state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
2208     // this matches the 'diagonal' test in TextPage::updateFont()
2209     horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
2210             fabs(m[2]) < 0.001 && m[3] < 0;
2211     if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
2212       return;
2213     }
2214   }
2215 
2216   // check for invisible text -- this is used by Acrobat Capture
2217   render = state->getRender();
2218   if (render == 3) {
2219     return;
2220   }
2221 
2222   if (needFontUpdate) {
2223     doUpdateFont(state);
2224   }
2225   if (!font) {
2226     return;
2227   }
2228 
2229   x -= originX;
2230   y -= originY;
2231 
2232   doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking();
2233   doStroke = ((render & 3) == 1 || (render & 3) == 2) &&
2234              !state->getStrokeColorSpace()->isNonMarking();
2235   doClip = render & 4;
2236 
2237   path = NULL;
2238   if (doStroke || doClip) {
2239     if ((path = font->getGlyphPath(code))) {
2240       path->offset((SplashCoord)x, (SplashCoord)y);
2241     }
2242   }
2243 
2244   // don't use stroke adjustment when stroking text -- the results
2245   // tend to be ugly (because characters with horizontal upper or
2246   // lower edges get misaligned relative to the other characters)
2247   strokeAdjust = splashStrokeAdjustOff; // make gcc happy
2248   if (doStroke) {
2249     strokeAdjust = splash->getStrokeAdjust();
2250     splash->setStrokeAdjust(splashStrokeAdjustOff);
2251   }
2252 
2253   // fill and stroke
2254   if (doFill && doStroke) {
2255     if (path) {
2256       setOverprintMask(state, state->getFillColorSpace(),
2257 		       state->getFillOverprint(), state->getOverprintMode(),
2258 		       state->getFillColor());
2259       splash->fill(path, gFalse);
2260       setOverprintMask(state, state->getStrokeColorSpace(),
2261 		       state->getStrokeOverprint(), state->getOverprintMode(),
2262 		       state->getStrokeColor());
2263       splash->stroke(path);
2264     }
2265 
2266   // fill
2267   } else if (doFill) {
2268     setOverprintMask(state, state->getFillColorSpace(),
2269 		     state->getFillOverprint(), state->getOverprintMode(),
2270 		     state->getFillColor());
2271     splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font);
2272 
2273   // stroke
2274   } else if (doStroke) {
2275     if (path) {
2276       setOverprintMask(state, state->getStrokeColorSpace(),
2277 		       state->getStrokeOverprint(), state->getOverprintMode(),
2278 		       state->getStrokeColor());
2279       splash->stroke(path);
2280     }
2281   }
2282 
2283   // clip
2284   if (doClip) {
2285     if (path) {
2286       if (textClipPath) {
2287 	textClipPath->append(path);
2288       } else {
2289 	textClipPath = path;
2290 	path = NULL;
2291       }
2292     }
2293   }
2294 
2295   if (doStroke) {
2296     splash->setStrokeAdjust(strokeAdjust);
2297   }
2298 
2299   if (path) {
2300     delete path;
2301   }
2302 }
2303 
2304 GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
2305 				      double dx, double dy,
2306 				      CharCode code, Unicode *u, int uLen) {
2307   GfxFont *gfxFont;
2308   Ref *fontID;
2309   double *ctm, *bbox;
2310   T3FontCache *t3Font;
2311   T3GlyphStack *t3gs;
2312   GBool validBBox;
2313   double m[4];
2314   GBool horiz;
2315   double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
2316   int render, i, j;
2317 
2318   if (skipHorizText || skipRotatedText) {
2319     state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
2320     horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
2321             fabs(m[2]) < 0.001 && m[3] < 0;
2322     if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
2323       return gTrue;
2324     }
2325   }
2326 
2327   // check for invisible text
2328   render = state->getRender();
2329   if (render == 3 || render == 7) {
2330     return gTrue;
2331   }
2332 
2333   if (!(gfxFont = state->getFont())) {
2334     return gTrue;
2335   }
2336   fontID = gfxFont->getID();
2337   ctm = state->getCTM();
2338   state->transform(0, 0, &xt, &yt);
2339 
2340   // is it the first (MRU) font in the cache?
2341   if (!(nT3Fonts > 0 &&
2342 	t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
2343 
2344     // is the font elsewhere in the cache?
2345     for (i = 1; i < nT3Fonts; ++i) {
2346       if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
2347 	t3Font = t3FontCache[i];
2348 	for (j = i; j > 0; --j) {
2349 	  t3FontCache[j] = t3FontCache[j - 1];
2350 	}
2351 	t3FontCache[0] = t3Font;
2352 	break;
2353       }
2354     }
2355     if (i >= nT3Fonts) {
2356 
2357       // create new entry in the font cache
2358       if (nT3Fonts < splashOutT3FontCacheSize) {
2359 	for (j = nT3Fonts; j > 0; --j) {
2360 	  t3FontCache[j] = t3FontCache[j - 1];
2361 	}
2362       } else {
2363 	for (j = nT3Fonts - 1; j >= 0; --j) {
2364 	  if (t3FontCache[j]->refCount == 0) {
2365 	    break;
2366 	  }
2367 	}
2368 	if (j < 0) {
2369 	  error(errSyntaxError, -1, "Type 3 fonts nested too deeply");
2370 	  return gTrue;
2371 	}
2372 	delete t3FontCache[j];
2373 	--nT3Fonts;
2374 	for (; j > 0; --j) {
2375 	  t3FontCache[j] = t3FontCache[j - 1];
2376 	}
2377       }
2378       ++nT3Fonts;
2379       bbox = gfxFont->getFontBBox();
2380       if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
2381 	// unspecified bounding box -- just take a guess
2382 	xMin = xt - 5;
2383 	xMax = xMin + 30;
2384 	yMax = yt + 15;
2385 	yMin = yMax - 45;
2386 	validBBox = gFalse;
2387       } else {
2388 	state->transform(bbox[0], bbox[1], &x1, &y1);
2389 	xMin = xMax = x1;
2390 	yMin = yMax = y1;
2391 	state->transform(bbox[0], bbox[3], &x1, &y1);
2392 	if (x1 < xMin) {
2393 	  xMin = x1;
2394 	} else if (x1 > xMax) {
2395 	  xMax = x1;
2396 	}
2397 	if (y1 < yMin) {
2398 	  yMin = y1;
2399 	} else if (y1 > yMax) {
2400 	  yMax = y1;
2401 	}
2402 	state->transform(bbox[2], bbox[1], &x1, &y1);
2403 	if (x1 < xMin) {
2404 	  xMin = x1;
2405 	} else if (x1 > xMax) {
2406 	  xMax = x1;
2407 	}
2408 	if (y1 < yMin) {
2409 	  yMin = y1;
2410 	} else if (y1 > yMax) {
2411 	  yMax = y1;
2412 	}
2413 	state->transform(bbox[2], bbox[3], &x1, &y1);
2414 	if (x1 < xMin) {
2415 	  xMin = x1;
2416 	} else if (x1 > xMax) {
2417 	  xMax = x1;
2418 	}
2419 	if (y1 < yMin) {
2420 	  yMin = y1;
2421 	} else if (y1 > yMax) {
2422 	  yMax = y1;
2423 	}
2424 	validBBox = gTrue;
2425       }
2426       t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
2427 	                               (int)floor(xMin - xt) - 2,
2428 				       (int)floor(yMin - yt) - 2,
2429 				       (int)ceil(xMax) - (int)floor(xMin) + 4,
2430 				       (int)ceil(yMax) - (int)floor(yMin) + 4,
2431 				       validBBox,
2432 				       colorMode != splashModeMono1);
2433     }
2434   }
2435   t3Font = t3FontCache[0];
2436 
2437   // is the glyph in the cache?
2438   i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2439   for (j = 0; j < t3Font->cacheAssoc; ++j) {
2440     if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
2441 	t3Font->cacheTags[i+j].code == code) {
2442       drawType3Glyph(state, t3Font, &t3Font->cacheTags[i+j],
2443 		     t3Font->cacheData + (i+j) * t3Font->glyphSize);
2444       return gTrue;
2445     }
2446   }
2447 
2448   if (t3Font->refCount > 1000) {
2449     error(errSyntaxError, -1, "Type 3 CharProcs nested too deeply");
2450     return gTrue;
2451   }
2452   ++t3Font->refCount;
2453 
2454   // push a new Type 3 glyph record
2455   t3gs = new T3GlyphStack();
2456   t3gs->next = t3GlyphStack;
2457   t3GlyphStack = t3gs;
2458   t3GlyphStack->code = (Gushort)code;
2459   t3GlyphStack->cache = t3Font;
2460   t3GlyphStack->cacheTag = NULL;
2461   t3GlyphStack->cacheData = NULL;
2462   t3GlyphStack->haveDx = gFalse;
2463   t3GlyphStack->doNotCache = gFalse;
2464 #if 1 //~t3-sa
2465   t3GlyphStack->savedStrokeAdjust = splash->getStrokeAdjust();
2466   splash->setStrokeAdjust(splashStrokeAdjustOff);
2467 #endif
2468 
2469   return gFalse;
2470 }
2471 
2472 void SplashOutputDev::endType3Char(GfxState *state) {
2473   T3GlyphStack *t3gs;
2474   double *ctm;
2475 
2476   if (t3GlyphStack->cacheTag) {
2477     --nestCount;
2478     memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
2479 	   t3GlyphStack->cache->glyphSize);
2480     delete bitmap;
2481     delete splash;
2482     bitmap = t3GlyphStack->origBitmap;
2483     colorMode = bitmap->getMode();
2484     splash = t3GlyphStack->origSplash;
2485     ctm = state->getCTM();
2486     state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2487 		  t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
2488     updateCTM(state, 0, 0, 0, 0, 0, 0);
2489     drawType3Glyph(state, t3GlyphStack->cache,
2490 		   t3GlyphStack->cacheTag, t3GlyphStack->cacheData);
2491   }
2492 #if 1 //~t3-sa
2493   splash->setStrokeAdjust(t3GlyphStack->savedStrokeAdjust);
2494 #endif
2495   t3gs = t3GlyphStack;
2496   t3GlyphStack = t3gs->next;
2497   --t3gs->cache->refCount;
2498   delete t3gs;
2499 }
2500 
2501 void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
2502   if (!t3GlyphStack) {
2503     error(errSyntaxError, -1,
2504 	  "Encountered d0 operator outside of Type 3 CharProc");
2505     return;
2506   }
2507   t3GlyphStack->haveDx = gTrue;
2508 }
2509 
2510 void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
2511 			      double llx, double lly, double urx, double ury) {
2512   double *ctm;
2513   T3FontCache *t3Font;
2514   SplashColor color;
2515   double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
2516   int i, j;
2517 
2518   if (!t3GlyphStack) {
2519     error(errSyntaxError, -1,
2520 	  "Encountered d1 operator outside of Type 3 CharProc");
2521     return;
2522   }
2523 
2524   // ignore multiple d0/d1 operators
2525   if (t3GlyphStack->haveDx) {
2526     return;
2527   }
2528   t3GlyphStack->haveDx = gTrue;
2529   // don't cache if we got a gsave/grestore before the d1
2530   if (t3GlyphStack->doNotCache) {
2531     return;
2532   }
2533 
2534   t3Font = t3GlyphStack->cache;
2535 
2536   // check for a valid bbox
2537   state->transform(0, 0, &xt, &yt);
2538   state->transform(llx, lly, &x1, &y1);
2539   xMin = xMax = x1;
2540   yMin = yMax = y1;
2541   state->transform(llx, ury, &x1, &y1);
2542   if (x1 < xMin) {
2543     xMin = x1;
2544   } else if (x1 > xMax) {
2545     xMax = x1;
2546   }
2547   if (y1 < yMin) {
2548     yMin = y1;
2549   } else if (y1 > yMax) {
2550     yMax = y1;
2551   }
2552   state->transform(urx, lly, &x1, &y1);
2553   if (x1 < xMin) {
2554     xMin = x1;
2555   } else if (x1 > xMax) {
2556     xMax = x1;
2557   }
2558   if (y1 < yMin) {
2559     yMin = y1;
2560   } else if (y1 > yMax) {
2561     yMax = y1;
2562   }
2563   state->transform(urx, ury, &x1, &y1);
2564   if (x1 < xMin) {
2565     xMin = x1;
2566   } else if (x1 > xMax) {
2567     xMax = x1;
2568   }
2569   if (y1 < yMin) {
2570     yMin = y1;
2571   } else if (y1 > yMax) {
2572     yMax = y1;
2573   }
2574   if (xMin - xt < t3Font->glyphX ||
2575       yMin - yt < t3Font->glyphY ||
2576       xMax - xt > t3Font->glyphX + t3Font->glyphW ||
2577       yMax - yt > t3Font->glyphY + t3Font->glyphH) {
2578     if (t3Font->validBBox) {
2579       error(errSyntaxWarning, -1, "Bad bounding box in Type 3 glyph");
2580     }
2581     return;
2582   }
2583 
2584   // allocate a cache entry
2585   i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2586   for (j = 0; j < t3Font->cacheAssoc; ++j) {
2587     if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
2588       t3Font->cacheTags[i+j].mru = 0x8000;
2589       t3Font->cacheTags[i+j].code = t3GlyphStack->code;
2590       t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
2591       t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
2592     } else {
2593       ++t3Font->cacheTags[i+j].mru;
2594     }
2595   }
2596 
2597   // save state
2598   t3GlyphStack->origBitmap = bitmap;
2599   t3GlyphStack->origSplash = splash;
2600   ctm = state->getCTM();
2601   t3GlyphStack->origCTM4 = ctm[4];
2602   t3GlyphStack->origCTM5 = ctm[5];
2603 
2604   // create the temporary bitmap
2605   if (colorMode == splashModeMono1) {
2606     colorMode = splashModeMono1;
2607     traceMessage("T3 glyph bitmap");
2608     bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
2609 			      splashModeMono1, gFalse, gTrue, bitmap);
2610     splash = new Splash(bitmap, gFalse,
2611 			t3GlyphStack->origSplash->getImageCache(),
2612 			t3GlyphStack->origSplash->getScreen());
2613     color[0] = 0;
2614     splash->clear(color);
2615     color[0] = 0xff;
2616   } else {
2617     colorMode = splashModeMono8;
2618     traceMessage("T3 glyph bitmap");
2619     bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
2620 			      splashModeMono8, gFalse, gTrue, bitmap);
2621     splash = new Splash(bitmap, vectorAntialias,
2622 			t3GlyphStack->origSplash->getImageCache(),
2623 			t3GlyphStack->origSplash->getScreen());
2624     color[0] = 0x00;
2625     splash->clear(color);
2626     color[0] = 0xff;
2627   }
2628   splash->setMinLineWidth(globalParams->getMinLineWidth());
2629   splash->setStrokeAdjust(t3GlyphStack->origSplash->getStrokeAdjust());
2630   splash->setEnablePathSimplification(
2631 		 globalParams->getEnablePathSimplification());
2632   copyState(t3GlyphStack->origSplash, gFalse);
2633   splash->setFillPattern(new SplashSolidColor(color));
2634   splash->setStrokePattern(new SplashSolidColor(color));
2635   state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2636 		-t3Font->glyphX, -t3Font->glyphY);
2637   updateCTM(state, 0, 0, 0, 0, 0, 0);
2638   ++nestCount;
2639 }
2640 
2641 void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font,
2642 				     T3FontCacheTag *tag, Guchar *data) {
2643   SplashGlyphBitmap glyph;
2644 
2645   setOverprintMask(state, state->getFillColorSpace(),
2646 		   state->getFillOverprint(), state->getOverprintMode(),
2647 		   state->getFillColor());
2648   glyph.x = -t3Font->glyphX;
2649   glyph.y = -t3Font->glyphY;
2650   glyph.w = t3Font->glyphW;
2651   glyph.h = t3Font->glyphH;
2652   glyph.aa = colorMode != splashModeMono1;
2653   glyph.data = data;
2654   glyph.freeData = gFalse;
2655   splash->fillGlyph(0, 0, &glyph);
2656 }
2657 
2658 void SplashOutputDev::endTextObject(GfxState *state) {
2659   if (textClipPath) {
2660     splash->clipToPath(textClipPath, gFalse);
2661     delete textClipPath;
2662     textClipPath = NULL;
2663   }
2664 }
2665 
2666 struct SplashOutImageMaskData {
2667   ImageStream *imgStr;
2668   Guchar invert;
2669   int width, height, y;
2670 };
2671 
2672 GBool SplashOutputDev::imageMaskSrc(void *data, Guchar *line) {
2673   SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
2674   Guchar *p;
2675   SplashColorPtr q;
2676   int x;
2677 
2678   if (imgMaskData->y == imgMaskData->height ||
2679       !(p = imgMaskData->imgStr->getLine())) {
2680     memset(line, 0, imgMaskData->width);
2681     return gFalse;
2682   }
2683   for (x = 0, q = line; x < imgMaskData->width; ++x) {
2684     *q++ = *p++ ^ imgMaskData->invert;
2685   }
2686   ++imgMaskData->y;
2687   return gTrue;
2688 }
2689 
2690 void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2691 				    int width, int height, GBool invert,
2692 				    GBool inlineImg, GBool interpolate) {
2693   double *ctm;
2694   SplashCoord mat[6];
2695   SplashOutImageMaskData imgMaskData;
2696   GString *imgTag;
2697 
2698   if (state->getFillColorSpace()->isNonMarking()) {
2699     return;
2700   }
2701   setOverprintMask(state, state->getFillColorSpace(),
2702 		   state->getFillOverprint(), state->getOverprintMode(),
2703 		   state->getFillColor());
2704 
2705   ctm = state->getCTM();
2706   mat[0] = ctm[0];
2707   mat[1] = ctm[1];
2708   mat[2] = -ctm[2];
2709   mat[3] = -ctm[3];
2710   mat[4] = ctm[2] + ctm[4];
2711   mat[5] = ctm[3] + ctm[5];
2712 
2713   reduceImageResolution(str, ctm, &width, &height);
2714 
2715   imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
2716   imgMaskData.imgStr->reset();
2717   imgMaskData.invert = invert ? 0 : 1;
2718   imgMaskData.width = width;
2719   imgMaskData.height = height;
2720   imgMaskData.y = 0;
2721 
2722   imgTag = makeImageTag(ref, gfxRenderingIntentRelativeColorimetric, NULL);
2723   splash->fillImageMask(imgTag,
2724 			&imageMaskSrc, &imgMaskData, width, height, mat,
2725 			t3GlyphStack != NULL, interpolate);
2726 
2727   if (inlineImg) {
2728     while (imgMaskData.y < height) {
2729       imgMaskData.imgStr->getLine();
2730       ++imgMaskData.y;
2731     }
2732   }
2733 
2734   delete imgTag;
2735   delete imgMaskData.imgStr;
2736   str->close();
2737 }
2738 
2739 void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
2740 					       Object *ref, Stream *str,
2741 					       int width, int height,
2742 					       GBool invert,
2743 					       GBool inlineImg,
2744 					       GBool interpolate) {
2745   double *ctm;
2746   SplashCoord mat[6];
2747   SplashOutImageMaskData imgMaskData;
2748   SplashBitmap *maskBitmap;
2749   Splash *maskSplash;
2750   SplashColor maskColor;
2751   GString *imgTag;
2752 
2753   ctm = state->getCTM();
2754   mat[0] = ctm[0];
2755   mat[1] = ctm[1];
2756   mat[2] = -ctm[2];
2757   mat[3] = -ctm[3];
2758   mat[4] = ctm[2] + ctm[4];
2759   mat[5] = ctm[3] + ctm[5];
2760   reduceImageResolution(str, ctm, &width, &height);
2761   imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
2762   imgMaskData.imgStr->reset();
2763   imgMaskData.invert = invert ? 0 : 1;
2764   imgMaskData.width = width;
2765   imgMaskData.height = height;
2766   imgMaskData.y = 0;
2767   traceMessage("image mask soft mask bitmap");
2768   maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
2769 				1, splashModeMono8, gFalse, gTrue, bitmap);
2770   maskSplash = new Splash(maskBitmap, gTrue, splash->getImageCache());
2771   maskSplash->setStrokeAdjust(
2772 		     mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
2773   maskSplash->setEnablePathSimplification(
2774 		     globalParams->getEnablePathSimplification());
2775   clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
2776   maskColor[0] = 0xff;
2777   maskSplash->setFillPattern(new SplashSolidColor(maskColor));
2778   imgTag = makeImageTag(ref, gfxRenderingIntentRelativeColorimetric, NULL);
2779   maskSplash->fillImageMask(imgTag, &imageMaskSrc, &imgMaskData,
2780 			    width, height, mat, gFalse, interpolate);
2781   delete imgTag;
2782   delete imgMaskData.imgStr;
2783   str->close();
2784   delete maskSplash;
2785   splash->setSoftMask(maskBitmap);
2786 }
2787 
2788 struct SplashOutImageData {
2789   ImageStream *imgStr;
2790   GfxImageColorMap *colorMap;
2791   GfxRenderingIntent ri;
2792   SplashColorPtr lookup;
2793   int *maskColors;
2794   SplashColorMode colorMode;
2795   GBool invert;
2796   int width, height, y;
2797 };
2798 
2799 GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
2800 				Guchar *alphaLine) {
2801   SplashOutImageData *imgData = (SplashOutImageData *)data;
2802   Guchar *p;
2803   SplashColorPtr q, col;
2804   int n, x;
2805 
2806   if (imgData->y == imgData->height ||
2807       !(p = imgData->imgStr->getLine())) {
2808     memset(colorLine, 0,
2809 	   imgData->width * splashColorModeNComps[imgData->colorMode]);
2810     return gFalse;
2811   }
2812 
2813   if (imgData->lookup) {
2814     switch (imgData->colorMode) {
2815     case splashModeMono1:
2816     case splashModeMono8:
2817       for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2818 	*q++ = imgData->lookup[*p];
2819       }
2820       break;
2821     case splashModeRGB8:
2822     case splashModeBGR8:
2823       for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2824 	col = &imgData->lookup[3 * *p];
2825 	*q++ = col[0];
2826 	*q++ = col[1];
2827 	*q++ = col[2];
2828       }
2829       break;
2830 #if SPLASH_CMYK
2831     case splashModeCMYK8:
2832       for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2833 	col = &imgData->lookup[4 * *p];
2834 	*q++ = col[0];
2835 	*q++ = col[1];
2836 	*q++ = col[2];
2837 	*q++ = col[3];
2838       }
2839       break;
2840 #endif
2841     }
2842   } else {
2843     switch (imgData->colorMode) {
2844     case splashModeMono1:
2845     case splashModeMono8:
2846       imgData->colorMap->getGrayByteLine(p, colorLine, imgData->width,
2847 					 imgData->ri);
2848       break;
2849     case splashModeRGB8:
2850     case splashModeBGR8:
2851       imgData->colorMap->getRGBByteLine(p, colorLine, imgData->width,
2852 					imgData->ri);
2853       break;
2854 #if SPLASH_CMYK
2855     case splashModeCMYK8:
2856       imgData->colorMap->getCMYKByteLine(p, colorLine, imgData->width,
2857 					 imgData->ri);
2858       break;
2859 #endif
2860     }
2861   }
2862 
2863   if (imgData->invert) {
2864     n = imgData->width * splashColorModeNComps[imgData->colorMode];
2865     for (x = 0, p = colorLine; x < n; ++x, ++p) {
2866       *p ^= 0xff;
2867     }
2868   }
2869 
2870   ++imgData->y;
2871   return gTrue;
2872 }
2873 
2874 GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
2875 				     Guchar *alphaLine) {
2876   SplashOutImageData *imgData = (SplashOutImageData *)data;
2877   Guchar *p0, *p, *aq;
2878   SplashColorPtr q, col;
2879   Guchar alpha;
2880   int nComps, x, n, i;
2881 
2882   if (imgData->y == imgData->height ||
2883       !(p0 = imgData->imgStr->getLine())) {
2884     memset(colorLine, 0,
2885 	   imgData->width * splashColorModeNComps[imgData->colorMode]);
2886     memset(alphaLine, 0, imgData->width);
2887     return gFalse;
2888   }
2889 
2890   nComps = imgData->colorMap->getNumPixelComps();
2891 
2892   if (imgData->lookup) {
2893     switch (imgData->colorMode) {
2894     case splashModeMono1:
2895     case splashModeMono8:
2896       for (x = 0, p = p0, q = colorLine; x < imgData->width; ++x, ++p) {
2897 	*q++ = imgData->lookup[*p];
2898       }
2899       break;
2900     case splashModeRGB8:
2901     case splashModeBGR8:
2902       for (x = 0, p = p0, q = colorLine; x < imgData->width; ++x, ++p) {
2903 	col = &imgData->lookup[3 * *p];
2904 	*q++ = col[0];
2905 	*q++ = col[1];
2906 	*q++ = col[2];
2907       }
2908       break;
2909 #if SPLASH_CMYK
2910     case splashModeCMYK8:
2911       for (x = 0, p = p0, q = colorLine; x < imgData->width; ++x, ++p) {
2912 	col = &imgData->lookup[4 * *p];
2913 	*q++ = col[0];
2914 	*q++ = col[1];
2915 	*q++ = col[2];
2916 	*q++ = col[3];
2917       }
2918       break;
2919 #endif
2920     }
2921   } else {
2922     switch (imgData->colorMode) {
2923     case splashModeMono1:
2924     case splashModeMono8:
2925       imgData->colorMap->getGrayByteLine(p0, colorLine, imgData->width,
2926 					 imgData->ri);
2927       break;
2928     case splashModeRGB8:
2929     case splashModeBGR8:
2930       imgData->colorMap->getRGBByteLine(p0, colorLine, imgData->width,
2931 					imgData->ri);
2932       break;
2933 #if SPLASH_CMYK
2934     case splashModeCMYK8:
2935       imgData->colorMap->getCMYKByteLine(p0, colorLine, imgData->width,
2936 					 imgData->ri);
2937       break;
2938 #endif
2939     }
2940   }
2941 
2942   for (x = 0, p = p0, aq = alphaLine; x < imgData->width; ++x, p += nComps) {
2943     alpha = 0;
2944     for (i = 0; i < nComps; ++i) {
2945       if (p[i] < imgData->maskColors[2*i] ||
2946 	  p[i] > imgData->maskColors[2*i+1]) {
2947 	alpha = 0xff;
2948 	break;
2949       }
2950     }
2951     *aq++ = alpha;
2952   }
2953 
2954   if (imgData->invert) {
2955     n = imgData->width * splashColorModeNComps[imgData->colorMode];
2956     for (x = 0, p = colorLine; x < n; ++x, ++p) {
2957       *p ^= 0xff;
2958     }
2959   }
2960 
2961   ++imgData->y;
2962   return gTrue;
2963 }
2964 
2965 
2966 void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2967 				int width, int height,
2968 				GfxImageColorMap *colorMap,
2969 				int *maskColors, GBool inlineImg,
2970 				GBool interpolate) {
2971   double *ctm;
2972   SplashCoord mat[6];
2973   SplashOutImageData imgData;
2974   SplashColorMode srcMode;
2975   SplashImageSource src;
2976   GString *imgTag;
2977   GfxGray gray;
2978   GfxRGB rgb;
2979 #if SPLASH_CMYK
2980   GfxCMYK cmyk;
2981 #endif
2982   Guchar pix;
2983   int n, i;
2984 
2985   setOverprintMask(state, colorMap->getColorSpace(),
2986 		   state->getFillOverprint(), state->getOverprintMode(),
2987 		   NULL);
2988 
2989   ctm = state->getCTM();
2990   mat[0] = ctm[0];
2991   mat[1] = ctm[1];
2992   mat[2] = -ctm[2];
2993   mat[3] = -ctm[3];
2994   mat[4] = ctm[2] + ctm[4];
2995   mat[5] = ctm[3] + ctm[5];
2996 
2997   reduceImageResolution(str, ctm, &width, &height);
2998 
2999   imgData.imgStr = new ImageStream(str, width,
3000 				   colorMap->getNumPixelComps(),
3001 				   colorMap->getBits());
3002   imgData.imgStr->reset();
3003   imgData.colorMap = colorMap;
3004   imgData.ri = state->getRenderingIntent();
3005   imgData.maskColors = maskColors;
3006   imgData.colorMode = colorMode;
3007   imgData.invert = reverseVideo && reverseVideoInvertImages;
3008   imgData.width = width;
3009   imgData.height = height;
3010   imgData.y = 0;
3011 
3012   // special case for one-channel (monochrome/gray/separation) images:
3013   // build a lookup table here
3014   imgData.lookup = NULL;
3015   if (colorMap->getNumPixelComps() == 1) {
3016     if (colorMap->getBits() <= 8) {
3017       n = 1 << colorMap->getBits();
3018     } else {
3019       // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit
3020       n = 1 << 8;
3021     }
3022     switch (colorMode) {
3023     case splashModeMono1:
3024     case splashModeMono8:
3025       imgData.lookup = (SplashColorPtr)gmalloc(n);
3026       for (i = 0; i < n; ++i) {
3027 	pix = (Guchar)i;
3028 	colorMap->getGray(&pix, &gray, state->getRenderingIntent());
3029 	imgData.lookup[i] = colToByte(gray);
3030       }
3031       break;
3032     case splashModeRGB8:
3033     case splashModeBGR8:
3034       imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
3035       for (i = 0; i < n; ++i) {
3036 	pix = (Guchar)i;
3037 	colorMap->getRGB(&pix, &rgb, state->getRenderingIntent());
3038 	imgData.lookup[3*i] = colToByte(rgb.r);
3039 	imgData.lookup[3*i+1] = colToByte(rgb.g);
3040 	imgData.lookup[3*i+2] = colToByte(rgb.b);
3041       }
3042       break;
3043 #if SPLASH_CMYK
3044     case splashModeCMYK8:
3045       imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3046       for (i = 0; i < n; ++i) {
3047 	pix = (Guchar)i;
3048 	colorMap->getCMYK(&pix, &cmyk, state->getRenderingIntent());
3049 	imgData.lookup[4*i] = colToByte(cmyk.c);
3050 	imgData.lookup[4*i+1] = colToByte(cmyk.m);
3051 	imgData.lookup[4*i+2] = colToByte(cmyk.y);
3052 	imgData.lookup[4*i+3] = colToByte(cmyk.k);
3053       }
3054       break;
3055 #endif
3056     }
3057   }
3058 
3059   if (colorMode == splashModeMono1) {
3060     srcMode = splashModeMono8;
3061   } else if (colorMode == splashModeBGR8) {
3062     srcMode = splashModeRGB8;
3063   } else {
3064     srcMode = colorMode;
3065   }
3066   src = maskColors ? &alphaImageSrc : &imageSrc;
3067   imgTag = makeImageTag(ref, state->getRenderingIntent(),
3068 			colorMap->getColorSpace());
3069   splash->drawImage(imgTag,
3070 		    src, &imgData, srcMode, maskColors ? gTrue : gFalse,
3071 		    width, height, mat, interpolate);
3072   if (inlineImg) {
3073     while (imgData.y < height) {
3074       imgData.imgStr->getLine();
3075       ++imgData.y;
3076     }
3077   }
3078 
3079   delete imgTag;
3080   gfree(imgData.lookup);
3081   delete imgData.imgStr;
3082   str->close();
3083 }
3084 
3085 struct SplashOutMaskedImageData {
3086   ImageStream *imgStr;
3087   GfxImageColorMap *colorMap;
3088   GfxRenderingIntent ri;
3089   SplashBitmap *mask;
3090   SplashColorPtr lookup;
3091   SplashColorMode colorMode;
3092   GBool invert;
3093   int width, height, y;
3094 };
3095 
3096 GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
3097 				      Guchar *alphaLine) {
3098   SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;
3099   Guchar *p, *aq;
3100   SplashColorPtr q, col;
3101   static Guchar bitToByte[2] = {0x00, 0xff};
3102   Guchar *maskPtr;
3103   int maskShift;
3104   int n, x;
3105 
3106   if (imgData->y == imgData->height ||
3107       !(p = imgData->imgStr->getLine())) {
3108     memset(colorLine, 0,
3109 	   imgData->width * splashColorModeNComps[imgData->colorMode]);
3110     memset(alphaLine, 0, imgData->width);
3111     return gFalse;
3112   }
3113 
3114   maskPtr = imgData->mask->getDataPtr() +
3115               imgData->y * imgData->mask->getRowSize();
3116   aq = alphaLine;
3117   for (x = 0; x <= imgData->width - 8; x += 8) {
3118     aq[0] = bitToByte[(*maskPtr >> 7) & 1];
3119     aq[1] = bitToByte[(*maskPtr >> 6) & 1];
3120     aq[2] = bitToByte[(*maskPtr >> 5) & 1];
3121     aq[3] = bitToByte[(*maskPtr >> 4) & 1];
3122     aq[4] = bitToByte[(*maskPtr >> 3) & 1];
3123     aq[5] = bitToByte[(*maskPtr >> 2) & 1];
3124     aq[6] = bitToByte[(*maskPtr >> 1) & 1];
3125     aq[7] = bitToByte[*maskPtr & 1];
3126     aq += 8;
3127     ++maskPtr;
3128   }
3129   maskShift = 7;
3130   for (; x < imgData->width; ++x) {
3131     *aq++ = bitToByte[(*maskPtr >> maskShift) & 1];
3132     --maskShift;
3133   }
3134 
3135   if (imgData->lookup) {
3136     switch (imgData->colorMode) {
3137     case splashModeMono1:
3138     case splashModeMono8:
3139       for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
3140 	*q++ = imgData->lookup[*p];
3141       }
3142       break;
3143     case splashModeRGB8:
3144     case splashModeBGR8:
3145       for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
3146 	col = &imgData->lookup[3 * *p];
3147 	*q++ = col[0];
3148 	*q++ = col[1];
3149 	*q++ = col[2];
3150       }
3151       break;
3152 #if SPLASH_CMYK
3153     case splashModeCMYK8:
3154       for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
3155 	col = &imgData->lookup[4 * *p];
3156 	*q++ = col[0];
3157 	*q++ = col[1];
3158 	*q++ = col[2];
3159 	*q++ = col[3];
3160       }
3161       break;
3162 #endif
3163     }
3164   } else {
3165     switch (imgData->colorMode) {
3166     case splashModeMono1:
3167     case splashModeMono8:
3168       imgData->colorMap->getGrayByteLine(p, colorLine, imgData->width,
3169 					 imgData->ri);
3170       break;
3171     case splashModeRGB8:
3172     case splashModeBGR8:
3173       imgData->colorMap->getRGBByteLine(p, colorLine, imgData->width,
3174 					imgData->ri);
3175       break;
3176 #if SPLASH_CMYK
3177     case splashModeCMYK8:
3178       imgData->colorMap->getCMYKByteLine(p, colorLine, imgData->width,
3179 					 imgData->ri);
3180       break;
3181 #endif
3182     }
3183   }
3184 
3185   if (imgData->invert) {
3186     n = imgData->width * splashColorModeNComps[imgData->colorMode];
3187     for (x = 0, p = colorLine; x < n; ++x, ++p) {
3188       *p ^= 0xff;
3189     }
3190   }
3191 
3192   ++imgData->y;
3193   return gTrue;
3194 }
3195 
3196 
3197 void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
3198 				      Stream *str, int width, int height,
3199 				      GfxImageColorMap *colorMap,
3200 				      Object *maskRef, Stream *maskStr,
3201 				      int maskWidth, int maskHeight,
3202 				      GBool maskInvert, GBool interpolate) {
3203   GfxImageColorMap *maskColorMap;
3204   Object maskDecode, decodeLow, decodeHigh;
3205   double *ctm;
3206   SplashCoord mat[6];
3207   SplashOutMaskedImageData imgData;
3208   SplashOutImageMaskData imgMaskData;
3209   SplashColorMode srcMode;
3210   SplashBitmap *maskBitmap;
3211   Splash *maskSplash;
3212   GString *imgTag;
3213   SplashColor maskColor;
3214   GfxGray gray;
3215   GfxRGB rgb;
3216 #if SPLASH_CMYK
3217   GfxCMYK cmyk;
3218 #endif
3219   Guchar pix;
3220   int n, i;
3221 
3222   setOverprintMask(state, colorMap->getColorSpace(),
3223 		   state->getFillOverprint(), state->getOverprintMode(),
3224 		   NULL);
3225 
3226   ctm = state->getCTM();
3227   reduceImageResolution(str, ctm, &width, &height);
3228   reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
3229 
3230   // If the mask is higher resolution than the image, use
3231   // drawSoftMaskedImage() instead.
3232   if (maskWidth > width || maskHeight > height) {
3233     decodeLow.initInt(maskInvert ? 0 : 1);
3234     decodeHigh.initInt(maskInvert ? 1 : 0);
3235     maskDecode.initArray(xref);
3236     maskDecode.arrayAdd(&decodeLow);
3237     maskDecode.arrayAdd(&decodeHigh);
3238     maskColorMap = new GfxImageColorMap(1, &maskDecode,
3239 					new GfxDeviceGrayColorSpace());
3240     maskDecode.free();
3241     drawSoftMaskedImage(state, ref, str, width, height, colorMap,
3242 			maskRef, maskStr, maskWidth, maskHeight, maskColorMap,
3243 			NULL, interpolate);
3244     delete maskColorMap;
3245 
3246   } else {
3247 
3248     //----- scale the mask image to the same size as the source image
3249 
3250     mat[0] = (SplashCoord)width;
3251     mat[1] = 0;
3252     mat[2] = 0;
3253     mat[3] = (SplashCoord)height;
3254     mat[4] = 0;
3255     mat[5] = 0;
3256     imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
3257     imgMaskData.imgStr->reset();
3258     imgMaskData.invert = maskInvert ? 0 : 1;
3259     imgMaskData.width = maskWidth;
3260     imgMaskData.height = maskHeight;
3261     imgMaskData.y = 0;
3262     traceMessage("masked image bitmap");
3263     maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1,
3264 				  gFalse, gTrue, bitmap);
3265     maskSplash = new Splash(maskBitmap, gFalse, splash->getImageCache());
3266     maskSplash->setStrokeAdjust(
3267 		       mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
3268     maskSplash->setEnablePathSimplification(
3269 		       globalParams->getEnablePathSimplification());
3270     maskColor[0] = 0;
3271     maskSplash->clear(maskColor);
3272     maskColor[0] = 0xff;
3273     maskSplash->setFillPattern(new SplashSolidColor(maskColor));
3274     // use "glyph mode" here to get the correct scaled size
3275     maskSplash->fillImageMask(NULL, &imageMaskSrc, &imgMaskData,
3276 			      maskWidth, maskHeight, mat, gTrue, interpolate);
3277     delete imgMaskData.imgStr;
3278     maskStr->close();
3279     delete maskSplash;
3280 
3281     //----- draw the source image
3282 
3283     mat[0] = ctm[0];
3284     mat[1] = ctm[1];
3285     mat[2] = -ctm[2];
3286     mat[3] = -ctm[3];
3287     mat[4] = ctm[2] + ctm[4];
3288     mat[5] = ctm[3] + ctm[5];
3289 
3290     imgData.imgStr = new ImageStream(str, width,
3291 				     colorMap->getNumPixelComps(),
3292 				     colorMap->getBits());
3293     imgData.imgStr->reset();
3294     imgData.colorMap = colorMap;
3295     imgData.ri = state->getRenderingIntent();
3296     imgData.mask = maskBitmap;
3297     imgData.colorMode = colorMode;
3298     imgData.invert = reverseVideo && reverseVideoInvertImages;
3299     imgData.width = width;
3300     imgData.height = height;
3301     imgData.y = 0;
3302 
3303     // special case for one-channel (monochrome/gray/separation) images:
3304     // build a lookup table here
3305     imgData.lookup = NULL;
3306     if (colorMap->getNumPixelComps() == 1) {
3307       if (colorMap->getBits() <= 8) {
3308 	n = 1 << colorMap->getBits();
3309       } else {
3310 	// GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit
3311 	n = 1 << 8;
3312       }
3313       switch (colorMode) {
3314       case splashModeMono1:
3315       case splashModeMono8:
3316 	imgData.lookup = (SplashColorPtr)gmalloc(n);
3317 	for (i = 0; i < n; ++i) {
3318 	  pix = (Guchar)i;
3319 	  colorMap->getGray(&pix, &gray, state->getRenderingIntent());
3320 	  imgData.lookup[i] = colToByte(gray);
3321 	}
3322 	break;
3323       case splashModeRGB8:
3324       case splashModeBGR8:
3325 	imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
3326 	for (i = 0; i < n; ++i) {
3327 	  pix = (Guchar)i;
3328 	  colorMap->getRGB(&pix, &rgb, state->getRenderingIntent());
3329 	  imgData.lookup[3*i] = colToByte(rgb.r);
3330 	  imgData.lookup[3*i+1] = colToByte(rgb.g);
3331 	  imgData.lookup[3*i+2] = colToByte(rgb.b);
3332 	}
3333 	break;
3334 #if SPLASH_CMYK
3335       case splashModeCMYK8:
3336 	imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3337 	for (i = 0; i < n; ++i) {
3338 	  pix = (Guchar)i;
3339 	  colorMap->getCMYK(&pix, &cmyk, state->getRenderingIntent());
3340 	  imgData.lookup[4*i] = colToByte(cmyk.c);
3341 	  imgData.lookup[4*i+1] = colToByte(cmyk.m);
3342 	  imgData.lookup[4*i+2] = colToByte(cmyk.y);
3343 	  imgData.lookup[4*i+3] = colToByte(cmyk.k);
3344 	}
3345 	break;
3346 #endif
3347       }
3348     }
3349 
3350     if (colorMode == splashModeMono1) {
3351       srcMode = splashModeMono8;
3352     } else if (colorMode == splashModeBGR8) {
3353       srcMode = splashModeRGB8;
3354     } else {
3355       srcMode = colorMode;
3356     }
3357     imgTag = makeImageTag(ref, state->getRenderingIntent(),
3358 			  colorMap->getColorSpace());
3359     splash->drawImage(imgTag,
3360 		      &maskedImageSrc, &imgData, srcMode, gTrue,
3361 		      width, height, mat, interpolate);
3362 
3363     delete imgTag;
3364     delete maskBitmap;
3365     gfree(imgData.lookup);
3366     delete imgData.imgStr;
3367     str->close();
3368   }
3369 }
3370 
3371 struct SplashOutSoftMaskMatteImageData {
3372   ImageStream *imgStr;
3373   ImageStream *maskStr;
3374   GfxImageColorMap *colorMap;
3375   GfxRenderingIntent ri;
3376   Guchar matte[gfxColorMaxComps];
3377   SplashColorMode colorMode;
3378   GBool invert;
3379   int width, height, y;
3380 };
3381 
3382 GBool SplashOutputDev::softMaskMatteImageSrc(void *data,
3383 					     SplashColorPtr colorLine,
3384 					     Guchar *alphaLine) {
3385   SplashOutSoftMaskMatteImageData *imgData =
3386       (SplashOutSoftMaskMatteImageData *)data;
3387   Guchar *p, *ap, *aq;
3388   SplashColorPtr q;
3389   GfxRGB rgb;
3390   GfxGray gray;
3391 #if SPLASH_CMYK
3392   GfxCMYK cmyk;
3393 #endif
3394   Guchar alpha;
3395   int nComps, n, x;
3396 
3397   if (imgData->y == imgData->height ||
3398       !(p = imgData->imgStr->getLine()) ||
3399       !(ap = imgData->maskStr->getLine())) {
3400     memset(colorLine, 0,
3401 	   imgData->width * splashColorModeNComps[imgData->colorMode]);
3402     memset(alphaLine, 0, imgData->width);
3403     return gFalse;
3404   }
3405 
3406   nComps = imgData->colorMap->getNumPixelComps();
3407 
3408   for (x = 0, q = colorLine, aq = alphaLine;
3409        x < imgData->width;
3410        ++x, p += nComps, ++ap) {
3411     alpha = *ap;
3412     switch (imgData->colorMode) {
3413     case splashModeMono1:
3414     case splashModeMono8:
3415       if (alpha) {
3416 	imgData->colorMap->getGray(p, &gray, imgData->ri);
3417 	*q++ = clip255(imgData->matte[0] +
3418 		       (255 * (colToByte(gray) - imgData->matte[0])) / alpha);
3419       } else {
3420 	*q++ = 0;
3421       }
3422       break;
3423     case splashModeRGB8:
3424     case splashModeBGR8:
3425       if (alpha) {
3426 	imgData->colorMap->getRGB(p, &rgb, imgData->ri);
3427 	*q++ = clip255(imgData->matte[0] +
3428 		       (255 * (colToByte(rgb.r) - imgData->matte[0])) / alpha);
3429 	*q++ = clip255(imgData->matte[1] +
3430 		       (255 * (colToByte(rgb.g) - imgData->matte[1])) / alpha);
3431 	*q++ = clip255(imgData->matte[2] +
3432 		       (255 * (colToByte(rgb.b) - imgData->matte[2])) / alpha);
3433       } else {
3434 	*q++ = 0;
3435 	*q++ = 0;
3436 	*q++ = 0;
3437       }
3438       break;
3439 #if SPLASH_CMYK
3440     case splashModeCMYK8:
3441       if (alpha) {
3442 	imgData->colorMap->getCMYK(p, &cmyk, imgData->ri);
3443 	*q++ = clip255(imgData->matte[0] +
3444 		       (255 * (colToByte(cmyk.c) - imgData->matte[0]))
3445 			 / alpha);
3446 	*q++ = clip255(imgData->matte[1] +
3447 		       (255 * (colToByte(cmyk.m) - imgData->matte[1]))
3448 			 / alpha);
3449 	*q++ = clip255(imgData->matte[2] +
3450 		       (255 * (colToByte(cmyk.y) - imgData->matte[2]))
3451 			 / alpha);
3452 	*q++ = clip255(imgData->matte[3] +
3453 		       (255 * (colToByte(cmyk.k) - imgData->matte[3]))
3454 		         / alpha);
3455       } else {
3456 	*q++ = 0;
3457 	*q++ = 0;
3458 	*q++ = 0;
3459 	*q++ = 0;
3460       }
3461       break;
3462 #endif
3463     }
3464     *aq++ = alpha;
3465   }
3466 
3467   if (imgData->invert) {
3468     n = imgData->width * splashColorModeNComps[imgData->colorMode];
3469     for (x = 0, p = colorLine; x < n; ++x, ++p) {
3470       *p ^= 0xff;
3471     }
3472   }
3473 
3474   ++imgData->y;
3475   return gTrue;
3476 }
3477 
3478 
3479 void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
3480 					  Stream *str, int width, int height,
3481 					  GfxImageColorMap *colorMap,
3482 					  Object *maskRef, Stream *maskStr,
3483 					  int maskWidth, int maskHeight,
3484 					  GfxImageColorMap *maskColorMap,
3485 					  double *matte, GBool interpolate) {
3486   double *ctm;
3487   SplashCoord mat[6];
3488   SplashOutImageData imgData;
3489   SplashOutImageData imgMaskData;
3490   SplashOutSoftMaskMatteImageData matteImgData;
3491   GString *imgTag;
3492   SplashColorMode srcMode;
3493   SplashBitmap *maskBitmap;
3494   Splash *maskSplash;
3495   GfxColor matteColor;
3496   GfxGray gray;
3497   GfxRGB rgb;
3498 #if SPLASH_CMYK
3499   GfxCMYK cmyk;
3500 #endif
3501   Guchar pix;
3502   int n, i;
3503 
3504   setOverprintMask(state, colorMap->getColorSpace(),
3505 		   state->getFillOverprint(), state->getOverprintMode(),
3506 		   NULL);
3507 
3508   ctm = state->getCTM();
3509   mat[0] = ctm[0];
3510   mat[1] = ctm[1];
3511   mat[2] = -ctm[2];
3512   mat[3] = -ctm[3];
3513   mat[4] = ctm[2] + ctm[4];
3514   mat[5] = ctm[3] + ctm[5];
3515 
3516   if (colorMode == splashModeMono1) {
3517     srcMode = splashModeMono8;
3518   } else if (colorMode == splashModeBGR8) {
3519     srcMode = splashModeRGB8;
3520   } else {
3521     srcMode = colorMode;
3522   }
3523 
3524   //----- handle a preblended image
3525 
3526   if (matte && width == maskWidth && height == maskHeight) {
3527 
3528     // the image and mask must be the same size, so don't call
3529     // reduceImageResolution(), which might result in different
3530     // reductions (e.g., if the image filter supports resolution
3531     // reduction but the mask filter doesn't)
3532 
3533     matteImgData.imgStr = new ImageStream(str, width,
3534 					  colorMap->getNumPixelComps(),
3535 					  colorMap->getBits());
3536     matteImgData.imgStr->reset();
3537     matteImgData.maskStr = new ImageStream(maskStr, maskWidth,
3538 					   maskColorMap->getNumPixelComps(),
3539 					   maskColorMap->getBits());
3540     matteImgData.maskStr->reset();
3541     matteImgData.colorMap = colorMap;
3542     matteImgData.ri = state->getRenderingIntent();
3543     n = colorMap->getNumPixelComps();
3544     for (i = 0; i < n; ++i) {
3545       matteColor.c[i] = dblToCol(matte[i]);
3546     }
3547     switch (colorMode) {
3548     case splashModeMono1:
3549     case splashModeMono8:
3550       colorMap->getColorSpace()->getGray(&matteColor, &gray,
3551 					 state->getRenderingIntent());
3552       matteImgData.matte[0] = colToByte(gray);
3553       break;
3554     case splashModeRGB8:
3555     case splashModeBGR8:
3556       colorMap->getColorSpace()->getRGB(&matteColor, &rgb,
3557 					state->getRenderingIntent());
3558       matteImgData.matte[0] = colToByte(rgb.r);
3559       matteImgData.matte[1] = colToByte(rgb.g);
3560       matteImgData.matte[2] = colToByte(rgb.b);
3561       break;
3562 #if SPLASH_CMYK
3563     case splashModeCMYK8:
3564       colorMap->getColorSpace()->getCMYK(&matteColor, &cmyk,
3565 					 state->getRenderingIntent());
3566       matteImgData.matte[0] = colToByte(cmyk.c);
3567       matteImgData.matte[1] = colToByte(cmyk.m);
3568       matteImgData.matte[2] = colToByte(cmyk.y);
3569       matteImgData.matte[3] = colToByte(cmyk.k);
3570       break;
3571 #endif
3572     }
3573     //~ could add the matteImgData.lookup special case
3574     matteImgData.colorMode = colorMode;
3575     matteImgData.invert = reverseVideo && reverseVideoInvertImages;
3576     matteImgData.width = width;
3577     matteImgData.height = height;
3578     matteImgData.y = 0;
3579     imgTag = makeImageTag(ref, state->getRenderingIntent(),
3580 			  colorMap->getColorSpace());
3581     splash->drawImage(imgTag, &softMaskMatteImageSrc, &matteImgData,
3582 		      srcMode, gTrue, width, height, mat, interpolate);
3583     delete imgTag;
3584     delete matteImgData.maskStr;
3585     delete matteImgData.imgStr;
3586     maskStr->close();
3587     str->close();
3588 
3589   } else {
3590 
3591     reduceImageResolution(str, ctm, &width, &height);
3592     reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
3593 
3594     //----- set up the soft mask
3595 
3596     imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
3597 					 maskColorMap->getNumPixelComps(),
3598 					 maskColorMap->getBits());
3599     imgMaskData.imgStr->reset();
3600     imgMaskData.colorMap = maskColorMap;
3601     imgMaskData.ri = state->getRenderingIntent();
3602     imgMaskData.maskColors = NULL;
3603     imgMaskData.colorMode = splashModeMono8;
3604     imgMaskData.invert = gFalse;
3605     imgMaskData.width = maskWidth;
3606     imgMaskData.height = maskHeight;
3607     imgMaskData.y = 0;
3608     if (maskColorMap->getBits() <= 8) {
3609       n = 1 << maskColorMap->getBits();
3610     } else {
3611       // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit
3612       n = 1 << 8;
3613     }
3614     imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
3615     for (i = 0; i < n; ++i) {
3616       pix = (Guchar)i;
3617       maskColorMap->getGray(&pix, &gray, state->getRenderingIntent());
3618       imgMaskData.lookup[i] = colToByte(gray);
3619     }
3620     traceMessage("soft masked image bitmap");
3621     maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
3622 				  1, splashModeMono8, gFalse, gTrue, bitmap);
3623     maskSplash = new Splash(maskBitmap, vectorAntialias,
3624 			    splash->getImageCache());
3625     maskSplash->setStrokeAdjust(
3626 		       mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
3627     maskSplash->setEnablePathSimplification(
3628 		       globalParams->getEnablePathSimplification());
3629     clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
3630     maskSplash->drawImage(NULL,
3631 			  &imageSrc, &imgMaskData, splashModeMono8, gFalse,
3632 			  maskWidth, maskHeight, mat, interpolate);
3633     delete imgMaskData.imgStr;
3634     maskStr->close();
3635     gfree(imgMaskData.lookup);
3636     delete maskSplash;
3637     splash->setSoftMask(maskBitmap);
3638 
3639     //----- draw the source image
3640 
3641     imgData.imgStr = new ImageStream(str, width,
3642 				     colorMap->getNumPixelComps(),
3643 				     colorMap->getBits());
3644     imgData.imgStr->reset();
3645     imgData.colorMap = colorMap;
3646     imgData.ri = state->getRenderingIntent();
3647     imgData.maskColors = NULL;
3648     imgData.colorMode = colorMode;
3649     imgData.invert = reverseVideo && reverseVideoInvertImages;
3650     imgData.width = width;
3651     imgData.height = height;
3652     imgData.y = 0;
3653 
3654     // special case for one-channel (monochrome/gray/separation) images:
3655     // build a lookup table here
3656     imgData.lookup = NULL;
3657     if (colorMap->getNumPixelComps() == 1) {
3658       if (colorMap->getBits() <= 8) {
3659 	n = 1 << colorMap->getBits();
3660       } else {
3661 	// GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit
3662 	n = 1 << 8;
3663       }
3664       switch (colorMode) {
3665       case splashModeMono1:
3666       case splashModeMono8:
3667 	imgData.lookup = (SplashColorPtr)gmalloc(n);
3668 	for (i = 0; i < n; ++i) {
3669 	  pix = (Guchar)i;
3670 	  colorMap->getGray(&pix, &gray, state->getRenderingIntent());
3671 	  imgData.lookup[i] = colToByte(gray);
3672 	}
3673 	break;
3674       case splashModeRGB8:
3675       case splashModeBGR8:
3676 	imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
3677 	for (i = 0; i < n; ++i) {
3678 	  pix = (Guchar)i;
3679 	  colorMap->getRGB(&pix, &rgb, state->getRenderingIntent());
3680 	  imgData.lookup[3*i] = colToByte(rgb.r);
3681 	  imgData.lookup[3*i+1] = colToByte(rgb.g);
3682 	  imgData.lookup[3*i+2] = colToByte(rgb.b);
3683 	}
3684 	break;
3685 #if SPLASH_CMYK
3686       case splashModeCMYK8:
3687 	imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3688 	for (i = 0; i < n; ++i) {
3689 	  pix = (Guchar)i;
3690 	  colorMap->getCMYK(&pix, &cmyk, state->getRenderingIntent());
3691 	  imgData.lookup[4*i] = colToByte(cmyk.c);
3692 	  imgData.lookup[4*i+1] = colToByte(cmyk.m);
3693 	  imgData.lookup[4*i+2] = colToByte(cmyk.y);
3694 	  imgData.lookup[4*i+3] = colToByte(cmyk.k);
3695 	}
3696 	break;
3697 #endif
3698       }
3699     }
3700 
3701     imgTag = makeImageTag(ref, state->getRenderingIntent(),
3702 			  colorMap->getColorSpace());
3703     splash->drawImage(imgTag,
3704 		      &imageSrc, &imgData, srcMode, gFalse, width, height, mat,
3705 		      interpolate);
3706 
3707     splash->setSoftMask(NULL);
3708     delete imgTag;
3709     gfree(imgData.lookup);
3710     delete imgData.imgStr;
3711 
3712 
3713     str->close();
3714   }
3715 }
3716 
3717 GString *SplashOutputDev::makeImageTag(Object *ref, GfxRenderingIntent ri,
3718 				       GfxColorSpace *colorSpace) {
3719   if (!ref || !ref->isRef() ||
3720       (colorSpace && colorSpace->isDefaultColorSpace())) {
3721     return NULL;
3722   }
3723   return GString::format("{0:d}_{1:d}_{2:d}",
3724 			 ref->getRefNum(), ref->getRefGen(), (int)ri);
3725 }
3726 
3727 void SplashOutputDev::reduceImageResolution(Stream *str, double *ctm,
3728 					    int *width, int *height) {
3729   double sw, sh;
3730   int reduction;
3731 
3732   if (str->getKind() == strJPX &&
3733       *width >= 256 &&
3734       *height >= 256 &&
3735       *width * *height > 10000000) {
3736     sw = (double)*width / (fabs(ctm[0]) + fabs(ctm[1]));
3737     sh = (double)*height / (fabs(ctm[2]) + fabs(ctm[3]));
3738     if (sw > 8 && sh > 8) {
3739       reduction = 3;
3740     } else if (sw > 4 && sh > 4) {
3741       reduction = 2;
3742     } else if (sw > 2 && sh > 2) {
3743       reduction = 1;
3744     } else {
3745       reduction = 0;
3746     }
3747     if (reduction > 0) {
3748       ((JPXStream *)str)->reduceResolution(reduction);
3749       *width >>= reduction;
3750       *height >>= reduction;
3751     }
3752   }
3753 }
3754 
3755 void SplashOutputDev::clearMaskRegion(GfxState *state,
3756 				      Splash *maskSplash,
3757 				      double xMin, double yMin,
3758 				      double xMax, double yMax) {
3759   SplashBitmap *maskBitmap;
3760   double xxMin, yyMin, xxMax, yyMax, xx, yy;
3761   int xxMinI, yyMinI, xxMaxI, yyMaxI, y, n;
3762   Guchar *p;
3763 
3764   maskBitmap = maskSplash->getBitmap();
3765   xxMin = maskBitmap->getWidth();
3766   xxMax = 0;
3767   yyMin = maskBitmap->getHeight();
3768   yyMax = 0;
3769   state->transform(xMin, yMin, &xx, &yy);
3770   if (xx < xxMin) { xxMin = xx; }
3771   if (xx > xxMax) { xxMax = xx; }
3772   if (yy < yyMin) { yyMin = yy; }
3773   if (yy > yyMax) { yyMax = yy; }
3774   state->transform(xMin, yMax, &xx, &yy);
3775   if (xx < xxMin) { xxMin = xx; }
3776   if (xx > xxMax) { xxMax = xx; }
3777   if (yy < yyMin) { yyMin = yy; }
3778   if (yy > yyMax) { yyMax = yy; }
3779   state->transform(xMax, yMin, &xx, &yy);
3780   if (xx < xxMin) { xxMin = xx; }
3781   if (xx > xxMax) { xxMax = xx; }
3782   if (yy < yyMin) { yyMin = yy; }
3783   if (yy > yyMax) { yyMax = yy; }
3784   state->transform(xMax, yMax, &xx, &yy);
3785   if (xx < xxMin) { xxMin = xx; }
3786   if (xx > xxMax) { xxMax = xx; }
3787   if (yy < yyMin) { yyMin = yy; }
3788   if (yy > yyMax) { yyMax = yy; }
3789   xxMinI = (int)floor(xxMin);
3790   if (xxMinI < 0) {
3791     xxMinI = 0;
3792   }
3793   xxMaxI = (int)ceil(xxMax);
3794   if (xxMaxI > maskBitmap->getWidth()) {
3795     xxMaxI = maskBitmap->getWidth();
3796   }
3797   yyMinI = (int)floor(yyMin);
3798   if (yyMinI < 0) {
3799     yyMinI = 0;
3800   }
3801   yyMaxI = (int)ceil(yyMax);
3802   if (yyMaxI > maskBitmap->getHeight()) {
3803     yyMaxI = maskBitmap->getHeight();
3804   }
3805   p = maskBitmap->getDataPtr() + yyMinI * maskBitmap->getRowSize();
3806   if (maskBitmap->getMode() == splashModeMono1) {
3807     n = (xxMaxI + 7) / 8 - xxMinI / 8;
3808     p += xxMinI / 8;
3809   } else {
3810     n = xxMaxI - xxMinI;
3811     p += xxMinI;
3812   }
3813   if (xxMaxI > xxMinI) {
3814     for (y = yyMinI; y < yyMaxI; ++y) {
3815       memset(p, 0, n);
3816       p += maskBitmap->getRowSize();
3817     }
3818   }
3819 }
3820 
3821 void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
3822 					     GfxColorSpace *blendingColorSpace,
3823 					     GBool isolated, GBool knockout,
3824 					     GBool forSoftMask) {
3825   SplashTransparencyGroup *transpGroup;
3826   SplashBitmap *backdropBitmap;
3827   SplashColor color;
3828   double xMin, yMin, xMax, yMax, x, y;
3829   int bw, bh, tx, ty, w, h, i;
3830 
3831   // transform the bbox
3832   state->transform(bbox[0], bbox[1], &x, &y);
3833   xMin = xMax = x;
3834   yMin = yMax = y;
3835   state->transform(bbox[0], bbox[3], &x, &y);
3836   if (x < xMin) {
3837     xMin = x;
3838   } else if (x > xMax) {
3839     xMax = x;
3840   }
3841   if (y < yMin) {
3842     yMin = y;
3843   } else if (y > yMax) {
3844     yMax = y;
3845   }
3846   state->transform(bbox[2], bbox[1], &x, &y);
3847   if (x < xMin) {
3848     xMin = x;
3849   } else if (x > xMax) {
3850     xMax = x;
3851   }
3852   if (y < yMin) {
3853     yMin = y;
3854   } else if (y > yMax) {
3855     yMax = y;
3856   }
3857   state->transform(bbox[2], bbox[3], &x, &y);
3858   if (x < xMin) {
3859     xMin = x;
3860   } else if (x > xMax) {
3861     xMax = x;
3862   }
3863   if (y < yMin) {
3864     yMin = y;
3865   } else if (y > yMax) {
3866     yMax = y;
3867   }
3868 
3869   // clip the box
3870   x = splash->getClip()->getXMin();
3871   if (x > xMin) {
3872     xMin = x;
3873   }
3874   x = splash->getClip()->getXMax();
3875   if (x < xMax) {
3876     xMax = x;
3877   }
3878   y = splash->getClip()->getYMin();
3879   if (y > yMin) {
3880     yMin = y;
3881   }
3882   y = splash->getClip()->getYMax();
3883   if (y < yMax) {
3884     yMax = y;
3885   }
3886 
3887   // convert box coords to integers
3888   bw = bitmap->getWidth();
3889   bh = bitmap->getHeight();
3890   tx = (int)floor(xMin);
3891   if (tx < 0) {
3892     tx = 0;
3893   } else if (tx >= bw) {
3894     tx = bw - 1;
3895   }
3896   ty = (int)floor(yMin);
3897   if (ty < 0) {
3898     ty = 0;
3899   } else if (ty >= bh) {
3900     ty = bh - 1;
3901   }
3902   w = (int)ceil(xMax) - tx + 1;
3903   // NB bw and tx are both non-negative, so 'bw - tx' can't overflow
3904   if (bw - tx < w) {
3905     w = bw - tx;
3906   }
3907   if (w < 1) {
3908     w = 1;
3909   }
3910   h = (int)ceil(yMax) - ty + 1;
3911   // NB bh and ty are both non-negative, so 'bh - ty' can't overflow
3912   if (bh - ty < h) {
3913     h = bh - ty;
3914   }
3915   if (h < 1) {
3916     h = 1;
3917   }
3918 
3919   // push a new stack entry
3920   transpGroup = new SplashTransparencyGroup();
3921   transpGroup->tx = tx;
3922   transpGroup->ty = ty;
3923   transpGroup->blendingColorSpace = blendingColorSpace;
3924   transpGroup->isolated = isolated;
3925   transpGroup->next = transpGroupStack;
3926   transpGroupStack = transpGroup;
3927 
3928   // save state
3929   transpGroup->origBitmap = bitmap;
3930   transpGroup->origSplash = splash;
3931 
3932   //~ this handles the blendingColorSpace arg for soft masks, but
3933   //~   not yet for transparency groups
3934 
3935   // switch to the blending color space
3936   if (forSoftMask && isolated && !knockout && blendingColorSpace) {
3937     if (blendingColorSpace->getMode() == csDeviceGray ||
3938 	blendingColorSpace->getMode() == csCalGray ||
3939 	(blendingColorSpace->getMode() == csICCBased &&
3940 	 blendingColorSpace->getNComps() == 1)) {
3941       colorMode = splashModeMono8;
3942     } else if (blendingColorSpace->getMode() == csDeviceRGB ||
3943 	       blendingColorSpace->getMode() == csCalRGB ||
3944 	       (blendingColorSpace->getMode() == csICCBased &&
3945 		blendingColorSpace->getNComps() == 3)) {
3946       //~ does this need to use BGR8?
3947       colorMode = splashModeRGB8;
3948 #if SPLASH_CMYK
3949     } else if (blendingColorSpace->getMode() == csDeviceCMYK ||
3950 	       (blendingColorSpace->getMode() == csICCBased &&
3951 		blendingColorSpace->getNComps() == 4)) {
3952       colorMode = splashModeCMYK8;
3953 #endif
3954     }
3955   }
3956 
3957   // create the temporary bitmap
3958   traceMessage("t-group bitmap");
3959   bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
3960 			    bitmapTopDown, transpGroup->origBitmap);
3961   splash = new Splash(bitmap, vectorAntialias,
3962 		      transpGroup->origSplash->getImageCache(),
3963 		      transpGroup->origSplash->getScreen());
3964   splash->setMinLineWidth(globalParams->getMinLineWidth());
3965   splash->setStrokeAdjust(
3966 		 mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
3967   splash->setEnablePathSimplification(
3968 		 globalParams->getEnablePathSimplification());
3969   copyState(transpGroup->origSplash, gTrue);
3970   if (!isolated || knockout) {
3971     // non-isolated and knockout groups nested in another group will
3972     // read the parent group bitmap, so we need to force any deferred
3973     // initialization on the parent
3974     transpGroup->origSplash->forceDeferredInit(ty, h);
3975   }
3976   if (isolated) {
3977     // isolated group
3978     backdropBitmap = transpGroup->origBitmap;
3979     transpGroup->backdropBitmap = NULL;
3980     if (forSoftMask) {
3981       // setSoftMask uses the whole bitmap, not just the mod region,
3982       // so we can't use the deferred initialization optimization
3983       for (i = 0; i < splashMaxColorComps; ++i) {
3984 	color[i] = 0;
3985       }
3986       splash->clear(color, 0);
3987       splash->setInTransparencyGroup(backdropBitmap, tx, ty,
3988 				     splashGroupDestPreInit,
3989 				     gFalse, knockout);
3990     } else {
3991       splash->setInTransparencyGroup(backdropBitmap, tx, ty,
3992 				     splashGroupDestInitZero,
3993 				     gFalse, knockout);
3994     }
3995   } else if (transpGroup->origBitmap->getAlphaPtr() &&
3996 	     transpGroup->origSplash->getInNonIsolatedGroup() &&
3997 	     colorMode != splashModeMono1) {
3998     // non-isolated group drawn in another non-isolated group:
3999     // compute a backdrop bitmap with corrected alpha values
4000     traceMessage("t-group backdrop bitmap");
4001     backdropBitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
4002 				      bitmapTopDown, transpGroup->origBitmap);
4003     transpGroup->origSplash->blitCorrectedAlpha(backdropBitmap,
4004 						tx, ty, 0, 0, w, h);
4005     transpGroup->backdropBitmap = backdropBitmap;
4006     if (forSoftMask) {
4007       // setSoftMask uses the whole bitmap, not just the mod region,
4008       // so we can't use the deferred initialization optimization
4009       splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
4010       splash->setInTransparencyGroup(backdropBitmap, 0, 0,
4011 				     splashGroupDestPreInit,
4012 				     gTrue, knockout);
4013     } else {
4014       splash->setInTransparencyGroup(backdropBitmap, 0, 0,
4015 				     splashGroupDestInitCopy,
4016 				     gTrue, knockout);
4017     }
4018   } else {
4019     // other non-isolated group
4020     backdropBitmap = transpGroup->origBitmap;
4021     transpGroup->backdropBitmap = NULL;
4022     if (forSoftMask) {
4023       // setSoftMask uses the whole bitmap, not just the mod region,
4024       // so we can't use the deferred initialization optimization
4025       splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
4026       splash->setInTransparencyGroup(backdropBitmap, tx, ty,
4027 				     splashGroupDestPreInit,
4028 				     gTrue, knockout);
4029     } else {
4030       splash->setInTransparencyGroup(backdropBitmap, tx, ty,
4031 				     splashGroupDestInitCopy,
4032 				     gTrue, knockout);
4033     }
4034   }
4035   splash->clearModRegion();
4036   transpGroup->tBitmap = bitmap;
4037 #if 1 //~tmp
4038   if (knockout) {
4039     splash->setInShading(gTrue);
4040   }
4041 #endif
4042   state->shiftCTM(-tx, -ty);
4043   updateCTM(state, 0, 0, 0, 0, 0, 0);
4044   ++nestCount;
4045 }
4046 
4047 void SplashOutputDev::endTransparencyGroup(GfxState *state) {
4048   splash->getModRegion(&transpGroupStack->modXMin, &transpGroupStack->modYMin,
4049 		       &transpGroupStack->modXMax, &transpGroupStack->modYMax);
4050 
4051   // restore state
4052   --nestCount;
4053   delete splash;
4054   bitmap = transpGroupStack->origBitmap;
4055   colorMode = bitmap->getMode();
4056   splash = transpGroupStack->origSplash;
4057   state->shiftCTM(transpGroupStack->tx, transpGroupStack->ty);
4058   updateCTM(state, 0, 0, 0, 0, 0, 0);
4059 }
4060 
4061 void SplashOutputDev::paintTransparencyGroup(GfxState *state, double *bbox) {
4062   SplashBitmap *tBitmap;
4063   SplashTransparencyGroup *transpGroup;
4064   GBool isolated;
4065   int xSrc, ySrc, xDest, yDest, w, h;
4066 
4067   xSrc = transpGroupStack->modXMin;
4068   ySrc = transpGroupStack->modYMin;
4069   xDest = transpGroupStack->tx + transpGroupStack->modXMin;
4070   yDest = transpGroupStack->ty + transpGroupStack->modYMin;
4071   w = transpGroupStack->modXMax - transpGroupStack->modXMin + 1;
4072   h = transpGroupStack->modYMax - transpGroupStack->modYMin + 1;
4073   tBitmap = transpGroupStack->tBitmap;
4074   isolated = transpGroupStack->isolated;
4075 
4076   // paint the transparency group onto the parent bitmap
4077   // - the clip path was set in the parent's state)
4078   if (xDest < bitmap->getWidth() && yDest < bitmap->getHeight() &&
4079       w > 0 && h > 0) {
4080     splash->setOverprintMask(0xffffffff);
4081     splash->composite(tBitmap, xSrc, ySrc, xDest, yDest, w, h,
4082 		      gFalse, !isolated);
4083   }
4084 
4085   // free the temporary backdrop bitmap
4086   if (transpGroupStack->backdropBitmap) {
4087     delete transpGroupStack->backdropBitmap;
4088   }
4089 
4090   // pop the stack
4091   transpGroup = transpGroupStack;
4092   transpGroupStack = transpGroup->next;
4093   delete transpGroup;
4094 
4095   delete tBitmap;
4096 }
4097 
4098 void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
4099 				  GBool alpha, Function *transferFunc,
4100 				  GfxColor *backdropColor) {
4101   SplashBitmap *softMask, *tBitmap;
4102   Splash *tSplash;
4103   SplashTransparencyGroup *transpGroup;
4104   SplashColor color;
4105   SplashColorPtr p, colorPtr, colorPtr2;
4106   Guchar *alphaPtr;
4107   GfxGray gray;
4108   GfxRGB rgb;
4109 #if SPLASH_CMYK
4110   GfxCMYK cmyk;
4111 #endif
4112   double backdrop, backdrop2, lum, lum2;
4113   Guchar lum8;
4114   SplashBitmapRowSize rowSize;
4115   int tw, th, tNComps, tx, ty, x, y;
4116 
4117   tx = transpGroupStack->tx;
4118   ty = transpGroupStack->ty;
4119   tBitmap = transpGroupStack->tBitmap;
4120 
4121   // composite with backdrop color
4122   backdrop = 0;
4123   if (!alpha && tBitmap->getMode() != splashModeMono1) {
4124     //~ need to correctly handle the case where no blending color
4125     //~ space is given
4126     if (transpGroupStack->blendingColorSpace) {
4127       tSplash = new Splash(tBitmap, vectorAntialias,
4128 			   transpGroupStack->origSplash->getImageCache(),
4129 			   transpGroupStack->origSplash->getScreen());
4130       tSplash->setStrokeAdjust(
4131 		      mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
4132       tSplash->setEnablePathSimplification(
4133 		      globalParams->getEnablePathSimplification());
4134       switch (tBitmap->getMode()) {
4135       case splashModeMono1:
4136 	// transparency is not supported in mono1 mode
4137 	break;
4138       case splashModeMono8:
4139 	transpGroupStack->blendingColorSpace->getGray(
4140 		    backdropColor, &gray, state->getRenderingIntent());
4141 	backdrop = colToDbl(gray);
4142 	color[0] = colToByte(gray);
4143 	tSplash->compositeBackground(color);
4144 	break;
4145       case splashModeRGB8:
4146       case splashModeBGR8:
4147 	transpGroupStack->blendingColorSpace->getRGB(
4148 		    backdropColor, &rgb, state->getRenderingIntent());
4149 	backdrop = 0.3 * colToDbl(rgb.r) +
4150 	           0.59 * colToDbl(rgb.g) +
4151 	           0.11 * colToDbl(rgb.b);
4152 	color[0] = colToByte(rgb.r);
4153 	color[1] = colToByte(rgb.g);
4154 	color[2] = colToByte(rgb.b);
4155 	tSplash->compositeBackground(color);
4156 	break;
4157 #if SPLASH_CMYK
4158       case splashModeCMYK8:
4159 	transpGroupStack->blendingColorSpace->getCMYK(
4160 		    backdropColor, &cmyk, state->getRenderingIntent());
4161 	backdrop = (1 - colToDbl(cmyk.k))
4162 	           - 0.3 * colToDbl(cmyk.c)
4163 	           - 0.59 * colToDbl(cmyk.m)
4164 	           - 0.11 * colToDbl(cmyk.y);
4165 	if (backdrop < 0) {
4166 	  backdrop = 0;
4167 	}
4168 	color[0] = colToByte(cmyk.c);
4169 	color[1] = colToByte(cmyk.m);
4170 	color[2] = colToByte(cmyk.y);
4171 	color[3] = colToByte(cmyk.k);
4172 	tSplash->compositeBackground(color);
4173 	break;
4174 #endif
4175       }
4176       delete tSplash;
4177     }
4178   }
4179   if (transferFunc) {
4180     transferFunc->transform(&backdrop, &backdrop2);
4181   } else {
4182     backdrop2 = backdrop;
4183   }
4184 
4185   traceMessage("soft mask bitmap");
4186   softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
4187 			      1, splashModeMono8, gFalse, gTrue, bitmap);
4188   memset(softMask->getDataPtr(), (int)(backdrop2 * 255.0 + 0.5),
4189 	 softMask->getRowSize() * softMask->getHeight());
4190   if (tx < softMask->getWidth() && ty < softMask->getHeight()) {
4191     p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;
4192     tw = tBitmap->getWidth();
4193     th = tBitmap->getHeight();
4194     rowSize = softMask->getRowSize();
4195     if (alpha) {
4196       alphaPtr = tBitmap->getAlphaPtr();
4197       for (y = 0; y < th; ++y) {
4198 	for (x = 0; x < tw; ++x) {
4199 	  lum = *alphaPtr++ / 255.0;
4200 	  if (transferFunc) {
4201 	    transferFunc->transform(&lum, &lum2);
4202 	  } else {
4203 	    lum2 = lum;
4204 	  }
4205 	  p[x] = (Guchar)(lum2 * 255.0 + 0.5);
4206 	}
4207 	p += rowSize;
4208       }
4209     } else {
4210       colorPtr = tBitmap->getDataPtr();
4211       tNComps = splashColorModeNComps[tBitmap->getMode()];
4212       lum8 = 0;  // make gcc happy
4213       for (y = 0; y < th; ++y) {
4214 	colorPtr2 = colorPtr;
4215 	for (x = 0; x < tw; ++x) {
4216 	  // convert to luminosity
4217 	  switch (tBitmap->getMode()) {
4218 	  case splashModeMono1:
4219 	    lum8 = 0;
4220 	    break;
4221 	  case splashModeMono8:
4222 	    lum8 = colorPtr2[0];
4223 	    break;
4224 	  case splashModeRGB8:
4225 	  case splashModeBGR8:
4226 	    // [0.3, 0.59, 0.11] * 255 = [77, 150, 28]
4227 	    lum8 = div255(77 * colorPtr2[0] +
4228 			  150 * colorPtr2[1] +
4229 			  28 * colorPtr2[1]);
4230 	    break;
4231 #if SPLASH_CMYK
4232 	  case splashModeCMYK8:
4233 	    lum8 = clip255(255 - colorPtr2[3]
4234 			   - div255(77 * colorPtr2[0] +
4235 				    150 * colorPtr2[1] +
4236 				    28 * colorPtr2[2]));
4237 	    break;
4238 #endif
4239 	  }
4240 	  if (transferFunc) {
4241 	    lum = lum8 / 255.0;
4242 	    transferFunc->transform(&lum, &lum2);
4243 	    lum8 = (Guchar)(lum2 * 255.0 + 0.5);
4244 	  }
4245 	  p[x] = lum8;
4246 	  colorPtr2 += tNComps;
4247 	}
4248 	p += rowSize;
4249 	colorPtr += tBitmap->getRowSize();
4250       }
4251     }
4252   }
4253   splash->setSoftMask(softMask);
4254 
4255   // free the temporary backdrop bitmap
4256   if (transpGroupStack->backdropBitmap) {
4257     delete transpGroupStack->backdropBitmap;
4258   }
4259 
4260   // pop the stack
4261   transpGroup = transpGroupStack;
4262   transpGroupStack = transpGroup->next;
4263   delete transpGroup;
4264 
4265   delete tBitmap;
4266 }
4267 
4268 void SplashOutputDev::clearSoftMask(GfxState *state) {
4269   splash->setSoftMask(NULL);
4270 }
4271 
4272 void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) {
4273   splashColorCopy(paperColor, paperColorA);
4274 }
4275 
4276 int SplashOutputDev::getBitmapWidth() {
4277   return bitmap->getWidth();
4278 }
4279 
4280 int SplashOutputDev::getBitmapHeight() {
4281   return bitmap->getHeight();
4282 }
4283 
4284 SplashBitmap *SplashOutputDev::takeBitmap() {
4285   SplashBitmap *ret;
4286 
4287   ret = bitmap;
4288   bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
4289 			    colorMode != splashModeMono1, bitmapTopDown, NULL);
4290   return ret;
4291 }
4292 
4293 void SplashOutputDev::getModRegion(int *xMin, int *yMin,
4294 				   int *xMax, int *yMax) {
4295   splash->getModRegion(xMin, yMin, xMax, yMax);
4296 }
4297 
4298 void SplashOutputDev::clearModRegion() {
4299   splash->clearModRegion();
4300 }
4301 
4302 void SplashOutputDev::setFillColor(int r, int g, int b) {
4303   GfxRGB rgb;
4304   GfxGray gray;
4305 #if SPLASH_CMYK
4306   GfxCMYK cmyk;
4307 #endif
4308 
4309   rgb.r = byteToCol((Guchar)r);
4310   rgb.g = byteToCol((Guchar)g);
4311   rgb.b = byteToCol((Guchar)b);
4312   switch (colorMode) {
4313   case splashModeMono1:
4314   case splashModeMono8:
4315     gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5);
4316     if (gray > gfxColorComp1) {
4317       gray = gfxColorComp1;
4318     }
4319     splash->setFillPattern(getColor(gray));
4320     break;
4321   case splashModeRGB8:
4322   case splashModeBGR8:
4323     splash->setFillPattern(getColor(&rgb));
4324     break;
4325 #if SPLASH_CMYK
4326   case splashModeCMYK8:
4327     cmyk.c = gfxColorComp1 - rgb.r;
4328     cmyk.m = gfxColorComp1 - rgb.g;
4329     cmyk.y = gfxColorComp1 - rgb.b;
4330     cmyk.k = 0;
4331     splash->setFillPattern(getColor(&cmyk));
4332     break;
4333 #endif
4334   }
4335 }
4336 
4337 SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) {
4338   Ref ref;
4339   SplashOutFontFileID *id;
4340   GfxFontLoc *fontLoc;
4341 #if LOAD_FONTS_FROM_MEM
4342   GString *fontBuf;
4343   FILE *extFontFile;
4344   char blk[4096];
4345   int n;
4346 #endif
4347   SplashFontFile *fontFile;
4348   SplashFont *fontObj;
4349   FoFiTrueType *ff;
4350   int *codeToGID;
4351   Unicode u;
4352   SplashCoord textMat[4];
4353   SplashCoord oblique;
4354   int cmap, cmapPlatform, cmapEncoding, i;
4355 
4356   for (i = 0; i < nBuiltinFonts; ++i) {
4357     if (!name->cmp(builtinFonts[i].name)) {
4358       break;
4359     }
4360   }
4361   if (i == nBuiltinFonts) {
4362     return NULL;
4363   }
4364   ref.num = i;
4365   ref.gen = -1;
4366   id = new SplashOutFontFileID(&ref);
4367 
4368   // check the font file cache
4369   if ((fontFile = fontEngine->getFontFile(id))) {
4370     delete id;
4371 
4372   // load the font file
4373   } else {
4374     if (!(fontLoc = GfxFont::locateBase14Font(name))) {
4375       return NULL;
4376     }
4377 #if LOAD_FONTS_FROM_MEM
4378     fontBuf = NULL;
4379     if (fontLoc->fontType == fontType1 ||
4380 	fontLoc->fontType == fontTrueType) {
4381       if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) {
4382 	delete fontLoc;
4383 	delete id;
4384 	return NULL;
4385       }
4386       fontBuf = new GString();
4387       while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) {
4388 	fontBuf->append(blk, n);
4389       }
4390       fclose(extFontFile);
4391     }
4392 #endif
4393     if (fontLoc->fontType == fontType1) {
4394       fontFile = fontEngine->loadType1Font(id,
4395 #if LOAD_FONTS_FROM_MEM
4396 					   fontBuf,
4397 #else
4398 					   fontLoc->path->getCString(),
4399 					   gFalse,
4400 #endif
4401 					   winAnsiEncoding);
4402     } else if (fontLoc->fontType == fontTrueType) {
4403 #if LOAD_FONTS_FROM_MEM
4404       if (!(ff = FoFiTrueType::make(fontBuf->getCString(),
4405 				    fontBuf->getLength(),
4406 				    fontLoc->fontNum))) {
4407 #else
4408       if (!(ff = FoFiTrueType::load(fontLoc->path->getCString(),
4409 				    fontLoc->fontNum))) {
4410 #endif
4411 	delete fontLoc;
4412 	delete id;
4413 	return NULL;
4414       }
4415       for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
4416 	cmapPlatform = ff->getCmapPlatform(cmap);
4417 	cmapEncoding = ff->getCmapEncoding(cmap);
4418 	if ((cmapPlatform == 3 && cmapEncoding == 1) ||
4419 	    (cmapPlatform == 0 && cmapEncoding <= 4)) {
4420 	  break;
4421 	}
4422       }
4423       if (cmap == ff->getNumCmaps()) {
4424 	delete ff;
4425 	delete fontLoc;
4426 	delete id;
4427 	return NULL;
4428       }
4429       codeToGID = (int *)gmallocn(256, sizeof(int));
4430       for (i = 0; i < 256; ++i) {
4431 	codeToGID[i] = 0;
4432 	if (winAnsiEncoding[i] &&
4433 	    (u = globalParams->mapNameToUnicode(winAnsiEncoding[i]))) {
4434 	  codeToGID[i] = ff->mapCodeToGID(cmap, u);
4435 	}
4436       }
4437       delete ff;
4438       fontFile = fontEngine->loadTrueTypeFont(id,
4439 #if LOAD_FONTS_FROM_MEM
4440 					      fontBuf,
4441 #else
4442 					      fontLoc->path->getCString(),
4443 					      gFalse,
4444 #endif
4445 					      fontLoc->fontNum,
4446 					      codeToGID, 256, NULL);
4447     } else {
4448       delete fontLoc;
4449       delete id;
4450       return NULL;
4451     }
4452     delete fontLoc;
4453   }
4454   if (!fontFile) {
4455     return NULL;
4456   }
4457 
4458   // create the scaled font
4459   oblique = (SplashCoord)
4460               ((SplashOutFontFileID *)fontFile->getID())->getOblique();
4461   textMat[0] = (SplashCoord)textMatA[0];
4462   textMat[1] = (SplashCoord)textMatA[1];
4463   textMat[2] = oblique * textMatA[0] + textMatA[2];
4464   textMat[3] = oblique * textMatA[1] + textMatA[3];
4465   fontObj = fontEngine->getFont(fontFile, textMat, splash->getMatrix());
4466 
4467   return fontObj;
4468 }
4469 
4470 // This is called when initializing a temporary Splash object for Type
4471 // 3 characters and transparency groups.  Acrobat apparently copies at
4472 // least the fill and stroke colors, and the line parameters.
4473 //~ not sure what else should be copied -- the PDF spec is unclear
4474 //~   - fill and stroke alpha?
4475 void SplashOutputDev::copyState(Splash *oldSplash, GBool copyColors) {
4476   // cached Type 3 chars set a color, so no need to copy the color here
4477   if (copyColors) {
4478     splash->setFillPattern(oldSplash->getFillPattern()->copy());
4479     splash->setStrokePattern(oldSplash->getStrokePattern()->copy());
4480   }
4481   splash->setLineDash(oldSplash->getLineDash(),
4482 		      oldSplash->getLineDashLength(),
4483 		      oldSplash->getLineDashPhase());
4484   splash->setLineCap(oldSplash->getLineCap());
4485   splash->setLineJoin(oldSplash->getLineJoin());
4486   splash->setLineWidth(oldSplash->getLineWidth());
4487 }
4488 
4489 #if 1 //~tmp: turn off anti-aliasing temporarily
4490 // This was originally used with gradient shadings -- that's no longer
4491 // necessary, now that shadings are all converted to device space
4492 // images.  It's still used with knockout groups, however, because the
4493 // rasterizer doesn't properly separate opacity and shape.
4494 void SplashOutputDev::setInShading(GBool sh) {
4495   splash->setInShading(sh);
4496 }
4497 #endif
4498