Coverage Report

Created: 2026-10-31 06:21

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
757
#  define maxTileSize 2000000000
61
#endif
62
63
//------------------------------------------------------------------------
64
65
// Type 3 font cache size parameters
66
10.8k
#define type3FontCacheAssoc   8
67
10.8k
#define type3FontCacheMaxSets 8
68
16.5k
#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
270
static inline Guchar div255(int x) {
82
270
  return (Guchar)((x + (x >> 8) + 0x80) >> 8);
83
270
}
84
85
// Clip x to lie in [0, 255].
86
155
static inline Guchar clip255(int x) {
87
155
  return x < 0 ? 0 : x > 255 ? 255 : (Guchar)x;
88
155
}
89
90
91
//------------------------------------------------------------------------
92
// Blend functions
93
//------------------------------------------------------------------------
94
95
static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest,
96
110
           SplashColorPtr blend, SplashColorMode cm) {
97
110
  int i;
98
99
440
  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
100
330
    blend[i] = (Guchar)((dest[i] * src[i]) / 255);
101
330
  }
102
110
}
103
104
static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest,
105
48
         SplashColorPtr blend, SplashColorMode cm) {
106
48
  int i;
107
108
192
  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
109
144
    blend[i] = (Guchar)(dest[i] + src[i] - (dest[i] * src[i]) / 255);
110
144
  }
111
48
}
112
113
// note: this is the same as HardLight, with src/dest reversed
114
static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest,
115
53
          SplashColorPtr blend, SplashColorMode cm) {
116
53
  int i;
117
118
212
  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
119
    // the spec says "if Cs <= 0.5" -- note that 0x80 is 128/255=0.5020
120
159
    blend[i] = dest[i] < 0x80
121
159
                 ? (Guchar)((src[i] * 2 * dest[i]) / 255)
122
159
                 : (Guchar)(255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255);
123
159
  }
124
53
}
125
126
static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest,
127
43
         SplashColorPtr blend, SplashColorMode cm) {
128
43
  int i;
129
130
172
  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
131
129
    blend[i] = dest[i] < src[i] ? dest[i] : src[i];
132
129
  }
133
43
}
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
102
             SplashColorMode cm) {
147
102
  int i;
148
149
408
  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
150
306
    if (dest[i] == 0) {
151
105
      blend[i] = 0;
152
201
    } else if (dest[i] >= 255 - src[i]) {
153
87
      blend[i] = 255;
154
114
    } else {
155
114
      blend[i] = (Guchar)((dest[i] * 255) / (255 - src[i]));
156
114
    }
157
306
  }
158
102
}
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
98
            SplashColorPtr blend, SplashColorMode cm) {
177
98
  int i;
178
179
392
  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
180
    // the spec says "if Cs <= 0.5" -- note that 0x80 is 128/255=0.5020
181
294
    blend[i] = src[i] < 0x80
182
294
                 ? (Guchar)((dest[i] * 2 * src[i]) / 255)
183
294
                 : (Guchar)(255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255);
184
294
  }
185
98
}
186
187
static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest,
188
12
            SplashColorPtr blend, SplashColorMode cm) {
189
12
  int i, x;
190
191
48
  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
192
    // the spec says "if Cs <= 0.5" -- note that 0x80 is 128/255=0.5020
193
36
    if (src[i] < 0x80) {
194
15
      blend[i] = (Guchar)(dest[i] -
195
15
        (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) /
196
15
          (255 * 255));
197
21
    } else {
198
      // the spec says "if Cb <= 0.25" -- note that 0x40 is 64/255=0.2510
199
21
      if (dest[i] < 0x40) {
200
9
  x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255)
201
9
        + 4 * 255) * dest[i]) / 255;
202
12
      } else {
203
12
  x = (int)sqrt(255.0 * dest[i]);
204
12
      }
205
21
      blend[i] = (Guchar)(dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255);
206
21
    }
207
36
  }
208
12
}
209
210
static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest,
211
             SplashColorPtr blend,
212
120
             SplashColorMode cm) {
213
120
  int i;
214
215
480
  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
216
360
    blend[i] = dest[i] < src[i] ? (Guchar)(src[i] - dest[i])
217
360
              : (Guchar)(dest[i] - src[i]);
218
360
  }
219
120
}
220
221
static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest,
222
43
            SplashColorPtr blend, SplashColorMode cm) {
223
43
  int i;
224
225
172
  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
226
129
    blend[i] = (Guchar)(dest[i] + src[i] - (2 * dest[i] * src[i]) / 255);
227
129
  }
228
43
}
229
230
1.92k
static int getLum(int r, int g, int b) {
231
1.92k
  return (int)(0.3 * r + 0.59 * g + 0.11 * b);
232
1.92k
}
233
234
506
static int getSat(int r, int g, int b) {
235
506
  int rgbMin, rgbMax;
236
237
506
  rgbMin = rgbMax = r;
238
506
  if (g < rgbMin) {
239
88
    rgbMin = g;
240
418
  } else if (g > rgbMax) {
241
85
    rgbMax = g;
242
85
  }
243
506
  if (b < rgbMin) {
244
97
    rgbMin = b;
245
409
  } else if (b > rgbMax) {
246
90
    rgbMax = b;
247
90
  }
248
506
  return rgbMax - rgbMin;
249
506
}
250
251
static void clipColor(int rIn, int gIn, int bIn,
252
640
          Guchar *rOut, Guchar *gOut, Guchar *bOut) {
253
640
  int lum, rgbMin, rgbMax, r, g, b;
254
255
640
  lum = getLum(rIn, gIn, bIn);
256
640
  rgbMin = rgbMax = rIn;
257
640
  if (gIn < rgbMin) {
258
141
    rgbMin = gIn;
259
499
  } else if (gIn > rgbMax) {
260
97
    rgbMax = gIn;
261
97
  }
262
640
  if (bIn < rgbMin) {
263
121
    rgbMin = bIn;
264
519
  } else if (bIn > rgbMax) {
265
79
    rgbMax = bIn;
266
79
  }
267
640
  r = rIn;
268
640
  g = gIn;
269
640
  b = bIn;
270
640
  if (rgbMin < 0) {
271
28
    r = lum + ((r - lum) * lum) / (lum - rgbMin);
272
28
    g = lum + ((g - lum) * lum) / (lum - rgbMin);
273
28
    b = lum + ((b - lum) * lum) / (lum - rgbMin);
274
28
  }
275
640
  if (rgbMax > 255) {
276
14
    r = lum + ((r - lum) * (255 - lum)) / (rgbMax - lum);
277
14
    g = lum + ((g - lum) * (255 - lum)) / (rgbMax - lum);
278
14
    b = lum + ((b - lum) * (255 - lum)) / (rgbMax - lum);
279
14
  }
280
640
  *rOut = (Guchar)r;
281
640
  *gOut = (Guchar)g;
282
640
  *bOut = (Guchar)b;
283
640
}
284
285
static void setLum(Guchar rIn, Guchar gIn, Guchar bIn, int lum,
286
640
       Guchar *rOut, Guchar *gOut, Guchar *bOut) {
287
640
  int d;
288
289
640
  d = lum - getLum(rIn, gIn, bIn);
290
640
  clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut);
291
640
}
292
293
static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat,
294
506
       Guchar *rOut, Guchar *gOut, Guchar *bOut) {
295
506
  int rgbMin, rgbMid, rgbMax;
296
506
  Guchar *minOut, *midOut, *maxOut;
297
298
506
  if (rIn < gIn) {
299
105
    rgbMin = rIn;  minOut = rOut;
300
105
    rgbMid = gIn;  midOut = gOut;
301
401
  } else {
302
401
    rgbMin = gIn;  minOut = gOut;
303
401
    rgbMid = rIn;  midOut = rOut;
304
401
  }
305
506
  if (bIn > rgbMid) {
306
84
    rgbMax = bIn;  maxOut = bOut;
307
422
  } else if (bIn > rgbMin) {
308
95
    rgbMax = rgbMid;  maxOut = midOut;
309
95
    rgbMid = bIn;     midOut = bOut;
310
327
  } else {
311
327
    rgbMax = rgbMid;  maxOut = midOut;
312
327
    rgbMid = rgbMin;  midOut = minOut;
313
327
    rgbMin = bIn;     minOut = bOut;
314
327
  }
315
506
  if (rgbMax > rgbMin) {
316
282
    *midOut = (Guchar)(((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin));
317
282
    *maxOut = (Guchar)sat;
318
282
  } else {
319
224
    *midOut = *maxOut = 0;
320
224
  }
321
506
  *minOut = 0;
322
506
}
323
324
static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
325
319
            SplashColorPtr blend, SplashColorMode cm) {
326
319
  Guchar r0, g0, b0;
327
328
319
  switch (cm) {
329
0
  case splashModeMono1:
330
0
  case splashModeMono8:
331
0
    blend[0] = dest[0];
332
0
    break;
333
319
  case splashModeRGB8:
334
319
  case splashModeBGR8:
335
319
    setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
336
319
     &r0, &g0, &b0);
337
319
    setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
338
319
     &blend[0], &blend[1], &blend[2]);
339
319
    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
319
#endif
350
319
  }
351
319
}
352
353
static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
354
             SplashColorPtr blend,
355
187
             SplashColorMode cm) {
356
187
  Guchar r0, g0, b0;
357
358
187
  switch (cm) {
359
0
  case splashModeMono1:
360
0
  case splashModeMono8:
361
0
    blend[0] = dest[0];
362
0
    break;
363
187
  case splashModeRGB8:
364
187
  case splashModeBGR8:
365
187
    setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]),
366
187
     &r0, &g0, &b0);
367
187
    setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
368
187
     &blend[0], &blend[1], &blend[2]);
369
187
    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
187
#endif
380
187
  }
381
187
}
382
383
static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,
384
97
        SplashColorPtr blend, SplashColorMode cm) {
385
386
97
  switch (cm) {
387
0
  case splashModeMono1:
388
0
  case splashModeMono8:
389
0
    blend[0] = dest[0];
390
0
    break;
391
97
  case splashModeRGB8:
392
97
  case splashModeBGR8:
393
97
    setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]),
394
97
     &blend[0], &blend[1], &blend[2]);
395
97
    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
97
#endif
404
97
  }
405
97
}
406
407
static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,
408
             SplashColorPtr blend,
409
37
             SplashColorMode cm) {
410
411
37
  switch (cm) {
412
0
  case splashModeMono1:
413
0
  case splashModeMono8:
414
0
    blend[0] = dest[0];
415
0
    break;
416
37
  case splashModeRGB8:
417
37
  case splashModeBGR8:
418
37
    setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
419
37
     &blend[0], &blend[1], &blend[2]);
420
37
    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
37
#endif
429
37
  }
430
37
}
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
77.4k
  SplashOutFontFileID(Ref *rA) {
461
77.4k
    r = *rA;
462
77.4k
    substIdx = -1;
463
77.4k
    oblique = 0;
464
77.4k
  }
465
466
0
  ~SplashOutFontFileID() {}
467
468
37.2k
  GBool matches(SplashFontFileID *id) {
469
37.2k
    return ((SplashOutFontFileID *)id)->r.num == r.num &&
470
32.9k
           ((SplashOutFontFileID *)id)->r.gen == r.gen;
471
37.2k
  }
472
473
0
  void setOblique(double obliqueA) { oblique = obliqueA; }
474
30.1k
  double getOblique() { return oblique; }
475
0
  void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
476
30.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
2.18M
    { return fontID.num == idA->num && fontID.gen == idA->gen &&
505
2.17M
       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
10.8k
       GBool validBBoxA, GBool aa) {
524
10.8k
  int i;
525
526
10.8k
  fontID = *fontIDA;
527
10.8k
  m11 = m11A;
528
10.8k
  m12 = m12A;
529
10.8k
  m21 = m21A;
530
10.8k
  m22 = m22A;
531
10.8k
  glyphX = glyphXA;
532
10.8k
  glyphY = glyphYA;
533
10.8k
  glyphW = glyphWA;
534
10.8k
  glyphH = glyphHA;
535
10.8k
  validBBox = validBBoxA;
536
  // sanity check for excessively large glyphs (which most likely
537
  // indicate an incorrect BBox)
538
10.8k
  i = glyphW * glyphH;
539
10.8k
  if (i > 100000 || glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0) {
540
2.84k
    glyphW = glyphH = 100;
541
2.84k
    validBBox = gFalse;
542
2.84k
  }
543
10.8k
  if (aa) {
544
10.8k
    glyphSize = glyphW * glyphH;
545
10.8k
  } else {
546
0
    glyphSize = ((glyphW + 7) >> 3) * glyphH;
547
0
  }
548
10.8k
  cacheAssoc = type3FontCacheAssoc;
549
10.8k
  for (cacheSets = type3FontCacheMaxSets;
550
19.4k
       cacheSets > 1 &&
551
16.5k
   cacheSets * cacheAssoc * glyphSize > type3FontCacheSize;
552
10.8k
       cacheSets >>= 1) ;
553
10.8k
  cacheData = (Guchar *)gmallocn(cacheSets * cacheAssoc, glyphSize);
554
10.8k
  cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc,
555
10.8k
           sizeof(T3FontCacheTag));
556
546k
  for (i = 0; i < cacheSets * cacheAssoc; ++i) {
557
535k
    cacheTags[i].mru = (Gushort)(i & (cacheAssoc - 1));
558
535k
  }
559
10.8k
  refCount = 0;
560
10.8k
}
561
562
10.8k
T3FontCache::~T3FontCache() {
563
10.8k
  gfree(cacheData);
564
10.8k
  gfree(cacheTags);
565
10.8k
}
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
17.7k
         GBool allowAntialiasA) {
624
17.7k
  colorMode = colorModeA;
625
17.7k
  bitmapRowPad = bitmapRowPadA;
626
17.7k
  bitmapTopDown = bitmapTopDownA;
627
17.7k
  bitmapUpsideDown = gFalse;
628
17.7k
  noComposite = gFalse;
629
17.7k
  allowAntialias = allowAntialiasA;
630
17.7k
  vectorAntialias = allowAntialias &&
631
17.7k
          globalParams->getVectorAntialias() &&
632
17.7k
          colorMode != splashModeMono1;
633
17.7k
  setupScreenParams(72.0, 72.0);
634
17.7k
  reverseVideo = reverseVideoA;
635
17.7k
  splashColorCopy(paperColor, paperColorA);
636
17.7k
  skipHorizText = gFalse;
637
17.7k
  skipRotatedText = gFalse;
638
639
17.7k
  xref = NULL;
640
641
17.7k
  bitmapMemCache = new SplashBitmapMemCache();
642
17.7k
  bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
643
17.7k
          colorMode != splashModeMono1, bitmapTopDown,
644
17.7k
          bitmapMemCache);
645
17.7k
  splash = new Splash(bitmap, vectorAntialias, NULL, &screenParams);
646
17.7k
  splash->setMinLineWidth(globalParams->getMinLineWidth());
647
17.7k
  splash->setStrokeAdjust(
648
17.7k
     mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
649
17.7k
  splash->setEnablePathSimplification(
650
17.7k
     globalParams->getEnablePathSimplification());
651
17.7k
  splash->clear(paperColor, 0);
652
653
17.7k
  fontEngine = NULL;
654
655
17.7k
  nT3Fonts = 0;
656
17.7k
  t3GlyphStack = NULL;
657
658
17.7k
  tileCacheRef.num = -1;
659
17.7k
  tileCacheRef.gen = -1;
660
17.7k
  tileCacheBitmap = NULL;
661
17.7k
  tileCacheOverprintMaskBitmap = NULL;
662
663
17.7k
  font = NULL;
664
17.7k
  needFontUpdate = gFalse;
665
17.7k
  savedTextPath = NULL;
666
17.7k
  savedClipPath = NULL;
667
668
17.7k
  transpGroupStack = NULL;
669
670
17.7k
  nestCount = 0;
671
672
17.7k
  startPageCbk = NULL;
673
17.7k
  startPageCbkData = NULL;
674
17.7k
}
675
676
677
199k
void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) {
678
199k
  screenParams.size = globalParams->getScreenSize();
679
199k
  screenParams.dotRadius = globalParams->getScreenDotRadius();
680
199k
  screenParams.gamma = (SplashCoord)globalParams->getScreenGamma();
681
199k
  screenParams.blackThreshold =
682
199k
      (SplashCoord)globalParams->getScreenBlackThreshold();
683
199k
  screenParams.whiteThreshold =
684
199k
      (SplashCoord)globalParams->getScreenWhiteThreshold();
685
199k
  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
199k
  case screenUnset:
708
199k
  default:
709
    // use clustered dithering for resolution >= 300 dpi
710
    // (compare to 299.9 to avoid floating point issues)
711
199k
    if (hDPI > 299.9 && vDPI > 299.9) {
712
3.35k
      screenParams.type = splashScreenStochasticClustered;
713
3.35k
      if (screenParams.size < 0) {
714
3.35k
  screenParams.size = 64;
715
3.35k
      }
716
3.35k
      if (screenParams.dotRadius < 0) {
717
3.35k
  screenParams.dotRadius = 2;
718
3.35k
      }
719
196k
    } else {
720
196k
      screenParams.type = splashScreenDispersed;
721
196k
      if (screenParams.size < 0) {
722
196k
  screenParams.size = 4;
723
196k
      }
724
196k
    }
725
199k
  }
