]> git.ipfire.org Git - thirdparty/cups.git/blob - pdftops/SplashOutputDev.cxx
Load cups into easysw/current.
[thirdparty/cups.git] / pdftops / SplashOutputDev.cxx
1 //========================================================================
2 //
3 // SplashOutputDev.cc
4 //
5 // Copyright 2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <config.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include <string.h>
16 #include <math.h>
17 #include "gfile.h"
18 #include "GlobalParams.h"
19 #include "Error.h"
20 #include "Object.h"
21 #include "GfxFont.h"
22 #include "Link.h"
23 #include "CharCodeToUnicode.h"
24 #include "FontEncodingTables.h"
25 #include "FoFiTrueType.h"
26 #include "SplashBitmap.h"
27 #include "SplashGlyphBitmap.h"
28 #include "SplashPattern.h"
29 #include "SplashScreen.h"
30 #include "SplashPath.h"
31 #include "SplashState.h"
32 #include "SplashErrorCodes.h"
33 #include "SplashFontEngine.h"
34 #include "SplashFont.h"
35 #include "SplashFontFile.h"
36 #include "SplashFontFileID.h"
37 #include "Splash.h"
38 #include "SplashOutputDev.h"
39
40 //------------------------------------------------------------------------
41 // Blend functions
42 //------------------------------------------------------------------------
43
44 static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest,
45 SplashColorPtr blend, SplashColorMode cm) {
46 int i;
47
48 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
49 // note: floor(x / 255) = x >> 8 (for 16-bit x)
50 blend[i] = (dest[i] * src[i]) >> 8;
51 }
52 }
53
54 static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest,
55 SplashColorPtr blend, SplashColorMode cm) {
56 int i;
57
58 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
59 // note: floor(x / 255) = x >> 8 (for 16-bit x)
60 blend[i] = dest[i] + src[i] - ((dest[i] * src[i]) >> 8);
61 }
62 }
63
64 static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest,
65 SplashColorPtr blend, SplashColorMode cm) {
66 int i;
67
68 //~ not sure if this is right
69 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
70 // note: floor(x / 255) = x >> 8 (for 16-bit x)
71 blend[i] = dest[i] < 0x80 ? ((dest[i] * src[i]) >> 8)
72 : dest[i] + src[i] - ((dest[i] * src[i]) >> 8);
73 }
74 }
75
76 static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest,
77 SplashColorPtr blend, SplashColorMode cm) {
78 int i;
79
80 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
81 blend[i] = dest[i] < src[i] ? dest[i] : src[i];
82 }
83 }
84
85 static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest,
86 SplashColorPtr blend, SplashColorMode cm) {
87 int i;
88
89 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
90 blend[i] = dest[i] > src[i] ? dest[i] : src[i];
91 }
92 }
93
94 static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest,
95 SplashColorPtr blend,
96 SplashColorMode cm) {
97 int i, x;
98
99 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
100 x = dest[i] + src[i];
101 blend[i] = x <= 255 ? x : 255;
102 }
103 }
104
105 static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest,
106 SplashColorPtr blend, SplashColorMode cm) {
107 int i, x;
108
109 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
110 x = dest[i] - (255 - src[i]);
111 blend[i] = x >= 0 ? x : 0;
112 }
113 }
114
115 static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest,
116 SplashColorPtr blend, SplashColorMode cm) {
117 int i;
118
119 //~ not sure if this is right
120 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
121 // note: floor(x / 255) = x >> 8 (for 16-bit x)
122 blend[i] = src[i] < 0x80
123 ? ((dest[i] * (src[i] * 2)) >> 8)
124 : 0xff - (((0xff - dest[i]) * (0x1ff - src[i] * 2)) >> 8);
125 }
126 }
127
128 static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest,
129 SplashColorPtr blend, SplashColorMode cm) {
130 int i, x;
131
132 //~ not sure if this is right
133 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
134 if (src[i] < 0x80) {
135 x = dest[i] - (0x80 - src[i]);
136 blend[i] = x >= 0 ? x : 0;
137 } else {
138 x = dest[i] + (src[i] - 0x80);
139 blend[i] = x <= 255 ? x : 255;
140 }
141 }
142 }
143
144 static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest,
145 SplashColorPtr blend,
146 SplashColorMode cm) {
147 int i;
148
149 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
150 blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i];
151 }
152 }
153
154 static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest,
155 SplashColorPtr blend, SplashColorMode cm) {
156 int i;
157
158 //~ not sure what this is supposed to do
159 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
160 blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i];
161 }
162 }
163
164 static void cvtRGBToHSV(Guchar r, Guchar g, Guchar b, int *h, int *s, int *v) {
165 int cmax, cmid, cmin, x;
166
167 if (r >= g) {
168 if (g >= b) { x = 0; cmax = r; cmid = g; cmin = b; }
169 else if (b >= r) { x = 4; cmax = b; cmid = r; cmin = g; }
170 else { x = 5; cmax = r; cmid = b; cmin = g; }
171 } else {
172 if (r >= b) { x = 1; cmax = g; cmid = r; cmin = b; }
173 else if (g >= b) { x = 2; cmax = g; cmid = b; cmin = r; }
174 else { x = 3; cmax = b; cmid = g; cmin = r; }
175 }
176 if (cmax == cmin) {
177 *h = *s = 0;
178 } else {
179 *h = x * 60;
180 if (x & 1) {
181 *h += ((cmax - cmid) * 60) / (cmax - cmin);
182 } else {
183 *h += ((cmid - cmin) * 60) / (cmax - cmin);
184 }
185 *s = (255 * (cmax - cmin)) / cmax;
186 }
187 *v = cmax;
188 }
189
190 static void cvtHSVToRGB(int h, int s, int v, Guchar *r, Guchar *g, Guchar *b) {
191 int x, f, cmax, cmid, cmin;
192
193 if (s == 0) {
194 *r = *g = *b = v;
195 } else {
196 x = h / 60;
197 f = h % 60;
198 cmax = v;
199 if (x & 1) {
200 cmid = (v * 255 - ((s * f) / 60)) >> 8;
201 } else {
202 cmid = (v * (255 - ((s * (60 - f)) / 60))) >> 8;
203 }
204 // note: floor(x / 255) = x >> 8 (for 16-bit x)
205 cmin = (v * (255 - s)) >> 8;
206 switch (x) {
207 case 0: *r = cmax; *g = cmid; *b = cmin; break;
208 case 1: *g = cmax; *r = cmid; *b = cmin; break;
209 case 2: *g = cmax; *b = cmid; *r = cmin; break;
210 case 3: *b = cmax; *g = cmid; *r = cmin; break;
211 case 4: *b = cmax; *r = cmid; *g = cmin; break;
212 case 5: *r = cmax; *b = cmid; *g = cmin; break;
213 }
214 }
215 }
216
217 static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
218 SplashColorPtr blend, SplashColorMode cm) {
219 int hs, ss, vs, hd, sd, vd;
220 #if SPLASH_CMYK
221 Guchar r, g, b;
222 #endif
223
224 switch (cm) {
225 case splashModeMono1:
226 case splashModeMono8:
227 blend[0] = dest[0];
228 break;
229 case splashModeRGB8:
230 cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
231 cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
232 cvtHSVToRGB(hs, sd, vd, &blend[0], &blend[1], &blend[2]);
233 break;
234 case splashModeBGR8:
235 cvtRGBToHSV(src[2], src[1], src[0], &hs, &ss, &vs);
236 cvtRGBToHSV(dest[2], dest[1], dest[0], &hd, &sd, &vd);
237 cvtHSVToRGB(hs, sd, vd, &blend[2], &blend[1], &blend[0]);
238 break;
239 #if SPLASH_CMYK
240 case splashModeCMYK8:
241 //~ (0xff - ...) should be clipped
242 cvtRGBToHSV(0xff - (src[0] + src[3]),
243 0xff - (src[1] + src[3]),
244 0xff - (src[2] + src[3]), &hs, &ss, &vs);
245 cvtRGBToHSV(0xff - (dest[0] + dest[3]),
246 0xff - (dest[1] + dest[3]),
247 0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
248 cvtHSVToRGB(hs, sd, vd, &r, &g, &b);
249 //~ should do black generation
250 blend[0] = 0xff - r;
251 blend[0] = 0xff - g;
252 blend[0] = 0xff - b;
253 blend[3] = 0;
254 break;
255 #endif
256 default:
257 //~ unimplemented
258 break;
259 }
260 }
261
262 static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
263 SplashColorPtr blend,
264 SplashColorMode cm) {
265 int hs, ss, vs, hd, sd, vd;
266 #if SPLASH_CMYK
267 Guchar r, g, b;
268 #endif
269
270 switch (cm) {
271 case splashModeMono1:
272 case splashModeMono8:
273 blend[0] = dest[0];
274 break;
275 case splashModeRGB8:
276 cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
277 cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
278 cvtHSVToRGB(hd, ss, vd, &blend[0], &blend[1], &blend[2]);
279 break;
280 case splashModeBGR8:
281 cvtRGBToHSV(src[2], src[1], src[0], &hs, &ss, &vs);
282 cvtRGBToHSV(dest[2], dest[1], dest[0], &hd, &sd, &vd);
283 cvtHSVToRGB(hd, ss, vd, &blend[2], &blend[1], &blend[0]);
284 break;
285 #if SPLASH_CMYK
286 case splashModeCMYK8:
287 //~ (0xff - ...) should be clipped
288 cvtRGBToHSV(0xff - (src[0] + src[3]),
289 0xff - (src[1] + src[3]),
290 0xff - (src[2] + src[3]), &hs, &ss, &vs);
291 cvtRGBToHSV(0xff - (dest[0] + dest[3]),
292 0xff - (dest[1] + dest[3]),
293 0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
294 cvtHSVToRGB(hd, ss, vd, &r, &g, &b);
295 //~ should do black generation
296 blend[0] = 0xff - r;
297 blend[0] = 0xff - g;
298 blend[0] = 0xff - b;
299 blend[3] = 0;
300 break;
301 #endif
302 default:
303 //~ unimplemented
304 break;
305 }
306 }
307
308 static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,
309 SplashColorPtr blend, SplashColorMode cm) {
310 int hs, ss, vs, hd, sd, vd;
311 #if SPLASH_CMYK
312 Guchar r, g, b;
313 #endif
314
315 switch (cm) {
316 case splashModeMono1:
317 case splashModeMono8:
318 blend[0] = dest[0];
319 break;
320 case splashModeRGB8:
321 cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
322 cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
323 cvtHSVToRGB(hs, ss, vd, &blend[0], &blend[1], &blend[2]);
324 break;
325 case splashModeBGR8:
326 cvtRGBToHSV(src[2], src[1], src[0], &hs, &ss, &vs);
327 cvtRGBToHSV(dest[2], dest[1], dest[0], &hd, &sd, &vd);
328 cvtHSVToRGB(hs, ss, vd, &blend[2], &blend[1], &blend[0]);
329 break;
330 #if SPLASH_CMYK
331 case splashModeCMYK8:
332 //~ (0xff - ...) should be clipped
333 cvtRGBToHSV(0xff - (src[0] + src[3]),
334 0xff - (src[1] + src[3]),
335 0xff - (src[2] + src[3]), &hs, &ss, &vs);
336 cvtRGBToHSV(0xff - (dest[0] + dest[3]),
337 0xff - (dest[1] + dest[3]),
338 0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
339 cvtHSVToRGB(hs, ss, vd, &r, &g, &b);
340 //~ should do black generation
341 blend[0] = 0xff - r;
342 blend[0] = 0xff - g;
343 blend[0] = 0xff - b;
344 blend[3] = 0;
345 break;
346 #endif
347 default:
348 //~ unimplemented
349 break;
350 }
351 }
352
353 static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,
354 SplashColorPtr blend,
355 SplashColorMode cm) {
356 int hs, ss, vs, hd, sd, vd;
357 #if SPLASH_CMYK
358 Guchar r, g, b;
359 #endif
360
361 switch (cm) {
362 case splashModeMono1:
363 case splashModeMono8:
364 blend[0] = dest[0];
365 break;
366 case splashModeRGB8:
367 cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
368 cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
369 cvtHSVToRGB(hd, sd, vs, &blend[0], &blend[1], &blend[2]);
370 break;
371 case splashModeBGR8:
372 cvtRGBToHSV(src[2], src[1], src[0], &hs, &ss, &vs);
373 cvtRGBToHSV(dest[2], dest[1], dest[0], &hd, &sd, &vd);
374 cvtHSVToRGB(hd, sd, vs, &blend[2], &blend[1], &blend[0]);
375 break;
376 #if SPLASH_CMYK
377 case splashModeCMYK8:
378 //~ (0xff - ...) should be clipped
379 cvtRGBToHSV(0xff - (src[0] + src[3]),
380 0xff - (src[1] + src[3]),
381 0xff - (src[2] + src[3]), &hs, &ss, &vs);
382 cvtRGBToHSV(0xff - (dest[0] + dest[3]),
383 0xff - (dest[1] + dest[3]),
384 0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
385 cvtHSVToRGB(hd, sd, vs, &r, &g, &b);
386 //~ should do black generation
387 blend[0] = 0xff - r;
388 blend[0] = 0xff - g;
389 blend[0] = 0xff - b;
390 blend[3] = 0;
391 break;
392 #endif
393 default:
394 //~ unimplemented
395 break;
396 }
397 }
398
399 // NB: This must match the GfxBlendMode enum defined in GfxState.h.
400 SplashBlendFunc splashOutBlendFuncs[] = {
401 NULL,
402 &splashOutBlendMultiply,
403 &splashOutBlendScreen,
404 &splashOutBlendOverlay,
405 &splashOutBlendDarken,
406 &splashOutBlendLighten,
407 &splashOutBlendColorDodge,
408 &splashOutBlendColorBurn,
409 &splashOutBlendHardLight,
410 &splashOutBlendSoftLight,
411 &splashOutBlendDifference,
412 &splashOutBlendExclusion,
413 &splashOutBlendHue,
414 &splashOutBlendSaturation,
415 &splashOutBlendColor,
416 &splashOutBlendLuminosity
417 };
418
419 //------------------------------------------------------------------------
420 // Font substitutions
421 //------------------------------------------------------------------------
422
423 struct SplashOutFontSubst {
424 char *name;
425 double mWidth;
426 };
427
428 // index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
429 static SplashOutFontSubst splashOutSubstFonts[16] = {
430 {"Helvetica", 0.833},
431 {"Helvetica-Oblique", 0.833},
432 {"Helvetica-Bold", 0.889},
433 {"Helvetica-BoldOblique", 0.889},
434 {"Times-Roman", 0.788},
435 {"Times-Italic", 0.722},
436 {"Times-Bold", 0.833},
437 {"Times-BoldItalic", 0.778},
438 {"Courier", 0.600},
439 {"Courier-Oblique", 0.600},
440 {"Courier-Bold", 0.600},
441 {"Courier-BoldOblique", 0.600},
442 {"Symbol", 0.576},
443 {"Symbol", 0.576},
444 {"Symbol", 0.576},
445 {"Symbol", 0.576}
446 };
447
448 //------------------------------------------------------------------------
449 // SplashOutFontFileID
450 //------------------------------------------------------------------------
451
452 class SplashOutFontFileID: public SplashFontFileID {
453 public:
454
455 SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; }
456
457 ~SplashOutFontFileID() {}
458
459 GBool matches(SplashFontFileID *id) {
460 return ((SplashOutFontFileID *)id)->r.num == r.num &&
461 ((SplashOutFontFileID *)id)->r.gen == r.gen;
462 }
463
464 void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
465 int getSubstIdx() { return substIdx; }
466
467 private:
468
469 Ref r;
470 int substIdx;
471 };
472
473 //------------------------------------------------------------------------
474 // T3FontCache
475 //------------------------------------------------------------------------
476
477 struct T3FontCacheTag {
478 Gushort code;
479 Gushort mru; // valid bit (0x8000) and MRU index
480 };
481
482 class T3FontCache {
483 public:
484
485 T3FontCache(Ref *fontID, double m11A, double m12A,
486 double m21A, double m22A,
487 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
488 GBool aa);
489 ~T3FontCache();
490 GBool matches(Ref *idA, double m11A, double m12A,
491 double m21A, double m22A)
492 { return fontID.num == idA->num && fontID.gen == idA->gen &&
493 m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
494
495 Ref fontID; // PDF font ID
496 double m11, m12, m21, m22; // transform matrix
497 int glyphX, glyphY; // pixel offset of glyph bitmaps
498 int glyphW, glyphH; // size of glyph bitmaps, in pixels
499 int glyphSize; // size of glyph bitmaps, in bytes
500 int cacheSets; // number of sets in cache
501 int cacheAssoc; // cache associativity (glyphs per set)
502 Guchar *cacheData; // glyph pixmap cache
503 T3FontCacheTag *cacheTags; // cache tags, i.e., char codes
504 };
505
506 T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
507 double m21A, double m22A,
508 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
509 GBool aa) {
510 int i;
511
512 fontID = *fontIDA;
513 m11 = m11A;
514 m12 = m12A;
515 m21 = m21A;
516 m22 = m22A;
517 glyphX = glyphXA;
518 glyphY = glyphYA;
519 glyphW = glyphWA;
520 glyphH = glyphHA;
521 if (aa) {
522 glyphSize = glyphW * glyphH;
523 } else {
524 glyphSize = ((glyphW + 7) >> 3) * glyphH;
525 }
526 cacheAssoc = 8;
527 if (glyphSize <= 256) {
528 cacheSets = 8;
529 } else if (glyphSize <= 512) {
530 cacheSets = 4;
531 } else if (glyphSize <= 1024) {
532 cacheSets = 2;
533 } else {
534 cacheSets = 1;
535 }
536 cacheData = (Guchar *)gmallocn(cacheSets * cacheAssoc, glyphSize);
537 cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc,
538 sizeof(T3FontCacheTag));
539 for (i = 0; i < cacheSets * cacheAssoc; ++i) {
540 cacheTags[i].mru = i & (cacheAssoc - 1);
541 }
542 }
543
544 T3FontCache::~T3FontCache() {
545 gfree(cacheData);
546 gfree(cacheTags);
547 }
548
549 struct T3GlyphStack {
550 Gushort code; // character code
551 double x, y; // position to draw the glyph
552
553 //----- cache info
554 T3FontCache *cache; // font cache for the current font
555 T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph
556 Guchar *cacheData; // pointer to cache data for the glyph
557
558 //----- saved state
559 SplashBitmap *origBitmap;
560 Splash *origSplash;
561 double origCTM4, origCTM5;
562
563 T3GlyphStack *next; // next object on stack
564 };
565
566 //------------------------------------------------------------------------
567 // SplashOutputDev
568 //------------------------------------------------------------------------
569
570 SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
571 int bitmapRowPadA,
572 GBool reverseVideoA,
573 SplashColorPtr paperColorA,
574 GBool bitmapTopDownA,
575 GBool allowAntialiasA) {
576 colorMode = colorModeA;
577 bitmapRowPad = bitmapRowPadA;
578 bitmapTopDown = bitmapTopDownA;
579 allowAntialias = allowAntialiasA;
580 reverseVideo = reverseVideoA;
581 splashColorCopy(paperColor, paperColorA);
582
583 xref = NULL;
584
585 bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, bitmapTopDown);
586 splash = new Splash(bitmap);
587 splash->clear(paperColor);
588
589 fontEngine = NULL;
590
591 nT3Fonts = 0;
592 t3GlyphStack = NULL;
593
594 font = NULL;
595 needFontUpdate = gFalse;
596 textClipPath = NULL;
597 }
598
599 SplashOutputDev::~SplashOutputDev() {
600 int i;
601
602 for (i = 0; i < nT3Fonts; ++i) {
603 delete t3FontCache[i];
604 }
605 if (fontEngine) {
606 delete fontEngine;
607 }
608 if (splash) {
609 delete splash;
610 }
611 if (bitmap) {
612 delete bitmap;
613 }
614 }
615
616 void SplashOutputDev::startDoc(XRef *xrefA) {
617 int i;
618
619 xref = xrefA;
620 if (fontEngine) {
621 delete fontEngine;
622 }
623 fontEngine = new SplashFontEngine(
624 #if HAVE_T1LIB_H
625 globalParams->getEnableT1lib(),
626 #endif
627 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
628 globalParams->getEnableFreeType(),
629 #endif
630 allowAntialias &&
631 globalParams->getAntialias() &&
632 colorMode != splashModeMono1);
633 for (i = 0; i < nT3Fonts; ++i) {
634 delete t3FontCache[i];
635 }
636 nT3Fonts = 0;
637 }
638
639 GBool SplashOutputDev::startPage(int pageNum, GfxState *state) {
640 int w, h;
641 SplashColor color;
642
643 w = state ? (int)(state->getPageWidth() + 0.5) : 1;
644 h = state ? (int)(state->getPageHeight() + 0.5) : 1;
645 if (splash) {
646 delete splash;
647 }
648 if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
649 if (bitmap) {
650 delete bitmap;
651 }
652 bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, bitmapTopDown);
653 }
654 splash = new Splash(bitmap);
655 switch (colorMode) {
656 case splashModeMono1:
657 case splashModeMono8:
658 color[0] = 0;
659 break;
660 case splashModeRGB8:
661 case splashModeBGR8:
662 color[0] = color[1] = color[2] = 0;
663 break;
664 case splashModeAMono8:
665 color[0] = 0xff;
666 color[1] = 0;
667 break;
668 case splashModeARGB8:
669 color[0] = 255;
670 color[1] = color[2] = color[3] = 0;
671 break;
672 case splashModeBGRA8:
673 color[0] = color[1] = color[2] = 0;
674 color[3] = 255;
675 break;
676 #if SPLASH_CMYK
677 case splashModeCMYK8:
678 color[0] = color[1] = color[2] = color[3] = 0;
679 break;
680 case splashModeACMYK8:
681 color[0] = 255;
682 color[1] = color[2] = color[3] = color[4] = 0;
683 break;
684 #endif
685 }
686 splash->setStrokePattern(new SplashSolidColor(color));
687 splash->setFillPattern(new SplashSolidColor(color));
688 splash->setLineCap(splashLineCapButt);
689 splash->setLineJoin(splashLineJoinMiter);
690 splash->setLineDash(NULL, 0, 0);
691 splash->setMiterLimit(10);
692 splash->setFlatness(1);
693 splash->clear(paperColor);
694
695 return (gTrue);
696 }
697
698 void SplashOutputDev::endPage() {
699 }
700
701 void SplashOutputDev::drawLink(Link *link, Catalog *catalog) {
702 double x1, y1, x2, y2;
703 LinkBorderStyle *borderStyle;
704 double r, g, b;
705 GfxRGB rgb;
706 GfxGray gray;
707 #if SPLASH_CMYK
708 GfxCMYK cmyk;
709 #endif
710 double *dash;
711 int dashLength;
712 SplashCoord dashList[20];
713 SplashPath *path;
714 int x, y, i;
715
716 link->getRect(&x1, &y1, &x2, &y2);
717 borderStyle = link->getBorderStyle();
718 if (borderStyle->getWidth() > 0) {
719 borderStyle->getColor(&r, &g, &b);
720 rgb.r = dblToCol(r);
721 rgb.g = dblToCol(g);
722 rgb.b = dblToCol(b);
723 gray = dblToCol(0.299 * r + 0.587 * g + 0.114 * b);
724 if (gray > gfxColorComp1) {
725 gray = gfxColorComp1;
726 }
727 #if SPLASH_CMYK
728 cmyk.c = gfxColorComp1 - rgb.r;
729 cmyk.m = gfxColorComp1 - rgb.g;
730 cmyk.y = gfxColorComp1 - rgb.b;
731 cmyk.k = 0;
732 splash->setStrokePattern(getColor(gray, &rgb, &cmyk));
733 #else
734 splash->setStrokePattern(getColor(gray, &rgb));
735 #endif
736 splash->setLineWidth((SplashCoord)borderStyle->getWidth());
737 borderStyle->getDash(&dash, &dashLength);
738 if (borderStyle->getType() == linkBorderDashed && dashLength > 0) {
739 if (dashLength > 20) {
740 dashLength = 20;
741 }
742 for (i = 0; i < dashLength; ++i) {
743 dashList[i] = (SplashCoord)dash[i];
744 }
745 splash->setLineDash(dashList, dashLength, 0);
746 }
747 path = new SplashPath();
748 if (borderStyle->getType() == linkBorderUnderlined) {
749 cvtUserToDev(x1, y1, &x, &y);
750 path->moveTo((SplashCoord)x, (SplashCoord)y);
751 cvtUserToDev(x2, y1, &x, &y);
752 path->lineTo((SplashCoord)x, (SplashCoord)y);
753 } else {
754 cvtUserToDev(x1, y1, &x, &y);
755 path->moveTo((SplashCoord)x, (SplashCoord)y);
756 cvtUserToDev(x2, y1, &x, &y);
757 path->lineTo((SplashCoord)x, (SplashCoord)y);
758 cvtUserToDev(x2, y2, &x, &y);
759 path->lineTo((SplashCoord)x, (SplashCoord)y);
760 cvtUserToDev(x1, y2, &x, &y);
761 path->lineTo((SplashCoord)x, (SplashCoord)y);
762 path->close();
763 }
764 splash->stroke(path);
765 delete path;
766 }
767 }
768
769 void SplashOutputDev::saveState(GfxState *state) {
770 splash->saveState();
771 }
772
773 void SplashOutputDev::restoreState(GfxState *state) {
774 splash->restoreState();
775 needFontUpdate = gTrue;
776 }
777
778 void SplashOutputDev::updateAll(GfxState *state) {
779 updateLineDash(state);
780 updateLineJoin(state);
781 updateLineCap(state);
782 updateLineWidth(state);
783 updateFlatness(state);
784 updateMiterLimit(state);
785 updateFillColor(state);
786 updateStrokeColor(state);
787 needFontUpdate = gTrue;
788 }
789
790 void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
791 double m21, double m22,
792 double m31, double m32) {
793 updateLineDash(state);
794 updateLineJoin(state);
795 updateLineCap(state);
796 updateLineWidth(state);
797 }
798
799 void SplashOutputDev::updateLineDash(GfxState *state) {
800 double *dashPattern;
801 int dashLength;
802 double dashStart;
803 SplashCoord dash[20];
804 SplashCoord phase;
805 int i;
806
807 state->getLineDash(&dashPattern, &dashLength, &dashStart);
808 if (dashLength > 20) {
809 dashLength = 20;
810 }
811 for (i = 0; i < dashLength; ++i) {
812 dash[i] = (SplashCoord)state->transformWidth(dashPattern[i]);
813 if (dash[i] < 1) {
814 dash[i] = 1;
815 }
816 }
817 phase = (SplashCoord)state->transformWidth(dashStart);
818 splash->setLineDash(dash, dashLength, phase);
819 }
820
821 void SplashOutputDev::updateFlatness(GfxState *state) {
822 splash->setFlatness(state->getFlatness());
823 }
824
825 void SplashOutputDev::updateLineJoin(GfxState *state) {
826 splash->setLineJoin(state->getLineJoin());
827 }
828
829 void SplashOutputDev::updateLineCap(GfxState *state) {
830 splash->setLineCap(state->getLineCap());
831 }
832
833 void SplashOutputDev::updateMiterLimit(GfxState *state) {
834 splash->setMiterLimit(state->getMiterLimit());
835 }
836
837 void SplashOutputDev::updateLineWidth(GfxState *state) {
838 splash->setLineWidth(state->getTransformedLineWidth());
839 }
840
841 void SplashOutputDev::updateFillColor(GfxState *state) {
842 GfxGray gray;
843 GfxRGB rgb;
844 #if SPLASH_CMYK
845 GfxCMYK cmyk;
846 #endif
847
848 state->getFillGray(&gray);
849 state->getFillRGB(&rgb);
850 #if SPLASH_CMYK
851 state->getFillCMYK(&cmyk);
852 splash->setFillPattern(getColor(gray, &rgb, &cmyk));
853 #else
854 splash->setFillPattern(getColor(gray, &rgb));
855 #endif
856 }
857
858 void SplashOutputDev::updateStrokeColor(GfxState *state) {
859 GfxGray gray;
860 GfxRGB rgb;
861 #if SPLASH_CMYK
862 GfxCMYK cmyk;
863 #endif
864
865 state->getStrokeGray(&gray);
866 state->getStrokeRGB(&rgb);
867 #if SPLASH_CMYK
868 state->getStrokeCMYK(&cmyk);
869 splash->setStrokePattern(getColor(gray, &rgb, &cmyk));
870 #else
871 splash->setStrokePattern(getColor(gray, &rgb));
872 #endif
873 }
874
875 #if SPLASH_CMYK
876 SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb,
877 GfxCMYK *cmyk) {
878 #else
879 SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb) {
880 #endif
881 SplashPattern *pattern;
882 SplashColor color0, color1;
883 GfxColorComp r, g, b;
884
885 if (reverseVideo) {
886 gray = gfxColorComp1 - gray;
887 r = gfxColorComp1 - rgb->r;
888 g = gfxColorComp1 - rgb->g;
889 b = gfxColorComp1 - rgb->b;
890 } else {
891 r = rgb->r;
892 g = rgb->g;
893 b = rgb->b;
894 }
895
896 pattern = NULL; // make gcc happy
897 switch (colorMode) {
898 case splashModeMono1:
899 color0[0] = 0;
900 color1[0] = 1;
901 pattern = new SplashHalftone(color0, color1,
902 splash->getScreen()->copy(),
903 (SplashCoord)colToDbl(gray));
904 break;
905 case splashModeMono8:
906 color1[0] = colToByte(gray);
907 pattern = new SplashSolidColor(color1);
908 break;
909 case splashModeAMono8:
910 color1[0] = 255;
911 color1[1] = colToByte(gray);
912 pattern = new SplashSolidColor(color1);
913 break;
914 case splashModeRGB8:
915 color1[0] = colToByte(r);
916 color1[1] = colToByte(g);
917 color1[2] = colToByte(b);
918 pattern = new SplashSolidColor(color1);
919 break;
920 case splashModeBGR8:
921 color1[2] = colToByte(r);
922 color1[1] = colToByte(g);
923 color1[0] = colToByte(b);
924 pattern = new SplashSolidColor(color1);
925 break;
926 case splashModeARGB8:
927 color1[0] = 255;
928 color1[1] = colToByte(r);
929 color1[2] = colToByte(g);
930 color1[3] = colToByte(b);
931 pattern = new SplashSolidColor(color1);
932 break;
933 case splashModeBGRA8:
934 color1[3] = 255;
935 color1[2] = colToByte(r);
936 color1[1] = colToByte(g);
937 color1[0] = colToByte(b);
938 pattern = new SplashSolidColor(color1);
939 break;
940 #if SPLASH_CMYK
941 case splashModeCMYK8:
942 color1[0] = colToByte(cmyk->c);
943 color1[1] = colToByte(cmyk->m);
944 color1[2] = colToByte(cmyk->y);
945 color1[3] = colToByte(cmyk->k);
946 pattern = new SplashSolidColor(color1);
947 break;
948 case splashModeACMYK8:
949 color1[0] = 255;
950 color1[1] = colToByte(cmyk->c);
951 color1[2] = colToByte(cmyk->m);
952 color1[3] = colToByte(cmyk->y);
953 color1[4] = colToByte(cmyk->k);
954 pattern = new SplashSolidColor(color1);
955 break;
956 #endif
957 }
958
959 return pattern;
960 }
961
962 void SplashOutputDev::updateBlendMode(GfxState *state) {
963 splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]);
964 }
965
966 void SplashOutputDev::updateFillOpacity(GfxState *state) {
967 splash->setFillAlpha((SplashCoord)state->getFillOpacity());
968 }
969
970 void SplashOutputDev::updateStrokeOpacity(GfxState *state) {
971 splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity());
972 }
973
974 void SplashOutputDev::updateFont(GfxState *state) {
975 GfxFont *gfxFont;
976 GfxFontType fontType;
977 SplashOutFontFileID *id;
978 SplashFontFile *fontFile;
979 FoFiTrueType *ff;
980 Ref embRef;
981 Object refObj, strObj;
982 GString *tmpFileName, *fileName, *substName;
983 FILE *tmpFile;
984 Gushort *codeToGID;
985 DisplayFontParam *dfp;
986 CharCodeToUnicode *ctu;
987 double m11, m12, m21, m22, w1, w2;
988 SplashCoord mat[4];
989 char *name;
990 Unicode uBuf[8];
991 int c, substIdx, n, code, cmap;
992
993 needFontUpdate = gFalse;
994 font = NULL;
995 tmpFileName = NULL;
996 substIdx = -1;
997 dfp = NULL;
998
999 if (!(gfxFont = state->getFont())) {
1000 goto err1;
1001 }
1002 fontType = gfxFont->getType();
1003 if (fontType == fontType3) {
1004 goto err1;
1005 }
1006
1007 // check the font file cache
1008 id = new SplashOutFontFileID(gfxFont->getID());
1009 if ((fontFile = fontEngine->getFontFile(id))) {
1010 delete id;
1011
1012 } else {
1013
1014 // if there is an embedded font, write it to disk
1015 if (gfxFont->getEmbeddedFontID(&embRef)) {
1016 if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
1017 error(-1, "Couldn't create temporary font file");
1018 goto err2;
1019 }
1020 refObj.initRef(embRef.num, embRef.gen);
1021 refObj.fetch(xref, &strObj);
1022 refObj.free();
1023 strObj.streamReset();
1024 while ((c = strObj.streamGetChar()) != EOF) {
1025 fputc(c, tmpFile);
1026 }
1027 strObj.streamClose();
1028 strObj.free();
1029 fclose(tmpFile);
1030 fileName = tmpFileName;
1031
1032 // if there is an external font file, use it
1033 } else if (!(fileName = gfxFont->getExtFontFile())) {
1034
1035 // look for a display font mapping or a substitute font
1036 if (gfxFont->isCIDFont()) {
1037 if (((GfxCIDFont *)gfxFont)->getCollection()) {
1038 dfp = globalParams->
1039 getDisplayCIDFont(gfxFont->getName(),
1040 ((GfxCIDFont *)gfxFont)->getCollection());
1041 }
1042 } else {
1043 if (gfxFont->getName()) {
1044 dfp = globalParams->getDisplayFont(gfxFont->getName());
1045 }
1046 if (!dfp) {
1047 // 8-bit font substitution
1048 if (gfxFont->isFixedWidth()) {
1049 substIdx = 8;
1050 } else if (gfxFont->isSerif()) {
1051 substIdx = 4;
1052 } else {
1053 substIdx = 0;
1054 }
1055 if (gfxFont->isBold()) {
1056 substIdx += 2;
1057 }
1058 if (gfxFont->isItalic()) {
1059 substIdx += 1;
1060 }
1061 substName = new GString(splashOutSubstFonts[substIdx].name);
1062 dfp = globalParams->getDisplayFont(substName);
1063 delete substName;
1064 id->setSubstIdx(substIdx);
1065 }
1066 }
1067 if (!dfp) {
1068 error(-1, "Couldn't find a font for '%s'",
1069 gfxFont->getName() ? gfxFont->getName()->getCString()
1070 : "(unnamed)");
1071 goto err2;
1072 }
1073 switch (dfp->kind) {
1074 case displayFontT1:
1075 fileName = dfp->t1.fileName;
1076 fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1;
1077 break;
1078 case displayFontTT:
1079 fileName = dfp->tt.fileName;
1080 fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType;
1081 break;
1082 }
1083 }
1084
1085 // load the font file
1086 switch (fontType) {
1087 case fontType1:
1088 if (!(fontFile = fontEngine->loadType1Font(
1089 id,
1090 fileName->getCString(),
1091 fileName == tmpFileName,
1092 ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1093 error(-1, "Couldn't create a font for '%s'",
1094 gfxFont->getName() ? gfxFont->getName()->getCString()
1095 : "(unnamed)");
1096 goto err2;
1097 }
1098 break;
1099 case fontType1C:
1100 if (!(fontFile = fontEngine->loadType1CFont(
1101 id,
1102 fileName->getCString(),
1103 fileName == tmpFileName,
1104 ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1105 error(-1, "Couldn't create a font for '%s'",
1106 gfxFont->getName() ? gfxFont->getName()->getCString()
1107 : "(unnamed)");
1108 goto err2;
1109 }
1110 break;
1111 case fontTrueType:
1112 if ((ff = FoFiTrueType::load(fileName->getCString()))) {
1113 codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
1114 n = 256;
1115 delete ff;
1116 } else {
1117 codeToGID = NULL;
1118 n = 0;
1119 }
1120 if (!(fontFile = fontEngine->loadTrueTypeFont(
1121 id,
1122 fileName->getCString(),
1123 fileName == tmpFileName,
1124 codeToGID, n))) {
1125 error(-1, "Couldn't create a font for '%s'",
1126 gfxFont->getName() ? gfxFont->getName()->getCString()
1127 : "(unnamed)");
1128 goto err2;
1129 }
1130 break;
1131 case fontCIDType0:
1132 case fontCIDType0C:
1133 if (!(fontFile = fontEngine->loadCIDFont(
1134 id,
1135 fileName->getCString(),
1136 fileName == tmpFileName))) {
1137 error(-1, "Couldn't create a font for '%s'",
1138 gfxFont->getName() ? gfxFont->getName()->getCString()
1139 : "(unnamed)");
1140 goto err2;
1141 }
1142 break;
1143 case fontCIDType2:
1144 codeToGID = NULL;
1145 n = 0;
1146 if (dfp) {
1147 // create a CID-to-GID mapping, via Unicode
1148 if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
1149 if ((ff = FoFiTrueType::load(fileName->getCString()))) {
1150 // look for a Unicode cmap
1151 for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
1152 if ((ff->getCmapPlatform(cmap) == 3 &&
1153 ff->getCmapEncoding(cmap) == 1) ||
1154 ff->getCmapPlatform(cmap) == 0) {
1155 break;
1156 }
1157 }
1158 if (cmap < ff->getNumCmaps()) {
1159 // map CID -> Unicode -> GID
1160 n = ctu->getLength();
1161 codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort));
1162 for (code = 0; code < n; ++code) {
1163 if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
1164 codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]);
1165 } else {
1166 codeToGID[code] = 0;
1167 }
1168 }
1169 }
1170 delete ff;
1171 }
1172 ctu->decRefCnt();
1173 } else {
1174 error(-1, "Couldn't find a mapping to Unicode for font '%s'",
1175 gfxFont->getName() ? gfxFont->getName()->getCString()
1176 : "(unnamed)");
1177 }
1178 } else {
1179 if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
1180 n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
1181 codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort));
1182 memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1183 n * sizeof(Gushort));
1184 }
1185 }
1186 if (!(fontFile = fontEngine->loadTrueTypeFont(
1187 id,
1188 fileName->getCString(),
1189 fileName == tmpFileName,
1190 codeToGID, n))) {
1191 error(-1, "Couldn't create a font for '%s'",
1192 gfxFont->getName() ? gfxFont->getName()->getCString()
1193 : "(unnamed)");
1194 goto err2;
1195 }
1196 break;
1197 default:
1198 // this shouldn't happen
1199 goto err2;
1200 }
1201 }
1202
1203 // get the font matrix
1204 state->getFontTransMat(&m11, &m12, &m21, &m22);
1205 m11 *= state->getHorizScaling();
1206 m12 *= state->getHorizScaling();
1207
1208 // for substituted fonts: adjust the font matrix -- compare the
1209 // width of 'm' in the original font and the substituted font
1210 substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx();
1211 if (substIdx >= 0) {
1212 for (code = 0; code < 256; ++code) {
1213 if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
1214 name[0] == 'm' && name[1] == '\0') {
1215 break;
1216 }
1217 }
1218 if (code < 256) {
1219 w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
1220 w2 = splashOutSubstFonts[substIdx].mWidth;
1221 if (!gfxFont->isSymbolic()) {
1222 // if real font is substantially narrower than substituted
1223 // font, reduce the font size accordingly
1224 if (w1 > 0.01 && w1 < 0.9 * w2) {
1225 w1 /= w2;
1226 m11 *= w1;
1227 m21 *= w1;
1228 }
1229 }
1230 }
1231 }
1232
1233 // create the scaled font
1234 mat[0] = m11; mat[1] = -m12;
1235 mat[2] = m21; mat[3] = -m22;
1236 if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.01) {
1237 // avoid a singular (or close-to-singular) matrix
1238 mat[0] = 0.01; mat[1] = 0;
1239 mat[2] = 0; mat[3] = 0.01;
1240 }
1241 font = fontEngine->getFont(fontFile, mat);
1242
1243 if (tmpFileName) {
1244 delete tmpFileName;
1245 }
1246 return;
1247
1248 err2:
1249 delete id;
1250 err1:
1251 if (tmpFileName) {
1252 delete tmpFileName;
1253 }
1254 return;
1255 }
1256
1257 void SplashOutputDev::stroke(GfxState *state) {
1258 SplashPath *path;
1259
1260 path = convertPath(state, state->getPath());
1261 splash->stroke(path);
1262 delete path;
1263 }
1264
1265 void SplashOutputDev::fill(GfxState *state) {
1266 SplashPath *path;
1267
1268 path = convertPath(state, state->getPath());
1269 splash->fill(path, gFalse);
1270 delete path;
1271 }
1272
1273 void SplashOutputDev::eoFill(GfxState *state) {
1274 SplashPath *path;
1275
1276 path = convertPath(state, state->getPath());
1277 splash->fill(path, gTrue);
1278 delete path;
1279 }
1280
1281 void SplashOutputDev::clip(GfxState *state) {
1282 SplashPath *path;
1283
1284 path = convertPath(state, state->getPath());
1285 splash->clipToPath(path, gFalse);
1286 delete path;
1287 }
1288
1289 void SplashOutputDev::eoClip(GfxState *state) {
1290 SplashPath *path;
1291
1292 path = convertPath(state, state->getPath());
1293 splash->clipToPath(path, gTrue);
1294 delete path;
1295 }
1296
1297 SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path) {
1298 SplashPath *sPath;
1299 GfxSubpath *subpath;
1300 double x1, y1, x2, y2, x3, y3;
1301 int i, j;
1302
1303 sPath = new SplashPath();
1304 for (i = 0; i < path->getNumSubpaths(); ++i) {
1305 subpath = path->getSubpath(i);
1306 if (subpath->getNumPoints() > 0) {
1307 state->transform(subpath->getX(0), subpath->getY(0), &x1, &y1);
1308 sPath->moveTo((SplashCoord)x1, (SplashCoord)y1);
1309 j = 1;
1310 while (j < subpath->getNumPoints()) {
1311 if (subpath->getCurve(j)) {
1312 state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
1313 state->transform(subpath->getX(j+1), subpath->getY(j+1), &x2, &y2);
1314 state->transform(subpath->getX(j+2), subpath->getY(j+2), &x3, &y3);
1315 sPath->curveTo((SplashCoord)x1, (SplashCoord)y1,
1316 (SplashCoord)x2, (SplashCoord)y2,
1317 (SplashCoord)x3, (SplashCoord)y3);
1318 j += 3;
1319 } else {
1320 state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
1321 sPath->lineTo((SplashCoord)x1, (SplashCoord)y1);
1322 ++j;
1323 }
1324 }
1325 if (subpath->isClosed()) {
1326 sPath->close();
1327 }
1328 }
1329 }
1330 return sPath;
1331 }
1332
1333 void SplashOutputDev::drawChar(GfxState *state, double x, double y,
1334 double dx, double dy,
1335 double originX, double originY,
1336 CharCode code, int nBytes,
1337 Unicode *u, int uLen) {
1338 double x1, y1;
1339 SplashPath *path;
1340 int render;
1341
1342 if (needFontUpdate) {
1343 updateFont(state);
1344 }
1345 if (!font) {
1346 return;
1347 }
1348
1349 // check for invisible text -- this is used by Acrobat Capture
1350 render = state->getRender();
1351 if (render == 3) {
1352 return;
1353 }
1354
1355 x -= originX;
1356 y -= originY;
1357 state->transform(x, y, &x1, &y1);
1358
1359 // fill
1360 if (!(render & 1)) {
1361 splash->fillChar((SplashCoord)x1, (SplashCoord)y1, code, font);
1362 }
1363
1364 // stroke
1365 if ((render & 3) == 1 || (render & 3) == 2) {
1366 if ((path = font->getGlyphPath(code))) {
1367 path->offset((SplashCoord)x1, (SplashCoord)y1);
1368 splash->stroke(path);
1369 delete path;
1370 }
1371 }
1372
1373 // clip
1374 if (render & 4) {
1375 path = font->getGlyphPath(code);
1376 path->offset((SplashCoord)x1, (SplashCoord)y1);
1377 if (textClipPath) {
1378 textClipPath->append(path);
1379 delete path;
1380 } else {
1381 textClipPath = path;
1382 }
1383 }
1384 }
1385
1386 GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
1387 double dx, double dy,
1388 CharCode code, Unicode *u, int uLen) {
1389 GfxFont *gfxFont;
1390 Ref *fontID;
1391 double *ctm, *bbox;
1392 T3FontCache *t3Font;
1393 T3GlyphStack *t3gs;
1394 double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
1395 int i, j;
1396
1397 if (!(gfxFont = state->getFont())) {
1398 return gFalse;
1399 }
1400 fontID = gfxFont->getID();
1401 ctm = state->getCTM();
1402 state->transform(0, 0, &xt, &yt);
1403
1404 // is it the first (MRU) font in the cache?
1405 if (!(nT3Fonts > 0 &&
1406 t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
1407
1408 // is the font elsewhere in the cache?
1409 for (i = 1; i < nT3Fonts; ++i) {
1410 if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
1411 t3Font = t3FontCache[i];
1412 for (j = i; j > 0; --j) {
1413 t3FontCache[j] = t3FontCache[j - 1];
1414 }
1415 t3FontCache[0] = t3Font;
1416 break;
1417 }
1418 }
1419 if (i >= nT3Fonts) {
1420
1421 // create new entry in the font cache
1422 if (nT3Fonts == splashOutT3FontCacheSize) {
1423 delete t3FontCache[nT3Fonts - 1];
1424 --nT3Fonts;
1425 }
1426 for (j = nT3Fonts; j > 0; --j) {
1427 t3FontCache[j] = t3FontCache[j - 1];
1428 }
1429 ++nT3Fonts;
1430 bbox = gfxFont->getFontBBox();
1431 if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
1432 // broken bounding box -- just take a guess
1433 xMin = xt - 5;
1434 xMax = xMin + 30;
1435 yMax = yt + 15;
1436 yMin = yMax - 45;
1437 } else {
1438 state->transform(bbox[0], bbox[1], &x1, &y1);
1439 xMin = xMax = x1;
1440 yMin = yMax = y1;
1441 state->transform(bbox[0], bbox[3], &x1, &y1);
1442 if (x1 < xMin) {
1443 xMin = x1;
1444 } else if (x1 > xMax) {
1445 xMax = x1;
1446 }
1447 if (y1 < yMin) {
1448 yMin = y1;
1449 } else if (y1 > yMax) {
1450 yMax = y1;
1451 }
1452 state->transform(bbox[2], bbox[1], &x1, &y1);
1453 if (x1 < xMin) {
1454 xMin = x1;
1455 } else if (x1 > xMax) {
1456 xMax = x1;
1457 }
1458 if (y1 < yMin) {
1459 yMin = y1;
1460 } else if (y1 > yMax) {
1461 yMax = y1;
1462 }
1463 state->transform(bbox[2], bbox[3], &x1, &y1);
1464 if (x1 < xMin) {
1465 xMin = x1;
1466 } else if (x1 > xMax) {
1467 xMax = x1;
1468 }
1469 if (y1 < yMin) {
1470 yMin = y1;
1471 } else if (y1 > yMax) {
1472 yMax = y1;
1473 }
1474 }
1475 t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
1476 (int)floor(xMin - xt),
1477 (int)floor(yMin - yt),
1478 (int)ceil(xMax) - (int)floor(xMin) + 3,
1479 (int)ceil(yMax) - (int)floor(yMin) + 3,
1480 colorMode != splashModeMono1);
1481 }
1482 }
1483 t3Font = t3FontCache[0];
1484
1485 // is the glyph in the cache?
1486 i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
1487 for (j = 0; j < t3Font->cacheAssoc; ++j) {
1488 if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
1489 t3Font->cacheTags[i+j].code == code) {
1490 drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
1491 t3Font->cacheData + (i+j) * t3Font->glyphSize,
1492 xt, yt);
1493 return gTrue;
1494 }
1495 }
1496
1497 // push a new Type 3 glyph record
1498 t3gs = new T3GlyphStack();
1499 t3gs->next = t3GlyphStack;
1500 t3GlyphStack = t3gs;
1501 t3GlyphStack->code = code;
1502 t3GlyphStack->x = xt;
1503 t3GlyphStack->y = yt;
1504 t3GlyphStack->cache = t3Font;
1505 t3GlyphStack->cacheTag = NULL;
1506 t3GlyphStack->cacheData = NULL;
1507
1508 return gFalse;
1509 }
1510
1511 void SplashOutputDev::endType3Char(GfxState *state) {
1512 T3GlyphStack *t3gs;
1513 double *ctm;
1514
1515 if (t3GlyphStack->cacheTag) {
1516 memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
1517 t3GlyphStack->cache->glyphSize);
1518 delete bitmap;
1519 delete splash;
1520 bitmap = t3GlyphStack->origBitmap;
1521 splash = t3GlyphStack->origSplash;
1522 ctm = state->getCTM();
1523 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
1524 t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
1525 drawType3Glyph(t3GlyphStack->cache,
1526 t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
1527 t3GlyphStack->x, t3GlyphStack->y);
1528 }
1529 t3gs = t3GlyphStack;
1530 t3GlyphStack = t3gs->next;
1531 delete t3gs;
1532 }
1533
1534 void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
1535 }
1536
1537 void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
1538 double llx, double lly, double urx, double ury) {
1539 double *ctm;
1540 T3FontCache *t3Font;
1541 SplashColor color;
1542 double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
1543 int i, j;
1544
1545 t3Font = t3GlyphStack->cache;
1546
1547 // check for a valid bbox
1548 state->transform(0, 0, &xt, &yt);
1549 state->transform(llx, lly, &x1, &y1);
1550 xMin = xMax = x1;
1551 yMin = yMax = y1;
1552 state->transform(llx, ury, &x1, &y1);
1553 if (x1 < xMin) {
1554 xMin = x1;
1555 } else if (x1 > xMax) {
1556 xMax = x1;
1557 }
1558 if (y1 < yMin) {
1559 yMin = y1;
1560 } else if (y1 > yMax) {
1561 yMax = y1;
1562 }
1563 state->transform(urx, lly, &x1, &y1);
1564 if (x1 < xMin) {
1565 xMin = x1;
1566 } else if (x1 > xMax) {
1567 xMax = x1;
1568 }
1569 if (y1 < yMin) {
1570 yMin = y1;
1571 } else if (y1 > yMax) {
1572 yMax = y1;
1573 }
1574 state->transform(urx, ury, &x1, &y1);
1575 if (x1 < xMin) {
1576 xMin = x1;
1577 } else if (x1 > xMax) {
1578 xMax = x1;
1579 }
1580 if (y1 < yMin) {
1581 yMin = y1;
1582 } else if (y1 > yMax) {
1583 yMax = y1;
1584 }
1585 if (xMin - xt < t3Font->glyphX ||
1586 yMin - yt < t3Font->glyphY ||
1587 xMax - xt > t3Font->glyphX + t3Font->glyphW ||
1588 yMax - yt > t3Font->glyphY + t3Font->glyphH) {
1589 error(-1, "Bad bounding box in Type 3 glyph");
1590 return;
1591 }
1592
1593 // allocate a cache entry
1594 i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
1595 for (j = 0; j < t3Font->cacheAssoc; ++j) {
1596 if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
1597 t3Font->cacheTags[i+j].mru = 0x8000;
1598 t3Font->cacheTags[i+j].code = t3GlyphStack->code;
1599 t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
1600 t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
1601 } else {
1602 ++t3Font->cacheTags[i+j].mru;
1603 }
1604 }
1605
1606 // save state
1607 t3GlyphStack->origBitmap = bitmap;
1608 t3GlyphStack->origSplash = splash;
1609 ctm = state->getCTM();
1610 t3GlyphStack->origCTM4 = ctm[4];
1611 t3GlyphStack->origCTM5 = ctm[5];
1612
1613 // create the temporary bitmap
1614 if (colorMode == splashModeMono1) {
1615 bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
1616 splashModeMono1);
1617 splash = new Splash(bitmap);
1618 color[0] = 0;
1619 splash->clear(color);
1620 color[0] = 1;
1621 } else {
1622 bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
1623 splashModeMono8);
1624 splash = new Splash(bitmap);
1625 color[0] = 0x00;
1626 splash->clear(color);
1627 color[0] = 0xff;
1628 }
1629 splash->setFillPattern(new SplashSolidColor(color));
1630 splash->setStrokePattern(new SplashSolidColor(color));
1631 //~ this should copy other state from t3GlyphStack->origSplash?
1632 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
1633 -t3Font->glyphX, -t3Font->glyphY);
1634 }
1635
1636 void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font,
1637 T3FontCacheTag *tag, Guchar *data,
1638 double x, double y) {
1639 SplashGlyphBitmap glyph;
1640
1641 glyph.x = -t3Font->glyphX;
1642 glyph.y = -t3Font->glyphY;
1643 glyph.w = t3Font->glyphW;
1644 glyph.h = t3Font->glyphH;
1645 glyph.aa = colorMode != splashModeMono1;
1646 glyph.data = data;
1647 glyph.freeData = gFalse;
1648 splash->fillGlyph((SplashCoord)x, (SplashCoord)y, &glyph);
1649 }
1650
1651 void SplashOutputDev::endTextObject(GfxState *state) {
1652 if (textClipPath) {
1653 splash->clipToPath(textClipPath, gFalse);
1654 delete textClipPath;
1655 textClipPath = NULL;
1656 }
1657 }
1658
1659 struct SplashOutImageMaskData {
1660 ImageStream *imgStr;
1661 GBool invert;
1662 int width, height, y;
1663 };
1664
1665 GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
1666 SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
1667 Guchar *p;
1668 SplashColorPtr q;
1669 int x;
1670
1671 if (imgMaskData->y == imgMaskData->height) {
1672 return gFalse;
1673 }
1674 for (x = 0, p = imgMaskData->imgStr->getLine(), q = line;
1675 x < imgMaskData->width;
1676 ++x) {
1677 *q++ = *p++ ^ imgMaskData->invert;
1678 }
1679 ++imgMaskData->y;
1680 return gTrue;
1681 }
1682
1683 void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1684 int width, int height, GBool invert,
1685 GBool inlineImg) {
1686 double *ctm;
1687 SplashCoord mat[6];
1688 SplashOutImageMaskData imgMaskData;
1689
1690 ctm = state->getCTM();
1691 mat[0] = ctm[0];
1692 mat[1] = ctm[1];
1693 mat[2] = -ctm[2];
1694 mat[3] = -ctm[3];
1695 mat[4] = ctm[2] + ctm[4];
1696 mat[5] = ctm[3] + ctm[5];
1697
1698 imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
1699 imgMaskData.imgStr->reset();
1700 imgMaskData.invert = invert ? 0 : 1;
1701 imgMaskData.width = width;
1702 imgMaskData.height = height;
1703 imgMaskData.y = 0;
1704
1705 splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat);
1706 if (inlineImg) {
1707 while (imgMaskData.y < height) {
1708 imgMaskData.imgStr->getLine();
1709 ++imgMaskData.y;
1710 }
1711 }
1712
1713 delete imgMaskData.imgStr;
1714 str->close();
1715 }
1716
1717 struct SplashOutImageData {
1718 ImageStream *imgStr;
1719 GfxImageColorMap *colorMap;
1720 SplashColorPtr lookup;
1721 int *maskColors;
1722 SplashColorMode colorMode;
1723 int width, height, y;
1724 };
1725
1726 GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr line) {
1727 SplashOutImageData *imgData = (SplashOutImageData *)data;
1728 Guchar *p;
1729 SplashColorPtr q, col;
1730 GfxRGB rgb;
1731 GfxGray gray;
1732 #if SPLASH_CMYK
1733 GfxCMYK cmyk;
1734 #endif
1735 int nComps, x;
1736
1737 if (imgData->y == imgData->height) {
1738 return gFalse;
1739 }
1740
1741 nComps = imgData->colorMap->getNumPixelComps();
1742
1743 if (imgData->lookup) {
1744 switch (imgData->colorMode) {
1745 case splashModeMono1:
1746 case splashModeMono8:
1747 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1748 x < imgData->width;
1749 ++x, ++p) {
1750 *q++ = imgData->lookup[*p];
1751 }
1752 break;
1753 case splashModeRGB8:
1754 case splashModeBGR8:
1755 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1756 x < imgData->width;
1757 ++x, ++p) {
1758 col = &imgData->lookup[3 * *p];
1759 *q++ = col[0];
1760 *q++ = col[1];
1761 *q++ = col[2];
1762 }
1763 break;
1764 #if SPLASH_CMYK
1765 case splashModeCMYK8:
1766 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1767 x < imgData->width;
1768 ++x, ++p) {
1769 col = &imgData->lookup[4 * *p];
1770 *q++ = col[0];
1771 *q++ = col[1];
1772 *q++ = col[2];
1773 *q++ = col[3];
1774 }
1775 break;
1776 #endif
1777 case splashModeAMono8:
1778 case splashModeARGB8:
1779 case splashModeBGRA8:
1780 #if SPLASH_CMYK
1781 case splashModeACMYK8:
1782 #endif
1783 //~ unimplemented
1784 break;
1785 }
1786 } else {
1787 switch (imgData->colorMode) {
1788 case splashModeMono1:
1789 case splashModeMono8:
1790 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1791 x < imgData->width;
1792 ++x, p += nComps) {
1793 imgData->colorMap->getGray(p, &gray);
1794 *q++ = colToByte(gray);
1795 }
1796 break;
1797 case splashModeRGB8:
1798 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1799 x < imgData->width;
1800 ++x, p += nComps) {
1801 imgData->colorMap->getRGB(p, &rgb);
1802 *q++ = colToByte(rgb.r);
1803 *q++ = colToByte(rgb.g);
1804 *q++ = colToByte(rgb.b);
1805 }
1806 break;
1807 case splashModeBGR8:
1808 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1809 x < imgData->width;
1810 ++x, p += nComps) {
1811 imgData->colorMap->getRGB(p, &rgb);
1812 *q++ = colToByte(rgb.b);
1813 *q++ = colToByte(rgb.g);
1814 *q++ = colToByte(rgb.r);
1815 }
1816 break;
1817 #if SPLASH_CMYK
1818 case splashModeCMYK8:
1819 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1820 x < imgData->width;
1821 ++x, p += nComps) {
1822 imgData->colorMap->getCMYK(p, &cmyk);
1823 *q++ = colToByte(cmyk.c);
1824 *q++ = colToByte(cmyk.m);
1825 *q++ = colToByte(cmyk.y);
1826 *q++ = colToByte(cmyk.k);
1827 }
1828 break;
1829 #endif
1830 case splashModeAMono8:
1831 case splashModeARGB8:
1832 case splashModeBGRA8:
1833 #if SPLASH_CMYK
1834 case splashModeACMYK8:
1835 #endif
1836 //~ unimplemented
1837 break;
1838 }
1839 }
1840
1841 ++imgData->y;
1842 return gTrue;
1843 }
1844
1845 GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr line) {
1846 SplashOutImageData *imgData = (SplashOutImageData *)data;
1847 Guchar *p;
1848 SplashColorPtr q, col;
1849 GfxRGB rgb;
1850 GfxGray gray;
1851 #if SPLASH_CMYK
1852 GfxCMYK cmyk;
1853 #endif
1854 Guchar alpha;
1855 int nComps, x, i;
1856
1857 if (imgData->y == imgData->height) {
1858 return gFalse;
1859 }
1860
1861 nComps = imgData->colorMap->getNumPixelComps();
1862
1863 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1864 x < imgData->width;
1865 ++x, p += nComps) {
1866 alpha = 0;
1867 for (i = 0; i < nComps; ++i) {
1868 if (p[i] < imgData->maskColors[2*i] ||
1869 p[i] > imgData->maskColors[2*i+1]) {
1870 alpha = 0xff;
1871 break;
1872 }
1873 }
1874 if (imgData->lookup) {
1875 switch (imgData->colorMode) {
1876 case splashModeMono1:
1877 case splashModeMono8:
1878 *q++ = alpha;
1879 *q++ = imgData->lookup[*p];
1880 break;
1881 case splashModeRGB8:
1882 *q++ = alpha;
1883 col = &imgData->lookup[3 * *p];
1884 *q++ = col[0];
1885 *q++ = col[1];
1886 *q++ = col[2];
1887 break;
1888 case splashModeBGR8:
1889 col = &imgData->lookup[3 * *p];
1890 *q++ = col[0];
1891 *q++ = col[1];
1892 *q++ = col[2];
1893 *q++ = alpha;
1894 break;
1895 #if SPLASH_CMYK
1896 case splashModeCMYK8:
1897 *q++ = alpha;
1898 col = &imgData->lookup[4 * *p];
1899 *q++ = col[0];
1900 *q++ = col[1];
1901 *q++ = col[2];
1902 *q++ = col[3];
1903 break;
1904 #endif
1905 case splashModeAMono8:
1906 case splashModeARGB8:
1907 case splashModeBGRA8:
1908 #if SPLASH_CMYK
1909 case splashModeACMYK8:
1910 #endif
1911 //~ unimplemented
1912 break;
1913 }
1914 } else {
1915 switch (imgData->colorMode) {
1916 case splashModeMono1:
1917 case splashModeMono8:
1918 imgData->colorMap->getGray(p, &gray);
1919 *q++ = alpha;
1920 *q++ = colToByte(gray);
1921 break;
1922 case splashModeRGB8:
1923 imgData->colorMap->getRGB(p, &rgb);
1924 *q++ = alpha;
1925 *q++ = colToByte(rgb.r);
1926 *q++ = colToByte(rgb.g);
1927 *q++ = colToByte(rgb.b);
1928 break;
1929 case splashModeBGR8:
1930 imgData->colorMap->getRGB(p, &rgb);
1931 *q++ = colToByte(rgb.b);
1932 *q++ = colToByte(rgb.g);
1933 *q++ = colToByte(rgb.r);
1934 *q++ = alpha;
1935 break;
1936 #if SPLASH_CMYK
1937 case splashModeCMYK8:
1938 imgData->colorMap->getCMYK(p, &cmyk);
1939 *q++ = alpha;
1940 *q++ = colToByte(cmyk.c);
1941 *q++ = colToByte(cmyk.m);
1942 *q++ = colToByte(cmyk.y);
1943 *q++ = colToByte(cmyk.k);
1944 break;
1945 #endif
1946 case splashModeAMono8:
1947 case splashModeARGB8:
1948 case splashModeBGRA8:
1949 #if SPLASH_CMYK
1950 case splashModeACMYK8:
1951 #endif
1952 //~ unimplemented
1953 break;
1954 }
1955 }
1956 }
1957
1958 ++imgData->y;
1959 return gTrue;
1960 }
1961
1962 void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1963 int width, int height,
1964 GfxImageColorMap *colorMap,
1965 int *maskColors, GBool inlineImg) {
1966 double *ctm;
1967 SplashCoord mat[6];
1968 SplashOutImageData imgData;
1969 SplashColorMode srcMode;
1970 SplashImageSource src;
1971 GfxGray gray;
1972 GfxRGB rgb;
1973 #if SPLASH_CMYK
1974 GfxCMYK cmyk;
1975 #endif
1976 Guchar pix;
1977 int n, i;
1978
1979 ctm = state->getCTM();
1980 mat[0] = ctm[0];
1981 mat[1] = ctm[1];
1982 mat[2] = -ctm[2];
1983 mat[3] = -ctm[3];
1984 mat[4] = ctm[2] + ctm[4];
1985 mat[5] = ctm[3] + ctm[5];
1986
1987 imgData.imgStr = new ImageStream(str, width,
1988 colorMap->getNumPixelComps(),
1989 colorMap->getBits());
1990 imgData.imgStr->reset();
1991 imgData.colorMap = colorMap;
1992 imgData.maskColors = maskColors;
1993 imgData.colorMode = colorMode;
1994 imgData.width = width;
1995 imgData.height = height;
1996 imgData.y = 0;
1997
1998 // special case for one-channel (monochrome/gray/separation) images:
1999 // build a lookup table here
2000 imgData.lookup = NULL;
2001 if (colorMap->getNumPixelComps() == 1) {
2002 n = 1 << colorMap->getBits();
2003 switch (colorMode) {
2004 case splashModeMono1:
2005 case splashModeMono8:
2006 imgData.lookup = (SplashColorPtr)gmalloc(n);
2007 for (i = 0; i < n; ++i) {
2008 pix = (Guchar)i;
2009 colorMap->getGray(&pix, &gray);
2010 imgData.lookup[i] = colToByte(gray);
2011 }
2012 break;
2013 case splashModeRGB8:
2014 imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2015 for (i = 0; i < n; ++i) {
2016 pix = (Guchar)i;
2017 colorMap->getRGB(&pix, &rgb);
2018 imgData.lookup[3*i] = colToByte(rgb.r);
2019 imgData.lookup[3*i+1] = colToByte(rgb.g);
2020 imgData.lookup[3*i+2] = colToByte(rgb.b);
2021 }
2022 break;
2023 case splashModeBGR8:
2024 imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2025 for (i = 0; i < n; ++i) {
2026 pix = (Guchar)i;
2027 colorMap->getRGB(&pix, &rgb);
2028 imgData.lookup[3*i] = colToByte(rgb.b);
2029 imgData.lookup[3*i+1] = colToByte(rgb.g);
2030 imgData.lookup[3*i+2] = colToByte(rgb.r);
2031 }
2032 break;
2033 #if SPLASH_CMYK
2034 case splashModeCMYK8:
2035 imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
2036 for (i = 0; i < n; ++i) {
2037 pix = (Guchar)i;
2038 colorMap->getCMYK(&pix, &cmyk);
2039 imgData.lookup[4*i] = colToByte(cmyk.c);
2040 imgData.lookup[4*i+1] = colToByte(cmyk.m);
2041 imgData.lookup[4*i+2] = colToByte(cmyk.y);
2042 imgData.lookup[4*i+3] = colToByte(cmyk.k);
2043 }
2044 break;
2045 #endif
2046 default:
2047 //~ unimplemented
2048 break;
2049 }
2050 }
2051
2052 switch (colorMode) {
2053 case splashModeMono1:
2054 case splashModeMono8:
2055 srcMode = maskColors ? splashModeAMono8 : splashModeMono8;
2056 break;
2057 case splashModeRGB8:
2058 srcMode = maskColors ? splashModeARGB8 : splashModeRGB8;
2059 break;
2060 case splashModeBGR8:
2061 srcMode = maskColors ? splashModeBGRA8 : splashModeBGR8;
2062 break;
2063 #if SPLASH_CMYK
2064 case splashModeCMYK8:
2065 srcMode = maskColors ? splashModeACMYK8 : splashModeCMYK8;
2066 break;
2067 #endif
2068 default:
2069 //~ unimplemented
2070 srcMode = splashModeRGB8;
2071 break;
2072 }
2073 src = maskColors ? &alphaImageSrc : &imageSrc;
2074 splash->drawImage(src, &imgData, srcMode, width, height, mat);
2075 if (inlineImg) {
2076 while (imgData.y < height) {
2077 imgData.imgStr->getLine();
2078 ++imgData.y;
2079 }
2080 }
2081
2082 gfree(imgData.lookup);
2083 delete imgData.imgStr;
2084 str->close();
2085 }
2086
2087 struct SplashOutMaskedImageData {
2088 ImageStream *imgStr;
2089 GfxImageColorMap *colorMap;
2090 SplashBitmap *mask;
2091 SplashColorPtr lookup;
2092 SplashColorMode colorMode;
2093 int width, height, y;
2094 };
2095
2096 GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr line) {
2097 SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;
2098 Guchar *p;
2099 SplashColor maskColor;
2100 SplashColorPtr q, col;
2101 GfxRGB rgb;
2102 GfxGray gray;
2103 #if SPLASH_CMYK
2104 GfxCMYK cmyk;
2105 #endif
2106 Guchar alpha;
2107 int nComps, x;
2108
2109 if (imgData->y == imgData->height) {
2110 return gFalse;
2111 }
2112
2113 nComps = imgData->colorMap->getNumPixelComps();
2114
2115 for (x = 0, p = imgData->imgStr->getLine(), q = line;
2116 x < imgData->width;
2117 ++x, p += nComps) {
2118 imgData->mask->getPixel(x, imgData->y, maskColor);
2119 alpha = maskColor[0] ? 0xff : 0x00;
2120 if (imgData->lookup) {
2121 switch (imgData->colorMode) {
2122 case splashModeMono1:
2123 case splashModeMono8:
2124 *q++ = alpha;
2125 *q++ = imgData->lookup[*p];
2126 break;
2127 case splashModeRGB8:
2128 *q++ = alpha;
2129 col = &imgData->lookup[3 * *p];
2130 *q++ = col[0];
2131 *q++ = col[1];
2132 *q++ = col[2];
2133 break;
2134 case splashModeBGR8:
2135 col = &imgData->lookup[3 * *p];
2136 *q++ = col[0];
2137 *q++ = col[1];
2138 *q++ = col[2];
2139 *q++ = alpha;
2140 break;
2141 #if SPLASH_CMYK
2142 case splashModeCMYK8:
2143 *q++ = alpha;
2144 col = &imgData->lookup[4 * *p];
2145 *q++ = col[0];
2146 *q++ = col[1];
2147 *q++ = col[2];
2148 *q++ = col[3];
2149 break;
2150 #endif
2151 case splashModeAMono8:
2152 case splashModeARGB8:
2153 case splashModeBGRA8:
2154 #if SPLASH_CMYK
2155 case splashModeACMYK8:
2156 #endif
2157 //~ unimplemented
2158 break;
2159 }
2160 } else {
2161 switch (imgData->colorMode) {
2162 case splashModeMono1:
2163 case splashModeMono8:
2164 imgData->colorMap->getGray(p, &gray);
2165 *q++ = alpha;
2166 *q++ = colToByte(gray);
2167 break;
2168 case splashModeRGB8:
2169 imgData->colorMap->getRGB(p, &rgb);
2170 *q++ = alpha;
2171 *q++ = colToByte(rgb.r);
2172 *q++ = colToByte(rgb.g);
2173 *q++ = colToByte(rgb.b);
2174 break;
2175 case splashModeBGR8:
2176 imgData->colorMap->getRGB(p, &rgb);
2177 *q++ = colToByte(rgb.b);
2178 *q++ = colToByte(rgb.g);
2179 *q++ = colToByte(rgb.r);
2180 *q++ = alpha;
2181 break;
2182 #if SPLASH_CMYK
2183 case splashModeCMYK8:
2184 imgData->colorMap->getCMYK(p, &cmyk);
2185 *q++ = alpha;
2186 *q++ = colToByte(cmyk.c);
2187 *q++ = colToByte(cmyk.m);
2188 *q++ = colToByte(cmyk.y);
2189 *q++ = colToByte(cmyk.k);
2190 break;
2191 #endif
2192 case splashModeAMono8:
2193 case splashModeARGB8:
2194 case splashModeBGRA8:
2195 #if SPLASH_CMYK
2196 case splashModeACMYK8:
2197 #endif
2198 //~ unimplemented
2199 break;
2200 }
2201 }
2202 }
2203
2204 ++imgData->y;
2205 return gTrue;
2206 }
2207
2208 void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
2209 Stream *str, int width, int height,
2210 GfxImageColorMap *colorMap,
2211 Stream *maskStr, int maskWidth,
2212 int maskHeight, GBool maskInvert) {
2213 double *ctm;
2214 SplashCoord mat[6];
2215 SplashOutMaskedImageData imgData;
2216 SplashOutImageMaskData imgMaskData;
2217 SplashColorMode srcMode;
2218 SplashBitmap *maskBitmap;
2219 Splash *maskSplash;
2220 SplashColor maskColor;
2221 GfxGray gray;
2222 GfxRGB rgb;
2223 #if SPLASH_CMYK
2224 GfxCMYK cmyk;
2225 #endif
2226 Guchar pix;
2227 int n, i;
2228
2229 //----- scale the mask image to the same size as the source image
2230
2231 mat[0] = (SplashCoord)width;
2232 mat[1] = 0;
2233 mat[2] = 0;
2234 mat[3] = (SplashCoord)height;
2235 mat[4] = 0;
2236 mat[5] = 0;
2237 imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
2238 imgMaskData.imgStr->reset();
2239 imgMaskData.invert = maskInvert ? 0 : 1;
2240 imgMaskData.width = maskWidth;
2241 imgMaskData.height = maskHeight;
2242 imgMaskData.y = 0;
2243 maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1);
2244 maskSplash = new Splash(maskBitmap);
2245 maskColor[0] = 0;
2246 maskSplash->clear(maskColor);
2247 maskColor[0] = 1;
2248 maskSplash->setFillPattern(new SplashSolidColor(maskColor));
2249 maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
2250 maskWidth, maskHeight, mat);
2251 delete imgMaskData.imgStr;
2252 maskStr->close();
2253 delete maskSplash;
2254
2255 //----- draw the source image
2256
2257 ctm = state->getCTM();
2258 mat[0] = ctm[0];
2259 mat[1] = ctm[1];
2260 mat[2] = -ctm[2];
2261 mat[3] = -ctm[3];
2262 mat[4] = ctm[2] + ctm[4];
2263 mat[5] = ctm[3] + ctm[5];
2264
2265 imgData.imgStr = new ImageStream(str, width,
2266 colorMap->getNumPixelComps(),
2267 colorMap->getBits());
2268 imgData.imgStr->reset();
2269 imgData.colorMap = colorMap;
2270 imgData.mask = maskBitmap;
2271 imgData.colorMode = colorMode;
2272 imgData.width = width;
2273 imgData.height = height;
2274 imgData.y = 0;
2275
2276 // special case for one-channel (monochrome/gray/separation) images:
2277 // build a lookup table here
2278 imgData.lookup = NULL;
2279 if (colorMap->getNumPixelComps() == 1) {
2280 n = 1 << colorMap->getBits();
2281 switch (colorMode) {
2282 case splashModeMono1:
2283 case splashModeMono8:
2284 imgData.lookup = (SplashColorPtr)gmalloc(n);
2285 for (i = 0; i < n; ++i) {
2286 pix = (Guchar)i;
2287 colorMap->getGray(&pix, &gray);
2288 imgData.lookup[i] = colToByte(gray);
2289 }
2290 break;
2291 case splashModeRGB8:
2292 imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2293 for (i = 0; i < n; ++i) {
2294 pix = (Guchar)i;
2295 colorMap->getRGB(&pix, &rgb);
2296 imgData.lookup[3*i] = colToByte(rgb.r);
2297 imgData.lookup[3*i+1] = colToByte(rgb.g);
2298 imgData.lookup[3*i+2] = colToByte(rgb.b);
2299 }
2300 break;
2301 case splashModeBGR8:
2302 imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2303 for (i = 0; i < n; ++i) {
2304 pix = (Guchar)i;
2305 colorMap->getRGB(&pix, &rgb);
2306 imgData.lookup[3*i] = colToByte(rgb.b);
2307 imgData.lookup[3*i+1] = colToByte(rgb.g);
2308 imgData.lookup[3*i+2] = colToByte(rgb.r);
2309 }
2310 break;
2311 #if SPLASH_CMYK
2312 case splashModeCMYK8:
2313 imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
2314 for (i = 0; i < n; ++i) {
2315 pix = (Guchar)i;
2316 colorMap->getCMYK(&pix, &cmyk);
2317 imgData.lookup[4*i] = colToByte(cmyk.c);
2318 imgData.lookup[4*i+1] = colToByte(cmyk.m);
2319 imgData.lookup[4*i+2] = colToByte(cmyk.y);
2320 imgData.lookup[4*i+3] = colToByte(cmyk.k);
2321 }
2322 break;
2323 #endif
2324 default:
2325 //~ unimplemented
2326 break;
2327 }
2328 }
2329
2330 switch (colorMode) {
2331 case splashModeMono1:
2332 case splashModeMono8:
2333 srcMode = splashModeAMono8;
2334 break;
2335 case splashModeRGB8:
2336 srcMode = splashModeARGB8;
2337 break;
2338 case splashModeBGR8:
2339 srcMode = splashModeBGRA8;
2340 break;
2341 #if SPLASH_CMYK
2342 case splashModeCMYK8:
2343 srcMode = splashModeACMYK8;
2344 break;
2345 #endif
2346 default:
2347 //~ unimplemented
2348 srcMode = splashModeARGB8;
2349 break;
2350 }
2351 splash->drawImage(&maskedImageSrc, &imgData, srcMode, width, height, mat);
2352
2353 delete maskBitmap;
2354 gfree(imgData.lookup);
2355 delete imgData.imgStr;
2356 str->close();
2357 }
2358
2359 void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
2360 Stream *str, int width, int height,
2361 GfxImageColorMap *colorMap,
2362 Stream *maskStr,
2363 int maskWidth, int maskHeight,
2364 GfxImageColorMap *maskColorMap) {
2365 double *ctm;
2366 SplashCoord mat[6];
2367 SplashOutImageData imgData;
2368 SplashOutImageData imgMaskData;
2369 SplashColorMode srcMode;
2370 SplashBitmap *maskBitmap;
2371 Splash *maskSplash;
2372 SplashColor maskColor;
2373 GfxGray gray;
2374 GfxRGB rgb;
2375 #if SPLASH_CMYK
2376 GfxCMYK cmyk;
2377 #endif
2378 Guchar pix;
2379 int n, i;
2380
2381 ctm = state->getCTM();
2382 mat[0] = ctm[0];
2383 mat[1] = ctm[1];
2384 mat[2] = -ctm[2];
2385 mat[3] = -ctm[3];
2386 mat[4] = ctm[2] + ctm[4];
2387 mat[5] = ctm[3] + ctm[5];
2388
2389 //----- set up the soft mask
2390
2391 imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
2392 maskColorMap->getNumPixelComps(),
2393 maskColorMap->getBits());
2394 imgMaskData.imgStr->reset();
2395 imgMaskData.colorMap = maskColorMap;
2396 imgMaskData.maskColors = NULL;
2397 imgMaskData.colorMode = splashModeMono8;
2398 imgMaskData.width = maskWidth;
2399 imgMaskData.height = maskHeight;
2400 imgMaskData.y = 0;
2401 n = 1 << maskColorMap->getBits();
2402 imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
2403 for (i = 0; i < n; ++i) {
2404 pix = (Guchar)i;
2405 maskColorMap->getGray(&pix, &gray);
2406 imgMaskData.lookup[i] = colToByte(gray);
2407 }
2408 maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
2409 1, splashModeMono8);
2410 maskSplash = new Splash(maskBitmap);
2411 maskColor[0] = 0;
2412 maskSplash->clear(maskColor);
2413 maskSplash->drawImage(&imageSrc, &imgMaskData,
2414 splashModeMono8, maskWidth, maskHeight, mat);
2415 delete imgMaskData.imgStr;
2416 maskStr->close();
2417 gfree(imgMaskData.lookup);
2418 delete maskSplash;
2419 splash->setSoftMask(maskBitmap);
2420
2421 //----- draw the source image
2422
2423 imgData.imgStr = new ImageStream(str, width,
2424 colorMap->getNumPixelComps(),
2425 colorMap->getBits());
2426 imgData.imgStr->reset();
2427 imgData.colorMap = colorMap;
2428 imgData.maskColors = NULL;
2429 imgData.colorMode = colorMode;
2430 imgData.width = width;
2431 imgData.height = height;
2432 imgData.y = 0;
2433
2434 // special case for one-channel (monochrome/gray/separation) images:
2435 // build a lookup table here
2436 imgData.lookup = NULL;
2437 if (colorMap->getNumPixelComps() == 1) {
2438 n = 1 << colorMap->getBits();
2439 switch (colorMode) {
2440 case splashModeMono1:
2441 case splashModeMono8:
2442 imgData.lookup = (SplashColorPtr)gmalloc(n);
2443 for (i = 0; i < n; ++i) {
2444 pix = (Guchar)i;
2445 colorMap->getGray(&pix, &gray);
2446 imgData.lookup[i] = colToByte(gray);
2447 }
2448 break;
2449 case splashModeRGB8:
2450 imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2451 for (i = 0; i < n; ++i) {
2452 pix = (Guchar)i;
2453 colorMap->getRGB(&pix, &rgb);
2454 imgData.lookup[3*i] = colToByte(rgb.r);
2455 imgData.lookup[3*i+1] = colToByte(rgb.g);
2456 imgData.lookup[3*i+2] = colToByte(rgb.b);
2457 }
2458 break;
2459 case splashModeBGR8:
2460 imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2461 for (i = 0; i < n; ++i) {
2462 pix = (Guchar)i;
2463 colorMap->getRGB(&pix, &rgb);
2464 imgData.lookup[3*i] = colToByte(rgb.b);
2465 imgData.lookup[3*i+1] = colToByte(rgb.g);
2466 imgData.lookup[3*i+2] = colToByte(rgb.r);
2467 }
2468 break;
2469 #if SPLASH_CMYK
2470 case splashModeCMYK8:
2471 imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
2472 for (i = 0; i < n; ++i) {
2473 pix = (Guchar)i;
2474 colorMap->getCMYK(&pix, &cmyk);
2475 imgData.lookup[4*i] = colToByte(cmyk.c);
2476 imgData.lookup[4*i+1] = colToByte(cmyk.m);
2477 imgData.lookup[4*i+2] = colToByte(cmyk.y);
2478 imgData.lookup[4*i+3] = colToByte(cmyk.k);
2479 }
2480 break;
2481 #endif
2482 default:
2483 //~ unimplemented
2484 break;
2485 }
2486 }
2487
2488 switch (colorMode) {
2489 case splashModeMono1:
2490 case splashModeMono8:
2491 srcMode = splashModeMono8;
2492 break;
2493 case splashModeRGB8:
2494 srcMode = splashModeRGB8;
2495 break;
2496 case splashModeBGR8:
2497 srcMode = splashModeBGR8;
2498 break;
2499 #if SPLASH_CMYK
2500 case splashModeCMYK8:
2501 srcMode = splashModeCMYK8;
2502 break;
2503 #endif
2504 default:
2505 //~ unimplemented
2506 srcMode = splashModeRGB8;
2507 break;
2508 }
2509 splash->drawImage(&imageSrc, &imgData, srcMode, width, height, mat);
2510
2511 splash->setSoftMask(NULL);
2512 gfree(imgData.lookup);
2513 delete imgData.imgStr;
2514 str->close();
2515 }
2516
2517 void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) {
2518 splashColorCopy(paperColor, paperColorA);
2519 }
2520
2521 int SplashOutputDev::getBitmapWidth() {
2522 return bitmap->getWidth();
2523 }
2524
2525 int SplashOutputDev::getBitmapHeight() {
2526 return bitmap->getHeight();
2527 }
2528
2529 SplashBitmap *SplashOutputDev::takeBitmap() {
2530 SplashBitmap *ret;
2531
2532 ret = bitmap;
2533 bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, bitmapTopDown);
2534 return ret;
2535 }
2536
2537 void SplashOutputDev::getModRegion(int *xMin, int *yMin,
2538 int *xMax, int *yMax) {
2539 splash->getModRegion(xMin, yMin, xMax, yMax);
2540 }
2541
2542 void SplashOutputDev::clearModRegion() {
2543 splash->clearModRegion();
2544 }
2545
2546 void SplashOutputDev::setFillColor(int r, int g, int b) {
2547 GfxRGB rgb;
2548 GfxGray gray;
2549 #if SPLASH_CMYK
2550 GfxCMYK cmyk;
2551 #endif
2552
2553 rgb.r = byteToCol(r);
2554 rgb.g = byteToCol(g);
2555 rgb.b = byteToCol(b);
2556 gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5);
2557 if (gray > gfxColorComp1) {
2558 gray = gfxColorComp1;
2559 }
2560 #if SPLASH_CMYK
2561 cmyk.c = gfxColorComp1 - rgb.r;
2562 cmyk.m = gfxColorComp1 - rgb.g;
2563 cmyk.y = gfxColorComp1 - rgb.b;
2564 cmyk.k = 0;
2565 splash->setFillPattern(getColor(gray, &rgb, &cmyk));
2566 #else
2567 splash->setFillPattern(getColor(gray, &rgb));
2568 #endif
2569 }
2570
2571 SplashFont *SplashOutputDev::getFont(GString *name, double *mat) {
2572 DisplayFontParam *dfp;
2573 Ref ref;
2574 SplashOutFontFileID *id;
2575 SplashFontFile *fontFile;
2576 SplashFont *fontObj;
2577 FoFiTrueType *ff;
2578 Gushort *codeToGID;
2579 Unicode u;
2580 int cmap, i;
2581
2582 for (i = 0; i < 16; ++i) {
2583 if (!name->cmp(splashOutSubstFonts[i].name)) {
2584 break;
2585 }
2586 }
2587 if (i == 16) {
2588 return NULL;
2589 }
2590 ref.num = i;
2591 ref.gen = -1;
2592 id = new SplashOutFontFileID(&ref);
2593
2594 // check the font file cache
2595 if ((fontFile = fontEngine->getFontFile(id))) {
2596 delete id;
2597
2598 // load the font file
2599 } else {
2600 dfp = globalParams->getDisplayFont(name);
2601 if (dfp && dfp->kind == displayFontT1) {
2602 fontFile = fontEngine->loadType1Font(id, dfp->t1.fileName->getCString(),
2603 gFalse, winAnsiEncoding);
2604 } else if (dfp && dfp->kind == displayFontTT) {
2605 if (!(ff = FoFiTrueType::load(dfp->tt.fileName->getCString()))) {
2606 return NULL;
2607 }
2608 for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
2609 if ((ff->getCmapPlatform(cmap) == 3 &&
2610 ff->getCmapEncoding(cmap) == 1) ||
2611 ff->getCmapPlatform(cmap) == 0) {
2612 break;
2613 }
2614 }
2615 if (cmap == ff->getNumCmaps()) {
2616 delete ff;
2617 return NULL;
2618 }
2619 codeToGID = (Gushort *)gmallocn(256, sizeof(Gushort));
2620 for (i = 0; i < 256; ++i) {
2621 codeToGID[i] = 0;
2622 if (winAnsiEncoding[i] &&
2623 (u = globalParams->mapNameToUnicode(winAnsiEncoding[i]))) {
2624 codeToGID[i] = ff->mapCodeToGID(cmap, u);
2625 }
2626 }
2627 delete ff;
2628 fontFile = fontEngine->loadTrueTypeFont(id,
2629 dfp->tt.fileName->getCString(),
2630 gFalse, codeToGID, 256);
2631 } else {
2632 return NULL;
2633 }
2634 }
2635
2636 // create the scaled font
2637 fontObj = fontEngine->getFont(fontFile, (SplashCoord *)mat);
2638
2639 return fontObj;
2640 }