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