726
199k
}
727
728
17.7k
SplashOutputDev::~SplashOutputDev() {
729
17.7k
  int i;
730
731
17.7k
  if (tileCacheBitmap) {
732
24
    delete tileCacheBitmap;
733
24
  }
734
17.7k
  gfree(tileCacheOverprintMaskBitmap);
735
19.9k
  for (i = 0; i < nT3Fonts; ++i) {
736
2.19k
    delete t3FontCache[i];
737
2.19k
  }
738
17.7k
  if (fontEngine) {
739
17.7k
    delete fontEngine;
740
17.7k
  }
741
17.7k
  if (splash) {
742
17.7k
    delete splash;
743
17.7k
  }
744
17.7k
  if (bitmap) {
745
17.7k
    delete bitmap;
746
17.7k
  }
747
17.7k
  delete bitmapMemCache;
748
17.7k
  if (savedTextPath) {
749
0
    delete savedTextPath;
750
0
  }
751
17.7k
  if (savedClipPath) {
752
171
    delete savedClipPath;
753
171
  }
754
17.7k
}
755
756
17.7k
void SplashOutputDev::startDoc(XRef *xrefA) {
757
17.7k
  int i;
758
759
17.7k
  xref = xrefA;
760
17.7k
  if (fontEngine) {
761
0
    delete fontEngine;
762
0
  }
763
17.7k
  fontEngine = new SplashFontEngine(
764
17.7k
#if HAVE_FREETYPE_H
765
17.7k
            globalParams->getEnableFreeType(),
766
17.7k
            globalParams->getDisableFreeTypeHinting()
767
17.7k
              ? splashFTNoHinting : 0,
768
17.7k
#endif
769
17.7k
            allowAntialias &&
770
17.7k
              globalParams->getAntialias() &&
771
17.7k
              colorMode != splashModeMono1);
772
17.7k
  for (i = 0; i < nT3Fonts; ++i) {
773
0
    delete t3FontCache[i];
774
0
  }
775
17.7k
  nT3Fonts = 0;
776
17.7k
  if (tileCacheBitmap) {
777
0
    delete tileCacheBitmap;
778
0
    tileCacheBitmap = NULL;
779
0
  }
780
17.7k
  gfree(tileCacheOverprintMaskBitmap);
781
17.7k
  tileCacheRef.num = -1;
782
17.7k
  tileCacheRef.gen = -1;
783
17.7k
}
784
785
181k
void SplashOutputDev::startPage(int pageNum, GfxState *state) {
786
181k
  int w, h;
787
181k
  double *ctm;
788
181k
  SplashCoord mat[6];
789
181k
  SplashColor color;
790
791
181k
  if (state) {
792
181k
    setupScreenParams(state->getHDPI(), state->getVDPI());
793
181k
    w = (int)(state->getPageWidth() + 0.5);
794
181k
    if (w <= 0) {
795
181k
      w = 1;
796
181k
    }
797
181k
    h = (int)(state->getPageHeight() + 0.5);
798
181k
    if (h <= 0) {
799
181k
      h = 1;
800
181k
    }
801
181k
  } else {
802
0
    w = h = 1;
803
0
  }
804
181k
  if (splash) {
805
181k
    delete splash;
806
181k
    splash = NULL;
807
181k
  }
808
181k
  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
181k
  splash = new Splash(bitmap, vectorAntialias, NULL, &screenParams);
819
181k
  splash->setMinLineWidth(globalParams->getMinLineWidth());
820
181k
  splash->setEnablePathSimplification(
821
181k
     globalParams->getEnablePathSimplification());
822
181k
  if (state) {
823
181k
    ctm = state->getCTM();
824
181k
    mat[0] = (SplashCoord)ctm[0];
825
181k
    mat[1] = (SplashCoord)ctm[1];
826
181k
    mat[2] = (SplashCoord)ctm[2];
827
181k
    mat[3] = (SplashCoord)ctm[3];
828
181k
    mat[4] = (SplashCoord)ctm[4];
829
181k
    mat[5] = (SplashCoord)ctm[5];
830
181k
    splash->setMatrix(mat);
831
181k
  }
832
181k
  switch (colorMode) {
833
0
  case splashModeMono1:
834
0
  case splashModeMono8:
835
0
    color[0] = 0;
836
0
    break;
837
181k
  case splashModeRGB8:
838
181k
  case splashModeBGR8:
839
181k
    color[0] = color[1] = color[2] = 0;
840
181k
    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
181k
#endif
846
181k
  }
847
181k
  splash->setStrokePattern(new SplashSolidColor(color));
848
181k
  splash->setFillPattern(new SplashSolidColor(color));
849
181k
  splash->setLineCap(splashLineCapButt);
850
181k
  splash->setLineJoin(splashLineJoinMiter);
851
181k
  splash->setLineDash(NULL, 0, 0);
852
181k
  splash->setMiterLimit(10);
853
181k
  splash->setFlatness(1);
854
  // the SA parameter supposedly defaults to false, but Acrobat
855
  // apparently hardwires it to true
856
181k
  splash->setStrokeAdjust(
857
181k
        mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
858
181k
  splash->clear(paperColor, 0);
859
181k
  reverseVideoInvertImages = globalParams->getReverseVideoInvertImages();
860
181k
  if (startPageCbk) {
861
0
    (*startPageCbk)(startPageCbkData);
862
0
  }
863
181k
}
864
865
181k
void SplashOutputDev::endPage() {
866
181k
  if (colorMode != splashModeMono1 && !noComposite) {
867
0
    splash->compositeBackground(paperColor);
868
0
  }
869
181k
}
870
871
2.85M
void SplashOutputDev::saveState(GfxState *state) {
872
2.85M
  splash->saveState();
873
2.85M
  if (t3GlyphStack && !t3GlyphStack->haveDx) {
874
1.93M
    t3GlyphStack->doNotCache = gTrue;
875
1.93M
    error(errSyntaxWarning, -1,
876
1.93M
    "Save (q) operator before d0/d1 in Type 3 glyph");
877
1.93M
  }
878
2.85M
}
879
880
3.00M
void SplashOutputDev::restoreState(GfxState *state) {
881
3.00M
  splash->restoreState();
882
3.00M
  needFontUpdate = gTrue;
883
3.00M
  if (t3GlyphStack && !t3GlyphStack->haveDx) {
884
1.94M
    t3GlyphStack->doNotCache = gTrue;
885
1.94M
    error(errSyntaxWarning, -1,
886
1.94M
    "Restore (Q) operator before d0/d1 in Type 3 glyph");
887
1.94M
  }
888
3.00M
}
889
890
181k
void SplashOutputDev::updateAll(GfxState *state) {
891
181k
  updateLineDash(state);
892
181k
  updateLineJoin(state);
893
181k
  updateLineCap(state);
894
181k
  updateLineWidth(state);
895
181k
  updateFlatness(state);
896
181k
  updateMiterLimit(state);
897
181k
  updateStrokeAdjust(state);
898
181k
  updateFillColor(state);
899
181k
  updateStrokeColor(state);
900
181k
  needFontUpdate = gTrue;
901
181k
}
902
903
void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
904
        double m21, double m22,
905
2.38M
        double m31, double m32) {
906
2.38M
  double *ctm;
907
2.38M
  SplashCoord mat[6];
908
909
2.38M
  ctm = state->getCTM();
910
2.38M
  mat[0] = (SplashCoord)ctm[0];
911
2.38M
  mat[1] = (SplashCoord)ctm[1];
912
2.38M
  mat[2] = (SplashCoord)ctm[2];
913
2.38M
  mat[3] = (SplashCoord)ctm[3];
914
2.38M
  mat[4] = (SplashCoord)ctm[4];
915
2.38M
  mat[5] = (SplashCoord)ctm[5];
916
2.38M
  splash->setMatrix(mat);
917
2.38M
}
918
919
213k
void SplashOutputDev::updateLineDash(GfxState *state) {
920
213k
  double *dashPattern;
921
213k
  int dashLength;
922
213k
  double dashStart;
923
213k
  SplashCoord dash[20];
924
213k
  int i;
925
926
213k
  state->getLineDash(&dashPattern, &dashLength, &dashStart);
927
213k
  if (dashLength > 20) {
928
137
    dashLength = 20;
929
137
  }
930
220k
  for (i = 0; i < dashLength; ++i) {
931
6.23k
    dash[i] = (SplashCoord)dashPattern[i];
932
6.23k
    if (dash[i] < 0) {
933
377
      dash[i] = 0;
934
377
    }
935
6.23k
  }
936
213k
  splash->setLineDash(dash, dashLength, (SplashCoord)dashStart);
937
213k
}
938
939
388k
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
388k
}
945
946
199k
void SplashOutputDev::updateLineJoin(GfxState *state) {
947
199k
  splash->setLineJoin(state->getLineJoin());
948
199k
}
949
950
223k
void SplashOutputDev::updateLineCap(GfxState *state) {
951
223k
  splash->setLineCap(state->getLineCap());
952
223k
}
953
954
207k
void SplashOutputDev::updateMiterLimit(GfxState *state) {
955
207k
  splash->setMiterLimit(state->getMiterLimit());
956
207k
}
957
958
306k
void SplashOutputDev::updateLineWidth(GfxState *state) {
959
306k
  splash->setLineWidth(state->getLineWidth());
960
306k
}
961
962
183k
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
183k
}
976
977
978
428k
void SplashOutputDev::updateFillColor(GfxState *state) {
979
428k
  GfxGray gray;
980
428k
  GfxRGB rgb;
981
428k
#if SPLASH_CMYK
982
428k
  GfxCMYK cmyk;
983
428k
#endif
984
985
428k
  switch (colorMode) {
986
0
  case splashModeMono1:
987
109
  case splashModeMono8:
988
109
    state->getFillGray(&gray);
989
109
    splash->setFillPattern(getColor(gray));
990
109
    break;
991
428k
  case splashModeRGB8:
992
428k
  case splashModeBGR8:
993
428k
    state->getFillRGB(&rgb);
994
428k
    splash->setFillPattern(getColor(&rgb));
995
428k
    break;
996
0
#if SPLASH_CMYK
997
127
  case splashModeCMYK8:
998
127
    state->getFillCMYK(&cmyk);
999
127
    splash->setFillPattern(getColor(&cmyk));
1000
127
    break;
1001
428k
#endif
1002
428k
  }
1003
428k
}
1004
1005
324k
void SplashOutputDev::updateStrokeColor(GfxState *state) {
1006
324k
  GfxGray gray;
1007
324k
  GfxRGB rgb;
1008
324k
#if SPLASH_CMYK
1009
324k
  GfxCMYK cmyk;
1010
324k
#endif
1011
1012
324k
  switch (colorMode) {
1013
0
  case splashModeMono1:
1014
10
  case splashModeMono8:
1015
10
    state->getStrokeGray(&gray);
1016
10
    splash->setStrokePattern(getColor(gray));
1017
10
    break;
1018
324k
  case splashModeRGB8:
1019
324k
  case splashModeBGR8:
1020
324k
    state->getStrokeRGB(&rgb);
1021
324k
    splash->setStrokePattern(getColor(&rgb));
1022
324k
    break;
1023
0
#if SPLASH_CMYK
1024
144
  case splashModeCMYK8:
1025
144
    state->getStrokeCMYK(&cmyk);
1026
144
    splash->setStrokePattern(getColor(&cmyk));
1027
144
    break;
1028
324k
#endif
1029
324k
  }
1030
324k
}
1031
1032
1033
119
SplashPattern *SplashOutputDev::getColor(GfxGray gray) {
1034
119
  SplashColor color;
1035
1036
119
  getColor(gray, color);
1037
119
  return new SplashSolidColor(color);
1038
119
}
1039
1040
753k
SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) {
1041
753k
  SplashColor color;
1042
1043
753k
  getColor(rgb, color);
1044
753k
  return new SplashSolidColor(color);
1045
753k
}
1046
1047
#if SPLASH_CMYK
1048
271
SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) {
1049
271
  SplashColor color;
1050
1051
271
  getColor(cmyk, color);
1052
271
  return new SplashSolidColor(color);
1053
271
}
1054
#endif
1055
1056
1057
119
void SplashOutputDev::getColor(GfxGray gray, SplashColorPtr color) {
1058
119
  if (reverseVideo) {
1059
0
    gray = gfxColorComp1 - gray;
1060
0
  }
1061
119
  color[0] = colToByte(gray);
1062
119
}
1063
1064
753k
void SplashOutputDev::getColor(GfxRGB *rgb, SplashColorPtr color) {
1065
753k
  GfxColorComp r, g, b;
1066
1067
753k
  if (reverseVideo) {
1068
0
    r = gfxColorComp1 - rgb->r;
1069
0
    g = gfxColorComp1 - rgb->g;
1070
0
    b = gfxColorComp1 - rgb->b;
1071
753k
  } else {
1072
753k
    r = rgb->r;
1073
753k
    g = rgb->g;
1074
753k
    b = rgb->b;
1075
753k
  }
1076
753k
  color[0] = colToByte(r);
1077
753k
  color[1] = colToByte(g);
1078
753k
  color[2] = colToByte(b);
1079
753k
}
1080
1081
#if SPLASH_CMYK
1082
271
void SplashOutputDev::getColor(GfxCMYK *cmyk, SplashColorPtr color) {
1083
271
  color[0] = colToByte(cmyk->c);
1084
271
  color[1] = colToByte(cmyk->m);
1085
271
  color[2] = colToByte(cmyk->y);
1086
271
  color[3] = colToByte(cmyk->k);
1087
271
}
1088
#endif
1089
1090
1091
1092
void SplashOutputDev::setOverprintMask(GfxState *state,
1093
               GfxColorSpace *colorSpace,
1094
               GBool overprintFlag,
1095
               int overprintMode,
1096
1.13M
               GfxColor *singleColor) {
1097
1.13M
#if SPLASH_CMYK
1098
1.13M
  Guint mask;
1099
1.13M
  GfxCMYK cmyk;
1100
1101
  // Adobe ignores overprint in soft masks.
1102
1.13M
  if (overprintFlag &&
1103
80.4k
      !(transpGroupStack && transpGroupStack->inSoftMask) &&
1104
80.4k
      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
1.13M
  } else {
1127
1.13M
    mask = 0xffffffff;
1128
1.13M
  }
1129
1.13M
  splash->setOverprintMask(mask);
1130
1.13M
#endif
1131
1132
1.13M
}
1133
1134
5.52k
void SplashOutputDev::updateBlendMode(GfxState *state) {
1135
5.52k
  splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]);
1136
5.52k
}
1137
1138
6.63k
void SplashOutputDev::updateFillOpacity(GfxState *state) {
1139
6.63k
  splash->setFillAlpha((SplashCoord)state->getFillOpacity());
1140
6.63k
}
1141
1142
6.50k
void SplashOutputDev::updateStrokeOpacity(GfxState *state) {
1143
6.50k
  splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity());
1144
6.50k
}
1145
1146
3.83k
void SplashOutputDev::updateRenderingIntent(GfxState *state) {
1147
3.83k
  updateFillColor(state);
1148
3.83k
  updateStrokeColor(state);
1149
3.83k
}
1150
1151
883
void SplashOutputDev::updateTransfer(GfxState *state) {
1152
883
  Function **transfer;
1153
883
  Guchar red[256], green[256], blue[256], gray[256];
1154
883
  double x, y;
1155
883
  int i;
1156
1157
883
  transfer = state->getTransfer();
1158
883
  if (transfer[0] &&
1159
281
      transfer[0]->getInputSize() == 1 &&
1160
281
      transfer[0]->getOutputSize() == 1) {
1161
281
    if (transfer[1] &&
1162
21
  transfer[1]->getInputSize() == 1 &&
1163
21
  transfer[1]->getOutputSize() == 1 &&
1164
21
  transfer[2] &&
1165
21
  transfer[2]->getInputSize() == 1 &&
1166
21
  transfer[2]->getOutputSize() == 1 &&
1167
21
  transfer[3] &&
1168
21
  transfer[3]->getInputSize() == 1 &&
1169
21
  transfer[3]->getOutputSize() == 1) {
1170
5.39k
      for (i = 0; i < 256; ++i) {
1171
5.37k
  x = i / 255.0;
1172
5.37k
  transfer[0]->transform(&x, &y);
1173
5.37k
  red[i] = (Guchar)(y * 255.0 + 0.5);
1174
5.37k
  transfer[1]->transform(&x, &y);
1175
5.37k
  green[i] = (Guchar)(y * 255.0 + 0.5);
1176
5.37k
  transfer[2]->transform(&x, &y);
1177
5.37k
  blue[i] = (Guchar)(y * 255.0 + 0.5);
1178
5.37k
  transfer[3]->transform(&x, &y);
1179
5.37k
  gray[i] = (Guchar)(y * 255.0 + 0.5);
1180
5.37k
      }
1181
260
    } else {
1182
66.8k
      for (i = 0; i < 256; ++i) {
1183
66.5k
  x = i / 255.0;
1184
66.5k
  transfer[0]->transform(&x, &y);
1185
66.5k
  red[i] = green[i] = blue[i] = gray[i] = (Guchar)(y * 255.0 + 0.5);
1186
66.5k
      }
1187
260
    }
1188
602
  } else {
1189
154k
    for (i = 0; i < 256; ++i) {
1190
154k
      red[i] = green[i] = blue[i] = gray[i] = (Guchar)i;
1191
154k
    }
1192
602
  }
1193
883
  splash->setTransfer(red, green, blue, gray);
1194
883
}
1195
1196
4.26k
void SplashOutputDev::updateAlphaIsShape(GfxState *state) {
1197
4.26k
  splash->setAlphaIsShape(state->getAlphaIsShape());
1198
4.26k
}
1199
1200
323k
void SplashOutputDev::updateFont(GfxState *state) {
1201
323k
  needFontUpdate = gTrue;
1202
323k
}
1203
1204
303k
void SplashOutputDev::doUpdateFont(GfxState *state) {
1205
303k
  GfxFont *gfxFont;
1206
303k
  GfxFontLoc *fontLoc;
1207
303k
  GfxFontType fontType;
1208
303k
  SplashOutFontFileID *id;
1209
303k
  SplashFontFile *fontFile;
1210
303k
  int fontNum;
1211
303k
  FoFiTrueType *ff;
1212
303k
  FoFiType1C *ffT1C;
1213
303k
  Ref embRef;
1214
303k
  Object refObj, strObj;
1215
#if LOAD_FONTS_FROM_MEM
1216
  GString *fontBuf;
1217
  FILE *extFontFile;
1218
#else
1219
303k
  GString *tmpFileName, *fileName;
1220
303k
  FILE *tmpFile;
1221
303k
#endif
1222
303k
  char blk[4096];
1223
303k
  int *codeToGID;
1224
303k
  CharCodeToUnicode *ctu;
1225
303k
  double *textMat;
1226
303k
  double m11, m12, m21, m22, fontSize, oblique;
1227
303k
  double fsx, fsy, w, fontScaleMin, fontScaleAvg, fontScale;
1228
303k
  Gushort ww;
1229
303k
  SplashCoord mat[4];
1230
303k
  char *name, *start;
1231
303k
  Unicode uBuf[8];
1232
303k
  int substIdx, n, code, cmap, cmapPlatform, cmapEncoding, length, i;
1233
1234
303k
  needFontUpdate = gFalse;
1235
303k
  font = NULL;
1236
#if LOAD_FONTS_FROM_MEM
1237
  fontBuf = NULL;
1238
#else
1239
303k
  tmpFileName = NULL;
1240
303k
  fileName = NULL;
1241
303k
#endif
1242
303k
  substIdx = -1;
1243
1244
303k
  if (!(gfxFont = state->getFont())) {
1245
0
    goto err1;
1246
0
  }
1247
303k
  fontType = gfxFont->getType();
1248
303k
  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
303k
  state->textTransformDelta(state->getFontSize(), state->getFontSize(),
1256
303k
          &fsx, &fsy);
1257
303k
  state->transformDelta(fsx, fsy, &fsx, &fsy);
1258
303k
  if (fabs(fsx) > 20000 || fabs(fsy) > 20000) {
1259
225k
    goto err1;
1260
225k
  }
1261
1262
  // check the font file cache
1263
77.4k
  id = new SplashOutFontFileID(gfxFont->getID());
1264
77.4k
  if (fontEngine->checkForBadFontFile(id)) {
1265
9.32k
    goto err2;
1266
9.32k
  }
1267
68.1k
  if ((fontFile = fontEngine->getFontFile(id))) {
1268
23.6k
    delete id;
1269
1270
44.5k
  } else {
1271
1272
44.5k
    fontNum = 0;
1273
1274
44.5k
    if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) {
1275
35.1k
      error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'",
1276
35.1k
      gfxFont->getName() ? gfxFont->getName()->getCString()
1277
35.1k
                         : "(unnamed)");
1278
35.1k
      goto err2;
1279
35.1k
    }
1280
1281
    // embedded font
1282
9.43k
    if (fontLoc->locType == gfxFontLocEmbedded) {
1283
9.43k
      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
9.43k
      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
9.43k
      refObj.initRef(embRef.num, embRef.gen);
1308
9.43k
      refObj.fetch(xref, &strObj);
1309
9.43k
      refObj.free();
1310
9.43k
      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
9.43k
      strObj.streamReset();
1318
99.1k
      while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) {
1319
89.6k
  fwrite(blk, 1, n, tmpFile);
1320
89.6k
      }
