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