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