1321
9.43k
      strObj.streamClose();
1322
9.43k
      strObj.free();
1323
9.43k
      fclose(tmpFile);
1324
9.43k
      fileName = tmpFileName;
1325
9.43k
#endif
1326
1327
    // external font
1328
9.43k
    } 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
9.43k
    switch (fontLoc->fontType) {
1355
1.67k
    case fontType1:
1356
1.67k
      if (!(fontFile = fontEngine->loadType1Font(
1357
1.67k
       id,
1358
#if LOAD_FONTS_FROM_MEM
1359
       fontBuf,
1360
#else
1361
1.67k
       fileName->getCString(),
1362
1.67k
       fileName == tmpFileName,
1363
1.67k
#endif
1364
1.67k
       (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1365
1.55k
  error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1366
1.55k
        gfxFont->getName() ? gfxFont->getName()->getCString()
1367
1.55k
                           : "(unnamed)");
1368
1.55k
  delete fontLoc;
1369
1.55k
  goto err1;
1370
1.55k
      }
1371
116
      break;
1372
2.16k
    case fontType1C:
1373
#if LOAD_FONTS_FROM_MEM
1374
      if ((ffT1C = FoFiType1C::make(fontBuf->getCString(),
1375
            fontBuf->getLength()))) {
1376
#else
1377
2.16k
      if ((ffT1C = FoFiType1C::load(fileName->getCString()))) {
1378
1.33k
#endif
1379
1.33k
  codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffT1C);
1380
1.33k
  delete ffT1C;
1381
1.33k
      } else {
1382
833
  codeToGID = NULL;
1383
833
      }
1384
2.16k
      if (!(fontFile = fontEngine->loadType1CFont(
1385
2.16k
       id,
1386
#if LOAD_FONTS_FROM_MEM
1387
       fontBuf,
1388
#else
1389
2.16k
       fileName->getCString(),
1390
2.16k
       fileName == tmpFileName,
1391
2.16k
#endif
1392
2.16k
       codeToGID,
1393
2.16k
       (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1394
495
  error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1395
495
        gfxFont->getName() ? gfxFont->getName()->getCString()
1396
495
                           : "(unnamed)");
1397
495
  delete fontLoc;
1398
495
  goto err1;
1399
495
      }
1400
1.67k
      break;
1401
1.67k
    case fontType1COT:
1402
829
      codeToGID = NULL;
1403
#if LOAD_FONTS_FROM_MEM
1404
      if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
1405
           fontNum, gTrue))) {
1406
#else
1407
829
  if ((ff = FoFiTrueType::load(fileName->getCString(),
1408
829
             fontNum, gTrue))) {
1409
819
#endif
1410
819
  if (ff->getCFFBlock(&start, &length) &&
1411
818
      (ffT1C = FoFiType1C::make(start, length))) {
1412
764
    codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffT1C);
1413
764
    delete ffT1C;
1414
764
  }
1415
819
  delete ff;
1416
819
      }
1417
829
      if (!(fontFile = fontEngine->loadOpenTypeT1CFont(
1418
829
       id,
1419
#if LOAD_FONTS_FROM_MEM
1420
       fontBuf,
1421
#else
1422
829
       fileName->getCString(),
1423
829
       fileName == tmpFileName,
1424
829
#endif
1425
829
       codeToGID,
1426
829
       (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1427
436
  error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1428
436
        gfxFont->getName() ? gfxFont->getName()->getCString()
1429
436
                           : "(unnamed)");
1430
436
  delete fontLoc;
1431
436
  goto err1;
1432
436
      }
1433
393
      break;
1434
2.03k
    case fontTrueType:
1435
2.03k
    case fontTrueTypeOT:
1436
#if LOAD_FONTS_FROM_MEM
1437
      if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
1438
           fontNum))) {
1439
#else
1440
2.03k
      if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
1441
2.01k
#endif
1442
2.01k
  codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
1443
2.01k
  n = 256;
1444
2.01k
  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
2.01k
  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
2.01k
      } else {
1457
21
  codeToGID = NULL;
1458
21
  n = 0;
1459
21
      }
1460
2.03k
      if (!(fontFile = fontEngine->loadTrueTypeFont(
1461
2.03k
         id,
1462
#if LOAD_FONTS_FROM_MEM
1463
         fontBuf,
1464
#else
1465
2.03k
         fileName->getCString(),
1466
2.03k
         fileName == tmpFileName,
1467
2.03k
#endif
1468
2.03k
         fontNum, codeToGID, n,
1469
2.03k
         gfxFont->getEmbeddedFontName()
1470
2.03k
           ? gfxFont->getEmbeddedFontName()->getCString()
1471
2.03k
           : (char *)NULL))) {
1472
90
  error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1473
90
        gfxFont->getName() ? gfxFont->getName()->getCString()
1474
90
                           : "(unnamed)");
1475
90
  delete fontLoc;
1476
90
  goto err1;
1477
90
      }
1478
1.94k
      break;
1479
1.94k
    case fontCIDType0:
1480
32
    case fontCIDType0C:
1481
32
      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
32
      } else {
1487
32
  codeToGID = NULL;
1488
32
  n = 0;
1489
32
      }
1490
32
      if (!(fontFile = fontEngine->loadCIDFont(
1491
32
         id,
1492
#if LOAD_FONTS_FROM_MEM
1493
         fontBuf,
1494
#else
1495
32
         fileName->getCString(),
1496
32
         fileName == tmpFileName,
1497
32
#endif
1498
32
         codeToGID, n))) {
1499
1500
7
  error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1501
7
        gfxFont->getName() ? gfxFont->getName()->getCString()
1502
7
                           : "(unnamed)");
1503
7
  delete fontLoc;
1504
7
  goto err1;
1505
7
      }
1506
25
      break;
1507
48
    case fontCIDType0COT:
1508
48
      codeToGID = NULL;
1509
48
      n = 0;
1510
48
      if (fontLoc->locType == gfxFontLocEmbedded) {
1511
48
  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
48
      } 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
48
      if (!(fontFile = fontEngine->loadOpenTypeCFFFont(
1562
48
         id,
1563
#if LOAD_FONTS_FROM_MEM
1564
         fontBuf,
1565
#else
1566
48
         fileName->getCString(),
1567
48
         fileName == tmpFileName,
1568
48
#endif
1569
48
         codeToGID, n))) {
1570
19
  error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1571
19
        gfxFont->getName() ? gfxFont->getName()->getCString()
1572
19
                           : "(unnamed)");
1573
19
  delete fontLoc;
1574
19
  goto err1;
1575
19
      }
1576
29
      break;
1577
2.65k
    case fontCIDType2:
1578
2.65k
    case fontCIDType2OT:
1579
2.65k
      codeToGID = NULL;
1580
2.65k
      n = 0;
1581
2.65k
      if (fontLoc->locType == gfxFontLocEmbedded) {
1582
2.65k
  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
2.65k
      } 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
2.65k
      if (!(fontFile = fontEngine->loadTrueTypeFont(
1634
2.65k
         id,
1635
#if LOAD_FONTS_FROM_MEM
1636
         fontBuf,
1637
#else
1638
2.65k
         fileName->getCString(),
1639
2.65k
         fileName == tmpFileName,
1640
2.65k
#endif
1641
2.65k
         fontNum, codeToGID, n,
1642
2.65k
         gfxFont->getEmbeddedFontName()
1643
2.65k
           ? gfxFont->getEmbeddedFontName()->getCString()
1644
2.65k
           : (char *)NULL))) {
1645
325
  error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1646
325
        gfxFont->getName() ? gfxFont->getName()->getCString()
1647
325
                           : "(unnamed)");
1648
325
  delete fontLoc;
1649
325
  goto err1;
1650
325
      }
1651
2.32k
      break;
1652
2.32k
    default:
1653
      // this shouldn't happen
1654
0
      delete fontLoc;
1655
0
      goto err2;
1656
9.43k
    }
1657
1658
6.50k
    delete fontLoc;
1659
6.50k
  }
1660
1661
  // get the font matrix
1662
30.1k
  textMat = state->getTextMat();
1663
30.1k
  fontSize = state->getFontSize();
1664
30.1k
  oblique = ((SplashOutFontFileID *)fontFile->getID())->getOblique();
1665
30.1k
  m11 = state->getHorizScaling() * textMat[0];
1666
30.1k
  m12 = state->getHorizScaling() * textMat[1];
1667
30.1k
  m21 = oblique * m11 + textMat[2];
1668
30.1k
  m22 = oblique * m12 + textMat[3];
1669
30.1k
  m11 *= fontSize;
1670
30.1k
  m12 *= fontSize;
1671
30.1k
  m21 *= fontSize;
1672
30.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
30.1k
  substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx();
1678
30.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
30.1k
  mat[0] = m11;  mat[1] = m12;
1717
30.1k
  mat[2] = m21;  mat[3] = m22;
1718
30.1k
  font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
1719
1720
30.1k
#if !LOAD_FONTS_FROM_MEM
1721
30.1k
  if (tmpFileName) {
1722
6.50k
    delete tmpFileName;
1723
6.50k
  }
1724
30.1k
#endif
1725
30.1k
  return;
1726
1727
44.4k
 err2:
1728
44.4k
  delete id;
1729
273k
 err1:
1730
#if LOAD_FONTS_FROM_MEM
1731
  if (fontBuf) {
1732
    delete fontBuf;
1733
  }
1734
#else
1735
273k
  if (tmpFileName) {
1736
2.92k
    unlink(tmpFileName->getCString());
1737
2.92k
    delete tmpFileName;
1738
2.92k
  }
1739
273k
#endif
1740
273k
  return;
