1 | //======================================================================== |
2 | // |
3 | // PreScanOutputDev.cc |
4 | // |
5 | // Copyright 2005 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) 2009 Carlos Garcia Campos <carlosgc@gnome.org> |
17 | // Copyright (C) 2010, 2011, 2018-2021 Albert Astals Cid <aacid@kde.org> |
18 | // Copyright (C) 2011, 2014 William Bader <williambader@hotmail.com> |
19 | // Copyright (C) 2011, 2013 Thomas Freitag <Thomas.Freitag@alfa.de> |
20 | // Copyright (C) 2011 Adrian Johnson <ajohnson@redneon.com> |
21 | // Copyright (C) 2022 Oliver Sander <oliver.sander@tu-dresden.de> |
22 | // |
23 | // To see a description of the changes please see the Changelog file that |
24 | // came with your tarball or type make ChangeLog if you are building from git |
25 | // |
26 | //======================================================================== |
27 | |
28 | #include <config.h> |
29 | |
30 | #include <cmath> |
31 | #include "GlobalParams.h" |
32 | #include "Gfx.h" |
33 | #include "GfxFont.h" |
34 | #include "Link.h" |
35 | #include "Catalog.h" |
36 | #include "Page.h" |
37 | #include "PreScanOutputDev.h" |
38 | |
39 | //------------------------------------------------------------------------ |
40 | // PreScanOutputDev |
41 | //------------------------------------------------------------------------ |
42 | |
43 | PreScanOutputDev::PreScanOutputDev(PSLevel levelA) : level(levelA) |
44 | { |
45 | clearStats(); |
46 | } |
47 | |
48 | PreScanOutputDev::~PreScanOutputDev() { } |
49 | |
50 | void PreScanOutputDev::startPage(int /*pageNum*/, GfxState * /*state*/, XRef * /*xref*/) { } |
51 | |
52 | void PreScanOutputDev::endPage() { } |
53 | |
54 | void PreScanOutputDev::stroke(GfxState *state) |
55 | { |
56 | double dashStart; |
57 | |
58 | check(colorSpace: state->getStrokeColorSpace(), color: state->getStrokeColor(), opacity: state->getStrokeOpacity(), blendMode: state->getBlendMode()); |
59 | const std::vector<double> &dash = state->getLineDash(start: &dashStart); |
60 | if (dash.size() != 0) { |
61 | gdi = false; |
62 | } |
63 | } |
64 | |
65 | void PreScanOutputDev::fill(GfxState *state) |
66 | { |
67 | check(colorSpace: state->getFillColorSpace(), color: state->getFillColor(), opacity: state->getFillOpacity(), blendMode: state->getBlendMode()); |
68 | } |
69 | |
70 | void PreScanOutputDev::eoFill(GfxState *state) |
71 | { |
72 | check(colorSpace: state->getFillColorSpace(), color: state->getFillColor(), opacity: state->getFillOpacity(), blendMode: state->getBlendMode()); |
73 | } |
74 | |
75 | bool PreScanOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *catalog, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep) |
76 | { |
77 | if (tPat->getPaintType() == 1) { |
78 | bool tilingNeeded = (x1 - x0 != 1 || y1 - y0 != 1); |
79 | if (tilingNeeded) { |
80 | inTilingPatternFill++; |
81 | } |
82 | gfx->drawForm(str: tPat->getContentStream(), resDict: tPat->getResDict(), matrix: mat, bbox: tPat->getBBox()); |
83 | if (tilingNeeded) { |
84 | inTilingPatternFill--; |
85 | } |
86 | } else { |
87 | check(colorSpace: state->getFillColorSpace(), color: state->getFillColor(), opacity: state->getFillOpacity(), blendMode: state->getBlendMode()); |
88 | } |
89 | return true; |
90 | } |
91 | |
92 | bool PreScanOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) |
93 | { |
94 | if (shading->getColorSpace()->getMode() != csDeviceGray && shading->getColorSpace()->getMode() != csCalGray) { |
95 | gray = false; |
96 | } |
97 | mono = false; |
98 | if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { |
99 | transparency = true; |
100 | } |
101 | return true; |
102 | } |
103 | |
104 | bool PreScanOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double /*tMin*/, double /*tMax*/) |
105 | { |
106 | if (shading->getColorSpace()->getMode() != csDeviceGray && shading->getColorSpace()->getMode() != csCalGray) { |
107 | gray = false; |
108 | } |
109 | mono = false; |
110 | if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { |
111 | transparency = true; |
112 | } |
113 | return true; |
114 | } |
115 | |
116 | bool PreScanOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double /*sMin*/, double /*sMax*/) |
117 | { |
118 | if (shading->getColorSpace()->getMode() != csDeviceGray && shading->getColorSpace()->getMode() != csCalGray) { |
119 | gray = false; |
120 | } |
121 | mono = false; |
122 | if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { |
123 | transparency = true; |
124 | } |
125 | return true; |
126 | } |
127 | |
128 | void PreScanOutputDev::clip(GfxState * /*state*/) |
129 | { |
130 | //~ check for a rectangle "near" the edge of the page; |
131 | //~ else set gdi to false |
132 | } |
133 | |
134 | void PreScanOutputDev::eoClip(GfxState * /*state*/) |
135 | { |
136 | //~ see clip() |
137 | } |
138 | |
139 | void PreScanOutputDev::beginStringOp(GfxState *state) |
140 | { |
141 | int render; |
142 | double m11, m12, m21, m22; |
143 | bool simpleTTF; |
144 | |
145 | render = state->getRender(); |
146 | if (!(render & 1)) { |
147 | check(colorSpace: state->getFillColorSpace(), color: state->getFillColor(), opacity: state->getFillOpacity(), blendMode: state->getBlendMode()); |
148 | } |
149 | if ((render & 3) == 1 || (render & 3) == 2) { |
150 | check(colorSpace: state->getStrokeColorSpace(), color: state->getStrokeColor(), opacity: state->getStrokeOpacity(), blendMode: state->getBlendMode()); |
151 | } |
152 | |
153 | std::shared_ptr<const GfxFont> font = state->getFont(); |
154 | state->getFontTransMat(m11: &m11, m12: &m12, m21: &m21, m22: &m22); |
155 | //~ this should check for external fonts that are non-TrueType |
156 | simpleTTF = fabs(x: m11 + m22) < 0.01 && m11 > 0 && fabs(x: m12) < 0.01 && fabs(x: m21) < 0.01 && fabs(x: state->getHorizScaling() - 1) < 0.001 && (font->getType() == fontTrueType || font->getType() == fontTrueTypeOT); |
157 | if (simpleTTF) { |
158 | //~ need to create a FoFiTrueType object, and check for a Unicode cmap |
159 | } |
160 | if (state->getRender() != 0 || !simpleTTF) { |
161 | gdi = false; |
162 | } |
163 | } |
164 | |
165 | void PreScanOutputDev::endStringOp(GfxState * /*state*/) { } |
166 | |
167 | bool PreScanOutputDev::beginType3Char(GfxState * /*state*/, double /*x*/, double /*y*/, double /*dx*/, double /*dy*/, CharCode /*code*/, const Unicode * /*u*/, int /*uLen*/) |
168 | { |
169 | // return false so all Type 3 chars get rendered (no caching) |
170 | return false; |
171 | } |
172 | |
173 | void PreScanOutputDev::endType3Char(GfxState * /*state*/) { } |
174 | |
175 | void PreScanOutputDev::drawImageMask(GfxState *state, Object * /*ref*/, Stream *str, int width, int height, bool /*invert*/, bool /*interpolate*/, bool inlineImg) |
176 | { |
177 | int i, j; |
178 | |
179 | check(colorSpace: state->getFillColorSpace(), color: state->getFillColor(), opacity: state->getFillOpacity(), blendMode: state->getBlendMode()); |
180 | gdi = false; |
181 | if ((level == psLevel1 || level == psLevel1Sep) && (state->getFillColorSpace()->getMode() == csPattern || inTilingPatternFill > 0)) { |
182 | patternImgMask = true; |
183 | } |
184 | |
185 | if (inlineImg) { |
186 | str->reset(); |
187 | j = height * ((width + 7) / 8); |
188 | for (i = 0; i < j; ++i) { |
189 | str->getChar(); |
190 | } |
191 | str->close(); |
192 | } |
193 | } |
194 | |
195 | void PreScanOutputDev::drawImage(GfxState *state, Object * /*ref*/, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool /*interpolate*/, const int * /*maskColors*/, bool inlineImg) |
196 | { |
197 | GfxColorSpace *colorSpace; |
198 | int i, j; |
199 | |
200 | colorSpace = colorMap->getColorSpace(); |
201 | if (colorSpace->getMode() == csIndexed) { |
202 | colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); |
203 | } |
204 | if (colorSpace->getMode() == csDeviceGray || colorSpace->getMode() == csCalGray) { |
205 | if (colorMap->getBits() > 1) { |
206 | mono = false; |
207 | } |
208 | } else { |
209 | gray = false; |
210 | mono = false; |
211 | } |
212 | if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { |
213 | transparency = true; |
214 | } |
215 | gdi = false; |
216 | if ((level == psLevel1 || level == psLevel1Sep) && inTilingPatternFill > 0) { |
217 | patternImgMask = true; |
218 | } |
219 | |
220 | if (inlineImg) { |
221 | str->reset(); |
222 | j = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); |
223 | for (i = 0; i < j; ++i) { |
224 | str->getChar(); |
225 | } |
226 | str->close(); |
227 | } |
228 | } |
229 | |
230 | void PreScanOutputDev::drawMaskedImage(GfxState *state, Object * /*ref*/, Stream * /*str*/, int /*width*/, int /*height*/, GfxImageColorMap *colorMap, bool /*interpolate*/, Stream * /*maskStr*/, int /*maskWidth*/, int /*maskHeight*/, |
231 | bool /*maskInvert*/, bool /*maskInterpolate*/) |
232 | { |
233 | GfxColorSpace *colorSpace; |
234 | |
235 | colorSpace = colorMap->getColorSpace(); |
236 | if (colorSpace->getMode() == csIndexed) { |
237 | colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); |
238 | } |
239 | if (colorSpace->getMode() == csDeviceGray || colorSpace->getMode() == csCalGray) { |
240 | if (colorMap->getBits() > 1) { |
241 | mono = false; |
242 | } |
243 | } else { |
244 | gray = false; |
245 | mono = false; |
246 | } |
247 | if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { |
248 | transparency = true; |
249 | } |
250 | gdi = false; |
251 | } |
252 | |
253 | void PreScanOutputDev::drawSoftMaskedImage(GfxState * /*state*/, Object * /*ref*/, Stream * /*str*/, int /*width*/, int /*height*/, GfxImageColorMap *colorMap, bool /*interpolate*/, Stream * /*maskStr*/, int /*maskWidth*/, |
254 | int /*maskHeight*/, GfxImageColorMap * /*maskColorMap*/, bool /*maskInterpolate*/) |
255 | { |
256 | GfxColorSpace *colorSpace; |
257 | |
258 | colorSpace = colorMap->getColorSpace(); |
259 | if (colorSpace->getMode() == csIndexed) { |
260 | colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); |
261 | } |
262 | if (colorSpace->getMode() != csDeviceGray && colorSpace->getMode() != csCalGray) { |
263 | gray = false; |
264 | } |
265 | mono = false; |
266 | transparency = true; |
267 | gdi = false; |
268 | } |
269 | |
270 | void PreScanOutputDev::beginTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/, GfxColorSpace * /*blendingColorSpace*/, bool /*isolated*/, bool /*knockout*/, bool /*forSoftMask*/) |
271 | { |
272 | gdi = false; |
273 | } |
274 | |
275 | void PreScanOutputDev::paintTransparencyGroup(GfxState *state, const double * /*bbox*/) |
276 | { |
277 | check(colorSpace: state->getFillColorSpace(), color: state->getFillColor(), opacity: state->getFillOpacity(), blendMode: state->getBlendMode()); |
278 | } |
279 | |
280 | void PreScanOutputDev::setSoftMask(GfxState * /*state*/, const double * /*bbox*/, bool /*alpha*/, Function * /*transferFunc*/, GfxColor * /*backdropColor*/) |
281 | { |
282 | transparency = true; |
283 | } |
284 | |
285 | void PreScanOutputDev::check(GfxColorSpace *colorSpace, const GfxColor *color, double opacity, GfxBlendMode blendMode) |
286 | { |
287 | GfxRGB rgb; |
288 | |
289 | if (colorSpace->getMode() == csPattern) { |
290 | mono = false; |
291 | gray = false; |
292 | gdi = false; |
293 | } else { |
294 | colorSpace->getRGB(color, rgb: &rgb); |
295 | if (rgb.r != rgb.g || rgb.g != rgb.b || rgb.b != rgb.r) { |
296 | mono = false; |
297 | gray = false; |
298 | } else if (!((rgb.r == 0 && rgb.g == 0 && rgb.b == 0) || (rgb.r == gfxColorComp1 && rgb.g == gfxColorComp1 && rgb.b == gfxColorComp1))) { |
299 | mono = false; |
300 | } |
301 | } |
302 | if (opacity != 1 || blendMode != gfxBlendNormal) { |
303 | transparency = true; |
304 | } |
305 | } |
306 | |
307 | void PreScanOutputDev::clearStats() |
308 | { |
309 | mono = true; |
310 | gray = true; |
311 | transparency = false; |
312 | gdi = true; |
313 | patternImgMask = false; |
314 | inTilingPatternFill = 0; |
315 | } |
316 | |