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