1741
44.4k
}
1742
1743
75.7k
void SplashOutputDev::stroke(GfxState *state) {
1744
75.7k
  SplashPath *path;
1745
1746
75.7k
  if (state->getStrokeColorSpace()->isNonMarking()) {
1747
0
    return;
1748
0
  }
1749
75.7k
  setOverprintMask(state, state->getStrokeColorSpace(),
1750
75.7k
       state->getStrokeOverprint(), state->getOverprintMode(),
1751
75.7k
       state->getStrokeColor());
1752
75.7k
  path = convertPath(state, state->getPath(), gFalse);
1753
75.7k
  splash->stroke(path);
1754
75.7k
  delete path;
1755
75.7k
}
1756
1757
120k
void SplashOutputDev::fill(GfxState *state) {
1758
120k
  SplashPath *path;
1759
1760
120k
  if (state->getFillColorSpace()->isNonMarking()) {
1761
0
    return;
1762
0
  }
1763
120k
  setOverprintMask(state, state->getFillColorSpace(),
1764
120k
       state->getFillOverprint(), state->getOverprintMode(),
1765
120k
       state->getFillColor());
1766
120k
  path = convertPath(state, state->getPath(), gTrue);
1767
120k
  splash->fill(path, gFalse);
1768
120k
  delete path;
1769
120k
}
1770
1771
930
void SplashOutputDev::eoFill(GfxState *state) {
1772
930
  SplashPath *path;
1773
1774
930
  if (state->getFillColorSpace()->isNonMarking()) {
1775
0
    return;
1776
0
  }
1777
930
  setOverprintMask(state, state->getFillColorSpace(),
1778
930
       state->getFillOverprint(), state->getOverprintMode(),
1779
930
       state->getFillColor());
1780
930
  path = convertPath(state, state->getPath(), gTrue);
1781
930
  splash->fill(path, gTrue);
1782
930
  delete path;
1783
930
}
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
1.80k
          double xStep, double yStep) {
1792
1.80k
  SplashBitmap *origBitmap, *tileBitmap;
1793
1.80k
  Splash *origSplash;
1794
1.80k
  SplashColor color;
1795
1.80k
  Guint *overprintMaskBitmap;
1796
1.80k
  double *ctm, *baseMatrix;
1797
1.80k
  double ictm[6], tileMat[6], mat1[6], mat2[6];
1798
1.80k
  double tileXMin, tileYMin, tileXMax, tileYMax;
1799
1.80k
  double xStepX, xStepY, yStepX, yStepY;
1800
1.80k
  double adjXMin, adjYMin;
1801
1.80k
  double sx, sy;
1802
1.80k
  double clipXMin, clipYMin, clipXMax, clipYMax, clipXC, clipYC;
1803
1.80k
  double tx, ty, idet, txMin, tyMin, txMax, tyMax;
1804
1.80k
  GBool clipped;
1805
1.80k
  int tileW, tileH, tileSize;
1806
1.80k
  int ixMin, ixMax, iyMin, iyMax, ix, iy, x, y;
1807
1.80k
  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
1.80k
  state->transform(bbox[0] * mat[0] + bbox[1] * mat[2] + mat[4],
1821
1.80k
       bbox[0] * mat[1] + bbox[1] * mat[3] + mat[5],
1822
1.80k
       &tx, &ty);
1823
1.80k
  tileXMin = tileXMax = tx;
1824
1.80k
  tileYMin = tileYMax = ty;
1825
1.80k
  state->transform(bbox[2] * mat[0] + bbox[1] * mat[2] + mat[4],
1826
1.80k
       bbox[2] * mat[1] + bbox[1] * mat[3] + mat[5],
1827
1.80k
       &tx, &ty);
1828
1.80k
  if (tx < tileXMin) {
1829
518
    tileXMin = tx;
1830
1.28k
  } else if (tx > tileXMax) {
1831
462
    tileXMax = tx;
1832
462
  }
1833
1.80k
  if (ty < tileYMin) {
1834
82
    tileYMin = ty;
1835
1.72k
  } else if (ty > tileYMax) {
1836
190
    tileYMax = ty;
1837
190
  }
1838
1.80k
  state->transform(bbox[2] * mat[0] + bbox[3] * mat[2] + mat[4],
1839
1.80k
       bbox[2] * mat[1] + bbox[3] * mat[3] + mat[5],
1840
1.80k
       &tx, &ty);
1841
1.80k
  if (tx < tileXMin) {
1842
12
    tileXMin = tx;
1843
1.79k
  } else if (tx > tileXMax) {
1844
21
    tileXMax = tx;
1845
21
  }
1846
1.80k
  if (ty < tileYMin) {
1847
825
    tileYMin = ty;
1848
980
  } else if (ty > tileYMax) {
1849
193
    tileYMax = ty;
1850
193
  }
1851
1.80k
  state->transform(bbox[0] * mat[0] + bbox[3] * mat[2] + mat[4],
1852
1.80k
       bbox[0] * mat[1] + bbox[3] * mat[3] + mat[5],
1853
1.80k
       &tx, &ty);
1854
1.80k
  if (tx < tileXMin) {
1855
24
    tileXMin = tx;
1856
1.78k
  } else if (tx > tileXMax) {
1857
140
    tileXMax = tx;
1858
140
  }
1859
1.80k
  if (ty < tileYMin) {
1860
209
    tileYMin = ty;
1861
1.59k
  } else if (ty > tileYMax) {
1862
5
    tileYMax = ty;
1863
5
  }
1864
1.80k
  if (tileXMin == tileXMax || tileYMin == tileYMax) {
1865
70
    return;
1866
70
  }
1867
1.73k
  tileW = (int)(tileXMax - tileXMin + 0.5);
1868
1.73k
  tileH = (int)(tileYMax - tileYMin + 0.5);
1869
1.73k
  if (tileW < 1) {
1870
1.73k
    tileW = 1;
1871
1.73k
  }
1872
1.73k
  if (tileH < 1) {
1873
1.73k
    tileH = 1;
1874
1.73k
  }
1875
1876
  // check for an excessively large tile size
1877
1.73k
  tileSize = tileW * tileH;
1878
1.73k
  if (tileXMax - tileXMin + 0.5 > (double)INT_MAX ||
1879
757
      tileYMax - tileYMin + 0.5 > (double)INT_MAX ||
1880
757
      tileW > INT_MAX / tileH ||
1881
978
      tileSize > maxTileSize) {
1882
978
    mat1[0] = mat[0];
1883
978
    mat1[1] = mat[1];
1884
978
    mat1[2] = mat[2];
1885
978
    mat1[3] = mat[3];
1886
4.16k
    for (iy = y0; iy < y1; ++iy) {
1887
66.9k
      for (ix = x0; ix < x1; ++ix) {
1888
63.7k
  tx = ix * xStep;
1889
63.7k
  ty = iy * yStep;
1890
63.7k
  mat1[4] = tx * mat[0] + ty * mat[2] + mat[4];
1891
63.7k
  mat1[5] = tx * mat[1] + ty * mat[3] + mat[5];
1892
63.7k
  gfx->drawForm(strRef, resDict, mat1, bbox);
1893
63.7k
      }
1894
3.18k
    }
1895
978
    return;
1896
978
  }
1897
1898
  // transform XStep and YStep to device space
1899
757
  state->transformDelta(xStep * mat[0], xStep * mat[1], &xStepX, &xStepY);
1900
757
  state->transformDelta(yStep * mat[2], yStep * mat[3], &yStepX, &yStepY);
1901
1902
  // get the clipping bbox (in device space)
1903
757
  state->getClipBBox(&clipXMin, &clipYMin, &clipXMax, &clipYMax);
1904
1905
  // compute tiling parameters
1906
757
  idet = xStepX * yStepY - yStepX * xStepY;
1907
757
  if (tilingType == 2 || idet == 0) {
1908
580
    adjXMin = tileXMin;
1909
580
    adjYMin = tileYMin;
1910
580
    sx = 1;
1911
580
    sy = 1;
1912
580
  } else {
1913
    // reposition the pattern origin to the center of the clipping bbox
1914
177
    idet = 1 / idet;
1915
177
    clipXC = 0.5 * (clipXMin + clipXMax);
1916
177
    clipYC = 0.5 * (clipYMin + clipYMax);
1917
177
    ix = (int)floor((yStepX * (tileYMin - clipYC)
1918
177
         - (tileXMin - clipXC) * yStepY) * idet + 0.5);
1919
177
    iy = (int)floor((xStepX * (clipYC - tileYMin)
1920
177
         - (clipXC - tileXMin) * xStepY) * idet + 0.5);
1921
177
    adjXMin = (int)floor(tileXMin + ix * xStepX + iy * yStepX + 0.5);
1922
177
    adjYMin = (int)floor(tileYMin + ix * xStepY + iy * yStepY + 0.5);
1923
177
    sx = tileW / (tileXMax - tileXMin);
1924
177
    sy = tileH / (tileYMax - tileYMin);
1925
177
    xStepX = (int)floor(sx * xStepX + 0.5);
1926
177
    xStepY = (int)floor(sy * xStepY + 0.5);
1927
177
    yStepX = (int)floor(sx * yStepX + 0.5);
1928
177
    yStepY = (int)floor(sy * yStepY + 0.5);
1929
177
  }
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
757
  idet = xStepX * yStepY - xStepY * yStepX;
1936
757
  if (idet == 0) {
1937
315
    return;
1938
315
  }
1939
442
  idet = 1 / idet;
1940
  // LL corner
1941
442
  tx = idet * (yStepY * (clipXMin - tileW - 1 - adjXMin)
1942
442
         - yStepX * (clipYMax + 1 - adjYMin));
1943
442
  ty = idet * (xStepX * (clipYMax + 1 - adjYMin)
1944
442
         - xStepY * (clipXMin - tileW - 1 - adjXMin));
1945
442
  txMin = txMax = tx;
1946
442
  tyMin = tyMax = ty;
1947
  // LR corner
1948
442
  tx = idet * (yStepY * (clipXMax + 1 - adjXMin)
1949
442
         - yStepX * (clipYMax + 1 - adjYMin));
1950
442
  ty = idet * (xStepX * (clipYMax + 1 - adjYMin)
1951
442
         - xStepY * (clipXMax + 1 - adjXMin));
1952
442
  if (tx < txMin) {
1953
0
    txMin = tx;
1954
442
  } else if (tx > txMax) {
1955
0
    txMax = tx;
1956
0
  }
1957
442
  if (ty < tyMin) {
1958
0
    tyMin = ty;
1959
442
  } else if (ty > tyMax) {
1960
0
    tyMax = ty;
1961
0
  }
1962
  // UL corner
1963
442
  tx = idet * (yStepY * (clipXMin - tileW - 1 - adjXMin)
1964
442
         - yStepX * (clipYMin - tileH - 1 - adjYMin));
1965
442
  ty = idet * (xStepX * (clipYMin - tileH - 1 - adjYMin)
1966
442
         - xStepY * (clipXMin - tileW - 1 - adjXMin));
1967
442
  if (tx < txMin) {
1968
0
    txMin = tx;
1969
442
  } else if (tx > txMax) {
1970
0
    txMax = tx;
1971
0
  }
1972
442
  if (ty < tyMin) {
1973
0
    tyMin = ty;
1974
442
  } else if (ty > tyMax) {
1975
0
    tyMax = ty;
1976
0
  }
1977
  // UR corner
1978
442
  tx = idet * (yStepY * (clipXMax + 1 - adjXMin)
1979
442
         - yStepX * (clipYMin - tileH - 1 - adjYMin));
1980
442
  ty = idet * (xStepX * (clipYMin - tileH - 1 - adjYMin)
1981
442
         - xStepY * (clipXMax + 1 - adjXMin));
1982
442
  if (tx < txMin) {
1983
0
    txMin = tx;
1984
442
  } else if (tx > txMax) {
1985
0
    txMax = tx;
1986
0
  }
1987
442
  if (ty < tyMin) {
1988
0
    tyMin = ty;
1989
442
  } else if (ty > tyMax) {
1990
0
    tyMax = ty;
1991
0
  }
1992
442
  ixMin = (int)ceil(txMin);
1993
442
  ixMax = (int)floor(txMax) + 1;
1994
442
  iyMin = (int)ceil(tyMin);
1995
442
  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
442
  if ((double)(ixMax - ixMin) * (double)(iyMax - iyMin) >
2001
442
      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
442
  if (ixMax - ixMin == 1 && iyMax - iyMin == 1 &&
2020
442
      (clipXMax - clipXMin < 0.5 * tileW ||
2021
275
       clipYMax - clipYMin < 0.5 * tileH)) {
2022
173
    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
173
    tileW = (int)(clipXMax - clipXMin + 0.5);
2027
173
    tileH = (int)(clipYMax - clipYMin + 0.5);
2028
173
    if (tileW < 1) {
2029
173
      tileW = 1;
2030
173
    }
2031
173
    if (tileH < 1) {
2032
173
      tileH = 1;
2033
173
    }
2034
173
    tileXMin += clipXMin - (adjXMin + ixMin * xStepX + iyMin * yStepX);
2035
173
    tileYMin += clipYMin - (adjYMin + ixMin * xStepY + iyMin * yStepY);
2036
173
    ixMin = 0;
2037
173
    iyMin = 0;
2038
173
    ixMax = 1;
2039
173
    iyMax = 1;
2040
173
    adjXMin = clipXMin;
2041
173
    adjYMin = clipYMin;
2042
269
  } else {
2043
269
    clipped = gFalse;
2044
269
  }
2045
2046
  // compute tile matrix = PTM * BTM * Mtranslate * Mscale * iCTM
2047
  //                     = mat * CTM * Mtranslate * Mscale * iCTM
2048
442
  ctm = state->getCTM();
2049
442
  idet = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
2050
442
  ictm[0] = ctm[3] * idet;
2051
442
  ictm[1] = -ctm[1] * idet;
2052
442
  ictm[2] = -ctm[2] * idet;
2053
442
  ictm[3] = ctm[0] * idet;
2054
442
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * idet;
2055
442
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * idet;
2056
  // mat * CTM
2057
442
  mat1[0] = mat[0] * ctm[0] + mat[1] * ctm[2];
2058
442
  mat1[1] = mat[0] * ctm[1] + mat[1] * ctm[3];
2059
442
  mat1[2] = mat[2] * ctm[0] + mat[3] * ctm[2];
2060
442
  mat1[3] = mat[2] * ctm[1] + mat[3] * ctm[3];
2061
442
  mat1[4] = mat[4] * ctm[0] + mat[5] * ctm[2] + ctm[4];
2062
442
  mat1[5] = mat[4] * ctm[1] + mat[5] * ctm[3] + ctm[5];
2063
  // mat * CTM * (Mtranslate * Mscale)
2064
442
  mat2[0] = mat1[0] * sx;
2065
442
  mat2[1] = mat1[1] * sy;
2066
442
  mat2[2] = mat1[2] * sx;
2067
442
  mat2[3] = mat1[3] * sy;
2068
442
  mat2[4] = mat1[4] * sx - sx * tileXMin;
2069
442
  mat2[5] = mat1[5] * sy - sy * tileYMin;
2070
  // mat * CTM * (Mtranslate * Mscale) * iCTM
2071
442
  tileMat[0] = mat2[0] * ictm[0] + mat2[1] * ictm[2];
2072
442
  tileMat[1] = mat2[0] * ictm[1] + mat2[1] * ictm[3];
2073
442
  tileMat[2] = mat2[2] * ictm[0] + mat2[3] * ictm[2];
2074
442
  tileMat[3] = mat2[2] * ictm[1] + mat2[3] * ictm[3];
2075
442
  tileMat[4] = mat2[4] * ictm[0] + mat2[5] * ictm[2] + ictm[4];
2076
442
  tileMat[5] = mat2[4] * ictm[1] + mat2[5] * ictm[3] + ictm[5];
2077
2078
442
  baseMatrix = gfx->getBaseMatrix();
2079
442
  if (paintType == 1 &&   // colored tiling pattern
2080
377
      !clipped &&
2081
254
      strRef->isRef() &&
2082
254
      strRef->getRefNum() == tileCacheRef.num &&
2083
218
      strRef->getRefGen() == tileCacheRef.gen &&
2084
218
      tileCacheBitmap &&
2085
218
      tileCacheBitmap->getWidth() == tileW &&
2086
218
      tileCacheBitmap->getHeight() == tileH &&
2087
218
      tileCacheBaseMatrix[0] == baseMatrix[0] &&
2088
218
      tileCacheBaseMatrix[1] == baseMatrix[1] &&
2089
218
      tileCacheBaseMatrix[2] == baseMatrix[2] &&
2090
218
      tileCacheBaseMatrix[3] == baseMatrix[3] &&
2091
218
      tileCacheBaseMatrix[4] == baseMatrix[4] &&
2092
109
      tileCacheBaseMatrix[5] == baseMatrix[5]) {
2093
109
    tileBitmap = tileCacheBitmap;
2094
109
    overprintMaskBitmap = tileCacheOverprintMaskBitmap;
2095
2096
333
  } else {
2097
333
    if (tileCacheBitmap) {
2098
109
      delete tileCacheBitmap;
2099
109
      tileCacheBitmap = NULL;
2100
109
    }
2101
333
    gfree(tileCacheOverprintMaskBitmap);
2102
333
    tileCacheOverprintMaskBitmap = NULL;
2103
2104
    // create a temporary bitmap
2105
333
    origBitmap = bitmap;
2106
333
    origSplash = splash;
2107
333
    traceMessage("tiling pattern bitmap");
2108
333
    bitmap = tileBitmap = new SplashBitmap(tileW, tileH, bitmapRowPad,
2109
333
             colorMode, gTrue, bitmapTopDown,
2110
333
             bitmapMemCache);
2111
333
    splash = new Splash(bitmap, vectorAntialias,
2112
333
      origSplash->getImageCache(), origSplash->getScreen());
2113
1.66k
    for (i = 0; i < splashMaxColorComps; ++i) {
2114
1.33k
      color[i] = 0;
2115
1.33k
    }
2116
333
    splash->clear(color);
2117
333
#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
333
    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
333
    } else {
2127
333
      overprintMaskBitmap = NULL;
2128
333
    }
2129
#else // SPLASH_CMYK
2130
    overprintMaskBitmap = NULL;
2131
#endif // SPLASH_CMYK
2132
333
    splash->setMinLineWidth(globalParams->getMinLineWidth());
2133
333
    splash->setStrokeAdjust(
2134
333
       mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
2135
333
    splash->setEnablePathSimplification(
2136
333
       globalParams->getEnablePathSimplification());
2137
333
    ++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
333
    splash->setFillPattern(origSplash->getFillPattern()->copy());
2143
333
    splash->setStrokePattern(origSplash->getStrokePattern()->copy());
2144
2145
    // reset the clip rectangle
2146
333
    state->resetDevClipRect(0, 0, tileW, tileH);
2147
2148
    // render the tile
2149
333
    gfx->drawForm(strRef, resDict, tileMat, bbox);
2150
2151
    // restore the original bitmap
2152
333
    --nestCount;
2153
333
    delete splash;
2154
333
    bitmap = origBitmap;
2155
333
    splash = origSplash;
2156
333
    splash->setOverprintMask(0xffffffff);
2157
2158
333
  }
2159
2160
  // draw the tiles
2161
442
  if (tileW == 1 && tileH == 1 &&
2162
442
      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
442
  } else {
2179
884
    for (iy = iyMin; iy < iyMax; ++iy) {
2180
884
      for (ix = ixMin; ix < ixMax; ++ix) {
2181
442
  x = (int)floor(adjXMin + ix * xStepX + iy * yStepX + 0.5);
2182
442
  y = (int)floor(adjYMin + ix * xStepY + iy * yStepY + 0.5);
2183
442
  if (overprintMaskBitmap) {
2184
0
    splash->compositeWithOverprint(tileBitmap, NULL, overprintMaskBitmap,
2185
0
           0, 0, x, y, tileW, tileH,
2186
0
           gFalse, gFalse);
2187
442
  } else {
2188
442
    splash->composite(tileBitmap, NULL, 0, 0, x, y, tileW, tileH,
2189
442
          gFalse, gFalse);
2190
442
  }
2191
442
      }
2192
442
    }
2193
442
  }
2194
2195
  // NB: a nested pattern might have been cached
2196
442
  if (tileBitmap != tileCacheBitmap) {
2197
333
    if (paintType == 1 &&   // colored tiling pattern
2198
268
  !clipped) {
2199
145
      tileCacheRef = strRef->getRef();
2200
145
      tileCacheBaseMatrix[0] = baseMatrix[0];
2201
145
      tileCacheBaseMatrix[1] = baseMatrix[1];
2202
145
      tileCacheBaseMatrix[2] = baseMatrix[2];
2203
145
      tileCacheBaseMatrix[3] = baseMatrix[3];
2204
145
      tileCacheBaseMatrix[4] = baseMatrix[4];
2205
145
      tileCacheBaseMatrix[5] = baseMatrix[5];
2206
145
      if (tileCacheBitmap) {
2207
12
  delete tileCacheBitmap;
2208
12
      }
2209
145
      tileCacheBitmap = tileBitmap;
2210
145
      gfree(tileCacheOverprintMaskBitmap);
2211
145
      tileCacheOverprintMaskBitmap = overprintMaskBitmap;
2212
188
    } else {
2213
188
      delete tileBitmap;
2214
188
      gfree(overprintMaskBitmap);
2215
188
    }
2216
333
  }
2217
442
}
2218
2219
3.14k
GBool SplashOutputDev::shadedFill(GfxState *state, GfxShading *shading) {
2220
2221
  // generate the bitmap
2222
3.14k
  SplashColorMode srcMode;
2223
3.14k
  if (colorMode == splashModeMono1) {
2224
0
    srcMode = splashModeMono8;
2225
3.14k
  } else if (colorMode == splashModeBGR8) {
2226
0
    srcMode = splashModeRGB8;
2227
3.14k
  } else {
2228
3.14k
    srcMode = colorMode;
2229
3.14k
  }
2230
3.14k
  int x, y;
2231
3.14k
  SplashBitmap *tBitmap = ShadingImage::generateBitmap(state, shading, srcMode,
2232
3.14k
                   reverseVideo,
2233
3.14k
                   splash, bitmapMemCache,
2234
3.14k
                   &x, &y);
2235
3.14k
  if (!tBitmap) {
2236
    // clip region is empty - nothing to draw
2237
1.53k
    return gTrue;
2238
1.53k
  }
2239
2240
  // check clipping and composite the bitmap
2241
1.60k
  int xMin = x;
2242
1.60k
  int yMin = y;
2243
1.60k
  int xMax = x + tBitmap->getWidth();
2244
1.60k
  int yMax = y + tBitmap->getHeight();
2245
1.60k
  SplashClipResult clipRes = splash->limitRectToClipRect(&xMin, &yMin,
2246
1.60k
               &xMax, &yMax);
2247
1.60k
  if (clipRes != splashClipAllOutside) {
2248
116
    setOverprintMask(state, state->getFillColorSpace(),
2249
116
         state->getFillOverprint(), state->getOverprintMode(),
2250
116
         NULL);
2251
116
    splash->composite(tBitmap, NULL, xMin - x, yMin - y, xMin, yMin,
2252
116
          xMax - xMin, yMax - yMin,
2253
116
          clipRes == splashClipAllInside, gFalse);
2254
116
  }
2255
2256
1.60k
  delete tBitmap;
2257
2258
1.60k
  return gTrue;
2259
3.14k
}
2260
2261
236k
void SplashOutputDev::clip(GfxState *state) {
2262
236k
  SplashPath *path;
2263
2264
236k
  path = convertPath(state, state->getPath(), gTrue);
2265
236k
  splash->clipToPath(path, gFalse);
2266
236k
  delete path;
2267
236k
}
2268
2269
5.01k
void SplashOutputDev::eoClip(GfxState *state) {
2270
5.01k
  SplashPath *path;
2271
2272
5.01k
  path = convertPath(state, state->getPath(), gTrue);
2273
5.01k
  splash->clipToPath(path, gTrue);
2274
5.01k
  delete path;
2275
5.01k
}
2276
2277
288
void SplashOutputDev::clipToStrokePath(GfxState *state) {
2278
288
  SplashPath *path, *path2;
2279
2280
288
  path = convertPath(state, state->getPath(), gFalse);
2281
288
  path2 = splash->makeStrokePath(path, state->getLineWidth(),
2282
288
         state->getLineCap(), state->getLineJoin());
2283
288
  delete path;
2284
288
  splash->clipToPath(path2, gFalse);
2285
288
  delete path2;
2286
288
}
2287
2288
SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path,
2289
438k
           GBool dropEmptySubpaths) {
2290
438k
  SplashPath *sPath;
2291
438k
  GfxSubpath *subpath;
2292
438k
  int n, i, j;
2293
2294
438k
  n = dropEmptySubpaths ? 1 : 0;
2295
438k
  sPath = new SplashPath();
2296
915k
  for (i = 0; i < path->getNumSubpaths(); ++i) {
2297
477k
    subpath = path->getSubpath(i);
2298
477k
    if (subpath->getNumPoints() > n) {
2299
471k
      sPath->moveTo((SplashCoord)subpath->getX(0),
2300
471k
        (SplashCoord)subpath->getY(0));
2301
471k
      j = 1;
2302
2.10M
      while (j < subpath->getNumPoints()) {
2303
1.63M
  if (subpath->getCurve(j)) {
2304
106k
    sPath->curveTo((SplashCoord)subpath->getX(j),
2305
106k
       (SplashCoord)subpath->getY(j),
2306
106k
       (SplashCoord)subpath->getX(j+1),
2307
106k
       (SplashCoord)subpath->getY(j+1),
2308
106k
       (SplashCoord)subpath->getX(j+2),
2309
106k
       (SplashCoord)subpath->getY(j+2));
2310
106k
    j += 3;
2311
1.52M
  } else {
2312
1.52M
    sPath->lineTo((SplashCoord)subpath->getX(j),
2313
1.52M
      (SplashCoord)subpath->getY(j));
2314
1.52M
    ++j;
2315
1.52M
  }
2316
1.63M
      }
2317
471k
      if (subpath->isClosed()) {
2318
402k
  sPath->close();
2319
402k
      }
2320
471k
    }
2321
477k
  }
2322
438k
  return sPath;
2323
438k
}
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.52M
             GBool fill, GBool stroke, GBool makePath) {
2331
4.52M
  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.52M
  fill = fill && !state->getFillColorSpace()->isNonMarking();
2343
4.52M
  stroke = stroke && !state->getStrokeColorSpace()->isNonMarking();
2344
2345
  // check for invisible text -- this is used by Acrobat Capture
2346
4.52M
  if (!fill && !stroke && !makePath) {
2347
1.17k
    return;
2348
1.17k
  }
2349
2350
4.52M
  if (needFontUpdate) {
2351
303k
    doUpdateFont(state);
2352
303k
  }
2353
4.52M
  if (!font) {
2354
2.63M
    return;
2355
2.63M
  }
2356
2357
1.89M
  x -= originX;
2358
1.89M
  y -= originY;
2359
2360
1.89M
  SplashPath *path = NULL;
2361
1.89M
  if (stroke || makePath) {
2362
1.02M
    if ((path = font->getGlyphPath(code))) {
2363
791k
      path->offset((SplashCoord)x, (SplashCoord)y);
2364
791k
    }
2365
1.02M
  }
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
1.89M
  SplashStrokeAdjustMode savedStrokeAdjust = splashStrokeAdjustOff;
2371
1.89M
  if (stroke) {
2372
8.88k
    savedStrokeAdjust = splash->getStrokeAdjust();
2373
8.88k
    splash->setStrokeAdjust(splashStrokeAdjustOff);
2374
8.88k
  }
2375
2376
  // the possible operations are:
2377
  //   - fill
2378
  //   - stroke
2379
  //   - fill + stroke
2380
  //   - makePath
2381
2382
1.89M
  if (fill && stroke) {
2383
7.27k
    if (path) {
2384
4.52k
      setOverprintMask(state, state->getFillColorSpace(),
2385
4.52k
           state->getFillOverprint(), state->getOverprintMode(),
2386
4.52k
           state->getFillColor());
2387
4.52k
      splash->fill(path, gFalse);
2388
4.52k
      setOverprintMask(state, state->getStrokeColorSpace(),
2389
4.52k
           state->getStrokeOverprint(), state->getOverprintMode(),
2390
4.52k
           state->getStrokeColor());
2391
4.52k
      splash->stroke(path);
2392
4.52k
    }
2393
2394
1.88M
  } else if (fill) {
2395
870k
    setOverprintMask(state, state->getFillColorSpace(),
2396
870k
         state->getFillOverprint(), state->getOverprintMode(),
2397
870k
         state->getFillColor());
2398
870k
    splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font);
2399
2400
1.01M
  } else if (stroke) {
2401
1.60k
    if (path) {
2402
762
      setOverprintMask(state, state->getStrokeColorSpace(),
2403
762
           state->getStrokeOverprint(), state->getOverprintMode(),
2404
762
           state->getStrokeColor());
2405
762
      splash->stroke(path);
2406
762
    }
2407
2408
1.01M
  } else if (makePath) {
2409
1.01M
    if (path) {
2410
785k
      if (savedTextPath) {
2411
774k
  savedTextPath->append(path);
2412
774k
      } else {
2413
11.2k
  savedTextPath = path;
2414
11.2k
  path = NULL;
2415
11.2k
      }
2416
785k
    }
2417
1.01M
  }
