Coverage Report

Created: 2026-02-04 06:14

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