Coverage Report

Created: 2026-07-16 06:52

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