2418
2419
1.89M
  if (stroke) {
2420
8.88k
    splash->setStrokeAdjust(savedStrokeAdjust);
2421
8.88k
  }
2422
2423
1.89M
  if (path) {
2424
779k
    delete path;
2425
779k
  }
2426
1.89M
}
2427
2428
13.8k
void SplashOutputDev::fillTextPath(GfxState *state) {
2429
13.8k
  if (!savedTextPath) {
2430
13.8k
    return;
2431
13.8k
  }
2432
88
  setOverprintMask(state, state->getFillColorSpace(),
2433
88
       state->getFillOverprint(), state->getOverprintMode(),
2434
88
       state->getFillColor());
2435
88
  splash->fill(savedTextPath, gFalse);
2436
88
}
2437
2438
7.20k
void SplashOutputDev::strokeTextPath(GfxState *state) {
2439
7.20k
  if (!savedTextPath) {
2440
6.95k
    return;
2441
6.95k
  }
2442
243
  setOverprintMask(state, state->getStrokeColorSpace(),
2443
243
       state->getStrokeOverprint(), state->getOverprintMode(),
2444
243
       state->getStrokeColor());
2445
243
  splash->stroke(savedTextPath);
2446
243
}
2447
2448
26.4k
void SplashOutputDev::clipToTextPath(GfxState *state) {
2449
26.4k
  if (!savedTextPath) {
2450
18.3k
    return;
2451
18.3k
  }
2452
8.05k
  splash->clipToPath(savedTextPath, gFalse);
2453
8.05k
}
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
26.1k
void SplashOutputDev::clearTextPath(GfxState *state) {
2468
26.1k
  if (savedTextPath) {
2469
8.05k
    delete savedTextPath;
2470
8.05k
    savedTextPath = NULL;
2471
8.05k
  }
2472
26.1k
}
2473
2474
21.1k
void SplashOutputDev::addTextPathToSavedClipPath(GfxState *state) {
2475
21.1k
  if (savedTextPath) {
2476
3.22k
    if (savedClipPath) {
2477
2.04k
      savedClipPath->append(savedTextPath);
2478
2.04k
      delete savedTextPath;
2479
2.04k
    } else {
2480
1.17k
      savedClipPath = savedTextPath;
2481
1.17k
    }
2482
3.22k
    savedTextPath = NULL;
2483
3.22k
  }
2484
21.1k
}
2485
2486
10.6k
void SplashOutputDev::clipToSavedClipPath(GfxState *state) {
2487
10.6k
  if (!savedClipPath) {
2488
9.60k
    return;
2489
9.60k
  }
2490
1.00k
  splash->clipToPath(savedClipPath, gFalse);
2491
1.00k
  delete savedClipPath;
2492
1.00k
  savedClipPath = NULL;
2493
1.00k
}
2494
2495
GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
2496
              double dx, double dy,
2497
2.09M
              CharCode code, Unicode *u, int uLen) {
2498
2.09M
  GfxFont *gfxFont;
2499
2.09M
  Ref *fontID;
2500
2.09M
  double *ctm, *bbox;
2501
2.09M
  T3FontCache *t3Font;
2502
2.09M
  T3GlyphStack *t3gs;
2503
2.09M
  GBool validBBox;
2504
2.09M
  double m[4];
2505
2.09M
  GBool horiz;
2506
2.09M
  double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
2507
2.09M
  int render, i, j;
2508
2509
2.09M
  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
2.09M
  render = state->getRender();
2520
2.09M
  if (render == 3 || render == 7) {
2521
3.50k
    return gTrue;
2522
3.50k
  }
2523
2524
2.09M
  if (!(gfxFont = state->getFont())) {
2525
0
    return gTrue;
2526
0
  }
2527
2.09M
  fontID = gfxFont->getID();
2528
2.09M
  ctm = state->getCTM();
2529
2.09M
  state->transform(0, 0, &xt, &yt);
2530
2531
  // is it the first (MRU) font in the cache?
2532
2.09M
  if (!(nT3Fonts > 0 &&
2533
2.09M
  t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
2534
2535
    // is the font elsewhere in the cache?
2536
102k
    for (i = 1; i < nT3Fonts; ++i) {
2537
91.3k
      if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
2538
14.2k
  t3Font = t3FontCache[i];
2539
42.1k
  for (j = i; j > 0; --j) {
2540
27.8k
    t3FontCache[j] = t3FontCache[j - 1];
2541
27.8k
  }
2542
14.2k
  t3FontCache[0] = t3Font;
2543
14.2k
  break;
2544
14.2k
      }
2545
91.3k
    }
2546
25.1k
    if (i >= nT3Fonts) {
2547
2548
      // create new entry in the font cache
2549
10.8k
      if (nT3Fonts < splashOutT3FontCacheSize) {
2550
6.56k
  for (j = nT3Fonts; j > 0; --j) {
2551
4.36k
    t3FontCache[j] = t3FontCache[j - 1];
2552
4.36k
  }
2553
8.66k
      } else {
2554
14.4k
  for (j = nT3Fonts - 1; j >= 0; --j) {
2555
14.4k
    if (t3FontCache[j]->refCount == 0) {
2556
8.66k
      break;
2557
8.66k
    }
2558
14.4k
  }
2559
8.66k
  if (j < 0) {
2560
0
    error(errSyntaxError, -1, "Type 3 fonts nested too deeply");
2561
0
    return gTrue;
2562
0
  }
2563
8.66k
  delete t3FontCache[j];
2564
8.66k
  --nT3Fonts;
2565
63.4k
  for (; j > 0; --j) {
2566
54.8k
    t3FontCache[j] = t3FontCache[j - 1];
2567
54.8k
  }
2568
8.66k
      }
2569
10.8k
      ++nT3Fonts;
2570
10.8k
      bbox = gfxFont->getFontBBox();
2571
10.8k
      if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
2572
  // unspecified bounding box -- just take a guess
2573
1.82k
  xMin = xt - 5;
2574
1.82k
  xMax = xMin + 30;
2575
1.82k
  yMax = yt + 15;
2576
1.82k
  yMin = yMax - 45;
2577
1.82k
  validBBox = gFalse;
2578
9.03k
      } else {
2579
9.03k
  state->transform(bbox[0], bbox[1], &x1, &y1);
2580
9.03k
  xMin = xMax = x1;
2581
9.03k
  yMin = yMax = y1;
2582
9.03k
  state->transform(bbox[0], bbox[3], &x1, &y1);
2583
9.03k
  if (x1 < xMin) {
2584
222
    xMin = x1;
2585
8.81k
  } else if (x1 > xMax) {
2586
483
    xMax = x1;
2587
483
  }
2588
9.03k
  if (y1 < yMin) {
2589
1.17k
    yMin = y1;
2590
7.85k
  } else if (y1 > yMax) {
2591
2.72k
    yMax = y1;
2592
2.72k
  }
2593
9.03k
  state->transform(bbox[2], bbox[1], &x1, &y1);
2594
9.03k
  if (x1 < xMin) {
2595
2.18k
    xMin = x1;
2596
6.84k
  } else if (x1 > xMax) {
2597
505
    xMax = x1;
2598
505
  }
2599
9.03k
  if (y1 < yMin) {
2600
754
    yMin = y1;
2601
8.27k
  } else if (y1 > yMax) {
2602
2.49k
    yMax = y1;
2603
2.49k
  }
2604
9.03k
  state->transform(bbox[2], bbox[3], &x1, &y1);
2605
9.03k
  if (x1 < xMin) {
2606
88
    xMin = x1;
2607
8.94k
  } else if (x1 > xMax) {
2608
80
    xMax = x1;
2609
80
  }
2610
9.03k
  if (y1 < yMin) {
2611
805
    yMin = y1;
2612
8.22k
  } else if (y1 > yMax) {
2613
1.83k
    yMax = y1;
2614
1.83k
  }
2615
9.03k
  validBBox = gTrue;
2616
9.03k
      }
2617
10.8k
      t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
2618
10.8k
                                 (int)floor(xMin - xt) - 2,
2619
10.8k
               (int)floor(yMin - yt) - 2,
2620
10.8k
               (int)ceil(xMax) - (int)floor(xMin) + 4,
2621
10.8k
               (int)ceil(yMax) - (int)floor(yMin) + 4,
2622
10.8k
               validBBox,
2623
10.8k
               colorMode != splashModeMono1);
2624
10.8k
    }
2625
25.1k
  }
2626
2.09M
  t3Font = t3FontCache[0];
2627
2628
  // is the glyph in the cache?
2629
2.09M
  i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2630
18.8M
  for (j = 0; j < t3Font->cacheAssoc; ++j) {
2631
16.7M
    if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
2632
5.88k
  t3Font->cacheTags[i+j].code == code) {
2633
1.78k
      drawType3Glyph(state, t3Font, &t3Font->cacheTags[i+j],
2634
1.78k
         t3Font->cacheData + (i+j) * t3Font->glyphSize);
2635
1.78k
      return gTrue;
2636
1.78k
    }
2637
16.7M
  }
2638
2639
2.08M
  if (t3Font->refCount > 1000) {
2640
0
    error(errSyntaxError, -1, "Type 3 CharProcs nested too deeply");
2641
0
    return gTrue;
2642
0
  }
2643
2.08M
  ++t3Font->refCount;
2644
2645
  // push a new Type 3 glyph record
2646
2.08M
  t3gs = new T3GlyphStack();
2647
2.08M
  t3gs->next = t3GlyphStack;
2648
2.08M
  t3GlyphStack = t3gs;
2649
2.08M
  t3GlyphStack->code = (Gushort)code;
2650
2.08M
  t3GlyphStack->cache = t3Font;
2651
2.08M
  t3GlyphStack->cacheTag = NULL;
2652
2.08M
  t3GlyphStack->cacheData = NULL;
2653
2.08M
  t3GlyphStack->haveDx = gFalse;
2654
2.08M
  t3GlyphStack->doNotCache = gFalse;
2655
2.08M
#if 1 //~t3-sa
2656
2.08M
  t3GlyphStack->savedStrokeAdjust = splash->getStrokeAdjust();
2657
2.08M
  splash->setStrokeAdjust(splashStrokeAdjustOff);
2658
2.08M
#endif
2659
2660
2.08M
  return gFalse;
2661
2.08M
}
2662
2663
2.08M
void SplashOutputDev::endType3Char(GfxState *state) {
2664
2.08M
  T3GlyphStack *t3gs;
2665
2.08M
  double *ctm;
2666
2667
2.08M
  if (t3GlyphStack->cacheTag) {
2668
273
    --nestCount;
2669
273
    memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
2670
273
     t3GlyphStack->cache->glyphSize);
2671
273
    delete bitmap;
2672
273
    delete splash;
2673
273
    bitmap = t3GlyphStack->origBitmap;
2674
273
    colorMode = bitmap->getMode();
2675
273
    splash = t3GlyphStack->origSplash;
2676
273
    ctm = state->getCTM();
2677
273
    state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2678
273
      t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
2679
273
    updateCTM(state, 0, 0, 0, 0, 0, 0);
2680
273
    drawType3Glyph(state, t3GlyphStack->cache,
2681
273
       t3GlyphStack->cacheTag, t3GlyphStack->cacheData);
2682
273
  }
2683
2.08M
#if 1 //~t3-sa
2684
2.08M
  splash->setStrokeAdjust(t3GlyphStack->savedStrokeAdjust);
2685
2.08M
#endif
2686
2.08M
  t3gs = t3GlyphStack;
2687
2.08M
  t3GlyphStack = t3gs->next;
2688
2.08M
  --t3gs->cache->refCount;
2689
2.08M
  delete t3gs;
2690
2.08M
}
2691
2692
14.8k
void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
2693
14.8k
  if (!t3GlyphStack) {
2694
9.26k
    error(errSyntaxError, -1,
2695
9.26k
    "Encountered d0 operator outside of Type 3 CharProc");
2696
9.26k
    return;
2697
9.26k
  }
2698
5.54k
  t3GlyphStack->haveDx = gTrue;
2699
5.54k
}
2700
2701
void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
2702
15.0k
            double llx, double lly, double urx, double ury) {
2703
15.0k
  double *ctm;
2704
15.0k
  T3FontCache *t3Font;
2705
15.0k
  SplashColor color;
2706
15.0k
  double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
2707
15.0k
  int i, j;
2708
2709
15.0k
  if (!t3GlyphStack) {
2710
1.08k
    error(errSyntaxError, -1,
2711
1.08k
    "Encountered d1 operator outside of Type 3 CharProc");
2712
1.08k
    return;
2713
1.08k
  }
2714
2715
  // ignore multiple d0/d1 operators
2716
13.9k
  if (t3GlyphStack->haveDx) {
2717
4.47k
    return;
2718
4.47k
  }
2719
9.51k
  t3GlyphStack->haveDx = gTrue;
2720
  // don't cache if we got a gsave/grestore before the d1
2721
9.51k
  if (t3GlyphStack->doNotCache) {
2722
737
    return;
2723
737
  }
2724
2725
8.77k
  t3Font = t3GlyphStack->cache;
2726
2727
  // check for a valid bbox
2728
8.77k
  state->transform(0, 0, &xt, &yt);
2729
8.77k
  state->transform(llx, lly, &x1, &y1);
2730
8.77k
  xMin = xMax = x1;
2731
8.77k
  yMin = yMax = y1;
2732
8.77k
  state->transform(llx, ury, &x1, &y1);
2733
8.77k
  if (x1 < xMin) {
2734
563
    xMin = x1;
2735
8.21k
  } else if (x1 > xMax) {
2736
161
    xMax = x1;
2737
161
  }
2738
8.77k
  if (y1 < yMin) {
2739
5.68k
    yMin = y1;
2740
5.68k
  } else if (y1 > yMax) {
2741
1.12k
    yMax = y1;
2742
1.12k
  }
2743
8.77k
  state->transform(urx, lly, &x1, &y1);
2744
8.77k
  if (x1 < xMin) {
2745
4.38k
    xMin = x1;
2746
4.38k
  } else if (x1 > xMax) {
2747
2.21k
    xMax = x1;
2748
2.21k
  }
2749
8.77k
  if (y1 < yMin) {
2750
607
    yMin = y1;
2751
8.16k
  } else if (y1 > yMax) {
2752
528
    yMax = y1;
2753
528
  }
2754
8.77k
  state->transform(urx, ury, &x1, &y1);
2755
8.77k
  if (x1 < xMin) {
2756
169
    xMin = x1;
2757
8.60k
  } else if (x1 > xMax) {
2758
71
    xMax = x1;
2759
71
  }
2760
8.77k
  if (y1 < yMin) {
2761
837
    yMin = y1;
2762
7.93k
  } else if (y1 > yMax) {
2763
115
    yMax = y1;
2764
115
  }
2765
8.77k
  if (xMin - xt < t3Font->glyphX ||
2766
3.63k
      yMin - yt < t3Font->glyphY ||
2767
1.36k
      xMax - xt > t3Font->glyphX + t3Font->glyphW ||
2768
8.50k
      yMax - yt > t3Font->glyphY + t3Font->glyphH) {
2769
8.50k
    if (t3Font->validBBox) {
2770
1.75k
      error(errSyntaxWarning, -1, "Bad bounding box in Type 3 glyph");
2771
1.75k
    }
2772
8.50k
    return;
2773
8.50k
  }
2774
2775
  // allocate a cache entry
2776
273
  i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2777
2.45k
  for (j = 0; j < t3Font->cacheAssoc; ++j) {
2778
2.18k
    if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
2779
273
      t3Font->cacheTags[i+j].mru = 0x8000;
2780
273
      t3Font->cacheTags[i+j].code = t3GlyphStack->code;
2781
273
      t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
2782
273
      t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
2783
1.91k
    } else {
2784
1.91k
      ++t3Font->cacheTags[i+j].mru;
2785
1.91k
    }
2786
2.18k
  }
2787
2788
  // save state
2789
273
  t3GlyphStack->origBitmap = bitmap;
2790
273
  t3GlyphStack->origSplash = splash;
2791
273
  ctm = state->getCTM();
2792
273
  t3GlyphStack->origCTM4 = ctm[4];
2793
273
  t3GlyphStack->origCTM5 = ctm[5];
2794
2795
  // create the temporary bitmap
2796
273
  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
273
  } else {
2808
273
    colorMode = splashModeMono8;
2809
273
    traceMessage("T3 glyph bitmap");
2810
273
    bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
2811
273
            splashModeMono8, gFalse, gTrue, bitmapMemCache);
2812
273
    splash = new Splash(bitmap, vectorAntialias,
2813
273
      t3GlyphStack->origSplash->getImageCache(), 
2814
273
      t3GlyphStack->origSplash->getScreen());
2815
273
    color[0] = 0x00;
2816
273
    splash->clear(color);
2817
273
    color[0] = 0xff;
2818
273
  }
2819
273
  splash->setMinLineWidth(globalParams->getMinLineWidth());
2820
273
  splash->setStrokeAdjust(t3GlyphStack->origSplash->getStrokeAdjust());
2821
273
  splash->setEnablePathSimplification(
2822
273
     globalParams->getEnablePathSimplification());
2823
273
  copyState(t3GlyphStack->origSplash, gFalse);
2824
273
  splash->setFillPattern(new SplashSolidColor(color));
2825
273
  splash->setStrokePattern(new SplashSolidColor(color));
2826
273
  state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2827
273
    -t3Font->glyphX, -t3Font->glyphY);
2828
273
  updateCTM(state, 0, 0, 0, 0, 0, 0);
2829
273
  ++nestCount;
2830
273
}
2831
2832
void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font,
2833
2.05k
             T3FontCacheTag *tag, Guchar *data) {
2834
2.05k
  SplashGlyphBitmap glyph;
2835
2836
2.05k
  setOverprintMask(state, state->getFillColorSpace(),
2837
2.05k
       state->getFillOverprint(), state->getOverprintMode(),
2838
2.05k
       state->getFillColor());
2839
2.05k
  glyph.x = -t3Font->glyphX;
2840
2.05k
  glyph.y = -t3Font->glyphY;
2841
2.05k
  glyph.w = t3Font->glyphW;
2842
2.05k
  glyph.h = t3Font->glyphH;
2843
2.05k
  glyph.aa = colorMode != splashModeMono1;
2844
2.05k
  glyph.data = data;
2845
2.05k
  glyph.freeData = gFalse;
2846
2.05k
  splash->fillGlyph(0, 0, &glyph);
2847
2.05k
}
2848
2849
378k
void SplashOutputDev::endTextObject(GfxState *state) {
2850
378k
}
2851
2852
struct SplashOutImageMaskData {
2853
  ImageStream *imgStr;
2854
  Guchar invert;
2855
  int width, height, y;
2856
};
2857
2858
3.16M
GBool SplashOutputDev::imageMaskSrc(void *data, Guchar *line) {
2859
3.16M
  SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
2860
3.16M
  Guchar *p;
2861
3.16M
  SplashColorPtr q;
2862
3.16M
  int x;
2863
2864
3.16M
  if (imgMaskData->y == imgMaskData->height ||
2865
3.16M
      !(p = imgMaskData->imgStr->getLine())) {
2866
3.09M
    memset(line, 0, imgMaskData->width);
2867
3.09M
    return gFalse;
2868
3.09M
  }
2869
909k
  for (x = 0, q = line; x < imgMaskData->width; ++x) {
2870
840k
    *q++ = *p++ ^ imgMaskData->invert;
2871
840k
  }
2872
69.3k
  ++imgMaskData->y;
2873
69.3k
  return gTrue;
2874
3.16M
}
2875
2876
void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2877
            int width, int height, GBool invert,
