Coverage Report

Created: 2025-07-11 07:47

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