Bringing apdf from vendor into main branch.
[AROS-Contrib.git] / apdf / xpdf / SplashOutputDev.cc
blob15a44945fb711c835089afbd81dc984db9b5956f
1 //========================================================================
2 //
3 // SplashOutputDev.cc
4 //
5 // Copyright 2003 Glyph & Cog, LLC
6 //
7 //========================================================================
9 #include <aconf.h>
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
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"
40 //------------------------------------------------------------------------
41 // Blend functions
42 //------------------------------------------------------------------------
44 static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest,
45 SplashColorPtr blend, SplashColorMode cm) {
46 int i;
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;
54 static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest,
55 SplashColorPtr blend, SplashColorMode cm) {
56 int i;
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);
64 static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest,
65 SplashColorPtr blend, SplashColorMode cm) {
66 int i;
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);
76 static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest,
77 SplashColorPtr blend, SplashColorMode cm) {
78 int i;
80 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
81 blend[i] = dest[i] < src[i] ? dest[i] : src[i];
85 static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest,
86 SplashColorPtr blend, SplashColorMode cm) {
87 int i;
89 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
90 blend[i] = dest[i] > src[i] ? dest[i] : src[i];
94 static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest,
95 SplashColorPtr blend,
96 SplashColorMode cm) {
97 int i, x;
99 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
100 x = dest[i] + src[i];
101 blend[i] = x <= 255 ? x : 255;
105 static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest,
106 SplashColorPtr blend, SplashColorMode cm) {
107 int i, x;
109 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
110 x = dest[i] - (255 - src[i]);
111 blend[i] = x >= 0 ? x : 0;
115 static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest,
116 SplashColorPtr blend, SplashColorMode cm) {
117 int i;
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);
128 static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest,
129 SplashColorPtr blend, SplashColorMode cm) {
130 int i, x;
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;
144 static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest,
145 SplashColorPtr blend,
146 SplashColorMode cm) {
147 int i;
149 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
150 blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i];
154 static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest,
155 SplashColorPtr blend, SplashColorMode cm) {
156 int i;
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];
164 static void cvtRGBToHSV(Guchar r, Guchar g, Guchar b, int *h, int *s, int *v) {
165 int cmax, cmid, cmin, x;
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; }
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);
185 *s = (255 * (cmax - cmin)) / cmax;
187 *v = cmax;
190 static void cvtHSVToRGB(int h, int s, int v, Guchar *r, Guchar *g, Guchar *b) {
191 int x, f, cmax, cmid, cmin;
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;
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;
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
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;
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
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;
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
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;
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
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;
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
419 //------------------------------------------------------------------------
420 // Font substitutions
421 //------------------------------------------------------------------------
423 struct SplashOutFontSubst {
424 char *name;
425 double mWidth;
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}
448 //------------------------------------------------------------------------
449 // SplashOutFontFileID
450 //------------------------------------------------------------------------
452 class SplashOutFontFileID: public SplashFontFileID {
453 public:
455 SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; }
457 ~SplashOutFontFileID() {}
459 GBool matches(SplashFontFileID *id) {
460 return ((SplashOutFontFileID *)id)->r.num == r.num &&
461 ((SplashOutFontFileID *)id)->r.gen == r.gen;
464 void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
465 int getSubstIdx() { return substIdx; }
467 private:
469 Ref r;
470 int substIdx;
473 //------------------------------------------------------------------------
474 // T3FontCache
475 //------------------------------------------------------------------------
477 struct T3FontCacheTag {
478 Gushort code;
479 Gushort mru; // valid bit (0x8000) and MRU index
482 class T3FontCache {
483 public:
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; }
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
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;
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;
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;
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);
544 T3FontCache::~T3FontCache() {
545 gfree(cacheData);
546 gfree(cacheTags);
549 struct T3GlyphStack {
550 Gushort code; // character code
551 double x, y; // position to draw the glyph
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
558 //----- saved state
559 SplashBitmap *origBitmap;
560 Splash *origSplash;
561 double origCTM4, origCTM5;
563 T3GlyphStack *next; // next object on stack
566 //------------------------------------------------------------------------
567 // SplashOutputDev
568 //------------------------------------------------------------------------
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);
583 xref = NULL;
585 bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, bitmapTopDown);
586 splash = new Splash(bitmap);
587 splash->clear(paperColor);
589 fontEngine = NULL;
591 nT3Fonts = 0;
592 t3GlyphStack = NULL;
594 font = NULL;
595 needFontUpdate = gFalse;
596 textClipPath = NULL;
599 SplashOutputDev::~SplashOutputDev() {
600 int i;
602 for (i = 0; i < nT3Fonts; ++i) {
603 delete t3FontCache[i];
605 if (fontEngine) {
606 delete fontEngine;
608 if (splash) {
609 delete splash;
611 if (bitmap) {
612 delete bitmap;
616 void SplashOutputDev::startDoc(XRef *xrefA) {
617 int i;
619 xref = xrefA;
620 if (fontEngine) {
621 delete fontEngine;
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];
636 nT3Fonts = 0;
639 void SplashOutputDev::startPage(int pageNum, GfxState *state) {
640 int w, h;
641 SplashColor color;
643 w = state ? (int)(state->getPageWidth() + 0.5) : 1;
644 h = state ? (int)(state->getPageHeight() + 0.5) : 1;
645 if (splash) {
646 delete splash;
648 if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
649 if (bitmap) {
650 delete bitmap;
652 bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, bitmapTopDown);
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
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);
696 void SplashOutputDev::endPage() {
699 void SplashOutputDev::drawLink(Link *link, Catalog *catalog) {
700 double x1, y1, x2, y2;
701 LinkBorderStyle *borderStyle;
702 double r, g, b;
703 GfxRGB rgb;
704 GfxGray gray;
705 #if SPLASH_CMYK
706 GfxCMYK cmyk;
707 #endif
708 double *dash;
709 int dashLength;
710 SplashCoord dashList[20];
711 SplashPath *path;
712 int x, y, i;
714 link->getRect(&x1, &y1, &x2, &y2);
715 borderStyle = link->getBorderStyle();
716 if (borderStyle->getWidth() > 0) {
717 borderStyle->getColor(&r, &g, &b);
718 rgb.r = dblToCol(r);
719 rgb.g = dblToCol(g);
720 rgb.b = dblToCol(b);
721 gray = dblToCol(0.299 * r + 0.587 * g + 0.114 * b);
722 if (gray > gfxColorComp1) {
723 gray = gfxColorComp1;
725 #if SPLASH_CMYK
726 cmyk.c = gfxColorComp1 - rgb.r;
727 cmyk.m = gfxColorComp1 - rgb.g;
728 cmyk.y = gfxColorComp1 - rgb.b;
729 cmyk.k = 0;
730 splash->setStrokePattern(getColor(gray, &rgb, &cmyk));
731 #else
732 splash->setStrokePattern(getColor(gray, &rgb));
733 #endif
734 splash->setLineWidth((SplashCoord)borderStyle->getWidth());
735 borderStyle->getDash(&dash, &dashLength);
736 if (borderStyle->getType() == linkBorderDashed && dashLength > 0) {
737 if (dashLength > 20) {
738 dashLength = 20;
740 for (i = 0; i < dashLength; ++i) {
741 dashList[i] = (SplashCoord)dash[i];
743 splash->setLineDash(dashList, dashLength, 0);
745 path = new SplashPath();
746 if (borderStyle->getType() == linkBorderUnderlined) {
747 cvtUserToDev(x1, y1, &x, &y);
748 path->moveTo((SplashCoord)x, (SplashCoord)y);
749 cvtUserToDev(x2, y1, &x, &y);
750 path->lineTo((SplashCoord)x, (SplashCoord)y);
751 } else {
752 cvtUserToDev(x1, y1, &x, &y);
753 path->moveTo((SplashCoord)x, (SplashCoord)y);
754 cvtUserToDev(x2, y1, &x, &y);
755 path->lineTo((SplashCoord)x, (SplashCoord)y);
756 cvtUserToDev(x2, y2, &x, &y);
757 path->lineTo((SplashCoord)x, (SplashCoord)y);
758 cvtUserToDev(x1, y2, &x, &y);
759 path->lineTo((SplashCoord)x, (SplashCoord)y);
760 path->close();
762 splash->stroke(path);
763 delete path;
767 void SplashOutputDev::saveState(GfxState *state) {
768 splash->saveState();
771 void SplashOutputDev::restoreState(GfxState *state) {
772 splash->restoreState();
773 needFontUpdate = gTrue;
776 void SplashOutputDev::updateAll(GfxState *state) {
777 updateLineDash(state);
778 updateLineJoin(state);
779 updateLineCap(state);
780 updateLineWidth(state);
781 updateFlatness(state);
782 updateMiterLimit(state);
783 updateFillColor(state);
784 updateStrokeColor(state);
785 needFontUpdate = gTrue;
788 void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
789 double m21, double m22,
790 double m31, double m32) {
791 updateLineDash(state);
792 updateLineJoin(state);
793 updateLineCap(state);
794 updateLineWidth(state);
797 void SplashOutputDev::updateLineDash(GfxState *state) {
798 double *dashPattern;
799 int dashLength;
800 double dashStart;
801 SplashCoord dash[20];
802 SplashCoord phase;
803 int i;
805 state->getLineDash(&dashPattern, &dashLength, &dashStart);
806 if (dashLength > 20) {
807 dashLength = 20;
809 for (i = 0; i < dashLength; ++i) {
810 dash[i] = (SplashCoord)state->transformWidth(dashPattern[i]);
811 if (dash[i] < 1) {
812 dash[i] = 1;
815 phase = (SplashCoord)state->transformWidth(dashStart);
816 splash->setLineDash(dash, dashLength, phase);
819 void SplashOutputDev::updateFlatness(GfxState *state) {
820 splash->setFlatness(state->getFlatness());
823 void SplashOutputDev::updateLineJoin(GfxState *state) {
824 splash->setLineJoin(state->getLineJoin());
827 void SplashOutputDev::updateLineCap(GfxState *state) {
828 splash->setLineCap(state->getLineCap());
831 void SplashOutputDev::updateMiterLimit(GfxState *state) {
832 splash->setMiterLimit(state->getMiterLimit());
835 void SplashOutputDev::updateLineWidth(GfxState *state) {
836 splash->setLineWidth(state->getTransformedLineWidth());
839 void SplashOutputDev::updateFillColor(GfxState *state) {
840 GfxGray gray;
841 GfxRGB rgb;
842 #if SPLASH_CMYK
843 GfxCMYK cmyk;
844 #endif
846 state->getFillGray(&gray);
847 state->getFillRGB(&rgb);
848 #if SPLASH_CMYK
849 state->getFillCMYK(&cmyk);
850 splash->setFillPattern(getColor(gray, &rgb, &cmyk));
851 #else
852 splash->setFillPattern(getColor(gray, &rgb));
853 #endif
856 void SplashOutputDev::updateStrokeColor(GfxState *state) {
857 GfxGray gray;
858 GfxRGB rgb;
859 #if SPLASH_CMYK
860 GfxCMYK cmyk;
861 #endif
863 state->getStrokeGray(&gray);
864 state->getStrokeRGB(&rgb);
865 #if SPLASH_CMYK
866 state->getStrokeCMYK(&cmyk);
867 splash->setStrokePattern(getColor(gray, &rgb, &cmyk));
868 #else
869 splash->setStrokePattern(getColor(gray, &rgb));
870 #endif
873 #if SPLASH_CMYK
874 SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb,
875 GfxCMYK *cmyk) {
876 #else
877 SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb) {
878 #endif
879 SplashPattern *pattern;
880 SplashColor color0, color1;
881 GfxColorComp r, g, b;
883 if (reverseVideo) {
884 gray = gfxColorComp1 - gray;
885 r = gfxColorComp1 - rgb->r;
886 g = gfxColorComp1 - rgb->g;
887 b = gfxColorComp1 - rgb->b;
888 } else {
889 r = rgb->r;
890 g = rgb->g;
891 b = rgb->b;
894 pattern = NULL; // make gcc happy
895 switch (colorMode) {
896 case splashModeMono1:
897 color0[0] = 0;
898 color1[0] = 1;
899 pattern = new SplashHalftone(color0, color1,
900 splash->getScreen()->copy(),
901 (SplashCoord)colToDbl(gray));
902 break;
903 case splashModeMono8:
904 color1[0] = colToByte(gray);
905 pattern = new SplashSolidColor(color1);
906 break;
907 case splashModeAMono8:
908 color1[0] = 255;
909 color1[1] = colToByte(gray);
910 pattern = new SplashSolidColor(color1);
911 break;
912 case splashModeRGB8:
913 color1[0] = colToByte(r);
914 color1[1] = colToByte(g);
915 color1[2] = colToByte(b);
916 pattern = new SplashSolidColor(color1);
917 break;
918 case splashModeBGR8:
919 color1[2] = colToByte(r);
920 color1[1] = colToByte(g);
921 color1[0] = colToByte(b);
922 pattern = new SplashSolidColor(color1);
923 break;
924 case splashModeARGB8:
925 color1[0] = 255;
926 color1[1] = colToByte(r);
927 color1[2] = colToByte(g);
928 color1[3] = colToByte(b);
929 pattern = new SplashSolidColor(color1);
930 break;
931 case splashModeBGRA8:
932 color1[3] = 255;
933 color1[2] = colToByte(r);
934 color1[1] = colToByte(g);
935 color1[0] = colToByte(b);
936 pattern = new SplashSolidColor(color1);
937 break;
938 #if SPLASH_CMYK
939 case splashModeCMYK8:
940 color1[0] = colToByte(cmyk->c);
941 color1[1] = colToByte(cmyk->m);
942 color1[2] = colToByte(cmyk->y);
943 color1[3] = colToByte(cmyk->k);
944 pattern = new SplashSolidColor(color1);
945 break;
946 case splashModeACMYK8:
947 color1[0] = 255;
948 color1[1] = colToByte(cmyk->c);
949 color1[2] = colToByte(cmyk->m);
950 color1[3] = colToByte(cmyk->y);
951 color1[4] = colToByte(cmyk->k);
952 pattern = new SplashSolidColor(color1);
953 break;
954 #endif
957 return pattern;
960 void SplashOutputDev::updateBlendMode(GfxState *state) {
961 splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]);
964 void SplashOutputDev::updateFillOpacity(GfxState *state) {
965 splash->setFillAlpha((SplashCoord)state->getFillOpacity());
968 void SplashOutputDev::updateStrokeOpacity(GfxState *state) {
969 splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity());
972 void SplashOutputDev::updateFont(GfxState *state) {
973 GfxFont *gfxFont;
974 GfxFontType fontType;
975 SplashOutFontFileID *id;
976 SplashFontFile *fontFile;
977 FoFiTrueType *ff;
978 Ref embRef;
979 Object refObj, strObj;
980 GString *tmpFileName, *fileName, *substName;
981 FILE *tmpFile;
982 Gushort *codeToGID;
983 DisplayFontParam *dfp;
984 CharCodeToUnicode *ctu;
985 double m11, m12, m21, m22, w1, w2;
986 SplashCoord mat[4];
987 char *name;
988 Unicode uBuf[8];
989 int c, substIdx, n, code, cmap;
991 needFontUpdate = gFalse;
992 font = NULL;
993 tmpFileName = NULL;
994 substIdx = -1;
995 dfp = NULL;
997 if (!(gfxFont = state->getFont())) {
998 goto err1;
1000 fontType = gfxFont->getType();
1001 if (fontType == fontType3) {
1002 goto err1;
1005 // check the font file cache
1006 id = new SplashOutFontFileID(gfxFont->getID());
1007 if ((fontFile = fontEngine->getFontFile(id))) {
1008 delete id;
1010 } else {
1012 // if there is an embedded font, write it to disk
1013 if (gfxFont->getEmbeddedFontID(&embRef)) {
1014 if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
1015 error(-1, "Couldn't create temporary font file");
1016 goto err2;
1018 refObj.initRef(embRef.num, embRef.gen);
1019 refObj.fetch(xref, &strObj);
1020 refObj.free();
1021 strObj.streamReset();
1022 while ((c = strObj.streamGetChar()) != EOF) {
1023 fputc(c, tmpFile);
1025 strObj.streamClose();
1026 strObj.free();
1027 fclose(tmpFile);
1028 fileName = tmpFileName;
1030 // if there is an external font file, use it
1031 } else if (!(fileName = gfxFont->getExtFontFile())) {
1033 // look for a display font mapping or a substitute font
1034 if (gfxFont->isCIDFont()) {
1035 if (((GfxCIDFont *)gfxFont)->getCollection()) {
1036 dfp = globalParams->
1037 getDisplayCIDFont(gfxFont->getName(),
1038 ((GfxCIDFont *)gfxFont)->getCollection());
1040 } else {
1041 if (gfxFont->getName()) {
1042 dfp = globalParams->getDisplayFont(gfxFont->getName());
1044 if (!dfp) {
1045 // 8-bit font substitution
1046 if (gfxFont->isFixedWidth()) {
1047 substIdx = 8;
1048 } else if (gfxFont->isSerif()) {
1049 substIdx = 4;
1050 } else {
1051 substIdx = 0;
1053 if (gfxFont->isBold()) {
1054 substIdx += 2;
1056 if (gfxFont->isItalic()) {
1057 substIdx += 1;
1059 substName = new GString(splashOutSubstFonts[substIdx].name);
1060 dfp = globalParams->getDisplayFont(substName);
1061 delete substName;
1062 id->setSubstIdx(substIdx);
1065 if (!dfp) {
1066 error(-1, "Couldn't find a font for '%s'",
1067 gfxFont->getName() ? gfxFont->getName()->getCString()
1068 : "(unnamed)");
1069 goto err2;
1071 switch (dfp->kind) {
1072 case displayFontT1:
1073 fileName = dfp->t1.fileName;
1074 fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1;
1075 break;
1076 case displayFontTT:
1077 fileName = dfp->tt.fileName;
1078 fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType;
1079 break;
1083 // load the font file
1084 switch (fontType) {
1085 case fontType1:
1086 if (!(fontFile = fontEngine->loadType1Font(
1088 fileName->getCString(),
1089 fileName == tmpFileName,
1090 ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1091 error(-1, "Couldn't create a font for '%s'",
1092 gfxFont->getName() ? gfxFont->getName()->getCString()
1093 : "(unnamed)");
1094 goto err2;
1096 break;
1097 case fontType1C:
1098 if (!(fontFile = fontEngine->loadType1CFont(
1100 fileName->getCString(),
1101 fileName == tmpFileName,
1102 ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1103 error(-1, "Couldn't create a font for '%s'",
1104 gfxFont->getName() ? gfxFont->getName()->getCString()
1105 : "(unnamed)");
1106 goto err2;
1108 break;
1109 case fontTrueType:
1110 if ((ff = FoFiTrueType::load(fileName->getCString()))) {
1111 codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
1112 n = 256;
1113 delete ff;
1114 } else {
1115 codeToGID = NULL;
1116 n = 0;
1118 if (!(fontFile = fontEngine->loadTrueTypeFont(
1120 fileName->getCString(),
1121 fileName == tmpFileName,
1122 codeToGID, n))) {
1123 error(-1, "Couldn't create a font for '%s'",
1124 gfxFont->getName() ? gfxFont->getName()->getCString()
1125 : "(unnamed)");
1126 goto err2;
1128 break;
1129 case fontCIDType0:
1130 case fontCIDType0C:
1131 if (!(fontFile = fontEngine->loadCIDFont(
1133 fileName->getCString(),
1134 fileName == tmpFileName))) {
1135 error(-1, "Couldn't create a font for '%s'",
1136 gfxFont->getName() ? gfxFont->getName()->getCString()
1137 : "(unnamed)");
1138 goto err2;
1140 break;
1141 case fontCIDType2:
1142 codeToGID = NULL;
1143 n = 0;
1144 if (dfp) {
1145 // create a CID-to-GID mapping, via Unicode
1146 if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
1147 if ((ff = FoFiTrueType::load(fileName->getCString()))) {
1148 // look for a Unicode cmap
1149 for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
1150 if ((ff->getCmapPlatform(cmap) == 3 &&
1151 ff->getCmapEncoding(cmap) == 1) ||
1152 ff->getCmapPlatform(cmap) == 0) {
1153 break;
1156 if (cmap < ff->getNumCmaps()) {
1157 // map CID -> Unicode -> GID
1158 n = ctu->getLength();
1159 codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort));
1160 for (code = 0; code < n; ++code) {
1161 if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
1162 codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]);
1163 } else {
1164 codeToGID[code] = 0;
1168 delete ff;
1170 ctu->decRefCnt();
1171 } else {
1172 error(-1, "Couldn't find a mapping to Unicode for font '%s'",
1173 gfxFont->getName() ? gfxFont->getName()->getCString()
1174 : "(unnamed)");
1176 } else {
1177 if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
1178 n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
1179 codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort));
1180 memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1181 n * sizeof(Gushort));
1184 if (!(fontFile = fontEngine->loadTrueTypeFont(
1186 fileName->getCString(),
1187 fileName == tmpFileName,
1188 codeToGID, n))) {
1189 error(-1, "Couldn't create a font for '%s'",
1190 gfxFont->getName() ? gfxFont->getName()->getCString()
1191 : "(unnamed)");
1192 goto err2;
1194 break;
1195 default:
1196 // this shouldn't happen
1197 goto err2;
1201 // get the font matrix
1202 state->getFontTransMat(&m11, &m12, &m21, &m22);
1203 m11 *= state->getHorizScaling();
1204 m12 *= state->getHorizScaling();
1206 // for substituted fonts: adjust the font matrix -- compare the
1207 // width of 'm' in the original font and the substituted font
1208 substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx();
1209 if (substIdx >= 0) {
1210 for (code = 0; code < 256; ++code) {
1211 if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
1212 name[0] == 'm' && name[1] == '\0') {
1213 break;
1216 if (code < 256) {
1217 w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
1218 w2 = splashOutSubstFonts[substIdx].mWidth;
1219 if (!gfxFont->isSymbolic()) {
1220 // if real font is substantially narrower than substituted
1221 // font, reduce the font size accordingly
1222 if (w1 > 0.01 && w1 < 0.9 * w2) {
1223 w1 /= w2;
1224 m11 *= w1;
1225 m21 *= w1;
1231 // create the scaled font
1232 mat[0] = m11; mat[1] = -m12;
1233 mat[2] = m21; mat[3] = -m22;
1234 if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.01) {
1235 // avoid a singular (or close-to-singular) matrix
1236 mat[0] = 0.01; mat[1] = 0;
1237 mat[2] = 0; mat[3] = 0.01;
1239 font = fontEngine->getFont(fontFile, mat);
1241 if (tmpFileName) {
1242 delete tmpFileName;
1244 return;
1246 err2:
1247 delete id;
1248 err1:
1249 if (tmpFileName) {
1250 delete tmpFileName;
1252 return;
1255 void SplashOutputDev::stroke(GfxState *state) {
1256 SplashPath *path;
1258 path = convertPath(state, state->getPath());
1259 splash->stroke(path);
1260 delete path;
1263 void SplashOutputDev::fill(GfxState *state) {
1264 SplashPath *path;
1266 path = convertPath(state, state->getPath());
1267 splash->fill(path, gFalse);
1268 delete path;
1271 void SplashOutputDev::eoFill(GfxState *state) {
1272 SplashPath *path;
1274 path = convertPath(state, state->getPath());
1275 splash->fill(path, gTrue);
1276 delete path;
1279 void SplashOutputDev::clip(GfxState *state) {
1280 SplashPath *path;
1282 path = convertPath(state, state->getPath());
1283 splash->clipToPath(path, gFalse);
1284 delete path;
1287 void SplashOutputDev::eoClip(GfxState *state) {
1288 SplashPath *path;
1290 path = convertPath(state, state->getPath());
1291 splash->clipToPath(path, gTrue);
1292 delete path;
1295 SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path) {
1296 SplashPath *sPath;
1297 GfxSubpath *subpath;
1298 double x1, y1, x2, y2, x3, y3;
1299 int i, j;
1301 sPath = new SplashPath();
1302 for (i = 0; i < path->getNumSubpaths(); ++i) {
1303 subpath = path->getSubpath(i);
1304 if (subpath->getNumPoints() > 0) {
1305 state->transform(subpath->getX(0), subpath->getY(0), &x1, &y1);
1306 sPath->moveTo((SplashCoord)x1, (SplashCoord)y1);
1307 j = 1;
1308 while (j < subpath->getNumPoints()) {
1309 if (subpath->getCurve(j)) {
1310 state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
1311 state->transform(subpath->getX(j+1), subpath->getY(j+1), &x2, &y2);
1312 state->transform(subpath->getX(j+2), subpath->getY(j+2), &x3, &y3);
1313 sPath->curveTo((SplashCoord)x1, (SplashCoord)y1,
1314 (SplashCoord)x2, (SplashCoord)y2,
1315 (SplashCoord)x3, (SplashCoord)y3);
1316 j += 3;
1317 } else {
1318 state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
1319 sPath->lineTo((SplashCoord)x1, (SplashCoord)y1);
1320 ++j;
1323 if (subpath->isClosed()) {
1324 sPath->close();
1328 return sPath;
1331 void SplashOutputDev::drawChar(GfxState *state, double x, double y,
1332 double dx, double dy,
1333 double originX, double originY,
1334 CharCode code, int nBytes,
1335 Unicode *u, int uLen) {
1336 double x1, y1;
1337 SplashPath *path;
1338 int render;
1340 if (needFontUpdate) {
1341 updateFont(state);
1343 if (!font) {
1344 return;
1347 // check for invisible text -- this is used by Acrobat Capture
1348 render = state->getRender();
1349 if (render == 3) {
1350 return;
1353 x -= originX;
1354 y -= originY;
1355 state->transform(x, y, &x1, &y1);
1357 // fill
1358 if (!(render & 1)) {
1359 splash->fillChar((SplashCoord)x1, (SplashCoord)y1, code, font);
1362 // stroke
1363 if ((render & 3) == 1 || (render & 3) == 2) {
1364 if ((path = font->getGlyphPath(code))) {
1365 path->offset((SplashCoord)x1, (SplashCoord)y1);
1366 splash->stroke(path);
1367 delete path;
1371 // clip
1372 if (render & 4) {
1373 path = font->getGlyphPath(code);
1374 path->offset((SplashCoord)x1, (SplashCoord)y1);
1375 if (textClipPath) {
1376 textClipPath->append(path);
1377 delete path;
1378 } else {
1379 textClipPath = path;
1384 GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
1385 double dx, double dy,
1386 CharCode code, Unicode *u, int uLen) {
1387 GfxFont *gfxFont;
1388 Ref *fontID;
1389 double *ctm, *bbox;
1390 T3FontCache *t3Font;
1391 T3GlyphStack *t3gs;
1392 double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
1393 int i, j;
1395 if (!(gfxFont = state->getFont())) {
1396 return gFalse;
1398 fontID = gfxFont->getID();
1399 ctm = state->getCTM();
1400 state->transform(0, 0, &xt, &yt);
1402 // is it the first (MRU) font in the cache?
1403 if (!(nT3Fonts > 0 &&
1404 t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
1406 // is the font elsewhere in the cache?
1407 for (i = 1; i < nT3Fonts; ++i) {
1408 if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
1409 t3Font = t3FontCache[i];
1410 for (j = i; j > 0; --j) {
1411 t3FontCache[j] = t3FontCache[j - 1];
1413 t3FontCache[0] = t3Font;
1414 break;
1417 if (i >= nT3Fonts) {
1419 // create new entry in the font cache
1420 if (nT3Fonts == splashOutT3FontCacheSize) {
1421 delete t3FontCache[nT3Fonts - 1];
1422 --nT3Fonts;
1424 for (j = nT3Fonts; j > 0; --j) {
1425 t3FontCache[j] = t3FontCache[j - 1];
1427 ++nT3Fonts;
1428 bbox = gfxFont->getFontBBox();
1429 if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
1430 // broken bounding box -- just take a guess
1431 xMin = xt - 5;
1432 xMax = xMin + 30;
1433 yMax = yt + 15;
1434 yMin = yMax - 45;
1435 } else {
1436 state->transform(bbox[0], bbox[1], &x1, &y1);
1437 xMin = xMax = x1;
1438 yMin = yMax = y1;
1439 state->transform(bbox[0], bbox[3], &x1, &y1);
1440 if (x1 < xMin) {
1441 xMin = x1;
1442 } else if (x1 > xMax) {
1443 xMax = x1;
1445 if (y1 < yMin) {
1446 yMin = y1;
1447 } else if (y1 > yMax) {
1448 yMax = y1;
1450 state->transform(bbox[2], bbox[1], &x1, &y1);
1451 if (x1 < xMin) {
1452 xMin = x1;
1453 } else if (x1 > xMax) {
1454 xMax = x1;
1456 if (y1 < yMin) {
1457 yMin = y1;
1458 } else if (y1 > yMax) {
1459 yMax = y1;
1461 state->transform(bbox[2], bbox[3], &x1, &y1);
1462 if (x1 < xMin) {
1463 xMin = x1;
1464 } else if (x1 > xMax) {
1465 xMax = x1;
1467 if (y1 < yMin) {
1468 yMin = y1;
1469 } else if (y1 > yMax) {
1470 yMax = y1;
1473 t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
1474 (int)floor(xMin - xt),
1475 (int)floor(yMin - yt),
1476 (int)ceil(xMax) - (int)floor(xMin) + 3,
1477 (int)ceil(yMax) - (int)floor(yMin) + 3,
1478 colorMode != splashModeMono1);
1481 t3Font = t3FontCache[0];
1483 // is the glyph in the cache?
1484 i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
1485 for (j = 0; j < t3Font->cacheAssoc; ++j) {
1486 if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
1487 t3Font->cacheTags[i+j].code == code) {
1488 drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
1489 t3Font->cacheData + (i+j) * t3Font->glyphSize,
1490 xt, yt);
1491 return gTrue;
1495 // push a new Type 3 glyph record
1496 t3gs = new T3GlyphStack();
1497 t3gs->next = t3GlyphStack;
1498 t3GlyphStack = t3gs;
1499 t3GlyphStack->code = code;
1500 t3GlyphStack->x = xt;
1501 t3GlyphStack->y = yt;
1502 t3GlyphStack->cache = t3Font;
1503 t3GlyphStack->cacheTag = NULL;
1504 t3GlyphStack->cacheData = NULL;
1506 return gFalse;
1509 void SplashOutputDev::endType3Char(GfxState *state) {
1510 T3GlyphStack *t3gs;
1511 double *ctm;
1513 if (t3GlyphStack->cacheTag) {
1514 memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
1515 t3GlyphStack->cache->glyphSize);
1516 delete bitmap;
1517 delete splash;
1518 bitmap = t3GlyphStack->origBitmap;
1519 splash = t3GlyphStack->origSplash;
1520 ctm = state->getCTM();
1521 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
1522 t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
1523 drawType3Glyph(t3GlyphStack->cache,
1524 t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
1525 t3GlyphStack->x, t3GlyphStack->y);
1527 t3gs = t3GlyphStack;
1528 t3GlyphStack = t3gs->next;
1529 delete t3gs;
1532 void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
1535 void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
1536 double llx, double lly, double urx, double ury) {
1537 double *ctm;
1538 T3FontCache *t3Font;
1539 SplashColor color;
1540 double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
1541 int i, j;
1543 t3Font = t3GlyphStack->cache;
1545 // check for a valid bbox
1546 state->transform(0, 0, &xt, &yt);
1547 state->transform(llx, lly, &x1, &y1);
1548 xMin = xMax = x1;
1549 yMin = yMax = y1;
1550 state->transform(llx, ury, &x1, &y1);
1551 if (x1 < xMin) {
1552 xMin = x1;
1553 } else if (x1 > xMax) {
1554 xMax = x1;
1556 if (y1 < yMin) {
1557 yMin = y1;
1558 } else if (y1 > yMax) {
1559 yMax = y1;
1561 state->transform(urx, lly, &x1, &y1);
1562 if (x1 < xMin) {
1563 xMin = x1;
1564 } else if (x1 > xMax) {
1565 xMax = x1;
1567 if (y1 < yMin) {
1568 yMin = y1;
1569 } else if (y1 > yMax) {
1570 yMax = y1;
1572 state->transform(urx, ury, &x1, &y1);
1573 if (x1 < xMin) {
1574 xMin = x1;
1575 } else if (x1 > xMax) {
1576 xMax = x1;
1578 if (y1 < yMin) {
1579 yMin = y1;
1580 } else if (y1 > yMax) {
1581 yMax = y1;
1583 if (xMin - xt < t3Font->glyphX ||
1584 yMin - yt < t3Font->glyphY ||
1585 xMax - xt > t3Font->glyphX + t3Font->glyphW ||
1586 yMax - yt > t3Font->glyphY + t3Font->glyphH) {
1587 error(-1, "Bad bounding box in Type 3 glyph");
1588 return;
1591 // allocate a cache entry
1592 i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
1593 for (j = 0; j < t3Font->cacheAssoc; ++j) {
1594 if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
1595 t3Font->cacheTags[i+j].mru = 0x8000;
1596 t3Font->cacheTags[i+j].code = t3GlyphStack->code;
1597 t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
1598 t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
1599 } else {
1600 ++t3Font->cacheTags[i+j].mru;
1604 // save state
1605 t3GlyphStack->origBitmap = bitmap;
1606 t3GlyphStack->origSplash = splash;
1607 ctm = state->getCTM();
1608 t3GlyphStack->origCTM4 = ctm[4];
1609 t3GlyphStack->origCTM5 = ctm[5];
1611 // create the temporary bitmap
1612 if (colorMode == splashModeMono1) {
1613 bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
1614 splashModeMono1);
1615 splash = new Splash(bitmap);
1616 color[0] = 0;
1617 splash->clear(color);
1618 color[0] = 1;
1619 } else {
1620 bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
1621 splashModeMono8);
1622 splash = new Splash(bitmap);
1623 color[0] = 0x00;
1624 splash->clear(color);
1625 color[0] = 0xff;
1627 splash->setFillPattern(new SplashSolidColor(color));
1628 splash->setStrokePattern(new SplashSolidColor(color));
1629 //~ this should copy other state from t3GlyphStack->origSplash?
1630 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
1631 -t3Font->glyphX, -t3Font->glyphY);
1634 void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font,
1635 T3FontCacheTag *tag, Guchar *data,
1636 double x, double y) {
1637 SplashGlyphBitmap glyph;
1639 glyph.x = -t3Font->glyphX;
1640 glyph.y = -t3Font->glyphY;
1641 glyph.w = t3Font->glyphW;
1642 glyph.h = t3Font->glyphH;
1643 glyph.aa = colorMode != splashModeMono1;
1644 glyph.data = data;
1645 glyph.freeData = gFalse;
1646 splash->fillGlyph((SplashCoord)x, (SplashCoord)y, &glyph);
1649 void SplashOutputDev::endTextObject(GfxState *state) {
1650 if (textClipPath) {
1651 splash->clipToPath(textClipPath, gFalse);
1652 delete textClipPath;
1653 textClipPath = NULL;
1657 struct SplashOutImageMaskData {
1658 ImageStream *imgStr;
1659 GBool invert;
1660 int width, height, y;
1663 GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
1664 SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
1665 Guchar *p;
1666 SplashColorPtr q;
1667 int x;
1669 if (imgMaskData->y == imgMaskData->height) {
1670 return gFalse;
1672 for (x = 0, p = imgMaskData->imgStr->getLine(), q = line;
1673 x < imgMaskData->width;
1674 ++x) {
1675 *q++ = *p++ ^ imgMaskData->invert;
1677 ++imgMaskData->y;
1678 return gTrue;
1681 void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1682 int width, int height, GBool invert,
1683 GBool inlineImg) {
1684 double *ctm;
1685 SplashCoord mat[6];
1686 SplashOutImageMaskData imgMaskData;
1688 ctm = state->getCTM();
1689 mat[0] = ctm[0];
1690 mat[1] = ctm[1];
1691 mat[2] = -ctm[2];
1692 mat[3] = -ctm[3];
1693 mat[4] = ctm[2] + ctm[4];
1694 mat[5] = ctm[3] + ctm[5];
1696 imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
1697 imgMaskData.imgStr->reset();
1698 imgMaskData.invert = invert ? 0 : 1;
1699 imgMaskData.width = width;
1700 imgMaskData.height = height;
1701 imgMaskData.y = 0;
1703 splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat);
1704 if (inlineImg) {
1705 while (imgMaskData.y < height) {
1706 imgMaskData.imgStr->getLine();
1707 ++imgMaskData.y;
1711 delete imgMaskData.imgStr;
1712 str->close();
1715 struct SplashOutImageData {
1716 ImageStream *imgStr;
1717 GfxImageColorMap *colorMap;
1718 SplashColorPtr lookup;
1719 int *maskColors;
1720 SplashColorMode colorMode;
1721 int width, height, y;
1724 GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr line) {
1725 SplashOutImageData *imgData = (SplashOutImageData *)data;
1726 Guchar *p;
1727 SplashColorPtr q, col;
1728 GfxRGB rgb;
1729 GfxGray gray;
1730 #if SPLASH_CMYK
1731 GfxCMYK cmyk;
1732 #endif
1733 int nComps, x;
1735 if (imgData->y == imgData->height) {
1736 return gFalse;
1739 nComps = imgData->colorMap->getNumPixelComps();
1741 if (imgData->lookup) {
1742 switch (imgData->colorMode) {
1743 case splashModeMono1:
1744 case splashModeMono8:
1745 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1746 x < imgData->width;
1747 ++x, ++p) {
1748 *q++ = imgData->lookup[*p];
1750 break;
1751 case splashModeRGB8:
1752 case splashModeBGR8:
1753 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1754 x < imgData->width;
1755 ++x, ++p) {
1756 col = &imgData->lookup[3 * *p];
1757 *q++ = col[0];
1758 *q++ = col[1];
1759 *q++ = col[2];
1761 break;
1762 #if SPLASH_CMYK
1763 case splashModeCMYK8:
1764 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1765 x < imgData->width;
1766 ++x, ++p) {
1767 col = &imgData->lookup[4 * *p];
1768 *q++ = col[0];
1769 *q++ = col[1];
1770 *q++ = col[2];
1771 *q++ = col[3];
1773 break;
1774 #endif
1775 case splashModeAMono8:
1776 case splashModeARGB8:
1777 case splashModeBGRA8:
1778 #if SPLASH_CMYK
1779 case splashModeACMYK8:
1780 #endif
1781 //~ unimplemented
1782 break;
1784 } else {
1785 switch (imgData->colorMode) {
1786 case splashModeMono1:
1787 case splashModeMono8:
1788 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1789 x < imgData->width;
1790 ++x, p += nComps) {
1791 imgData->colorMap->getGray(p, &gray);
1792 *q++ = colToByte(gray);
1794 break;
1795 case splashModeRGB8:
1796 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1797 x < imgData->width;
1798 ++x, p += nComps) {
1799 imgData->colorMap->getRGB(p, &rgb);
1800 *q++ = colToByte(rgb.r);
1801 *q++ = colToByte(rgb.g);
1802 *q++ = colToByte(rgb.b);
1804 break;
1805 case splashModeBGR8:
1806 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1807 x < imgData->width;
1808 ++x, p += nComps) {
1809 imgData->colorMap->getRGB(p, &rgb);
1810 *q++ = colToByte(rgb.b);
1811 *q++ = colToByte(rgb.g);
1812 *q++ = colToByte(rgb.r);
1814 break;
1815 #if SPLASH_CMYK
1816 case splashModeCMYK8:
1817 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1818 x < imgData->width;
1819 ++x, p += nComps) {
1820 imgData->colorMap->getCMYK(p, &cmyk);
1821 *q++ = colToByte(cmyk.c);
1822 *q++ = colToByte(cmyk.m);
1823 *q++ = colToByte(cmyk.y);
1824 *q++ = colToByte(cmyk.k);
1826 break;
1827 #endif
1828 case splashModeAMono8:
1829 case splashModeARGB8:
1830 case splashModeBGRA8:
1831 #if SPLASH_CMYK
1832 case splashModeACMYK8:
1833 #endif
1834 //~ unimplemented
1835 break;
1839 ++imgData->y;
1840 return gTrue;
1843 GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr line) {
1844 SplashOutImageData *imgData = (SplashOutImageData *)data;
1845 Guchar *p;
1846 SplashColorPtr q, col;
1847 GfxRGB rgb;
1848 GfxGray gray;
1849 #if SPLASH_CMYK
1850 GfxCMYK cmyk;
1851 #endif
1852 Guchar alpha;
1853 int nComps, x, i;
1855 if (imgData->y == imgData->height) {
1856 return gFalse;
1859 nComps = imgData->colorMap->getNumPixelComps();
1861 for (x = 0, p = imgData->imgStr->getLine(), q = line;
1862 x < imgData->width;
1863 ++x, p += nComps) {
1864 alpha = 0;
1865 for (i = 0; i < nComps; ++i) {
1866 if (p[i] < imgData->maskColors[2*i] ||
1867 p[i] > imgData->maskColors[2*i+1]) {
1868 alpha = 0xff;
1869 break;
1872 if (imgData->lookup) {
1873 switch (imgData->colorMode) {
1874 case splashModeMono1:
1875 case splashModeMono8:
1876 *q++ = alpha;
1877 *q++ = imgData->lookup[*p];
1878 break;
1879 case splashModeRGB8:
1880 *q++ = alpha;
1881 col = &imgData->lookup[3 * *p];
1882 *q++ = col[0];
1883 *q++ = col[1];
1884 *q++ = col[2];
1885 break;
1886 case splashModeBGR8:
1887 col = &imgData->lookup[3 * *p];
1888 *q++ = col[0];
1889 *q++ = col[1];
1890 *q++ = col[2];
1891 *q++ = alpha;
1892 break;
1893 #if SPLASH_CMYK
1894 case splashModeCMYK8:
1895 *q++ = alpha;
1896 col = &imgData->lookup[4 * *p];
1897 *q++ = col[0];
1898 *q++ = col[1];
1899 *q++ = col[2];
1900 *q++ = col[3];
1901 break;
1902 #endif
1903 case splashModeAMono8:
1904 case splashModeARGB8:
1905 case splashModeBGRA8:
1906 #if SPLASH_CMYK
1907 case splashModeACMYK8:
1908 #endif
1909 //~ unimplemented
1910 break;
1912 } else {
1913 switch (imgData->colorMode) {
1914 case splashModeMono1:
1915 case splashModeMono8:
1916 imgData->colorMap->getGray(p, &gray);
1917 *q++ = alpha;
1918 *q++ = colToByte(gray);
1919 break;
1920 case splashModeRGB8:
1921 imgData->colorMap->getRGB(p, &rgb);
1922 *q++ = alpha;
1923 *q++ = colToByte(rgb.r);
1924 *q++ = colToByte(rgb.g);
1925 *q++ = colToByte(rgb.b);
1926 break;
1927 case splashModeBGR8:
1928 imgData->colorMap->getRGB(p, &rgb);
1929 *q++ = colToByte(rgb.b);
1930 *q++ = colToByte(rgb.g);
1931 *q++ = colToByte(rgb.r);
1932 *q++ = alpha;
1933 break;
1934 #if SPLASH_CMYK
1935 case splashModeCMYK8:
1936 imgData->colorMap->getCMYK(p, &cmyk);
1937 *q++ = alpha;
1938 *q++ = colToByte(cmyk.c);
1939 *q++ = colToByte(cmyk.m);
1940 *q++ = colToByte(cmyk.y);
1941 *q++ = colToByte(cmyk.k);
1942 break;
1943 #endif
1944 case splashModeAMono8:
1945 case splashModeARGB8:
1946 case splashModeBGRA8:
1947 #if SPLASH_CMYK
1948 case splashModeACMYK8:
1949 #endif
1950 //~ unimplemented
1951 break;
1956 ++imgData->y;
1957 return gTrue;
1960 void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1961 int width, int height,
1962 GfxImageColorMap *colorMap,
1963 int *maskColors, GBool inlineImg) {
1964 double *ctm;
1965 SplashCoord mat[6];
1966 SplashOutImageData imgData;
1967 SplashColorMode srcMode;
1968 SplashImageSource src;
1969 GfxGray gray;
1970 GfxRGB rgb;
1971 #if SPLASH_CMYK
1972 GfxCMYK cmyk;
1973 #endif
1974 Guchar pix;
1975 int n, i;
1977 ctm = state->getCTM();
1978 mat[0] = ctm[0];
1979 mat[1] = ctm[1];
1980 mat[2] = -ctm[2];
1981 mat[3] = -ctm[3];
1982 mat[4] = ctm[2] + ctm[4];
1983 mat[5] = ctm[3] + ctm[5];
1985 imgData.imgStr = new ImageStream(str, width,
1986 colorMap->getNumPixelComps(),
1987 colorMap->getBits());
1988 imgData.imgStr->reset();
1989 imgData.colorMap = colorMap;
1990 imgData.maskColors = maskColors;
1991 imgData.colorMode = colorMode;
1992 imgData.width = width;
1993 imgData.height = height;
1994 imgData.y = 0;
1996 // special case for one-channel (monochrome/gray/separation) images:
1997 // build a lookup table here
1998 imgData.lookup = NULL;
1999 if (colorMap->getNumPixelComps() == 1) {
2000 n = 1 << colorMap->getBits();
2001 switch (colorMode) {
2002 case splashModeMono1:
2003 case splashModeMono8:
2004 imgData.lookup = (SplashColorPtr)gmalloc(n);
2005 for (i = 0; i < n; ++i) {
2006 pix = (Guchar)i;
2007 colorMap->getGray(&pix, &gray);
2008 imgData.lookup[i] = colToByte(gray);
2010 break;
2011 case splashModeRGB8:
2012 imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2013 for (i = 0; i < n; ++i) {
2014 pix = (Guchar)i;
2015 colorMap->getRGB(&pix, &rgb);
2016 imgData.lookup[3*i] = colToByte(rgb.r);
2017 imgData.lookup[3*i+1] = colToByte(rgb.g);
2018 imgData.lookup[3*i+2] = colToByte(rgb.b);
2020 break;
2021 case splashModeBGR8:
2022 imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2023 for (i = 0; i < n; ++i) {
2024 pix = (Guchar)i;
2025 colorMap->getRGB(&pix, &rgb);
2026 imgData.lookup[3*i] = colToByte(rgb.b);
2027 imgData.lookup[3*i+1] = colToByte(rgb.g);
2028 imgData.lookup[3*i+2] = colToByte(rgb.r);
2030 break;
2031 #if SPLASH_CMYK
2032 case splashModeCMYK8:
2033 imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
2034 for (i = 0; i < n; ++i) {
2035 pix = (Guchar)i;
2036 colorMap->getCMYK(&pix, &cmyk);
2037 imgData.lookup[4*i] = colToByte(cmyk.c);
2038 imgData.lookup[4*i+1] = colToByte(cmyk.m);
2039 imgData.lookup[4*i+2] = colToByte(cmyk.y);
2040 imgData.lookup[4*i+3] = colToByte(cmyk.k);
2042 break;
2043 #endif
2044 default:
2045 //~ unimplemented
2046 break;
2050 switch (colorMode) {
2051 case splashModeMono1:
2052 case splashModeMono8:
2053 srcMode = maskColors ? splashModeAMono8 : splashModeMono8;
2054 break;
2055 case splashModeRGB8:
2056 srcMode = maskColors ? splashModeARGB8 : splashModeRGB8;
2057 break;
2058 case splashModeBGR8:
2059 srcMode = maskColors ? splashModeBGRA8 : splashModeBGR8;
2060 break;
2061 #if SPLASH_CMYK
2062 case splashModeCMYK8:
2063 srcMode = maskColors ? splashModeACMYK8 : splashModeCMYK8;
2064 break;
2065 #endif
2066 default:
2067 //~ unimplemented
2068 srcMode = splashModeRGB8;
2069 break;
2071 src = maskColors ? &alphaImageSrc : &imageSrc;
2072 splash->drawImage(src, &imgData, srcMode, width, height, mat);
2073 if (inlineImg) {
2074 while (imgData.y < height) {
2075 imgData.imgStr->getLine();
2076 ++imgData.y;
2080 gfree(imgData.lookup);
2081 delete imgData.imgStr;
2082 str->close();
2085 struct SplashOutMaskedImageData {
2086 ImageStream *imgStr;
2087 GfxImageColorMap *colorMap;
2088 SplashBitmap *mask;
2089 SplashColorPtr lookup;
2090 SplashColorMode colorMode;
2091 int width, height, y;
2094 GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr line) {
2095 SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;
2096 Guchar *p;
2097 SplashColor maskColor;
2098 SplashColorPtr q, col;
2099 GfxRGB rgb;
2100 GfxGray gray;
2101 #if SPLASH_CMYK
2102 GfxCMYK cmyk;
2103 #endif
2104 Guchar alpha;
2105 int nComps, x;
2107 if (imgData->y == imgData->height) {
2108 return gFalse;
2111 nComps = imgData->colorMap->getNumPixelComps();
2113 for (x = 0, p = imgData->imgStr->getLine(), q = line;
2114 x < imgData->width;
2115 ++x, p += nComps) {
2116 imgData->mask->getPixel(x, imgData->y, maskColor);
2117 alpha = maskColor[0] ? 0xff : 0x00;
2118 if (imgData->lookup) {
2119 switch (imgData->colorMode) {
2120 case splashModeMono1:
2121 case splashModeMono8:
2122 *q++ = alpha;
2123 *q++ = imgData->lookup[*p];
2124 break;
2125 case splashModeRGB8:
2126 *q++ = alpha;
2127 col = &imgData->lookup[3 * *p];
2128 *q++ = col[0];
2129 *q++ = col[1];
2130 *q++ = col[2];
2131 break;
2132 case splashModeBGR8:
2133 col = &imgData->lookup[3 * *p];
2134 *q++ = col[0];
2135 *q++ = col[1];
2136 *q++ = col[2];
2137 *q++ = alpha;
2138 break;
2139 #if SPLASH_CMYK
2140 case splashModeCMYK8:
2141 *q++ = alpha;
2142 col = &imgData->lookup[4 * *p];
2143 *q++ = col[0];
2144 *q++ = col[1];
2145 *q++ = col[2];
2146 *q++ = col[3];
2147 break;
2148 #endif
2149 case splashModeAMono8:
2150 case splashModeARGB8:
2151 case splashModeBGRA8:
2152 #if SPLASH_CMYK
2153 case splashModeACMYK8:
2154 #endif
2155 //~ unimplemented
2156 break;
2158 } else {
2159 switch (imgData->colorMode) {
2160 case splashModeMono1:
2161 case splashModeMono8:
2162 imgData->colorMap->getGray(p, &gray);
2163 *q++ = alpha;
2164 *q++ = colToByte(gray);
2165 break;
2166 case splashModeRGB8:
2167 imgData->colorMap->getRGB(p, &rgb);
2168 *q++ = alpha;
2169 *q++ = colToByte(rgb.r);
2170 *q++ = colToByte(rgb.g);
2171 *q++ = colToByte(rgb.b);
2172 break;
2173 case splashModeBGR8:
2174 imgData->colorMap->getRGB(p, &rgb);
2175 *q++ = colToByte(rgb.b);
2176 *q++ = colToByte(rgb.g);
2177 *q++ = colToByte(rgb.r);
2178 *q++ = alpha;
2179 break;
2180 #if SPLASH_CMYK
2181 case splashModeCMYK8:
2182 imgData->colorMap->getCMYK(p, &cmyk);
2183 *q++ = alpha;
2184 *q++ = colToByte(cmyk.c);
2185 *q++ = colToByte(cmyk.m);
2186 *q++ = colToByte(cmyk.y);
2187 *q++ = colToByte(cmyk.k);
2188 break;
2189 #endif
2190 case splashModeAMono8:
2191 case splashModeARGB8:
2192 case splashModeBGRA8:
2193 #if SPLASH_CMYK
2194 case splashModeACMYK8:
2195 #endif
2196 //~ unimplemented
2197 break;
2202 ++imgData->y;
2203 return gTrue;
2206 void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
2207 Stream *str, int width, int height,
2208 GfxImageColorMap *colorMap,
2209 Stream *maskStr, int maskWidth,
2210 int maskHeight, GBool maskInvert) {
2211 double *ctm;
2212 SplashCoord mat[6];
2213 SplashOutMaskedImageData imgData;
2214 SplashOutImageMaskData imgMaskData;
2215 SplashColorMode srcMode;
2216 SplashBitmap *maskBitmap;
2217 Splash *maskSplash;
2218 SplashColor maskColor;
2219 GfxGray gray;
2220 GfxRGB rgb;
2221 #if SPLASH_CMYK
2222 GfxCMYK cmyk;
2223 #endif
2224 Guchar pix;
2225 int n, i;
2227 //----- scale the mask image to the same size as the source image
2229 mat[0] = (SplashCoord)width;
2230 mat[1] = 0;
2231 mat[2] = 0;
2232 mat[3] = (SplashCoord)height;
2233 mat[4] = 0;
2234 mat[5] = 0;
2235 imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
2236 imgMaskData.imgStr->reset();
2237 imgMaskData.invert = maskInvert ? 0 : 1;
2238 imgMaskData.width = maskWidth;
2239 imgMaskData.height = maskHeight;
2240 imgMaskData.y = 0;
2241 maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1);
2242 maskSplash = new Splash(maskBitmap);
2243 maskColor[0] = 0;
2244 maskSplash->clear(maskColor);
2245 maskColor[0] = 1;
2246 maskSplash->setFillPattern(new SplashSolidColor(maskColor));
2247 maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
2248 maskWidth, maskHeight, mat);
2249 delete imgMaskData.imgStr;
2250 maskStr->close();
2251 delete maskSplash;
2253 //----- draw the source image
2255 ctm = state->getCTM();
2256 mat[0] = ctm[0];
2257 mat[1] = ctm[1];
2258 mat[2] = -ctm[2];
2259 mat[3] = -ctm[3];
2260 mat[4] = ctm[2] + ctm[4];
2261 mat[5] = ctm[3] + ctm[5];
2263 imgData.imgStr = new ImageStream(str, width,
2264 colorMap->getNumPixelComps(),
2265 colorMap->getBits());
2266 imgData.imgStr->reset();
2267 imgData.colorMap = colorMap;
2268 imgData.mask = maskBitmap;
2269 imgData.colorMode = colorMode;
2270 imgData.width = width;
2271 imgData.height = height;
2272 imgData.y = 0;
2274 // special case for one-channel (monochrome/gray/separation) images:
2275 // build a lookup table here
2276 imgData.lookup = NULL;
2277 if (colorMap->getNumPixelComps() == 1) {
2278 n = 1 << colorMap->getBits();
2279 switch (colorMode) {
2280 case splashModeMono1:
2281 case splashModeMono8:
2282 imgData.lookup = (SplashColorPtr)gmalloc(n);
2283 for (i = 0; i < n; ++i) {
2284 pix = (Guchar)i;
2285 colorMap->getGray(&pix, &gray);
2286 imgData.lookup[i] = colToByte(gray);
2288 break;
2289 case splashModeRGB8:
2290 imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2291 for (i = 0; i < n; ++i) {
2292 pix = (Guchar)i;
2293 colorMap->getRGB(&pix, &rgb);
2294 imgData.lookup[3*i] = colToByte(rgb.r);
2295 imgData.lookup[3*i+1] = colToByte(rgb.g);
2296 imgData.lookup[3*i+2] = colToByte(rgb.b);
2298 break;
2299 case splashModeBGR8:
2300 imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2301 for (i = 0; i < n; ++i) {
2302 pix = (Guchar)i;
2303 colorMap->getRGB(&pix, &rgb);
2304 imgData.lookup[3*i] = colToByte(rgb.b);
2305 imgData.lookup[3*i+1] = colToByte(rgb.g);
2306 imgData.lookup[3*i+2] = colToByte(rgb.r);
2308 break;
2309 #if SPLASH_CMYK
2310 case splashModeCMYK8:
2311 imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
2312 for (i = 0; i < n; ++i) {
2313 pix = (Guchar)i;
2314 colorMap->getCMYK(&pix, &cmyk);
2315 imgData.lookup[4*i] = colToByte(cmyk.c);
2316 imgData.lookup[4*i+1] = colToByte(cmyk.m);
2317 imgData.lookup[4*i+2] = colToByte(cmyk.y);
2318 imgData.lookup[4*i+3] = colToByte(cmyk.k);
2320 break;
2321 #endif
2322 default:
2323 //~ unimplemented
2324 break;
2328 switch (colorMode) {
2329 case splashModeMono1:
2330 case splashModeMono8:
2331 srcMode = splashModeAMono8;
2332 break;
2333 case splashModeRGB8:
2334 srcMode = splashModeARGB8;
2335 break;
2336 case splashModeBGR8:
2337 srcMode = splashModeBGRA8;
2338 break;
2339 #if SPLASH_CMYK
2340 case splashModeCMYK8:
2341 srcMode = splashModeACMYK8;
2342 break;
2343 #endif
2344 default:
2345 //~ unimplemented
2346 srcMode = splashModeARGB8;
2347 break;
2349 splash->drawImage(&maskedImageSrc, &imgData, srcMode, width, height, mat);
2351 delete maskBitmap;
2352 gfree(imgData.lookup);
2353 delete imgData.imgStr;
2354 str->close();
2357 void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
2358 Stream *str, int width, int height,
2359 GfxImageColorMap *colorMap,
2360 Stream *maskStr,
2361 int maskWidth, int maskHeight,
2362 GfxImageColorMap *maskColorMap) {
2363 double *ctm;
2364 SplashCoord mat[6];
2365 SplashOutImageData imgData;
2366 SplashOutImageData imgMaskData;
2367 SplashColorMode srcMode;
2368 SplashBitmap *maskBitmap;
2369 Splash *maskSplash;
2370 SplashColor maskColor;
2371 GfxGray gray;
2372 GfxRGB rgb;
2373 #if SPLASH_CMYK
2374 GfxCMYK cmyk;
2375 #endif
2376 Guchar pix;
2377 int n, i;
2379 ctm = state->getCTM();
2380 mat[0] = ctm[0];
2381 mat[1] = ctm[1];
2382 mat[2] = -ctm[2];
2383 mat[3] = -ctm[3];
2384 mat[4] = ctm[2] + ctm[4];
2385 mat[5] = ctm[3] + ctm[5];
2387 //----- set up the soft mask
2389 imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
2390 maskColorMap->getNumPixelComps(),
2391 maskColorMap->getBits());
2392 imgMaskData.imgStr->reset();
2393 imgMaskData.colorMap = maskColorMap;
2394 imgMaskData.maskColors = NULL;
2395 imgMaskData.colorMode = splashModeMono8;
2396 imgMaskData.width = maskWidth;
2397 imgMaskData.height = maskHeight;
2398 imgMaskData.y = 0;
2399 n = 1 << maskColorMap->getBits();
2400 imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
2401 for (i = 0; i < n; ++i) {
2402 pix = (Guchar)i;
2403 maskColorMap->getGray(&pix, &gray);
2404 imgMaskData.lookup[i] = colToByte(gray);
2406 maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
2407 1, splashModeMono8);
2408 maskSplash = new Splash(maskBitmap);
2409 maskColor[0] = 0;
2410 maskSplash->clear(maskColor);
2411 maskSplash->drawImage(&imageSrc, &imgMaskData,
2412 splashModeMono8, maskWidth, maskHeight, mat);
2413 delete imgMaskData.imgStr;
2414 maskStr->close();
2415 gfree(imgMaskData.lookup);
2416 delete maskSplash;
2417 splash->setSoftMask(maskBitmap);
2419 //----- draw the source image
2421 imgData.imgStr = new ImageStream(str, width,
2422 colorMap->getNumPixelComps(),
2423 colorMap->getBits());
2424 imgData.imgStr->reset();
2425 imgData.colorMap = colorMap;
2426 imgData.maskColors = NULL;
2427 imgData.colorMode = colorMode;
2428 imgData.width = width;
2429 imgData.height = height;
2430 imgData.y = 0;
2432 // special case for one-channel (monochrome/gray/separation) images:
2433 // build a lookup table here
2434 imgData.lookup = NULL;
2435 if (colorMap->getNumPixelComps() == 1) {
2436 n = 1 << colorMap->getBits();
2437 switch (colorMode) {
2438 case splashModeMono1:
2439 case splashModeMono8:
2440 imgData.lookup = (SplashColorPtr)gmalloc(n);
2441 for (i = 0; i < n; ++i) {
2442 pix = (Guchar)i;
2443 colorMap->getGray(&pix, &gray);
2444 imgData.lookup[i] = colToByte(gray);
2446 break;
2447 case splashModeRGB8:
2448 imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2449 for (i = 0; i < n; ++i) {
2450 pix = (Guchar)i;
2451 colorMap->getRGB(&pix, &rgb);
2452 imgData.lookup[3*i] = colToByte(rgb.r);
2453 imgData.lookup[3*i+1] = colToByte(rgb.g);
2454 imgData.lookup[3*i+2] = colToByte(rgb.b);
2456 break;
2457 case splashModeBGR8:
2458 imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2459 for (i = 0; i < n; ++i) {
2460 pix = (Guchar)i;
2461 colorMap->getRGB(&pix, &rgb);
2462 imgData.lookup[3*i] = colToByte(rgb.b);
2463 imgData.lookup[3*i+1] = colToByte(rgb.g);
2464 imgData.lookup[3*i+2] = colToByte(rgb.r);
2466 break;
2467 #if SPLASH_CMYK
2468 case splashModeCMYK8:
2469 imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
2470 for (i = 0; i < n; ++i) {
2471 pix = (Guchar)i;
2472 colorMap->getCMYK(&pix, &cmyk);
2473 imgData.lookup[4*i] = colToByte(cmyk.c);
2474 imgData.lookup[4*i+1] = colToByte(cmyk.m);
2475 imgData.lookup[4*i+2] = colToByte(cmyk.y);
2476 imgData.lookup[4*i+3] = colToByte(cmyk.k);
2478 break;
2479 #endif
2480 default:
2481 //~ unimplemented
2482 break;
2486 switch (colorMode) {
2487 case splashModeMono1:
2488 case splashModeMono8:
2489 srcMode = splashModeMono8;
2490 break;
2491 case splashModeRGB8:
2492 srcMode = splashModeRGB8;
2493 break;
2494 case splashModeBGR8:
2495 srcMode = splashModeBGR8;
2496 break;
2497 #if SPLASH_CMYK
2498 case splashModeCMYK8:
2499 srcMode = splashModeCMYK8;
2500 break;
2501 #endif
2502 default:
2503 //~ unimplemented
2504 srcMode = splashModeRGB8;
2505 break;
2507 splash->drawImage(&imageSrc, &imgData, srcMode, width, height, mat);
2509 splash->setSoftMask(NULL);
2510 gfree(imgData.lookup);
2511 delete imgData.imgStr;
2512 str->close();
2515 void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) {
2516 splashColorCopy(paperColor, paperColorA);
2519 int SplashOutputDev::getBitmapWidth() {
2520 return bitmap->getWidth();
2523 int SplashOutputDev::getBitmapHeight() {
2524 return bitmap->getHeight();
2527 SplashBitmap *SplashOutputDev::takeBitmap() {
2528 SplashBitmap *ret;
2530 ret = bitmap;
2531 bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, bitmapTopDown);
2532 return ret;
2535 void SplashOutputDev::getModRegion(int *xMin, int *yMin,
2536 int *xMax, int *yMax) {
2537 splash->getModRegion(xMin, yMin, xMax, yMax);
2540 void SplashOutputDev::clearModRegion() {
2541 splash->clearModRegion();
2544 void SplashOutputDev::setFillColor(int r, int g, int b) {
2545 GfxRGB rgb;
2546 GfxGray gray;
2547 #if SPLASH_CMYK
2548 GfxCMYK cmyk;
2549 #endif
2551 rgb.r = byteToCol(r);
2552 rgb.g = byteToCol(g);
2553 rgb.b = byteToCol(b);
2554 gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5);
2555 if (gray > gfxColorComp1) {
2556 gray = gfxColorComp1;
2558 #if SPLASH_CMYK
2559 cmyk.c = gfxColorComp1 - rgb.r;
2560 cmyk.m = gfxColorComp1 - rgb.g;
2561 cmyk.y = gfxColorComp1 - rgb.b;
2562 cmyk.k = 0;
2563 splash->setFillPattern(getColor(gray, &rgb, &cmyk));
2564 #else
2565 splash->setFillPattern(getColor(gray, &rgb));
2566 #endif
2569 SplashFont *SplashOutputDev::getFont(GString *name, double *mat) {
2570 DisplayFontParam *dfp;
2571 Ref ref;
2572 SplashOutFontFileID *id;
2573 SplashFontFile *fontFile;
2574 SplashFont *fontObj;
2575 FoFiTrueType *ff;
2576 Gushort *codeToGID;
2577 Unicode u;
2578 int cmap, i;
2580 for (i = 0; i < 16; ++i) {
2581 if (!name->cmp(splashOutSubstFonts[i].name)) {
2582 break;
2585 if (i == 16) {
2586 return NULL;
2588 ref.num = i;
2589 ref.gen = -1;
2590 id = new SplashOutFontFileID(&ref);
2592 // check the font file cache
2593 if ((fontFile = fontEngine->getFontFile(id))) {
2594 delete id;
2596 // load the font file
2597 } else {
2598 dfp = globalParams->getDisplayFont(name);
2599 if (dfp && dfp->kind == displayFontT1) {
2600 fontFile = fontEngine->loadType1Font(id, dfp->t1.fileName->getCString(),
2601 gFalse, winAnsiEncoding);
2602 } else if (dfp && dfp->kind == displayFontTT) {
2603 if (!(ff = FoFiTrueType::load(dfp->tt.fileName->getCString()))) {
2604 return NULL;
2606 for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
2607 if ((ff->getCmapPlatform(cmap) == 3 &&
2608 ff->getCmapEncoding(cmap) == 1) ||
2609 ff->getCmapPlatform(cmap) == 0) {
2610 break;
2613 if (cmap == ff->getNumCmaps()) {
2614 delete ff;
2615 return NULL;
2617 codeToGID = (Gushort *)gmallocn(256, sizeof(Gushort));
2618 for (i = 0; i < 256; ++i) {
2619 codeToGID[i] = 0;
2620 if (winAnsiEncoding[i] &&
2621 (u = globalParams->mapNameToUnicode(winAnsiEncoding[i]))) {
2622 codeToGID[i] = ff->mapCodeToGID(cmap, u);
2625 delete ff;
2626 fontFile = fontEngine->loadTrueTypeFont(id,
2627 dfp->tt.fileName->getCString(),
2628 gFalse, codeToGID, 256);
2629 } else {
2630 return NULL;
2634 // create the scaled font
2635 fontObj = fontEngine->getFont(fontFile, (SplashCoord *)mat);
2637 return fontObj;