2878
26.6k
            GBool inlineImg, GBool interpolate) {
2879
26.6k
  double *ctm;
2880
26.6k
  SplashCoord mat[6];
2881
26.6k
  SplashOutImageMaskData imgMaskData;
2882
26.6k
  GString *imgTag;
2883
2884
26.6k
  if (state->getFillColorSpace()->isNonMarking()) {
2885
0
    return;
2886
0
  }
2887
26.6k
  setOverprintMask(state, state->getFillColorSpace(),
2888
26.6k
       state->getFillOverprint(), state->getOverprintMode(),
2889
26.6k
       state->getFillColor());
2890
2891
26.6k
  ctm = state->getCTM();
2892
26.6k
  mat[0] = ctm[0];
2893
26.6k
  mat[1] = ctm[1];
2894
26.6k
  mat[2] = -ctm[2];
2895
26.6k
  mat[3] = -ctm[3];
2896
26.6k
  mat[4] = ctm[2] + ctm[4];
2897
26.6k
  mat[5] = ctm[3] + ctm[5];
2898
2899
26.6k
  reduceImageResolution(str, ctm, &width, &height);
2900
2901
26.6k
  imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
2902
26.6k
  imgMaskData.imgStr->reset();
2903
26.6k
  imgMaskData.invert = invert ? 0 : 1;
2904
26.6k
  imgMaskData.width = width;
2905
26.6k
  imgMaskData.height = height;
2906
26.6k
  imgMaskData.y = 0;
2907
2908
26.6k
  imgTag = makeImageTag(ref, gfxRenderingIntentRelativeColorimetric, NULL);
2909
26.6k
  splash->fillImageMask(imgTag,
2910
26.6k
      &imageMaskSrc, &imgMaskData, width, height, mat,
2911
26.6k
      t3GlyphStack != NULL, interpolate,
2912
26.6k
      globalParams->getImageMaskAntialias());
2913
2914
26.6k
  if (inlineImg) {
2915
64.6M
    while (imgMaskData.y < height) {
2916
64.6M
      imgMaskData.imgStr->getLine();
2917
64.6M
      ++imgMaskData.y;
2918
64.6M
    }
2919
26.5k
  }
2920
2921
26.6k
  delete imgTag;
2922
26.6k
  delete imgMaskData.imgStr;
2923
26.6k
  str->close();
2924
26.6k
}
2925
2926
void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
2927
                 Object *ref, Stream *str,
2928
                 int width, int height,
2929
                 GBool invert,
2930
                 GBool inlineImg,
2931
36
                 GBool interpolate) {
2932
36
  double *ctm;
2933
36
  SplashCoord mat[6];
2934
36
  SplashOutImageMaskData imgMaskData;
2935
36
  SplashBitmap *maskBitmap;
2936
36
  Splash *maskSplash;
2937
36
  SplashColor maskColor;
2938
36
  GString *imgTag;
2939
2940
36
  ctm = state->getCTM();
2941
36
  mat[0] = ctm[0];
2942
36
  mat[1] = ctm[1];
2943
36
  mat[2] = -ctm[2];
2944
36
  mat[3] = -ctm[3];
2945
36
  mat[4] = ctm[2] + ctm[4];
2946
36
  mat[5] = ctm[3] + ctm[5];
2947
36
  reduceImageResolution(str, ctm, &width, &height);
2948
36
  imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
2949
36
  imgMaskData.imgStr->reset();
2950
36
  imgMaskData.invert = invert ? 0 : 1;
2951
36
  imgMaskData.width = width;
2952
36
  imgMaskData.height = height;
2953
36
  imgMaskData.y = 0;
2954
36
  traceMessage("image mask soft mask bitmap");
2955
36
  maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
2956
36
        1, splashModeMono8, gFalse, gTrue,
2957
36
        bitmapMemCache);
2958
36
  maskSplash = new Splash(maskBitmap, gTrue, splash->getImageCache());
2959
36
  maskSplash->setStrokeAdjust(
2960
36
         mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
2961
36
  maskSplash->setEnablePathSimplification(
2962
36
         globalParams->getEnablePathSimplification());
2963
36
  if (splash->getSoftMask()) {
2964
0
    maskSplash->setSoftMask(splash->getSoftMask(), gFalse);
2965
0
  }
2966
36
  clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
2967
36
  maskColor[0] = 0xff;
2968
36
  maskSplash->setFillPattern(new SplashSolidColor(maskColor));
2969
36
  imgTag = makeImageTag(ref, gfxRenderingIntentRelativeColorimetric, NULL);
2970
36
  maskSplash->fillImageMask(imgTag, &imageMaskSrc, &imgMaskData,
2971
36
          width, height, mat, gFalse, interpolate,
2972
36
          globalParams->getImageMaskAntialias());
2973
36
  delete imgTag;
2974
36
  delete imgMaskData.imgStr;
2975
36
  str->close();
2976
36
  delete maskSplash;
2977
36
  splash->setSoftMask(maskBitmap);
2978
36
}
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
96.3k
        Guchar *alphaLine) {
2993
96.3k
  SplashOutImageData *imgData = (SplashOutImageData *)data;
2994
2995
96.3k
  Guchar *p;
2996
96.3k
  if (imgData->y == imgData->height ||
2997
96.3k
      !(p = imgData->imgStr->getLine())) {
2998
89.3k
    memset(colorLine, 0,
2999
89.3k
     imgData->width * splashColorModeNComps[imgData->colorMode]);
3000
89.3k
    return gFalse;
3001
89.3k
  }
3002
3003
7.03k
  int x;
3004
7.03k
  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
7.03k
  case splashModeRGB8:
3011
7.03k
  case splashModeBGR8:
3012
7.03k
    imgData->colorMap->getRGBByteLine(p, colorLine, imgData->width,
3013
7.03k
              imgData->ri);
3014
7.03k
    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
7.03k
#endif
3021
7.03k
  }
3022
3023
7.03k
  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
7.03k
  ++imgData->y;
3031
7.03k
  return gTrue;
3032
7.03k
}
3033
3034
GBool SplashOutputDev::imageLookup1Src(void *data, SplashColorPtr colorLine,
3035
99.7k
               Guchar *alphaLine) {
3036
99.7k
  SplashOutImageData *imgData = (SplashOutImageData *)data;
3037
3038
99.7k
  Guchar *p;
3039
99.7k
  if (imgData->y == imgData->height ||
3040
99.7k
      !(p = imgData->imgStr->getLine())) {
3041
52.8k
    memset(colorLine, 0,
3042
52.8k
     imgData->width * splashColorModeNComps[imgData->colorMode]);
3043
52.8k
    return gFalse;
3044
52.8k
  }
3045
3046
46.9k
  int x;
3047
46.9k
  SplashColorPtr q, col;
3048
46.9k
  switch (imgData->colorMode) {
3049
0
  case splashModeMono1:
3050
30.0k
  case splashModeMono8:
3051
4.71M
    for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
3052
4.68M
      *q++ = imgData->lookup[*p];
3053
4.68M
    }
3054
30.0k
    break;
3055
16.6k
  case splashModeRGB8:
3056
16.6k
  case splashModeBGR8:
3057
721k
    for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
3058
704k
      col = &imgData->lookup[3 * *p];
3059
704k
      *q++ = col[0];
3060
704k
      *q++ = col[1];
3061
704k
      *q++ = col[2];
3062
704k
    }
3063
16.6k
    break;
3064
0
#if SPLASH_CMYK
3065
311
  case splashModeCMYK8:
3066
20.0k
    for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
3067
19.7k
      col = &imgData->lookup[4 * *p];
3068
19.7k
      *q++ = col[0];
3069
19.7k
      *q++ = col[1];
3070
19.7k
      *q++ = col[2];
3071
19.7k
      *q++ = col[3];
3072
19.7k
    }
3073
311
    break;
3074
46.9k
#endif
3075
46.9k
  }
3076
3077
46.9k
  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
46.9k
  ++imgData->y;
3085
46.9k
  return gTrue;
3086
46.9k
}
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
584
             Guchar *alphaLine) {
3151
584
  SplashOutImageData *imgData = (SplashOutImageData *)data;
3152
3153
584
  Guchar *p0;
3154
584
  if (imgData->y == imgData->height ||
3155
584
      !(p0 = imgData->imgStr->getLine())) {
3156
356
    memset(colorLine, 0,
3157
356
     imgData->width * splashColorModeNComps[imgData->colorMode]);
3158
356
    memset(alphaLine, 0, imgData->width);
3159
356
    return gFalse;
3160
356
  }
3161
3162
228
  int nComps = imgData->colorMap->getNumPixelComps();
3163
3164
228
  int x;
3165
228
  Guchar *p;
3166
228
  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
228
  case splashModeRGB8:
3173
228
  case splashModeBGR8:
3174
228
    imgData->colorMap->getRGBByteLine(p0, colorLine, imgData->width,
3175
228
              imgData->ri);
3176
228
    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
228
#endif
3183
228
  }
3184
3185
228
  Guchar *aq;
3186
1.08k
  for (x = 0, p = p0, aq = alphaLine; x < imgData->width; ++x, p += nComps) {
3187
852
    Guchar alpha = 0;
3188
860
    for (int i = 0; i < nComps; ++i) {
3189
860
      if (p[i] < imgData->maskColors[2*i] ||
3190
852
    p[i] > imgData->maskColors[2*i+1]) {
3191
852
  alpha = 0xff;
3192
852
  break;
3193
852
      }
3194
860
    }
3195
852
    *aq++ = alpha;
3196
852
  }
3197
3198
228
  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
228
  ++imgData->y;
3206
228
  return gTrue;
3207
228
}
3208
3209
GBool SplashOutputDev::alphaImageLookup1Src(void *data,
3210
              SplashColorPtr colorLine,
3211
7.45k
              Guchar *alphaLine) {
3212
7.45k
  SplashOutImageData *imgData = (SplashOutImageData *)data;
3213
3214
7.45k
  Guchar *p0;
3215
7.45k
  if (imgData->y == imgData->height ||
3216
7.45k
      !(p0 = imgData->imgStr->getLine())) {
3217
2.15k
    memset(colorLine, 0,
3218
2.15k
     imgData->width * splashColorModeNComps[imgData->colorMode]);
3219
2.15k
    memset(alphaLine, 0, imgData->width);
3220
2.15k
    return gFalse;
3221
2.15k
  }
3222
3223
5.30k
  int nComps = imgData->colorMap->getNumPixelComps();
3224
3225
5.30k
  int x;
3226
5.30k
  Guchar *p;
3227
5.30k
  SplashColorPtr q, col;
3228
5.30k
  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
5.30k
  case splashModeRGB8:
3236
5.30k
  case splashModeBGR8:
3237
11.5k
    for (x = 0, p = p0, q = colorLine; x < imgData->width; ++x, ++p) {
3238
6.23k
      col = &imgData->lookup[3 * *p];
3239
6.23k
      *q++ = col[0];
3240
6.23k
      *q++ = col[1];
3241
6.23k
      *q++ = col[2];
3242
6.23k
    }
3243
5.30k
    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
5.30k
#endif
3255
5.30k
  }
3256
3257
5.30k
  Guchar *aq;
3258
11.5k
  for (x = 0, p = p0, aq = alphaLine; x < imgData->width; ++x, p += nComps) {
3259
6.23k
    Guchar alpha = 0;
3260
6.23k
    for (int i = 0; i < nComps; ++i) {
3261
6.23k
      if (p[i] < imgData->maskColors[2*i] ||
3262
6.23k
    p[i] > imgData->maskColors[2*i+1]) {
3263
6.23k
  alpha = 0xff;
3264
6.23k
  break;
3265
6.23k
      }
3266
6.23k
    }
3267
6.23k
    *aq++ = alpha;
3268
6.23k
  }
3269
3270
5.30k
  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
5.30k
  ++imgData->y;
3278
5.30k
  return gTrue;
3279
5.30k
}
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
23.1k
        GBool interpolate) {
3366
3367
23.1k
  setOverprintMask(state, colorMap->getColorSpace(),
3368
23.1k
       state->getFillOverprint(), state->getOverprintMode(),
3369
23.1k
       NULL);
3370
3371
23.1k
  double *ctm = state->getCTM();
3372
23.1k
  SplashCoord mat[6];
3373
23.1k
  mat[0] = ctm[0];
3374
23.1k
  mat[1] = ctm[1];
3375
23.1k
  mat[2] = -ctm[2];
3376
23.1k
  mat[3] = -ctm[3];
3377
23.1k
  mat[4] = ctm[2] + ctm[4];
3378
23.1k
  mat[5] = ctm[3] + ctm[5];
3379
3380
23.1k
  reduceImageResolution(str, ctm, &width, &height);
3381
3382
23.1k
  SplashOutImageData imgData;
3383
23.1k
  imgData.imgStr = new ImageStream(str, width,
3384
23.1k
           colorMap->getNumPixelComps(),
3385
23.1k
           colorMap->getBits());
3386
23.1k
  imgData.imgStr->reset();
3387
23.1k
  imgData.colorMap = colorMap;
3388
23.1k
  imgData.ri = state->getRenderingIntent();
3389
23.1k
  imgData.maskColors = maskColors;
3390
23.1k
  imgData.colorMode = colorMode;
3391
23.1k
  imgData.invert = reverseVideo && reverseVideoInvertImages;
3392
23.1k
  imgData.width = width;
3393
23.1k
  imgData.height = height;
3394
23.1k
  imgData.y = 0;
3395
3396
  // special case for one-channel (monochrome/gray/separation) images:
3397
  // build a lookup table here
3398
23.1k
  SplashImageSource src = maskColors ? &alphaImageSrc : &imageSrc;
3399
23.1k
  imgData.lookup = NULL;
3400
23.1k
  if (colorMap->getNumPixelComps() == 1) {
3401
20.4k
    imgData.lookup = buildColorMapLookupTable1Idx(colorMap, state);
3402
20.4k
    if (imgData.lookup) {
3403
20.4k
      src = maskColors ? &alphaImageLookup1Src : &imageLookup1Src;
3404
20.4k
    }
3405
20.4k
  } 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
23.1k
  SplashColorMode srcMode;
3414
23.1k
  if (colorMode == splashModeMono1) {
3415
0
    srcMode = splashModeMono8;
3416
23.1k
  } else if (colorMode == splashModeBGR8) {
3417
0
    srcMode = splashModeRGB8;
3418
23.1k
  } else {
3419
23.1k
    srcMode = colorMode;
3420
23.1k
  }
3421
3422
23.1k
  GString *imgTag = makeImageTag(ref, state->getRenderingIntent(),
3423
23.1k
         colorMap->getColorSpace());
3424
23.1k
  splash->drawImage(imgTag,
3425
23.1k
        src, &imgData, srcMode, maskColors ? gTrue : gFalse,
3426
23.1k
        width, height, mat, interpolate);
3427
23.1k
  if (inlineImg) {
3428
724k
    while (imgData.y < height) {
3429
704k
      imgData.imgStr->getLine();
3430
704k
      ++imgData.y;
3431
704k
    }
3432
19.5k
  }
3433
23.1k
  delete imgTag;
3434
3435
23.1k
  gfree(imgData.lookup);
3436
23.1k
  delete imgData.imgStr;
3437
23.1k
  str->close();
3438
23.1k
}
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
131
              GBool maskInvert, GBool interpolate) {
3603
  // If the mask is higher resolution than the image, use
3604
  // drawSoftMaskedImage() instead.
3605
131
  if (maskWidth > width || maskHeight > height) {
3606
126
    Object decodeLow, decodeHigh, maskDecode;
3607
126
    decodeLow.initInt(maskInvert ? 0 : 1);
3608
126
    decodeHigh.initInt(maskInvert ? 1 : 0);
3609
126
    maskDecode.initArray(xref);
3610
126
    maskDecode.arrayAdd(&decodeLow);
3611
126
    maskDecode.arrayAdd(&decodeHigh);
3612
126
    GfxImageColorMap *maskColorMap =
3613
126
        new GfxImageColorMap(1, &maskDecode, new GfxDeviceGrayColorSpace());
3614
126
    maskDecode.free();
3615
126
    drawSoftMaskedImage(state, ref, str, width, height, colorMap,
3616
126
      maskRef, maskStr, maskWidth, maskHeight, maskColorMap,
3617
126
      NULL, interpolate);
3618
126
    delete maskColorMap;
3619
126
    return;
3620
126
  }
3621
3622
3623
5
  drawMaskedImage8(state, ref, str, width, height, colorMap,
3624
5
       maskRef, maskStr, maskWidth, maskHeight,
3625
5
       maskInvert, interpolate);
3626
5
}
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
5
               GBool maskInvert, GBool interpolate) {
3634
5
  setOverprintMask(state, colorMap->getColorSpace(),
3635
5
       state->getFillOverprint(), state->getOverprintMode(),
3636
5
       NULL);
3637
3638
5
  double *ctm = state->getCTM();
3639
5
  reduceImageResolution(str, ctm, &width, &height);
3640
5
  reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
3641
3642
  //----- scale the mask image to the same size as the source image
3643
3644
5
  SplashCoord mat[6];
3645
5
  mat[0] = (SplashCoord)width;
3646
5
  mat[1] = 0;
3647
5
  mat[2] = 0;
3648
5
  mat[3] = (SplashCoord)height;
3649
5
  mat[4] = 0;
3650
5
  mat[5] = 0;
3651
5
  SplashOutImageMaskData imgMaskData;
3652
5
  imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
3653
5
  imgMaskData.imgStr->reset();
3654
5
  imgMaskData.invert = maskInvert ? 0 : 1;
3655
5
  imgMaskData.width = maskWidth;
3656
5
  imgMaskData.height = maskHeight;
3657
5
  imgMaskData.y = 0;
3658
5
  traceMessage("masked image bitmap");
3659
5
  SplashBitmap *maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1,
3660
5
                gFalse, gTrue, bitmapMemCache);
3661
5
  Splash *maskSplash = new Splash(maskBitmap, gFalse, splash->getImageCache());
