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