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