3662
5
  maskSplash->setStrokeAdjust(
3663
5
      mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
3664
5
  maskSplash->setEnablePathSimplification(
3665
5
      globalParams->getEnablePathSimplification());
3666
5
  SplashColor maskColor;
3667
5
  maskColor[0] = 0;
3668
5
  maskSplash->clear(maskColor);
3669
5
  maskColor[0] = 0xff;
3670
5
  maskSplash->setFillPattern(new SplashSolidColor(maskColor));
3671
  // use "glyph mode" here to get the correct scaled size
3672
5
  maskSplash->fillImageMask(NULL, &imageMaskSrc, &imgMaskData,
3673
5
          maskWidth, maskHeight, mat, gTrue, interpolate,
3674
5
          globalParams->getImageMaskAntialias());
3675
5
  delete imgMaskData.imgStr;
3676
5
  maskStr->close();
3677
5
  delete maskSplash;
3678
3679
  //----- draw the source image
3680
3681
5
  mat[0] = ctm[0];
3682
5
  mat[1] = ctm[1];
3683
5
  mat[2] = -ctm[2];
3684
5
  mat[3] = -ctm[3];
3685
5
  mat[4] = ctm[2] + ctm[4];
3686
5
  mat[5] = ctm[3] + ctm[5];
3687
5
  SplashOutMaskedImageData imgData;
3688
5
  imgData.imgStr = new ImageStream(str, width,
3689
5
           colorMap->getNumPixelComps(),
3690
5
           colorMap->getBits());
3691
5
  imgData.imgStr->reset();
3692
5
  imgData.colorMap = colorMap;
3693
5
  imgData.ri = state->getRenderingIntent();
3694
5
  imgData.mask = maskBitmap;
3695
5
  imgData.colorMode = colorMode;
3696
5
  imgData.invert = reverseVideo && reverseVideoInvertImages;
3697
5
  imgData.width = width;
3698
5
  imgData.height = height;
3699
5
  imgData.y = 0;
3700
3701
  // special case for one-channel (monochrome/gray/separation) images:
3702
  // build a lookup table here
3703
5
  imgData.lookup = NULL;
3704
5
  if (colorMap->getNumPixelComps() == 1) {
3705
2
    imgData.lookup = buildColorMapLookupTable1Idx(colorMap, state);
3706
2
  }
3707
3708
5
  SplashColorMode srcMode;
3709
5
  if (colorMode == splashModeMono1) {
3710
0
    srcMode = splashModeMono8;
3711
5
  } else if (colorMode == splashModeBGR8) {
3712
0
    srcMode = splashModeRGB8;
3713
5
  } else {
3714
5
    srcMode = colorMode;
3715
5
  }
3716
5
  GString *imgTag = makeImageTag(ref, state->getRenderingIntent(),
3717
5
         colorMap->getColorSpace());
3718
5
  splash->drawImage(imgTag,
3719
5
        imgData.lookup ? &maskedImageLookup1Src : &maskedImageSrc,
3720
5
        &imgData, srcMode, gTrue,
3721
5
        width, height, mat, interpolate);
3722
5
  delete imgTag;
3723
3724
5
  delete maskBitmap;
3725
5
  gfree(imgData.lookup);
3726
5
  delete imgData.imgStr;
3727
5
  str->close();
3728
5
}
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
2.72k
            double *matte, GBool interpolate) {
3846
  // handle a preblended image
3847
2.72k
  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
2.72k
  drawSoftMaskedImage8(state, ref, str, width, height,
3857
2.72k
           colorMap, maskRef, maskStr,
3858
2.72k
           maskWidth, maskHeight, maskColorMap,
3859
2.72k
           matte, interpolate);
3860
2.72k
}
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
2.72k
             double *matte, GBool interpolate) {
3869
2.72k
  setOverprintMask(state, colorMap->getColorSpace(),
3870
2.72k
       state->getFillOverprint(), state->getOverprintMode(),
3871
2.72k
       NULL);
3872
3873
2.72k
  double *ctm = state->getCTM();
3874
2.72k
  SplashCoord mat[6];
3875
2.72k
  mat[0] = ctm[0];
3876
2.72k
  mat[1] = ctm[1];
3877
2.72k
  mat[2] = -ctm[2];
3878
2.72k
  mat[3] = -ctm[3];
3879
2.72k
  mat[4] = ctm[2] + ctm[4];
3880
2.72k
  mat[5] = ctm[3] + ctm[5];
3881
3882
2.72k
  SplashColorMode srcMode;
3883
2.72k
  if (colorMode == splashModeMono1) {
3884
0
    srcMode = splashModeMono8;
3885
2.72k
  } else if (colorMode == splashModeBGR8) {
3886
0
    srcMode = splashModeRGB8;
3887
2.72k
  } else {
3888
2.72k
    srcMode = colorMode;
3889
2.72k
  }
3890
3891
2.72k
  reduceImageResolution(str, ctm, &width, &height);
3892
2.72k
  reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
3893
3894
  //----- set up the soft mask
3895
3896
2.72k
  SplashOutImageData imgMaskData;
3897
2.72k
  imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
3898
2.72k
               maskColorMap->getNumPixelComps(),
3899
2.72k
               maskColorMap->getBits());
3900
2.72k
  imgMaskData.imgStr->reset();
3901
2.72k
  imgMaskData.colorMap = maskColorMap;
3902
2.72k
  imgMaskData.ri = state->getRenderingIntent();
3903
2.72k
  imgMaskData.maskColors = NULL;
3904
2.72k
  imgMaskData.colorMode = splashModeMono8;
3905
2.72k
  imgMaskData.invert = gFalse;
3906
2.72k
  imgMaskData.width = maskWidth;
3907
2.72k
  imgMaskData.height = maskHeight;
3908
2.72k
  imgMaskData.y = 0;
3909
2.72k
  int n;
3910
2.72k
  if (maskColorMap->getBits() <= 8) {
3911
2.61k
    n = 1 << maskColorMap->getBits();
3912
2.61k
  } else {
3913
    // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit
3914
109
    n = 1 << 8;
3915
109
  }
3916
2.72k
  imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
3917
2.72k
  GfxGray gray;
3918
666k
  for (int i = 0; i < n; ++i) {
3919
663k
    Guchar pix = (Guchar)i;
3920
663k
    maskColorMap->getGray(&pix, &gray, state->getRenderingIntent());
3921
663k
    imgMaskData.lookup[i] = colToByte(gray);
3922
663k
  }
3923
2.72k
  traceMessage("soft masked image bitmap");
3924
2.72k
  SplashBitmap *maskBitmap = new SplashBitmap(bitmap->getWidth(),
3925
2.72k
                bitmap->getHeight(),
3926
2.72k
                1, splashModeMono8,
3927
2.72k
                gFalse, gTrue,
3928
2.72k
                bitmapMemCache);
3929
2.72k
  Splash *maskSplash = new Splash(maskBitmap, vectorAntialias,
3930
2.72k
        splash->getImageCache());
3931
2.72k
  maskSplash->setStrokeAdjust(
3932
2.72k
      mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
3933
2.72k
  maskSplash->setEnablePathSimplification(
3934
2.72k
      globalParams->getEnablePathSimplification());
3935
2.72k
  clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
3936
2.72k
  maskSplash->drawImage(NULL, &imageLookup1Src, &imgMaskData,
3937
2.72k
      splashModeMono8, gFalse,
3938
2.72k
      maskWidth, maskHeight, mat, interpolate);
3939
2.72k
  delete imgMaskData.imgStr;
3940
2.72k
  maskStr->close();
3941
2.72k
  gfree(imgMaskData.lookup);
3942
2.72k
  delete maskSplash;
3943
2.72k
  splash->setSoftMask(maskBitmap);
3944
3945
  //----- draw the source image
3946
3947
2.72k
  SplashOutImageData imgData;
3948
2.72k
  imgData.imgStr = new ImageStream(str, width,
3949
2.72k
           colorMap->getNumPixelComps(),
3950
2.72k
           colorMap->getBits());
3951
2.72k
  imgData.imgStr->reset();
3952
2.72k
  imgData.colorMap = colorMap;
3953
2.72k
  imgData.ri = state->getRenderingIntent();
3954
2.72k
  imgData.maskColors = NULL;
3955
2.72k
  imgData.colorMode = colorMode;
3956
2.72k
  imgData.invert = reverseVideo && reverseVideoInvertImages;
3957
2.72k
  imgData.width = width;
3958
2.72k
  imgData.height = height;
3959
2.72k
  imgData.y = 0;
3960
3961
  // special case for one-channel (monochrome/gray/separation) images:
3962
  // build a lookup table here
3963
2.72k
  imgData.lookup = NULL;
3964
2.72k
  if (colorMap->getNumPixelComps() == 1) {
3965
52
    imgData.lookup = buildColorMapLookupTable1Idx(colorMap, state);
3966
52
  }
3967
3968
2.72k
  GString *imgTag = makeImageTag(ref, state->getRenderingIntent(),
3969
2.72k
         colorMap->getColorSpace());
3970
2.72k
  splash->drawImage(imgTag, imgData.lookup ? imageLookup1Src : imageSrc,
3971
2.72k
        &imgData, srcMode, gFalse, width, height, mat,
3972
2.72k
        interpolate);
3973
2.72k
  delete imgTag;
3974
3975
2.72k
  splash->setSoftMask(NULL);
3976
2.72k
  gfree(imgData.lookup);
3977
2.72k
  delete imgData.imgStr;
3978
2.72k
  str->close();
3979
2.72k
}
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
20.5k
            GfxState *state) {
4087
20.5k
  SplashColorPtr lookup = NULL;
4088
20.5k
  int n;
4089
20.5k
  if (colorMap->getBits() <= 8) {
4090
20.5k
    n = 1 << colorMap->getBits();
4091
20.5k
  } else {
4092
    // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit
4093
0
    n = 1 << 8;
4094
0
  }
4095
20.5k
  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
20.4k
  case splashModeRGB8:
4107
20.4k
  case splashModeBGR8:
4108
20.4k
    lookup = (SplashColorPtr)gmallocn(n, 3);
4109
20.4k
    GfxRGB rgb;
4110
2.64M
    for (int i = 0; i < n; ++i) {
4111
2.62M
      Guchar pix = (Guchar)i;
4112
2.62M
      colorMap->getRGB(&pix, &rgb, state->getRenderingIntent());
4113
2.62M
      lookup[3*i] = colToByte(rgb.r);
4114
2.62M
      lookup[3*i+1] = colToByte(rgb.g);
4115
2.62M
      lookup[3*i+2] = colToByte(rgb.b);
4116
2.62M
    }
4117
20.4k
    break;
4118
0
#if SPLASH_CMYK
4119
110
  case splashModeCMYK8:
4120
110
    lookup = (SplashColorPtr)gmallocn(n, 4);
4121
110
    GfxCMYK cmyk;
4122
24.9k
    for (int i = 0; i < n; ++i) {
4123
24.8k
      Guchar pix = (Guchar)i;
4124
24.8k
      colorMap->getCMYK(&pix, &cmyk, state->getRenderingIntent());
4125
24.8k
      lookup[4*i] = colToByte(cmyk.c);
4126
24.8k
      lookup[4*i+1] = colToByte(cmyk.m);
4127
24.8k
      lookup[4*i+2] = colToByte(cmyk.y);
4128
24.8k
      lookup[4*i+3] = colToByte(cmyk.k);
4129
24.8k
    }
4130
110
    break;
4131
20.5k
#endif
4132
20.5k
  }
4133
20.5k
  return lookup;
4134
20.5k
}
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
52.6k
               GfxColorSpace *colorSpace) {
4212
52.6k
  if (!ref || !ref->isRef() ||
4213
46.1k
      (colorSpace && colorSpace->isDefaultColorSpace())) {
4214
46.1k
    return NULL;
4215
46.1k
  }
4216
6.42k
  return GString::format("{0:d}_{1:d}_{2:d}",
4217
6.42k
       ref->getRefNum(), ref->getRefGen(), (int)ri);
4218
52.6k
}
4219
4220
void SplashOutputDev::reduceImageResolution(Stream *str, double *ctm,
4221
55.3k
              int *width, int *height) {
4222
55.3k
  double sw, sh;
4223
55.3k
  int reduction;
4224
4225
55.3k
  if (str->getKind() == strJPX &&
4226
832
      *width >= 256 &&
4227
714
      *height >= 256 &&
4228
650
      *width * *height > 10000000) {
4229
24
    sw = (double)*width / (fabs(ctm[0]) + fabs(ctm[1]));
4230
24
    sh = (double)*height / (fabs(ctm[2]) + fabs(ctm[3]));
4231
24
    if (sw > 8 && sh > 8) {
4232
0
      reduction = 3;
4233
24
    } else if (sw > 4 && sh > 4) {
4234
0
      reduction = 2;
4235
24
    } else if (sw > 2 && sh > 2) {
4236
0
      reduction = 1;
4237
24
    } else {
4238
24
      reduction = 0;
4239
24
    }
4240
24
    if (reduction > 0) {
4241
0
      ((JPXStream *)str)->reduceResolution(reduction);
4242
0
      *width >>= reduction;
4243
0
      *height >>= reduction;
4244
0
    }
4245
24
  }
4246
55.3k
}
4247
4248
void SplashOutputDev::clearMaskRegion(GfxState *state,
4249
              Splash *maskSplash,
4250
              double xMin, double yMin,
4251
2.76k
              double xMax, double yMax) {
4252
2.76k
  SplashBitmap *maskBitmap;
4253
2.76k
  double xxMin, yyMin, xxMax, yyMax, xx, yy;
4254
2.76k
  int xxMinI, yyMinI, xxMaxI, yyMaxI, y, n;
4255
2.76k
  Guchar *p;
4256
4257
2.76k
  maskBitmap = maskSplash->getBitmap();
4258
2.76k
  xxMin = maskBitmap->getWidth();
4259
2.76k
  xxMax = 0;
4260
2.76k
  yyMin = maskBitmap->getHeight();
4261
2.76k
  yyMax = 0;
4262
2.76k
  state->transform(xMin, yMin, &xx, &yy);
4263
2.76k
  if (xx < xxMin) { xxMin = xx; }
4264
2.76k
  if (xx > xxMax) { xxMax = xx; }
4265
2.76k
  if (yy < yyMin) { yyMin = yy; }
4266
2.76k
  if (yy > yyMax) { yyMax = yy; }
4267
2.76k
  state->transform(xMin, yMax, &xx, &yy);
4268
2.76k
  if (xx < xxMin) { xxMin = xx; }
4269
2.76k
  if (xx > xxMax) { xxMax = xx; }
4270
2.76k
  if (yy < yyMin) { yyMin = yy; }
4271
2.76k
  if (yy > yyMax) { yyMax = yy; }
4272
2.76k
  state->transform(xMax, yMin, &xx, &yy);
4273
2.76k
  if (xx < xxMin) { xxMin = xx; }
4274
2.76k
  if (xx > xxMax) { xxMax = xx; }
4275
2.76k
  if (yy < yyMin) { yyMin = yy; }
4276
2.76k
  if (yy > yyMax) { yyMax = yy; }
4277
2.76k
  state->transform(xMax, yMax, &xx, &yy);
4278
2.76k
  if (xx < xxMin) { xxMin = xx; }
4279
2.76k
  if (xx > xxMax) { xxMax = xx; }
4280
2.76k
  if (yy < yyMin) { yyMin = yy; }
4281
2.76k
  if (yy > yyMax) { yyMax = yy; }
4282
2.76k
  xxMinI = (int)floor(xxMin);
4283
2.76k
  if (xxMinI < 0) {
4284
696
    xxMinI = 0;
4285
696
  }
4286
2.76k
  xxMaxI = (int)ceil(xxMax);
4287
2.76k
  if (xxMaxI > maskBitmap->getWidth()) {
4288
10
    xxMaxI = maskBitmap->getWidth();
4289
10
  }
4290
2.76k
  yyMinI = (int)floor(yyMin);
4291
2.76k
  if (yyMinI < 0) {
4292
571
    yyMinI = 0;
4293
571
  }
4294
2.76k
  yyMaxI = (int)ceil(yyMax);
4295
2.76k
  if (yyMaxI > maskBitmap->getHeight()) {
4296
50
    yyMaxI = maskBitmap->getHeight();
4297
50
  }
4298
2.76k
  p = maskBitmap->getDataPtr() + yyMinI * maskBitmap->getRowSize();
4299
2.76k
  if (maskBitmap->getMode() == splashModeMono1) {
4300
0
    n = (xxMaxI + 7) / 8 - xxMinI / 8;
4301
0
    p += xxMinI / 8;
4302
2.76k
  } else {
4303
2.76k
    n = xxMaxI - xxMinI;
4304
2.76k
    p += xxMinI;
4305
2.76k
  }
4306
2.76k
  if (xxMaxI > xxMinI) {
4307
20
    for (y = yyMinI; y < yyMaxI; ++y) {
4308
10
      memset(p, 0, n);
4309
10
      p += maskBitmap->getRowSize();
4310
10
    }
4311
10
  }
4312
2.76k
}
4313
4314
GBool SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
4315
                GfxColorSpace *blendingColorSpace,
4316
                GBool isolated, GBool knockout,
4317
12.2k
                GBool forSoftMask) {
4318
12.2k
  SplashTransparencyGroup *transpGroup;
4319
12.2k
  SplashAlphaBitmap *alpha0Bitmap;
4320
12.2k
  double xMin, yMin, xMax, yMax, x, y;
4321
12.2k
  int bw, bh, tx, ty, w, h, xx, yy;
4322
4323
  // transform the bbox
4324
12.2k
  state->transform(bbox[0], bbox[1], &x, &y);
4325
12.2k
  xMin = xMax = x;
4326
12.2k
  yMin = yMax = y;
4327
12.2k
  state->transform(bbox[0], bbox[3], &x, &y);
4328
12.2k
  if (x < xMin) {
4329
439
    xMin = x;
4330
11.7k
  } else if (x > xMax) {
4331
566
    xMax = x;
4332
566
  }
4333
12.2k
  if (y < yMin) {
4334
5.78k
    yMin = y;
4335
6.42k
  } else if (y > yMax) {
4336
5.45k
    yMax = y;
4337
5.45k
  }
4338
12.2k
  state->transform(bbox[2], bbox[1], &x, &y);
4339
12.2k
  if (x < xMin) {
4340
4.40k
    xMin = x;
4341
7.80k
  } else if (x > xMax) {
4342
2.03k
    xMax = x;
4343
2.03k
  }
4344
12.2k
  if (y < yMin) {
4345
105
    yMin = y;
4346
12.1k
  } else if (y > yMax) {
4347
354
    yMax = y;
4348
354
  }
4349
12.2k
  state->transform(bbox[2], bbox[3], &x, &y);
4350
12.2k
  if (x < xMin) {
4351
398
    xMin = x;
4352
11.8k
  } else if (x > xMax) {
4353
403
    xMax = x;
4354
403
  }
4355
12.2k
  if (y < yMin) {
4356
318
    yMin = y;
4357
11.8k
  } else if (y > yMax) {
4358
147
    yMax = y;
4359
147
  }
4360
4361
  // clip the box
4362
12.2k
  x = splash->getClip()->getXMin();
4363
12.2k
  if (x > xMin) {
4364
7.15k
    xMin = x;
4365
7.15k
  }
4366
12.2k
  x = splash->getClip()->getXMax();
4367
12.2k
  if (x < xMax) {
4368
7.79k
    xMax = x;
4369
7.79k
  }
4370
12.2k
  y = splash->getClip()->getYMin();
4371
12.2k
  if (y > yMin) {
4372
11.0k
    yMin = y;
4373
11.0k
  }
4374
12.2k
  y = splash->getClip()->getYMax();
4375
12.2k
  if (y < yMax) {
4376
10.9k
    yMax = y;
4377
10.9k
  }
4378
4379
  // convert box coords to integers
4380
12.2k
  bw = bitmap->getWidth();
4381
12.2k
  bh = bitmap->getHeight();
4382
12.2k
  tx = (int)floor(xMin);
4383
12.2k
  if (tx < 0) {
4384
350
    tx = 0;
4385
11.8k
  } else if (tx >= bw) {
4386
1.86k
    tx = bw - 1;
4387
1.86k
  }
4388
12.2k
  ty = (int)floor(yMin);
4389
12.2k
  if (ty < 0) {
4390
641
    ty = 0;
4391
11.5k
  } else if (ty >= bh) {
4392
3.40k
    ty = bh - 1;
4393
3.40k
  }
4394
12.2k
  xx = (int)ceil(xMax);
4395
12.2k
  if (xx < tx) {
4396
2.45k
    w = 1;
4397
9.75k
  } else if (xx > bw - 1) {
4398
    // NB bw and tx are both non-negative, so 'bw - tx' can't overflow
4399
5.21k
    w = bw - tx;
4400
5.21k
  } else {
4401
4.54k
    w = xx - tx + 1;
4402
4.54k
  }
4403
12.2k
  yy = (int)ceil(yMax);
4404
12.2k
  if (yy < ty) {
4405
2.78k
    h = 1;
4406
9.42k
  } else if (yy > bh - 1) {
4407
    // NB bh and ty are both non-negative, so 'bh - ty' can't overflow
4408
8.07k
    h = bh - ty;
4409
8.07k
  } else {
4410
1.35k
    h = yy - ty + 1;
4411
1.35k
  }
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
12.2k
  if (!isolated &&
4424
10.7k
      !splash->getInNonIsolatedGroup() &&
4425
10.5k
      !knockout &&
4426
10.5k
      !splash->getInKnockoutGroup() &&
4427
10.5k
      !forSoftMask &&
4428
10.5k
      !splash->getSoftMask() &&
4429
10.4k
      state->getFillOpacity() == 1 &&
4430
10.4k
      state->getBlendMode() == gfxBlendNormal &&
4431
10.3k
      splash->checkTransparentRect(tx, ty, w, h)) {
4432
4.95k
    return gFalse;
4433
4.95k
  }
4434
4435
  // push a new stack entry
4436
7.25k
  transpGroup = new SplashTransparencyGroup();
4437
7.25k
  transpGroup->tx = tx;
4438
7.25k
  transpGroup->ty = ty;
4439
7.25k
  transpGroup->blendingColorSpace = blendingColorSpace;
4440
7.25k
  transpGroup->isolated = isolated;
4441
7.25k
  transpGroup->inKnockoutGroup = (transpGroupStack &&
4442
620
          transpGroupStack->inKnockoutGroup)
4443
7.25k
                                 || knockout;    
4444
7.25k
  transpGroup->inSoftMask = (transpGroupStack && transpGroupStack->inSoftMask)
4445
6.99k
                            || forSoftMask;
4446
7.25k
  transpGroup->next = transpGroupStack;
4447
7.25k
  transpGroupStack = transpGroup;
4448
4449
  // save state
4450
7.25k
  transpGroup->origBitmap = bitmap;
4451
7.25k
  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
7.25k
  if (forSoftMask && isolated && !knockout && blendingColorSpace) {
4458
1.09k
    if (blendingColorSpace->getMode() == csDeviceGray ||
4459
155
  blendingColorSpace->getMode() == csCalGray ||
4460
155
  (blendingColorSpace->getMode() == csICCBased &&
4461
940
   blendingColorSpace->getNComps() == 1)) {
4462
940
      colorMode = splashModeMono8;
4463
940
    } else if (blendingColorSpace->getMode() == csDeviceRGB ||
4464
155
         blendingColorSpace->getMode() == csCalRGB ||
4465
155
         (blendingColorSpace->getMode() == csICCBased &&
4466
0
    blendingColorSpace->getNComps() == 3)) {
4467
      //~ does this need to use BGR8?
4468
0
      colorMode = splashModeRGB8;
4469
0
#if SPLASH_CMYK
4470
155
    } else if (blendingColorSpace->getMode() == csDeviceCMYK ||
4471
0
         (blendingColorSpace->getMode() == csICCBased &&
4472
155
    blendingColorSpace->getNComps() == 4)) {
4473
155
      colorMode = splashModeCMYK8;
4474
155
#endif
4475
155
    }
4476
1.09k
  }
4477
4478
  // create the temporary bitmap
4479
7.25k
  traceMessage("t-group bitmap");
4480
7.25k
  bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
4481
7.25k
          bitmapTopDown, bitmapMemCache); 
4482
7.25k
  splash = new Splash(bitmap, vectorAntialias,
4483
7.25k
          transpGroup->origSplash->getImageCache(),
4484
7.25k
          transpGroup->origSplash->getScreen());
4485
7.25k
  splash->setMinLineWidth(globalParams->getMinLineWidth());
4486
7.25k
  splash->setStrokeAdjust(
4487
7.25k
     mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
4488
7.25k
  splash->setEnablePathSimplification(
4489
7.25k
     globalParams->getEnablePathSimplification());
4490
7.25k
  copyState(transpGroup->origSplash, gTrue);
4491
7.25k
  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
7.25k
  } else {
4496
7.25k
    transpGroup->shapeBitmap = NULL;
4497
7.25k
  }
4498
7.25k
  if (!isolated &&
4499
5.76k
      transpGroup->origBitmap->getAlphaPtr() &&
4500
5.76k
      transpGroup->origSplash->getInNonIsolatedGroup() &&
4501
168
      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
168
    traceMessage("t-group alpha0 bitmap");
4506
168
    alpha0Bitmap = new SplashAlphaBitmap(w, h, bitmapMemCache);
4507
7.08k
  } else {
4508
7.08k
    alpha0Bitmap = NULL;
4509
7.08k
  }
4510
7.25k
  splash->setInTransparencyGroup(transpGroup->origSplash, tx, ty,
4511
7.25k
         alpha0Bitmap, transpGroup->shapeBitmap,
4512
7.25k
         !isolated, knockout);
4513
7.25k
  splash->clearModRegion();
4514
7.25k
  transpGroup->tBitmap = bitmap;
4515
7.25k
  transpGroup->alpha0Bitmap = alpha0Bitmap;
4516
7.25k
  state->shiftCTM(-tx, -ty);
4517
7.25k
  updateCTM(state, 0, 0, 0, 0, 0, 0);
4518
7.25k
  ++nestCount;
4519
4520
7.25k
  return gTrue;
4521
12.2k
}
4522
4523
7.25k
void SplashOutputDev::endTransparencyGroup(GfxState *state) {
4524
7.25k
  splash->getModRegion(&transpGroupStack->modXMin, &transpGroupStack->modYMin,
4525
7.25k
           &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
7.25k
  if (transpGroupStack->inSoftMask) {
4530
1.26k
      splash->forceDeferredInit(0, transpGroupStack->tBitmap->getHeight());
4531
1.26k
  }
4532
4533
  // restore state
4534
7.25k
  --nestCount;
4535
7.25k
  delete splash;
4536
7.25k
  bitmap = transpGroupStack->origBitmap;
4537
7.25k
  colorMode = bitmap->getMode();
4538
7.25k
  splash = transpGroupStack->origSplash;
4539
7.25k
  state->shiftCTM(transpGroupStack->tx, transpGroupStack->ty);
4540
7.25k
  updateCTM(state, 0, 0, 0, 0, 0, 0);
4541
7.25k
}
4542
4543
5.99k
void SplashOutputDev::paintTransparencyGroup(GfxState *state, double *bbox) {
4544
5.99k
  SplashBitmap *tBitmap;
4545
5.99k
  SplashAlphaBitmap *shapeBitmap;
4546
5.99k
  SplashTransparencyGroup *transpGroup;
4547
5.99k
  GBool isolated;
4548
5.99k
  int xSrc, ySrc, xDest, yDest, w, h;
4549
4550
5.99k
  xSrc = transpGroupStack->modXMin;
4551
5.99k
  ySrc = transpGroupStack->modYMin;
4552
5.99k
  xDest = transpGroupStack->tx + transpGroupStack->modXMin;
4553
5.99k
  yDest = transpGroupStack->ty + transpGroupStack->modYMin;
4554
5.99k
  w = transpGroupStack->modXMax - transpGroupStack->modXMin + 1;
4555
5.99k
  h = transpGroupStack->modYMax - transpGroupStack->modYMin + 1;
4556
5.99k
  tBitmap = transpGroupStack->tBitmap;
4557
5.99k
  shapeBitmap = transpGroupStack->shapeBitmap;
4558
5.99k
  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
5.99k
  if (xDest < bitmap->getWidth() && yDest < bitmap->getHeight() &&
4563
5.20k
      w > 0 && h > 0) {
4564
5.20k
    splash->setOverprintMask(0xffffffff);
4565
5.20k
    splash->composite(tBitmap, shapeBitmap, xSrc, ySrc, xDest, yDest, w, h,
4566
5.20k
          gFalse, !isolated);
4567
5.20k
  }
4568
4569
  // free the shape bitmap
4570
5.99k
  if (shapeBitmap) {
4571
0
    delete shapeBitmap;
4572
0
  }
4573
4574
  // free the alpha0 bitmap
4575
5.99k
  if (transpGroupStack->alpha0Bitmap) {
4576
168
    delete transpGroupStack->alpha0Bitmap;
4577
168
  }
4578
4579
  // pop the stack
4580
5.99k
  transpGroup = transpGroupStack;
4581
5.99k
  transpGroupStack = transpGroup->next;
4582
5.99k
  delete transpGroup;
4583
4584
5.99k
  delete tBitmap;
4585
5.99k
}
4586
4587
void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
4588
          GBool alpha, Function *transferFunc,
4589
1.25k
          GfxColor *backdropColor) {
4590
1.25k
  SplashBitmap *softMask, *tBitmap;
4591
1.25k
  Splash *tSplash;
4592
1.25k
  SplashTransparencyGroup *transpGroup;
4593
1.25k
  SplashColor color;
4594
1.25k
  SplashColorPtr p, colorPtr, colorPtr2;
4595
1.25k
  Guchar *alphaPtr;
4596
1.25k
  GfxGray gray;
4597
1.25k
  GfxRGB rgb;
4598
1.25k
#if SPLASH_CMYK
4599
1.25k
  GfxCMYK cmyk;
4600
1.25k
#endif
4601
1.25k
  double backdrop, backdrop2, lum, lum2;
4602
1.25k
  Guchar lum8;
4603
1.25k
  SplashBitmapRowSize rowSize;
4604
1.25k
  int tw, th, tNComps, tx, ty, x, y;
4605
4606
1.25k
  tx = transpGroupStack->tx;
4607
1.25k
  ty = transpGroupStack->ty;
4608
1.25k
  tBitmap = transpGroupStack->tBitmap;
4609
4610
  // composite with backdrop color
4611
1.25k
  backdrop = 0;
4612
1.25k
  if (!alpha && tBitmap->getMode() != splashModeMono1) {
4613
    //~ need to correctly handle the case where no blending color
4614
    //~ space is given
4615
1.18k
    if (transpGroupStack->blendingColorSpace) {
4616
1.07k
      tSplash = new Splash(tBitmap, vectorAntialias,
4617
1.07k
         transpGroupStack->origSplash->getImageCache(),
4618
1.07k
         transpGroupStack->origSplash->getScreen());
4619
1.07k
      tSplash->setStrokeAdjust(
4620
1.07k
          mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
4621
1.07k
      tSplash->setEnablePathSimplification(
4622
1.07k
          globalParams->getEnablePathSimplification());
4623
1.07k
      switch (tBitmap->getMode()) {
4624
0
      case splashModeMono1:
4625
  // transparency is not supported in mono1 mode
4626
0
  break;
4627
916
      case splashModeMono8:
4628
916
  transpGroupStack->blendingColorSpace->getGray(
4629
916
        backdropColor, &gray, state->getRenderingIntent());
4630
916
  backdrop = colToDbl(gray);
4631
916
  color[0] = colToByte(gray);
4632
916
  tSplash->compositeBackground(color);
4633
916
  break;
4634
0
      case splashModeRGB8:
4635
0
      case splashModeBGR8:
4636
0
  transpGroupStack->blendingColorSpace->getRGB(
4637
0
        backdropColor, &rgb, state->getRenderingIntent());
4638
0
  backdrop = 0.3 * colToDbl(rgb.r) +
4639
0
             0.59 * colToDbl(rgb.g) +
4640
0
             0.11 * colToDbl(rgb.b);
4641
0
  color[0] = colToByte(rgb.r);
4642
0
  color[1] = colToByte(rgb.g);
4643
0
  color[2] = colToByte(rgb.b);
4644
0
  tSplash->compositeBackground(color);
4645
0
  break;
4646
0
#if SPLASH_CMYK
4647
155
      case splashModeCMYK8:
4648
155
  transpGroupStack->blendingColorSpace->getCMYK(
4649
155
        backdropColor, &cmyk, state->getRenderingIntent());
4650
155
  backdrop = (1 - colToDbl(cmyk.k))
4651
155
             - 0.3 * colToDbl(cmyk.c)
4652
155
             - 0.59 * colToDbl(cmyk.m)
4653
155
             - 0.11 * colToDbl(cmyk.y);
4654
155
  if (backdrop < 0) {
4655
119
    backdrop = 0;
4656
119
  }
4657
155
  color[0] = colToByte(cmyk.c);
4658
155
  color[1] = colToByte(cmyk.m);
4659
155
  color[2] = colToByte(cmyk.y);
4660
155
  color[3] = colToByte(cmyk.k);
4661
155
  tSplash->compositeBackground(color);
4662
155
  break;
4663
1.07k
#endif
4664
1.07k
      }
4665
1.07k
      delete tSplash;
4666
1.07k
    }
4667
1.18k
  }
4668
1.25k
  if (transferFunc) {
4669
135
    transferFunc->transform(&backdrop, &backdrop2);
4670
1.12k
  } else {
4671
1.12k
    backdrop2 = backdrop;
4672
1.12k
  }
4673
4674
1.25k
  traceMessage("soft mask bitmap");
4675
1.25k
  softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
4676
1.25k
            1, splashModeMono8, gFalse, gTrue,
4677
1.25k
            bitmapMemCache);
4678
1.25k
  memset(softMask->getDataPtr(), (int)(backdrop2 * 255.0 + 0.5),
4679
1.25k
   softMask->getRowSize() * softMask->getHeight());
4680
1.25k
  if (tx < softMask->getWidth() && ty < softMask->getHeight()) {
4681
1.25k
    p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;
4682
1.25k
    tw = tBitmap->getWidth();
4683
1.25k
    th = tBitmap->getHeight();
4684
1.25k
    rowSize = softMask->getRowSize();
4685
1.25k
    if (alpha) {
4686
71
      alphaPtr = tBitmap->getAlphaPtr();
4687
142
      for (y = 0; y < th; ++y) {
4688
142
  for (x = 0; x < tw; ++x) {
4689
71
    lum = *alphaPtr++ / 255.0;
4690
71
    if (transferFunc) {
4691
0
      transferFunc->transform(&lum, &lum2);
4692
71
    } else {
4693
71
      lum2 = lum;
4694
71
    }
4695
71
    p[x] = (Guchar)(lum2 * 255.0 + 0.5);
4696
71
  }
4697
71
  p += rowSize;
4698
71
      }
4699
1.18k
    } else {
4700
1.18k
      colorPtr = tBitmap->getDataPtr();
4701
1.18k
      tNComps = splashColorModeNComps[tBitmap->getMode()];
4702
1.18k
      lum8 = 0;  // make gcc happy
4703
2.37k
      for (y = 0; y < th; ++y) {
4704
1.18k
  colorPtr2 = colorPtr;
4705
2.37k
  for (x = 0; x < tw; ++x) {
4706
    // convert to luminosity
4707
1.18k
    switch (tBitmap->getMode()) {
4708
0
    case splashModeMono1:
4709
0
      lum8 = 0;
4710
0
      break;
4711
916
    case splashModeMono8:
4712
916
      lum8 = colorPtr2[0];
4713
916
      break;
4714
115
    case splashModeRGB8:
4715
115
    case splashModeBGR8:
4716
      // [0.3, 0.59, 0.11] * 255 = [77, 150, 28]
4717
115
      lum8 = div255(77 * colorPtr2[0] +
4718
115
        150 * colorPtr2[1] +
4719
115
        28 * colorPtr2[2]);
4720
115
      break;
4721
0
#if SPLASH_CMYK
4722
155
    case splashModeCMYK8:
4723
155
      lum8 = clip255(255 - colorPtr2[3]
4724
155
         - div255(77 * colorPtr2[0] +
4725
155
            150 * colorPtr2[1] +
4726
155
            28 * colorPtr2[2]));
4727
155
      break;
4728
1.18k
#endif
4729
1.18k
    }
4730
1.18k
    if (transferFunc) {
4731
135
      lum = lum8 / 255.0;
4732
135
      transferFunc->transform(&lum, &lum2);
4733
135
      lum8 = (Guchar)(lum2 * 255.0 + 0.5);
4734
135
    }
4735
1.18k
    p[x] = lum8;
4736
1.18k
    colorPtr2 += tNComps;
4737
1.18k
  }
4738
1.18k
  p += rowSize;
4739
1.18k
  colorPtr += tBitmap->getRowSize();
4740
1.18k
      }
4741
1.18k
    }
4742
1.25k
  }
4743
1.25k
  splash->setSoftMask(softMask);
4744
4745
  // free the alpha0 bitmap
4746
1.25k
  if (transpGroupStack->alpha0Bitmap) {
4747
0
    delete transpGroupStack->alpha0Bitmap;
4748
0
  }
4749
4750
  // pop the stack
4751
1.25k
  transpGroup = transpGroupStack;
4752
1.25k
  transpGroupStack = transpGroup->next;
4753
1.25k
  delete transpGroup;
4754
4755
1.25k
  delete tBitmap;
4756
1.25k
}
4757
4758
8.04k
void SplashOutputDev::clearSoftMask(GfxState *state) {
4759
8.04k
  splash->setSoftMask(NULL);
4760
8.04k
}
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
7.52k
void SplashOutputDev::copyState(Splash *oldSplash, GBool copyColors) {
4968
  // cached Type 3 chars set a color, so no need to copy the color here
4969
7.52k
  if (copyColors) {
4970
7.25k
    splash->setFillPattern(oldSplash->getFillPattern()->copy());
4971
7.25k
    splash->setStrokePattern(oldSplash->getStrokePattern()->copy());
4972
7.25k
  }
4973
7.52k
  splash->setLineDash(oldSplash->getLineDash(),
4974
7.52k
          oldSplash->getLineDashLength(),
4975
7.52k
          oldSplash->getLineDashPhase());
4976
7.52k
  splash->setLineCap(oldSplash->getLineCap());
4977
7.52k
  splash->setLineJoin(oldSplash->getLineJoin());
4978
7.52k
  splash->setLineWidth(oldSplash->getLineWidth());
4979
7.52k
}