filters

GfxState.cpp
1//========================================================================
2//
3// GfxState.cpp
4//
5// Copyright 1996-2002 Glyph & Cog, LLC
6//
7//========================================================================
8
9#include <aconf.h>
10
11#ifdef USE_GCC_PRAGMAS
12#pragma implementation
13#endif
14
15#include <stddef.h>
16#include <math.h>
17#include <string.h> // for memcpy()
18#include "gmem.h"
19#include "Error.h"
20#include "Object.h"
21#include "Array.h"
22#include "Page.h"
23#include "GfxState.h"
24
25//------------------------------------------------------------------------
26
27static inline double clip01(double x) {
28 return (x < 0) ? 0 : ((x > 1) ? 1 : x);
29}
30
31//------------------------------------------------------------------------
32// GfxColorSpace
33//------------------------------------------------------------------------
34
35GfxColorSpace::GfxColorSpace() {
36}
37
38GfxColorSpace::~GfxColorSpace() {
39}
40
41GfxColorSpace *GfxColorSpace::parse(Object *csObj) {
42 GfxColorSpace *cs;
43 Object obj1;
44
45 cs = NULL;
46 if (csObj->isName()) {
47 if (csObj->isName("DeviceGray") || csObj->isName("G")) {
48 cs = new GfxDeviceGrayColorSpace();
49 } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {
50 cs = new GfxDeviceRGBColorSpace();
51 } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {
52 cs = new GfxDeviceCMYKColorSpace();
53 } else if (csObj->isName("Pattern")) {
54 cs = new GfxPatternColorSpace(NULL);
55 } else {
56 error(-1, "Bad color space '%s'", csObj->getName());
57 }
58 } else if (csObj->isArray()) {
59 csObj->arrayGet(0, &obj1);
60 if (obj1.isName("DeviceGray") || obj1.isName("G")) {
61 cs = new GfxDeviceGrayColorSpace();
62 } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
63 cs = new GfxDeviceRGBColorSpace();
64 } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
65 cs = new GfxDeviceCMYKColorSpace();
66 } else if (obj1.isName("CalGray")) {
67 cs = GfxCalGrayColorSpace::parse(csObj->getArray());
68 } else if (obj1.isName("CalRGB")) {
69 cs = GfxCalRGBColorSpace::parse(csObj->getArray());
70 } else if (obj1.isName("Lab")) {
71 cs = GfxLabColorSpace::parse(csObj->getArray());
72 } else if (obj1.isName("ICCBased")) {
73 cs = GfxICCBasedColorSpace::parse(csObj->getArray());
74 } else if (obj1.isName("Indexed") || obj1.isName("I")) {
75 cs = GfxIndexedColorSpace::parse(csObj->getArray());
76 } else if (obj1.isName("Separation")) {
77 cs = GfxSeparationColorSpace::parse(csObj->getArray());
78 } else if (obj1.isName("DeviceN")) {
79 cs = GfxDeviceNColorSpace::parse(csObj->getArray());
80 } else if (obj1.isName("Pattern")) {
81 cs = GfxPatternColorSpace::parse(csObj->getArray());
82 } else {
83 error(-1, "Bad color space '%s'", csObj->getName());
84 }
85 obj1.free();
86 } else {
87 error(-1, "Bad color space - expected name or array");
88 }
89 return cs;
90}
91
92void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
93 int /*maxImgPixel*/) const {
94 int i;
95
96 for (i = 0; i < getNComps(); ++i) {
97 decodeLow[i] = 0;
98 decodeRange[i] = 1;
99 }
100}
101
102//------------------------------------------------------------------------
103// GfxDeviceGrayColorSpace
104//------------------------------------------------------------------------
105
106GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() {
107}
108
109GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {
110}
111
112GfxColorSpace *GfxDeviceGrayColorSpace::copy() const {
113 return new GfxDeviceGrayColorSpace();
114}
115
116void GfxDeviceGrayColorSpace::getGray(const GfxColor *color, double *gray) const {
117 *gray = clip01(color->c[0]);
118}
119
120void GfxDeviceGrayColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const {
121 rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
122}
123
124void GfxDeviceGrayColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const {
125 cmyk->c = cmyk->m = cmyk->y = 0;
126 cmyk->k = clip01(1 - color->c[0]);
127}
128
129//------------------------------------------------------------------------
130// GfxCalGrayColorSpace
131//------------------------------------------------------------------------
132
133GfxCalGrayColorSpace::GfxCalGrayColorSpace() {
134 whiteX = whiteY = whiteZ = 1;
135 blackX = blackY = blackZ = 0;
136 gamma = 1;
137}
138
139GfxCalGrayColorSpace::~GfxCalGrayColorSpace() {
140}
141
142GfxColorSpace *GfxCalGrayColorSpace::copy() const {
143 GfxCalGrayColorSpace *cs;
144
145 cs = new GfxCalGrayColorSpace();
146 cs->whiteX = whiteX;
147 cs->whiteY = whiteY;
148 cs->whiteZ = whiteZ;
149 cs->blackX = blackX;
150 cs->blackY = blackY;
151 cs->blackZ = blackZ;
152 cs->gamma = gamma;
153 return cs;
154}
155
156GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr) {
157 GfxCalGrayColorSpace *cs;
158 Object obj1, obj2, obj3;
159
160 arr->get(1, &obj1);
161 if (!obj1.isDict()) {
162 error(-1, "Bad CalGray color space");
163 obj1.free();
164 return NULL;
165 }
166 cs = new GfxCalGrayColorSpace();
167 if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
168 obj2.arrayGetLength() == 3) {
169 obj2.arrayGet(0, &obj3);
170 cs->whiteX = obj3.getNum();
171 obj3.free();
172 obj2.arrayGet(1, &obj3);
173 cs->whiteY = obj3.getNum();
174 obj3.free();
175 obj2.arrayGet(2, &obj3);
176 cs->whiteZ = obj3.getNum();
177 obj3.free();
178 }
179 obj2.free();
180 if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
181 obj2.arrayGetLength() == 3) {
182 obj2.arrayGet(0, &obj3);
183 cs->blackX = obj3.getNum();
184 obj3.free();
185 obj2.arrayGet(1, &obj3);
186 cs->blackY = obj3.getNum();
187 obj3.free();
188 obj2.arrayGet(2, &obj3);
189 cs->blackZ = obj3.getNum();
190 obj3.free();
191 }
192 obj2.free();
193 if (obj1.dictLookup("Gamma", &obj2)->isNum()) {
194 cs->gamma = obj2.getNum();
195 }
196 obj2.free();
197 obj1.free();
198 return cs;
199}
200
201void GfxCalGrayColorSpace::getGray(const GfxColor *color, double *gray) const {
202 *gray = clip01(color->c[0]);
203}
204
205void GfxCalGrayColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const {
206 rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
207}
208
209void GfxCalGrayColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const {
210 cmyk->c = cmyk->m = cmyk->y = 0;
211 cmyk->k = clip01(1 - color->c[0]);
212}
213
214//------------------------------------------------------------------------
215// GfxDeviceRGBColorSpace
216//------------------------------------------------------------------------
217
218GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() {
219}
220
221GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() {
222}
223
224GfxColorSpace *GfxDeviceRGBColorSpace::copy() const {
225 return new GfxDeviceRGBColorSpace();
226}
227
228void GfxDeviceRGBColorSpace::getGray(const GfxColor *color, double *gray) const {
229 *gray = clip01(0.299 * color->c[0] +
230 0.587 * color->c[1] +
231 0.114 * color->c[2]);
232}
233
234void GfxDeviceRGBColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const {
235 rgb->r = clip01(color->c[0]);
236 rgb->g = clip01(color->c[1]);
237 rgb->b = clip01(color->c[2]);
238}
239
240void GfxDeviceRGBColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const {
241 double c, m, y, k;
242
243 c = clip01(1 - color->c[0]);
244 m = clip01(1 - color->c[1]);
245 y = clip01(1 - color->c[2]);
246 k = c;
247 if (m < k) {
248 k = m;
249 }
250 if (y < k) {
251 k = y;
252 }
253 cmyk->c = c - k;
254 cmyk->m = m - k;
255 cmyk->y = y - k;
256 cmyk->k = k;
257}
258
259//------------------------------------------------------------------------
260// GfxCalRGBColorSpace
261//------------------------------------------------------------------------
262
263GfxCalRGBColorSpace::GfxCalRGBColorSpace() {
264 whiteX = whiteY = whiteZ = 1;
265 blackX = blackY = blackZ = 0;
266 gammaR = gammaG = gammaB = 1;
267 mat[0] = 1; mat[1] = 0; mat[2] = 0;
268 mat[3] = 0; mat[4] = 1; mat[5] = 0;
269 mat[6] = 0; mat[7] = 0; mat[8] = 1;
270}
271
272GfxCalRGBColorSpace::~GfxCalRGBColorSpace() {
273}
274
275GfxColorSpace *GfxCalRGBColorSpace::copy() const {
276 GfxCalRGBColorSpace *cs;
277 int i;
278
279 cs = new GfxCalRGBColorSpace();
280 cs->whiteX = whiteX;
281 cs->whiteY = whiteY;
282 cs->whiteZ = whiteZ;
283 cs->blackX = blackX;
284 cs->blackY = blackY;
285 cs->blackZ = blackZ;
286 cs->gammaR = gammaR;
287 cs->gammaG = gammaG;
288 cs->gammaB = gammaB;
289 for (i = 0; i < 9; ++i) {
290 cs->mat[i] = mat[i];
291 }
292 return cs;
293}
294
295GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr) {
296 GfxCalRGBColorSpace *cs;
297 Object obj1, obj2, obj3;
298 int i;
299
300 arr->get(1, &obj1);
301 if (!obj1.isDict()) {
302 error(-1, "Bad CalRGB color space");
303 obj1.free();
304 return NULL;
305 }
306 cs = new GfxCalRGBColorSpace();
307 if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
308 obj2.arrayGetLength() == 3) {
309 obj2.arrayGet(0, &obj3);
310 cs->whiteX = obj3.getNum();
311 obj3.free();
312 obj2.arrayGet(1, &obj3);
313 cs->whiteY = obj3.getNum();
314 obj3.free();
315 obj2.arrayGet(2, &obj3);
316 cs->whiteZ = obj3.getNum();
317 obj3.free();
318 }
319 obj2.free();
320 if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
321 obj2.arrayGetLength() == 3) {
322 obj2.arrayGet(0, &obj3);
323 cs->blackX = obj3.getNum();
324 obj3.free();
325 obj2.arrayGet(1, &obj3);
326 cs->blackY = obj3.getNum();
327 obj3.free();
328 obj2.arrayGet(2, &obj3);
329 cs->blackZ = obj3.getNum();
330 obj3.free();
331 }
332 obj2.free();
333 if (obj1.dictLookup("Gamma", &obj2)->isArray() &&
334 obj2.arrayGetLength() == 3) {
335 obj2.arrayGet(0, &obj3);
336 cs->gammaR = obj3.getNum();
337 obj3.free();
338 obj2.arrayGet(1, &obj3);
339 cs->gammaG = obj3.getNum();
340 obj3.free();
341 obj2.arrayGet(2, &obj3);
342 cs->gammaB = obj3.getNum();
343 obj3.free();
344 }
345 obj2.free();
346 if (obj1.dictLookup("Matrix", &obj2)->isArray() &&
347 obj2.arrayGetLength() == 9) {
348 for (i = 0; i < 9; ++i) {
349 obj2.arrayGet(i, &obj3);
350 cs->mat[i] = obj3.getNum();
351 obj3.free();
352 }
353 }
354 obj2.free();
355 obj1.free();
356 return cs;
357}
358
359void GfxCalRGBColorSpace::getGray(const GfxColor *color, double *gray) const {
360 *gray = clip01(0.299 * color->c[0] +
361 0.587 * color->c[1] +
362 0.114 * color->c[2]);
363}
364
365void GfxCalRGBColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const {
366 rgb->r = clip01(color->c[0]);
367 rgb->g = clip01(color->c[1]);
368 rgb->b = clip01(color->c[2]);
369}
370
371void GfxCalRGBColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const {
372 double c, m, y, k;
373
374 c = clip01(1 - color->c[0]);
375 m = clip01(1 - color->c[1]);
376 y = clip01(1 - color->c[2]);
377 k = c;
378 if (m < k) {
379 k = m;
380 }
381 if (y < k) {
382 k = y;
383 }
384 cmyk->c = c - k;
385 cmyk->m = m - k;
386 cmyk->y = y - k;
387 cmyk->k = k;
388}
389
390//------------------------------------------------------------------------
391// GfxDeviceCMYKColorSpace
392//------------------------------------------------------------------------
393
394GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() {
395}
396
397GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() {
398}
399
400GfxColorSpace *GfxDeviceCMYKColorSpace::copy() const {
401 return new GfxDeviceCMYKColorSpace();
402}
403
404void GfxDeviceCMYKColorSpace::getGray(const GfxColor *color, double *gray) const {
405 *gray = clip01(1 - color->c[3]
406 - 0.299 * color->c[0]
407 - 0.587 * color->c[1]
408 - 0.114 * color->c[2]);
409}
410
411void GfxDeviceCMYKColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const {
412 double c, m, y, aw, ac, am, ay, ar, ag, ab;
413
414 c = clip01(color->c[0] + color->c[3]);
415 m = clip01(color->c[1] + color->c[3]);
416 y = clip01(color->c[2] + color->c[3]);
417 aw = (1-c) * (1-m) * (1-y);
418 ac = c * (1-m) * (1-y);
419 am = (1-c) * m * (1-y);
420 ay = (1-c) * (1-m) * y;
421 ar = (1-c) * m * y;
422 ag = c * (1-m) * y;
423 ab = c * m * (1-y);
424 rgb->r = clip01(aw + 0.9137*am + 0.9961*ay + 0.9882*ar);
425 rgb->g = clip01(aw + 0.6196*ac + ay + 0.5176*ag);
426 rgb->b = clip01(aw + 0.7804*ac + 0.5412*am + 0.0667*ar + 0.2118*ag +
427 0.4863*ab);
428}
429
430void GfxDeviceCMYKColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const {
431 cmyk->c = clip01(color->c[0]);
432 cmyk->m = clip01(color->c[1]);
433 cmyk->y = clip01(color->c[2]);
434 cmyk->k = clip01(color->c[3]);
435}
436
437//------------------------------------------------------------------------
438// GfxLabColorSpace
439//------------------------------------------------------------------------
440
441// This is the inverse of MatrixLMN in Example 4.10 from the PostScript
442// Language Reference, Third Edition.
443static double xyzrgb[3][3] = {
444 { 3.240449, -1.537136, -0.498531 },
445 { -0.969265, 1.876011, 0.041556 },
446 { 0.055643, -0.204026, 1.057229 }
447};
448
449GfxLabColorSpace::GfxLabColorSpace() {
450 whiteX = whiteY = whiteZ = 1;
451 blackX = blackY = blackZ = 0;
452 aMin = bMin = -100;
453 aMax = bMax = 100;
454}
455
456GfxLabColorSpace::~GfxLabColorSpace() {
457}
458
459GfxColorSpace *GfxLabColorSpace::copy() const {
460 GfxLabColorSpace *cs;
461
462 cs = new GfxLabColorSpace();
463 cs->whiteX = whiteX;
464 cs->whiteY = whiteY;
465 cs->whiteZ = whiteZ;
466 cs->blackX = blackX;
467 cs->blackY = blackY;
468 cs->blackZ = blackZ;
469 cs->aMin = aMin;
470 cs->aMax = aMax;
471 cs->bMin = bMin;
472 cs->bMax = bMax;
473 cs->kr = kr;
474 cs->kg = kg;
475 cs->kb = kb;
476 return cs;
477}
478
479GfxColorSpace *GfxLabColorSpace::parse(Array *arr) {
480 GfxLabColorSpace *cs;
481 Object obj1, obj2, obj3;
482
483 arr->get(1, &obj1);
484 if (!obj1.isDict()) {
485 error(-1, "Bad Lab color space");
486 obj1.free();
487 return NULL;
488 }
489 cs = new GfxLabColorSpace();
490 if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
491 obj2.arrayGetLength() == 3) {
492 obj2.arrayGet(0, &obj3);
493 cs->whiteX = obj3.getNum();
494 obj3.free();
495 obj2.arrayGet(1, &obj3);
496 cs->whiteY = obj3.getNum();
497 obj3.free();
498 obj2.arrayGet(2, &obj3);
499 cs->whiteZ = obj3.getNum();
500 obj3.free();
501 }
502 obj2.free();
503 if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
504 obj2.arrayGetLength() == 3) {
505 obj2.arrayGet(0, &obj3);
506 cs->blackX = obj3.getNum();
507 obj3.free();
508 obj2.arrayGet(1, &obj3);
509 cs->blackY = obj3.getNum();
510 obj3.free();
511 obj2.arrayGet(2, &obj3);
512 cs->blackZ = obj3.getNum();
513 obj3.free();
514 }
515 obj2.free();
516 if (obj1.dictLookup("Range", &obj2)->isArray() &&
517 obj2.arrayGetLength() == 4) {
518 obj2.arrayGet(0, &obj3);
519 cs->aMin = obj3.getNum();
520 obj3.free();
521 obj2.arrayGet(1, &obj3);
522 cs->aMax = obj3.getNum();
523 obj3.free();
524 obj2.arrayGet(2, &obj3);
525 cs->bMin = obj3.getNum();
526 obj3.free();
527 obj2.arrayGet(3, &obj3);
528 cs->bMax = obj3.getNum();
529 obj3.free();
530 }
531 obj2.free();
532 obj1.free();
533
534 cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
535 xyzrgb[0][1] * cs->whiteY +
536 xyzrgb[0][2] * cs->whiteZ);
537 cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
538 xyzrgb[1][1] * cs->whiteY +
539 xyzrgb[1][2] * cs->whiteZ);
540 cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
541 xyzrgb[2][1] * cs->whiteY +
542 xyzrgb[2][2] * cs->whiteZ);
543
544 return cs;
545}
546
547void GfxLabColorSpace::getGray(const GfxColor *color, double *gray) const {
548 GfxRGB rgb;
549
550 getRGB(color, &rgb);
551 *gray = clip01(0.299 * rgb.r +
552 0.587 * rgb.g +
553 0.114 * rgb.b);
554}
555
556void GfxLabColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const {
557 double X, Y, Z;
558 double t1, t2;
559 double r, g, b;
560
561 // convert L*a*b* to CIE 1931 XYZ color space
562 t1 = (color->c[0] + 16) / 116;
563 t2 = t1 + color->c[1] / 500;
564 if (t2 >= (6.0 / 29.0)) {
565 X = t2 * t2 * t2;
566 } else {
567 X = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
568 }
569 X *= whiteX;
570 if (t1 >= (6.0 / 29.0)) {
571 Y = t1 * t1 * t1;
572 } else {
573 Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0));
574 }
575 Y *= whiteY;
576 t2 = t1 - color->c[2] / 200;
577 if (t2 >= (6.0 / 29.0)) {
578 Z = t2 * t2 * t2;
579 } else {
580 Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
581 }
582 Z *= whiteZ;
583
584 // convert XYZ to RGB, including gamut mapping and gamma correction
585 r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
586 g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
587 b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
588 rgb->r = pow(clip01(r * kr), 0.5);
589 rgb->g = pow(clip01(g * kg), 0.5);
590 rgb->b = pow(clip01(b * kb), 0.5);
591}
592
593void GfxLabColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const {
594 GfxRGB rgb;
595 double c, m, y, k;
596
597 getRGB(color, &rgb);
598 c = clip01(1 - rgb.r);
599 m = clip01(1 - rgb.g);
600 y = clip01(1 - rgb.b);
601 k = c;
602 if (m < k) {
603 k = m;
604 }
605 if (y < k) {
606 k = y;
607 }
608 cmyk->c = c - k;
609 cmyk->m = m - k;
610 cmyk->y = y - k;
611 cmyk->k = k;
612}
613
614void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
615 int /*maxImgPixel*/) const {
616 decodeLow[0] = 0;
617 decodeRange[0] = 100;
618 decodeLow[1] = aMin;
619 decodeRange[1] = aMax - aMin;
620 decodeLow[2] = bMin;
621 decodeRange[2] = bMax - bMin;
622}
623
624//------------------------------------------------------------------------
625// GfxICCBasedColorSpace
626//------------------------------------------------------------------------
627
628GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
629 const Ref *iccProfileStreamA) {
630 nComps = nCompsA;
631 alt = altA;
632 iccProfileStream = *iccProfileStreamA;
633 rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0;
634 rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1;
635}
636
637GfxICCBasedColorSpace::~GfxICCBasedColorSpace() {
638 delete alt;
639}
640
641GfxColorSpace *GfxICCBasedColorSpace::copy() const {
642 GfxICCBasedColorSpace *cs;
643 int i;
644
645 cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream);
646 for (i = 0; i < 4; ++i) {
647 cs->rangeMin[i] = rangeMin[i];
648 cs->rangeMax[i] = rangeMax[i];
649 }
650 return cs;
651}
652
653GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
654 GfxICCBasedColorSpace *cs;
655 Ref iccProfileStreamA;
656 int nCompsA;
657 GfxColorSpace *altA;
658 Dict *dict;
659 Object obj1, obj2, obj3;
660 int i;
661
662 arr->getNF(1, &obj1);
663 if (obj1.isRef()) {
664 iccProfileStreamA = obj1.getRef();
665 } else {
666 iccProfileStreamA.num = 0;
667 iccProfileStreamA.gen = 0;
668 }
669 obj1.free();
670 arr->get(1, &obj1);
671 if (!obj1.isStream()) {
672 error(-1, "Bad ICCBased color space (stream)");
673 obj1.free();
674 return NULL;
675 }
676 dict = obj1.streamGetDict();
677 if (!dict->lookup("N", &obj2)->isInt()) {
678 error(-1, "Bad ICCBased color space (N)");
679 obj2.free();
680 obj1.free();
681 return NULL;
682 }
683 nCompsA = obj2.getInt();
684 obj2.free();
685 if (nCompsA > gfxColorMaxComps) {
686 error(-1, "ICCBased color space with too many (%d > %d) components",
687 nCompsA, gfxColorMaxComps);
688 nCompsA = gfxColorMaxComps;
689 }
690 if (dict->lookup("Alternate", &obj2)->isNull() ||
691 !(altA = GfxColorSpace::parse(&obj2))) {
692 switch (nCompsA) {
693 case 1:
694 altA = new GfxDeviceGrayColorSpace();
695 break;
696 case 3:
697 altA = new GfxDeviceRGBColorSpace();
698 break;
699 case 4:
700 altA = new GfxDeviceCMYKColorSpace();
701 break;
702 default:
703 error(-1, "Bad ICCBased color space - invalid N");
704 obj2.free();
705 obj1.free();
706 return NULL;
707 }
708 }
709 obj2.free();
710 cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA);
711 if (dict->lookup("Range", &obj2)->isArray() &&
712 obj2.arrayGetLength() == 2 * nCompsA) {
713 for (i = 0; i < nCompsA; ++i) {
714 obj2.arrayGet(2*i, &obj3);
715 cs->rangeMin[i] = obj3.getNum();
716 obj3.free();
717 obj2.arrayGet(2*i+1, &obj3);
718 cs->rangeMax[i] = obj3.getNum();
719 obj3.free();
720 }
721 }
722 obj2.free();
723 obj1.free();
724 return cs;
725}
726
727void GfxICCBasedColorSpace::getGray(const GfxColor *color, double *gray) const {
728 alt->getGray(color, gray);
729}
730
731void GfxICCBasedColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const {
732 alt->getRGB(color, rgb);
733}
734
735void GfxICCBasedColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const {
736 alt->getCMYK(color, cmyk);
737}
738
739void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow,
740 double *decodeRange,
741 int /*maxImgPixel*/) const {
742 int i;
743
744 for (i = 0; i < nComps; ++i) {
745 decodeLow[i] = rangeMin[i];
746 decodeRange[i] = rangeMax[i] - rangeMin[i];
747 }
748}
749
750//------------------------------------------------------------------------
751// GfxIndexedColorSpace
752//------------------------------------------------------------------------
753
754GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA,
755 int indexHighA) {
756 base = baseA;
757 indexHigh = indexHighA;
758 lookup = (Guchar *)gmalloc((indexHigh + 1) * base->getNComps() *
759 sizeof(Guchar));
760}
761
762GfxIndexedColorSpace::~GfxIndexedColorSpace() {
763 delete base;
764 gfree(lookup);
765}
766
767GfxColorSpace *GfxIndexedColorSpace::copy() const {
768 GfxIndexedColorSpace *cs;
769
770 cs = new GfxIndexedColorSpace(base->copy(), indexHigh);
771 memcpy(cs->lookup, lookup,
772 (indexHigh + 1) * base->getNComps() * sizeof(Guchar));
773 return cs;
774}
775
776GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
777 GfxIndexedColorSpace *cs;
778 GfxColorSpace *baseA;
779 int indexHighA;
780 Object obj1;
781 int x;
782 char *s;
783 int n, i, j;
784
785 if (arr->getLength() != 4) {
786 error(-1, "Bad Indexed color space");
787 goto err1;
788 }
789 arr->get(1, &obj1);
790 if (!(baseA = GfxColorSpace::parse(&obj1))) {
791 error(-1, "Bad Indexed color space (base color space)");
792 goto err2;
793 }
794 obj1.free();
795 if (!arr->get(2, &obj1)->isInt()) {
796 error(-1, "Bad Indexed color space (hival)");
797 goto err2;
798 }
799 indexHighA = obj1.getInt();
800 obj1.free();
801 cs = new GfxIndexedColorSpace(baseA, indexHighA);
802 arr->get(3, &obj1);
803 n = baseA->getNComps();
804 if (obj1.isStream()) {
805 obj1.streamReset();
806 for (i = 0; i <= indexHighA; ++i) {
807 for (j = 0; j < n; ++j) {
808 if ((x = obj1.streamGetChar()) == EOF) {
809 error(-1, "Bad Indexed color space (lookup table stream too short)");
810 goto err3;
811 }
812 cs->lookup[i*n + j] = (Guchar)x;
813 }
814 }
815 obj1.streamClose();
816 } else if (obj1.isString()) {
817 if (obj1.getString()->getLength() < (indexHighA + 1) * n) {
818 error(-1, "Bad Indexed color space (lookup table string too short)");
819 goto err3;
820 }
821 s = obj1.getString()->getCString();
822 for (i = 0; i <= indexHighA; ++i) {
823 for (j = 0; j < n; ++j) {
824 cs->lookup[i*n + j] = (Guchar)*s++;
825 }
826 }
827 } else {
828 error(-1, "Bad Indexed color space (lookup table)");
829 goto err3;
830 }
831 obj1.free();
832 return cs;
833
834 err3:
835 delete cs;
836 err2:
837 obj1.free();
838 err1:
839 return NULL;
840}
841
842void GfxIndexedColorSpace::getGray(const GfxColor *color, double *gray) const {
843 Guchar *p;
844 GfxColor color2;
845 double low[gfxColorMaxComps], range[gfxColorMaxComps];
846 int n, i;
847
848 n = base->getNComps();
849 base->getDefaultRanges(low, range, indexHigh);
850 p = &lookup[(int)(color->c[0] + 0.5) * n];
851 for (i = 0; i < n; ++i) {
852 color2.c[i] = low[i] + (p[i] / 255.0) * range[i];
853 }
854 base->getGray(&color2, gray);
855}
856
857void GfxIndexedColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const {
858 Guchar *p;
859 GfxColor color2;
860 double low[gfxColorMaxComps], range[gfxColorMaxComps];
861 int n, i;
862
863 n = base->getNComps();
864 base->getDefaultRanges(low, range, indexHigh);
865 p = &lookup[(int)(color->c[0] + 0.5) * n];
866 for (i = 0; i < n; ++i) {
867 color2.c[i] = low[i] + (p[i] / 255.0) * range[i];
868 }
869 base->getRGB(&color2, rgb);
870}
871
872void GfxIndexedColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const {
873 Guchar *p;
874 GfxColor color2;
875 double low[gfxColorMaxComps], range[gfxColorMaxComps];
876 int n, i;
877
878 n = base->getNComps();
879 base->getDefaultRanges(low, range, indexHigh);
880 p = &lookup[(int)(color->c[0] + 0.5) * n];
881 for (i = 0; i < n; ++i) {
882 color2.c[i] = low[i] + (p[i] / 255.0) * range[i];
883 }
884 base->getCMYK(&color2, cmyk);
885}
886
887void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
888 double *decodeRange,
889 int maxImgPixel) const {
890 decodeLow[0] = 0;
891 decodeRange[0] = maxImgPixel;
892}
893
894//------------------------------------------------------------------------
895// GfxSeparationColorSpace
896//------------------------------------------------------------------------
897
898GfxSeparationColorSpace::GfxSeparationColorSpace(GString *nameA,
899 GfxColorSpace *altA,
900 Function *funcA) {
901 name = nameA;
902 alt = altA;
903 func = funcA;
904}
905
906GfxSeparationColorSpace::~GfxSeparationColorSpace() {
907 delete name;
908 delete alt;
909 delete func;
910}
911
912GfxColorSpace *GfxSeparationColorSpace::copy() const {
913 return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy());
914}
915
916//~ handle the 'All' and 'None' colorants
917GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr) {
918 GfxSeparationColorSpace *cs;
919 GString *nameA;
920 GfxColorSpace *altA;
921 Function *funcA;
922 Object obj1;
923
924 if (arr->getLength() != 4) {
925 error(-1, "Bad Separation color space");
926 goto err1;
927 }
928 if (!arr->get(1, &obj1)->isName()) {
929 error(-1, "Bad Separation color space (name)");
930 goto err2;
931 }
932 nameA = new GString(obj1.getName());
933 obj1.free();
934 arr->get(2, &obj1);
935 if (!(altA = GfxColorSpace::parse(&obj1))) {
936 error(-1, "Bad Separation color space (alternate color space)");
937 goto err3;
938 }
939 obj1.free();
940 arr->get(3, &obj1);
941 if (!(funcA = Function::parse(&obj1))) {
942 goto err4;
943 }
944 obj1.free();
945 cs = new GfxSeparationColorSpace(nameA, altA, funcA);
946 return cs;
947
948 err4:
949 delete altA;
950 err3:
951 delete nameA;
952 err2:
953 obj1.free();
954 err1:
955 return NULL;
956}
957
958void GfxSeparationColorSpace::getGray(const GfxColor *color, double *gray) const {
959 GfxColor color2;
960
961 func->transform(color->c, color2.c);
962 alt->getGray(&color2, gray);
963}
964
965void GfxSeparationColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const {
966 GfxColor color2;
967
968 func->transform(color->c, color2.c);
969 alt->getRGB(&color2, rgb);
970}
971
972void GfxSeparationColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const {
973 GfxColor color2;
974
975 func->transform(color->c, color2.c);
976 alt->getCMYK(&color2, cmyk);
977}
978
979//------------------------------------------------------------------------
980// GfxDeviceNColorSpace
981//------------------------------------------------------------------------
982
983GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
984 GfxColorSpace *altA,
985 Function *funcA) {
986 nComps = nCompsA;
987 alt = altA;
988 func = funcA;
989}
990
991GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
992 int i;
993
994 for (i = 0; i < nComps; ++i) {
995 delete names[i];
996 }
997 delete alt;
998 delete func;
999}
1000
1001GfxColorSpace *GfxDeviceNColorSpace::copy() const {
1002 GfxDeviceNColorSpace *cs;
1003 int i;
1004
1005 cs = new GfxDeviceNColorSpace(nComps, alt->copy(), func->copy());
1006 for (i = 0; i < nComps; ++i) {
1007 cs->names[i] = names[i]->copy();
1008 }
1009 return cs;
1010}
1011
1012//~ handle the 'None' colorant
1013GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) {
1014 GfxDeviceNColorSpace *cs;
1015 int nCompsA;
1016 GString *namesA[gfxColorMaxComps];
1017 GfxColorSpace *altA;
1018 Function *funcA;
1019 Object obj1, obj2;
1020 int i;
1021
1022 if (arr->getLength() != 4 && arr->getLength() != 5) {
1023 error(-1, "Bad DeviceN color space");
1024 goto err1;
1025 }
1026 if (!arr->get(1, &obj1)->isArray()) {
1027 error(-1, "Bad DeviceN color space (names)");
1028 goto err2;
1029 }
1030 nCompsA = obj1.arrayGetLength();
1031 if (nCompsA > gfxColorMaxComps) {
1032 error(-1, "DeviceN color space with too many (%d > %d) components",
1033 nCompsA, gfxColorMaxComps);
1034 nCompsA = gfxColorMaxComps;
1035 }
1036 for (i = 0; i < nCompsA; ++i) {
1037 if (!obj1.arrayGet(i, &obj2)->isName()) {
1038 error(-1, "Bad DeviceN color space (names)");
1039 obj2.free();
1040 goto err2;
1041 }
1042 namesA[i] = new GString(obj2.getName());
1043 obj2.free();
1044 }
1045 obj1.free();
1046 arr->get(2, &obj1);
1047 if (!(altA = GfxColorSpace::parse(&obj1))) {
1048 error(-1, "Bad DeviceN color space (alternate color space)");
1049 goto err3;
1050 }
1051 obj1.free();
1052 arr->get(3, &obj1);
1053 if (!(funcA = Function::parse(&obj1))) {
1054 goto err4;
1055 }
1056 obj1.free();
1057 cs = new GfxDeviceNColorSpace(nCompsA, altA, funcA);
1058 for (i = 0; i < nCompsA; ++i) {
1059 cs->names[i] = namesA[i];
1060 }
1061 return cs;
1062
1063 err4:
1064 delete altA;
1065 err3:
1066 for (i = 0; i < nCompsA; ++i) {
1067 delete namesA[i];
1068 }
1069 err2:
1070 obj1.free();
1071 err1:
1072 return NULL;
1073}
1074
1075void GfxDeviceNColorSpace::getGray(const GfxColor *color, double *gray) const {
1076 GfxColor color2;
1077
1078 func->transform(color->c, color2.c);
1079 alt->getGray(&color2, gray);
1080}
1081
1082void GfxDeviceNColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const {
1083 GfxColor color2;
1084
1085 func->transform(color->c, color2.c);
1086 alt->getRGB(&color2, rgb);
1087}
1088
1089void GfxDeviceNColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const {
1090 GfxColor color2;
1091
1092 func->transform(color->c, color2.c);
1093 alt->getCMYK(&color2, cmyk);
1094}
1095
1096//------------------------------------------------------------------------
1097// GfxPatternColorSpace
1098//------------------------------------------------------------------------
1099
1100GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) {
1101 under = underA;
1102}
1103
1104GfxPatternColorSpace::~GfxPatternColorSpace() {
1105 if (under) {
1106 delete under;
1107 }
1108}
1109
1110GfxColorSpace *GfxPatternColorSpace::copy() const {
1111 return new GfxPatternColorSpace(under ? under->copy() :
1112 (GfxColorSpace *)NULL);
1113}
1114
1115GfxColorSpace *GfxPatternColorSpace::parse(Array *arr) {
1116 GfxPatternColorSpace *cs;
1117 GfxColorSpace *underA;
1118 Object obj1;
1119
1120 if (arr->getLength() != 1 && arr->getLength() != 2) {
1121 error(-1, "Bad Pattern color space");
1122 return NULL;
1123 }
1124 underA = NULL;
1125 if (arr->getLength() == 2) {
1126 arr->get(1, &obj1);
1127 if (!(underA = GfxColorSpace::parse(&obj1))) {
1128 error(-1, "Bad Pattern color space (underlying color space)");
1129 obj1.free();
1130 return NULL;
1131 }
1132 obj1.free();
1133 }
1134 cs = new GfxPatternColorSpace(underA);
1135 return cs;
1136}
1137
1138void GfxPatternColorSpace::getGray(const GfxColor */*color*/, double *gray) const {
1139 *gray = 0;
1140}
1141
1142void GfxPatternColorSpace::getRGB(const GfxColor */*color*/, GfxRGB *rgb) const {
1143 rgb->r = rgb->g = rgb->b = 0;
1144}
1145
1146void GfxPatternColorSpace::getCMYK(const GfxColor */*color*/, GfxCMYK *cmyk) const {
1147 cmyk->c = cmyk->m = cmyk->y = 0;
1148 cmyk->k = 1;
1149}
1150
1151//------------------------------------------------------------------------
1152// Pattern
1153//------------------------------------------------------------------------
1154
1155GfxPattern::GfxPattern(int typeA) {
1156 type = typeA;
1157}
1158
1159GfxPattern::~GfxPattern() {
1160}
1161
1162GfxPattern *GfxPattern::parse(Object *obj) {
1163 GfxPattern *pattern;
1164 Dict *dict;
1165 Object obj1;
1166
1167 pattern = NULL;
1168 if (obj->isStream()) {
1169 dict = obj->streamGetDict();
1170 dict->lookup("PatternType", &obj1);
1171 if (obj1.isInt() && obj1.getInt() == 1) {
1172 pattern = new GfxTilingPattern(dict, obj);
1173 }
1174 obj1.free();
1175 }
1176 return pattern;
1177}
1178
1179//------------------------------------------------------------------------
1180// GfxTilingPattern
1181//------------------------------------------------------------------------
1182
1183GfxTilingPattern::GfxTilingPattern(Dict *streamDict, Object *stream):
1184 GfxPattern(1)
1185{
1186 Object obj1, obj2;
1187 int i;
1188
1189 if (streamDict->lookup("PaintType", &obj1)->isInt()) {
1190 paintType = obj1.getInt();
1191 } else {
1192 paintType = 1;
1193 error(-1, "Invalid or missing PaintType in pattern");
1194 }
1195 obj1.free();
1196 if (streamDict->lookup("TilingType", &obj1)->isInt()) {
1197 tilingType = obj1.getInt();
1198 } else {
1199 tilingType = 1;
1200 error(-1, "Invalid or missing TilingType in pattern");
1201 }
1202 obj1.free();
1203 bbox[0] = bbox[1] = 0;
1204 bbox[2] = bbox[3] = 1;
1205 if (streamDict->lookup("BBox", &obj1)->isArray() &&
1206 obj1.arrayGetLength() == 4) {
1207 for (i = 0; i < 4; ++i) {
1208 if (obj1.arrayGet(i, &obj2)->isNum()) {
1209 bbox[i] = obj2.getNum();
1210 }
1211 obj2.free();
1212 }
1213 } else {
1214 error(-1, "Invalid or missing BBox in pattern");
1215 }
1216 obj1.free();
1217 if (streamDict->lookup("XStep", &obj1)->isNum()) {
1218 xStep = obj1.getNum();
1219 } else {
1220 xStep = 1;
1221 error(-1, "Invalid or missing XStep in pattern");
1222 }
1223 obj1.free();
1224 if (streamDict->lookup("YStep", &obj1)->isNum()) {
1225 yStep = obj1.getNum();
1226 } else {
1227 yStep = 1;
1228 error(-1, "Invalid or missing YStep in pattern");
1229 }
1230 obj1.free();
1231 if (!streamDict->lookup("Resources", &resDict)->isDict()) {
1232 resDict.free();
1233 resDict.initNull();
1234 error(-1, "Invalid or missing Resources in pattern");
1235 }
1236 matrix[0] = 1; matrix[1] = 0;
1237 matrix[2] = 0; matrix[3] = 1;
1238 matrix[4] = 0; matrix[5] = 0;
1239 if (streamDict->lookup("Matrix", &obj1)->isArray() &&
1240 obj1.arrayGetLength() == 6) {
1241 for (i = 0; i < 6; ++i) {
1242 if (obj1.arrayGet(i, &obj2)->isNum()) {
1243 matrix[i] = obj2.getNum();
1244 }
1245 obj2.free();
1246 }
1247 }
1248 obj1.free();
1249 stream->copy(&contentStream);
1250}
1251
1252GfxTilingPattern::~GfxTilingPattern() {
1253 resDict.free();
1254 contentStream.free();
1255}
1256
1257GfxPattern *GfxTilingPattern::copy() {
1258 return new GfxTilingPattern(this);
1259}
1260
1261GfxTilingPattern::GfxTilingPattern(GfxTilingPattern *pat):
1262 GfxPattern(1)
1263{
1264 memcpy(this, pat, sizeof(GfxTilingPattern));
1265 pat->resDict.copy(&resDict);
1266 pat->contentStream.copy(&contentStream);
1267}
1268
1269//------------------------------------------------------------------------
1270// GfxShading
1271//------------------------------------------------------------------------
1272
1273GfxShading::GfxShading() {
1274}
1275
1276GfxShading::~GfxShading() {
1277 delete colorSpace;
1278}
1279
1280GfxShading *GfxShading::parse(Object *obj) {
1281 GfxShading *shading;
1282 int typeA;
1283 GfxColorSpace *colorSpaceA;
1284 GfxColor backgroundA;
1285 GBool hasBackgroundA;
1286 double xMinA, yMinA, xMaxA, yMaxA;
1287 GBool hasBBoxA;
1288 Object obj1, obj2;
1289 int i;
1290
1291 shading = NULL;
1292 if (obj->isDict()) {
1293
1294 if (!obj->dictLookup("ShadingType", &obj1)->isInt()) {
1295 error(-1, "Invalid ShadingType in shading dictionary");
1296 obj1.free();
1297 goto err1;
1298 }
1299 typeA = obj1.getInt();
1300 obj1.free();
1301
1302 obj->dictLookup("ColorSpace", &obj1);
1303 if (!(colorSpaceA = GfxColorSpace::parse(&obj1))) {
1304 error(-1, "Bad color space in shading dictionary");
1305 obj1.free();
1306 goto err1;
1307 }
1308 obj1.free();
1309
1310 for (i = 0; i < gfxColorMaxComps; ++i) {
1311 backgroundA.c[i] = 0;
1312 }
1313 hasBackgroundA = gFalse;
1314 if (obj->dictLookup("Background", &obj1)->isArray()) {
1315 if (obj1.arrayGetLength() == colorSpaceA->getNComps()) {
1316 hasBackgroundA = gTrue;
1317 for (i = 0; i < colorSpaceA->getNComps(); ++i) {
1318 backgroundA.c[i] = obj1.arrayGet(i, &obj2)->getNum();
1319 obj2.free();
1320 }
1321 } else {
1322 error(-1, "Bad Background in shading dictionary");
1323 }
1324 }
1325 obj1.free();
1326
1327 xMinA = yMinA = xMaxA = yMaxA = 0;
1328 hasBBoxA = gFalse;
1329 if (obj->dictLookup("BBox", &obj1)->isArray()) {
1330 if (obj1.arrayGetLength() == 4) {
1331 hasBBoxA = gTrue;
1332 xMinA = obj1.arrayGet(0, &obj2)->getNum();
1333 obj2.free();
1334 yMinA = obj1.arrayGet(1, &obj2)->getNum();
1335 obj2.free();
1336 xMaxA = obj1.arrayGet(2, &obj2)->getNum();
1337 obj2.free();
1338 yMaxA = obj1.arrayGet(3, &obj2)->getNum();
1339 obj2.free();
1340 } else {
1341 error(-1, "Bad BBox in shading dictionary");
1342 }
1343 }
1344 obj1.free();
1345
1346 switch (typeA) {
1347 case 2:
1348 shading = GfxAxialShading::parse(obj->getDict());
1349 break;
1350 case 3:
1351 shading = GfxRadialShading::parse(obj->getDict());
1352 break;
1353 default:
1354 error(-1, "Unimplemented shading type %d", typeA);
1355 goto err1;
1356 }
1357
1358 if (shading) {
1359 shading->type = typeA;
1360 shading->colorSpace = colorSpaceA;
1361 shading->background = backgroundA;
1362 shading->hasBackground = hasBackgroundA;
1363 shading->xMin = xMinA;
1364 shading->yMin = yMinA;
1365 shading->xMax = xMaxA;
1366 shading->yMax = yMaxA;
1367 shading->hasBBox = hasBBoxA;
1368 } else {
1369 delete colorSpaceA;
1370 }
1371 }
1372
1373 return shading;
1374
1375 err1:
1376 return NULL;
1377}
1378
1379//------------------------------------------------------------------------
1380// GfxAxialShading
1381//------------------------------------------------------------------------
1382
1383GfxAxialShading::GfxAxialShading(double x0A, double y0A,
1384 double x1A, double y1A,
1385 double t0A, double t1A,
1386 Function **funcsA, int nFuncsA,
1387 GBool extend0A, GBool extend1A) {
1388 int i;
1389
1390 x0 = x0A;
1391 y0 = y0A;
1392 x1 = x1A;
1393 y1 = y1A;
1394 t0 = t0A;
1395 t1 = t1A;
1396 nFuncs = nFuncsA;
1397 for (i = 0; i < nFuncs; ++i) {
1398 funcs[i] = funcsA[i];
1399 }
1400 extend0 = extend0A;
1401 extend1 = extend1A;
1402}
1403
1404GfxAxialShading::~GfxAxialShading() {
1405 int i;
1406
1407 for (i = 0; i < nFuncs; ++i) {
1408 delete funcs[i];
1409 }
1410}
1411
1412GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
1413 double x0A, y0A, x1A, y1A;
1414 double t0A, t1A;
1415 Function *funcsA[gfxColorMaxComps];
1416 int nFuncsA;
1417 GBool extend0A, extend1A;
1418 Object obj1, obj2;
1419 int i;
1420
1421 x0A = y0A = x1A = y1A = 0;
1422 if (dict->lookup("Coords", &obj1)->isArray() &&
1423 obj1.arrayGetLength() == 4) {
1424 x0A = obj1.arrayGet(0, &obj2)->getNum();
1425 obj2.free();
1426 y0A = obj1.arrayGet(1, &obj2)->getNum();
1427 obj2.free();
1428 x1A = obj1.arrayGet(2, &obj2)->getNum();
1429 obj2.free();
1430 y1A = obj1.arrayGet(3, &obj2)->getNum();
1431 obj2.free();
1432 } else {
1433 error(-1, "Missing or invalid Coords in shading dictionary");
1434 goto err1;
1435 }
1436 obj1.free();
1437
1438 t0A = 0;
1439 t1A = 1;
1440 if (dict->lookup("Domain", &obj1)->isArray() &&
1441 obj1.arrayGetLength() == 2) {
1442 t0A = obj1.arrayGet(0, &obj2)->getNum();
1443 obj2.free();
1444 t1A = obj1.arrayGet(1, &obj2)->getNum();
1445 obj2.free();
1446 }
1447 obj1.free();
1448
1449 dict->lookup("Function", &obj1);
1450 if (obj1.isArray()) {
1451 nFuncsA = obj1.arrayGetLength();
1452 for (i = 0; i < nFuncsA; ++i) {
1453 obj1.arrayGet(i, &obj2);
1454 if (!(funcsA[i] = Function::parse(&obj2))) {
1455 obj1.free();
1456 obj2.free();
1457 goto err1;
1458 }
1459 obj2.free();
1460 }
1461 } else {
1462 nFuncsA = 1;
1463 if (!(funcsA[0] = Function::parse(&obj1))) {
1464 obj1.free();
1465 goto err1;
1466 }
1467 }
1468 obj1.free();
1469
1470 extend0A = extend1A = gFalse;
1471 if (dict->lookup("Extend", &obj1)->isArray() &&
1472 obj1.arrayGetLength() == 2) {
1473 extend0A = obj1.arrayGet(0, &obj2)->getBool();
1474 obj2.free();
1475 extend1A = obj1.arrayGet(1, &obj2)->getBool();
1476 obj2.free();
1477 }
1478 obj1.free();
1479
1480 return new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
1481 funcsA, nFuncsA, extend0A, extend1A);
1482
1483 err1:
1484 return NULL;
1485}
1486
1487void GfxAxialShading::getColor(double t, GfxColor *color) {
1488 int i;
1489
1490 for (i = 0; i < nFuncs; ++i) {
1491 funcs[i]->transform(&t, &color->c[i]);
1492 }
1493}
1494
1495//------------------------------------------------------------------------
1496// GfxRadialShading
1497//------------------------------------------------------------------------
1498
1499GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
1500 double x1A, double y1A, double r1A,
1501 double t0A, double t1A,
1502 Function **funcsA, int nFuncsA,
1503 GBool extend0A, GBool extend1A) {
1504 int i;
1505
1506 x0 = x0A;
1507 y0 = y0A;
1508 r0 = r0A;
1509 x1 = x1A;
1510 y1 = y1A;
1511 r1 = r1A;
1512 t0 = t0A;
1513 t1 = t1A;
1514 nFuncs = nFuncsA;
1515 for (i = 0; i < nFuncs; ++i) {
1516 funcs[i] = funcsA[i];
1517 }
1518 extend0 = extend0A;
1519 extend1 = extend1A;
1520}
1521
1522GfxRadialShading::~GfxRadialShading() {
1523 int i;
1524
1525 for (i = 0; i < nFuncs; ++i) {
1526 delete funcs[i];
1527 }
1528}
1529
1530GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
1531 double x0A, y0A, r0A, x1A, y1A, r1A;
1532 double t0A, t1A;
1533 Function *funcsA[gfxColorMaxComps];
1534 int nFuncsA;
1535 GBool extend0A, extend1A;
1536 Object obj1, obj2;
1537 int i;
1538
1539 x0A = y0A = r0A = x1A = y1A = r1A = 0;
1540 if (dict->lookup("Coords", &obj1)->isArray() &&
1541 obj1.arrayGetLength() == 6) {
1542 x0A = obj1.arrayGet(0, &obj2)->getNum();
1543 obj2.free();
1544 y0A = obj1.arrayGet(1, &obj2)->getNum();
1545 obj2.free();
1546 r0A = obj1.arrayGet(2, &obj2)->getNum();
1547 obj2.free();
1548 x1A = obj1.arrayGet(3, &obj2)->getNum();
1549 obj2.free();
1550 y1A = obj1.arrayGet(4, &obj2)->getNum();
1551 obj2.free();
1552 r1A = obj1.arrayGet(5, &obj2)->getNum();
1553 obj2.free();
1554 } else {
1555 error(-1, "Missing or invalid Coords in shading dictionary");
1556 goto err1;
1557 }
1558 obj1.free();
1559
1560 t0A = 0;
1561 t1A = 1;
1562 if (dict->lookup("Domain", &obj1)->isArray() &&
1563 obj1.arrayGetLength() == 2) {
1564 t0A = obj1.arrayGet(0, &obj2)->getNum();
1565 obj2.free();
1566 t1A = obj1.arrayGet(1, &obj2)->getNum();
1567 obj2.free();
1568 }
1569 obj1.free();
1570
1571 dict->lookup("Function", &obj1);
1572 if (obj1.isArray()) {
1573 nFuncsA = obj1.arrayGetLength();
1574 for (i = 0; i < nFuncsA; ++i) {
1575 obj1.arrayGet(i, &obj2);
1576 if (!(funcsA[i] = Function::parse(&obj2))) {
1577 obj1.free();
1578 obj2.free();
1579 goto err1;
1580 }
1581 obj2.free();
1582 }
1583 } else {
1584 nFuncsA = 1;
1585 if (!(funcsA[0] = Function::parse(&obj1))) {
1586 obj1.free();
1587 goto err1;
1588 }
1589 }
1590 obj1.free();
1591
1592 extend0A = extend1A = gFalse;
1593 if (dict->lookup("Extend", &obj1)->isArray() &&
1594 obj1.arrayGetLength() == 2) {
1595 extend0A = obj1.arrayGet(0, &obj2)->getBool();
1596 obj2.free();
1597 extend1A = obj1.arrayGet(1, &obj2)->getBool();
1598 obj2.free();
1599 }
1600 obj1.free();
1601
1602 return new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
1603 funcsA, nFuncsA, extend0A, extend1A);
1604
1605 err1:
1606 return NULL;
1607}
1608
1609void GfxRadialShading::getColor(double t, GfxColor *color) {
1610 int i;
1611
1612 for (i = 0; i < nFuncs; ++i) {
1613 funcs[i]->transform(&t, &color->c[i]);
1614 }
1615}
1616
1617//------------------------------------------------------------------------
1618// GfxImageColorMap
1619//------------------------------------------------------------------------
1620
1621GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
1622 GfxColorSpace *colorSpaceA) {
1623 GfxIndexedColorSpace *indexedCS;
1624 GfxSeparationColorSpace *sepCS;
1625 int maxPixel, indexHigh;
1626 const Guchar *lookup2;
1627 const Function *sepFunc;
1628 Object obj;
1629 double x[gfxColorMaxComps];
1630 double y[gfxColorMaxComps];
1631 int i, j, k;
1632
1633 ok = gTrue;
1634
1635 // bits per component and color space
1636 bits = bitsA;
1637 maxPixel = (1 << bits) - 1;
1638 colorSpace = colorSpaceA;
1639
1640 // get decode map
1641 if (decode->isNull()) {
1642 nComps = colorSpace->getNComps();
1643 colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel);
1644 } else if (decode->isArray()) {
1645 nComps = decode->arrayGetLength() / 2;
1646 if (nComps != colorSpace->getNComps()) {
1647 goto err1;
1648 }
1649 for (i = 0; i < nComps; ++i) {
1650 decode->arrayGet(2*i, &obj);
1651 if (!obj.isNum()) {
1652 goto err2;
1653 }
1654 decodeLow[i] = obj.getNum();
1655 obj.free();
1656 decode->arrayGet(2*i+1, &obj);
1657 if (!obj.isNum()) {
1658 goto err2;
1659 }
1660 decodeRange[i] = obj.getNum() - decodeLow[i];
1661 obj.free();
1662 }
1663 } else {
1664 goto err1;
1665 }
1666
1667 // Construct a lookup table -- this stores pre-computed decoded
1668 // values for each component, i.e., the result of applying the
1669 // decode mapping to each possible image pixel component value.
1670 //
1671 // Optimization: for Indexed and Separation color spaces (which have
1672 // only one component), we store color values in the lookup table
1673 // rather than component values.
1674 colorSpace2 = NULL;
1675 nComps2 = 0;
1676 if (colorSpace->getMode() == csIndexed) {
1677 // Note that indexHigh may not be the same as maxPixel --
1678 // Distiller will remove unused palette entries, resulting in
1679 // indexHigh < maxPixel.
1680 indexedCS = (GfxIndexedColorSpace *)colorSpace;
1681 colorSpace2 = indexedCS->getBase();
1682 indexHigh = indexedCS->getIndexHigh();
1683 nComps2 = colorSpace2->getNComps();
1684 lookup = (double *)gmalloc((indexHigh + 1) * nComps2 * sizeof(double));
1685 lookup2 = indexedCS->getLookup();
1686 colorSpace2->getDefaultRanges(x, y, indexHigh);
1687 for (i = 0; i <= indexHigh; ++i) {
1688 j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5);
1689 for (k = 0; k < nComps2; ++k) {
1690 lookup[j*nComps2 + k] = x[k] + (lookup2[i*nComps2 + k] / 255.0) * y[k];
1691 }
1692 }
1693 } else if (colorSpace->getMode() == csSeparation) {
1694 sepCS = (GfxSeparationColorSpace *)colorSpace;
1695 colorSpace2 = sepCS->getAlt();
1696 nComps2 = colorSpace2->getNComps();
1697 lookup = (double *)gmalloc((maxPixel + 1) * nComps2 * sizeof(double));
1698 sepFunc = sepCS->getFunc();
1699 for (i = 0; i <= maxPixel; ++i) {
1700 x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
1701 sepFunc->transform(x, y);
1702 for (k = 0; k < nComps2; ++k) {
1703 lookup[i*nComps2 + k] = y[k];
1704 }
1705 }
1706 } else {
1707 lookup = (double *)gmalloc((maxPixel + 1) * nComps * sizeof(double));
1708 for (i = 0; i <= maxPixel; ++i) {
1709 for (k = 0; k < nComps; ++k) {
1710 lookup[i*nComps + k] = decodeLow[k] +
1711 (i * decodeRange[k]) / maxPixel;
1712 }
1713 }
1714 }
1715
1716 return;
1717
1718 err2:
1719 obj.free();
1720 err1:
1721 ok = gFalse;
1722}
1723
1724GfxImageColorMap::~GfxImageColorMap() {
1725 delete colorSpace;
1726 gfree(lookup);
1727}
1728
1729void GfxImageColorMap::getGray(Guchar *x, double *gray) {
1730 GfxColor color;
1731 double *p;
1732 int i;
1733
1734 if (colorSpace2) {
1735 p = &lookup[x[0] * nComps2];
1736 for (i = 0; i < nComps2; ++i) {
1737 color.c[i] = *p++;
1738 }
1739 colorSpace2->getGray(&color, gray);
1740 } else {
1741 for (i = 0; i < nComps; ++i) {
1742 color.c[i] = lookup[x[i] * nComps + i];
1743 }
1744 colorSpace->getGray(&color, gray);
1745 }
1746}
1747
1748void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {
1749 GfxColor color;
1750 double *p;
1751 int i;
1752
1753 if (colorSpace2) {
1754 p = &lookup[x[0] * nComps2];
1755 for (i = 0; i < nComps2; ++i) {
1756 color.c[i] = *p++;
1757 }
1758 colorSpace2->getRGB(&color, rgb);
1759 } else {
1760 for (i = 0; i < nComps; ++i) {
1761 color.c[i] = lookup[x[i] * nComps + i];
1762 }
1763 colorSpace->getRGB(&color, rgb);
1764 }
1765}
1766
1767void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
1768 GfxColor color;
1769 double *p;
1770 int i;
1771
1772 if (colorSpace2) {
1773 p = &lookup[x[0] * nComps2];
1774 for (i = 0; i < nComps2; ++i) {
1775 color.c[i] = *p++;
1776 }
1777 colorSpace2->getCMYK(&color, cmyk);
1778 } else {
1779 for (i = 0; i < nComps; ++i) {
1780 color.c[i] = lookup[x[i] * nComps + i];
1781 }
1782 colorSpace->getCMYK(&color, cmyk);
1783 }
1784}
1785
1786//------------------------------------------------------------------------
1787// GfxSubpath and GfxPath
1788//------------------------------------------------------------------------
1789
1790GfxSubpath::GfxSubpath(double x1, double y1) {
1791 size = 16;
1792 x = (double *)gmalloc(size * sizeof(double));
1793 y = (double *)gmalloc(size * sizeof(double));
1794 curve = (GBool *)gmalloc(size * sizeof(GBool));
1795 n = 1;
1796 x[0] = x1;
1797 y[0] = y1;
1798 curve[0] = gFalse;
1799 closed = gFalse;
1800}
1801
1802GfxSubpath::~GfxSubpath() {
1803 gfree(x);
1804 gfree(y);
1805 gfree(curve);
1806}
1807
1808// Used for copy().
1809GfxSubpath::GfxSubpath(GfxSubpath *subpath) {
1810 size = subpath->size;
1811 n = subpath->n;
1812 x = (double *)gmalloc(size * sizeof(double));
1813 y = (double *)gmalloc(size * sizeof(double));
1814 curve = (GBool *)gmalloc(size * sizeof(GBool));
1815 memcpy(x, subpath->x, n * sizeof(double));
1816 memcpy(y, subpath->y, n * sizeof(double));
1817 memcpy(curve, subpath->curve, n * sizeof(GBool));
1818 closed = subpath->closed;
1819}
1820
1821void GfxSubpath::lineTo(double x1, double y1) {
1822 if (n >= size) {
1823 size += 16;
1824 x = (double *)grealloc(x, size * sizeof(double));
1825 y = (double *)grealloc(y, size * sizeof(double));
1826 curve = (GBool *)grealloc(curve, size * sizeof(GBool));
1827 }
1828 x[n] = x1;
1829 y[n] = y1;
1830 curve[n] = gFalse;
1831 ++n;
1832}
1833
1834void GfxSubpath::curveTo(double x1, double y1, double x2, double y2,
1835 double x3, double y3) {
1836 if (n+3 > size) {
1837 size += 16;
1838 x = (double *)grealloc(x, size * sizeof(double));
1839 y = (double *)grealloc(y, size * sizeof(double));
1840 curve = (GBool *)grealloc(curve, size * sizeof(GBool));
1841 }
1842 x[n] = x1;
1843 y[n] = y1;
1844 x[n+1] = x2;
1845 y[n+1] = y2;
1846 x[n+2] = x3;
1847 y[n+2] = y3;
1848 curve[n] = curve[n+1] = gTrue;
1849 curve[n+2] = gFalse;
1850 n += 3;
1851}
1852
1853void GfxSubpath::close() {
1854 if (x[n-1] != x[0] || y[n-1] != y[0]) {
1855 lineTo(x[0], y[0]);
1856 }
1857 closed = gTrue;
1858}
1859
1860GfxPath::GfxPath() {
1861 justMoved = gFalse;
1862 size = 16;
1863 n = 0;
1864 firstX = firstY = 0;
1865 subpaths = (GfxSubpath **)gmalloc(size * sizeof(GfxSubpath *));
1866}
1867
1868GfxPath::~GfxPath() {
1869 int i;
1870
1871 for (i = 0; i < n; ++i)
1872 delete subpaths[i];
1873 gfree(subpaths);
1874}
1875
1876// Used for copy().
1877GfxPath::GfxPath(GBool justMoved1, double firstX1, double firstY1,
1878 GfxSubpath **subpaths1, int n1, int size1) {
1879 int i;
1880
1881 justMoved = justMoved1;
1882 firstX = firstX1;
1883 firstY = firstY1;
1884 size = size1;
1885 n = n1;
1886 subpaths = (GfxSubpath **)gmalloc(size * sizeof(GfxSubpath *));
1887 for (i = 0; i < n; ++i)
1888 subpaths[i] = subpaths1[i]->copy();
1889}
1890
1891void GfxPath::moveTo(double x, double y) {
1892 justMoved = gTrue;
1893 firstX = x;
1894 firstY = y;
1895}
1896
1897void GfxPath::lineTo(double x, double y) {
1898 if (justMoved) {
1899 if (n >= size) {
1900 size += 16;
1901 subpaths = (GfxSubpath **)
1902 grealloc(subpaths, size * sizeof(GfxSubpath *));
1903 }
1904 subpaths[n] = new GfxSubpath(firstX, firstY);
1905 ++n;
1906 justMoved = gFalse;
1907 }
1908 subpaths[n-1]->lineTo(x, y);
1909}
1910
1911void GfxPath::curveTo(double x1, double y1, double x2, double y2,
1912 double x3, double y3) {
1913 if (justMoved) {
1914 if (n >= size) {
1915 size += 16;
1916 subpaths = (GfxSubpath **)
1917 grealloc(subpaths, size * sizeof(GfxSubpath *));
1918 }
1919 subpaths[n] = new GfxSubpath(firstX, firstY);
1920 ++n;
1921 justMoved = gFalse;
1922 }
1923 subpaths[n-1]->curveTo(x1, y1, x2, y2, x3, y3);
1924}
1925
1926void GfxPath::close() {
1927 // this is necessary to handle the pathological case of
1928 // moveto/closepath/clip, which defines an empty clipping region
1929 if (justMoved) {
1930 if (n >= size) {
1931 size += 16;
1932 subpaths = (GfxSubpath **)
1933 grealloc(subpaths, size * sizeof(GfxSubpath *));
1934 }
1935 subpaths[n] = new GfxSubpath(firstX, firstY);
1936 ++n;
1937 justMoved = gFalse;
1938 }
1939 subpaths[n-1]->close();
1940}
1941
1942//------------------------------------------------------------------------
1943// GfxState
1944//------------------------------------------------------------------------
1945
1946GfxState::GfxState(double dpi, PDFRectangle *pageBox, int rotate,
1947 GBool upsideDown) {
1948 double k;
1949
1950 px1 = pageBox->x1;
1951 py1 = pageBox->y1;
1952 px2 = pageBox->x2;
1953 py2 = pageBox->y2;
1954 k = dpi / 72.0;
1955 if (rotate == 90) {
1956 ctm[0] = 0;
1957 ctm[1] = upsideDown ? k : -k;
1958 ctm[2] = k;
1959 ctm[3] = 0;
1960 ctm[4] = -k * py1;
1961 ctm[5] = k * (upsideDown ? -px1 : px2);
1962 pageWidth = k * (py2 - py1);
1963 pageHeight = k * (px2 - px1);
1964 } else if (rotate == 180) {
1965 ctm[0] = -k;
1966 ctm[1] = 0;
1967 ctm[2] = 0;
1968 ctm[3] = upsideDown ? k : -k;
1969 ctm[4] = k * px2;
1970 ctm[5] = k * (upsideDown ? -py1 : py2);
1971 pageWidth = k * (px2 - px1);
1972 pageHeight = k * (py2 - py1);
1973 } else if (rotate == 270) {
1974 ctm[0] = 0;
1975 ctm[1] = upsideDown ? -k : k;
1976 ctm[2] = -k;
1977 ctm[3] = 0;
1978 ctm[4] = k * py2;
1979 ctm[5] = k * (upsideDown ? px2 : -px1);
1980 pageWidth = k * (py2 - py1);
1981 pageHeight = k * (px2 - px1);
1982 } else {
1983 ctm[0] = k;
1984 ctm[1] = 0;
1985 ctm[2] = 0;
1986 ctm[3] = upsideDown ? -k : k;
1987 ctm[4] = -k * px1;
1988 ctm[5] = k * (upsideDown ? py2 : -py1);
1989 pageWidth = k * (px2 - px1);
1990 pageHeight = k * (py2 - py1);
1991 }
1992
1993 fillColorSpace = new GfxDeviceGrayColorSpace();
1994 strokeColorSpace = new GfxDeviceGrayColorSpace();
1995 fillColor.c[0] = 0;
1996 strokeColor.c[0] = 0;
1997 fillPattern = NULL;
1998 strokePattern = NULL;
1999 fillOpacity = 1;
2000 strokeOpacity = 1;
2001
2002 lineWidth = 1;
2003 lineDash = NULL;
2004 lineDashLength = 0;
2005 lineDashStart = 0;
2006 flatness = 0;
2007 lineJoin = 0;
2008 lineCap = 0;
2009 miterLimit = 10;
2010
2011 font = NULL;
2012 fontSize = 0;
2013 textMat[0] = 1; textMat[1] = 0;
2014 textMat[2] = 0; textMat[3] = 1;
2015 textMat[4] = 0; textMat[5] = 0;
2016 charSpace = 0;
2017 wordSpace = 0;
2018 horizScaling = 1;
2019 leading = 0;
2020 rise = 0;
2021 render = 0;
2022
2023 path = new GfxPath();
2024 curX = curY = 0;
2025 lineX = lineY = 0;
2026
2027 clipXMin = 0;
2028 clipYMin = 0;
2029 clipXMax = pageWidth;
2030 clipYMax = pageHeight;
2031
2032 saved = NULL;
2033}
2034
2035GfxState::~GfxState() {
2036 if (fillColorSpace) {
2037 delete fillColorSpace;
2038 }
2039 if (strokeColorSpace) {
2040 delete strokeColorSpace;
2041 }
2042 if (fillPattern) {
2043 delete fillPattern;
2044 }
2045 if (strokePattern) {
2046 delete strokePattern;
2047 }
2048 gfree(lineDash);
2049 if (path) {
2050 // this gets set to NULL by restore()
2051 delete path;
2052 }
2053 if (saved) {
2054 delete saved;
2055 }
2056}
2057
2058// Used for copy();
2059GfxState::GfxState(const GfxState *state) {
2060 memcpy(this, state, sizeof(GfxState));
2061 if (fillColorSpace) {
2062 fillColorSpace = state->fillColorSpace->copy();
2063 }
2064 if (strokeColorSpace) {
2065 strokeColorSpace = state->strokeColorSpace->copy();
2066 }
2067 if (fillPattern) {
2068 fillPattern = state->fillPattern->copy();
2069 }
2070 if (strokePattern) {
2071 strokePattern = state->strokePattern->copy();
2072 }
2073 if (lineDashLength > 0) {
2074 lineDash = (double *)gmalloc(lineDashLength * sizeof(double));
2075 memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double));
2076 }
2077 saved = NULL;
2078}
2079
2080void GfxState::getUserClipBBox(double *xMin, double *yMin,
2081 double *xMax, double *yMax) const {
2082 double ictm[6];
2083 double xMin1, yMin1, xMax1, yMax1, det, tx, ty;
2084
2085 // invert the CTM
2086 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
2087 ictm[0] = ctm[3] * det;
2088 ictm[1] = -ctm[1] * det;
2089 ictm[2] = -ctm[2] * det;
2090 ictm[3] = ctm[0] * det;
2091 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2092 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2093
2094 // transform all four corners of the clip bbox; find the min and max
2095 // x and y values
2096 xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4];
2097 yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5];
2098 tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4];
2099 ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5];
2100 if (tx < xMin1) {
2101 xMin1 = tx;
2102 } else if (tx > xMax1) {
2103 xMax1 = tx;
2104 }
2105 if (ty < yMin1) {
2106 yMin1 = ty;
2107 } else if (ty > yMax1) {
2108 yMax1 = ty;
2109 }
2110 tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4];
2111 ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5];
2112 if (tx < xMin1) {
2113 xMin1 = tx;
2114 } else if (tx > xMax1) {
2115 xMax1 = tx;
2116 }
2117 if (ty < yMin1) {
2118 yMin1 = ty;
2119 } else if (ty > yMax1) {
2120 yMax1 = ty;
2121 }
2122 tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4];
2123 ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5];
2124 if (tx < xMin1) {
2125 xMin1 = tx;
2126 } else if (tx > xMax1) {
2127 xMax1 = tx;
2128 }
2129 if (ty < yMin1) {
2130 yMin1 = ty;
2131 } else if (ty > yMax1) {
2132 yMax1 = ty;
2133 }
2134
2135 *xMin = xMin1;
2136 *yMin = yMin1;
2137 *xMax = xMax1;
2138 *yMax = yMax1;
2139}
2140
2141double GfxState::transformWidth(double w) {
2142 double x, y;
2143
2144 x = ctm[0] + ctm[2];
2145 y = ctm[1] + ctm[3];
2146 return w * sqrt(0.5 * (x * x + y * y));
2147}
2148
2149double GfxState::getTransformedFontSize() {
2150 double x1, y1, x2, y2;
2151
2152 x1 = textMat[2] * fontSize;
2153 y1 = textMat[3] * fontSize;
2154 x2 = ctm[0] * x1 + ctm[2] * y1;
2155 y2 = ctm[1] * x1 + ctm[3] * y1;
2156 return sqrt(x2 * x2 + y2 * y2);
2157}
2158
2159void GfxState::getFontTransMat(double *m11, double *m12,
2160 double *m21, double *m22) {
2161 *m11 = (textMat[0] * ctm[0] + textMat[1] * ctm[2]) * fontSize;
2162 *m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize;
2163 *m21 = (textMat[2] * ctm[0] + textMat[3] * ctm[2]) * fontSize;
2164 *m22 = (textMat[2] * ctm[1] + textMat[3] * ctm[3]) * fontSize;
2165}
2166
2167void GfxState::setCTM(double a, double b, double c,
2168 double d, double e, double f) {
2169 int i;
2170
2171 ctm[0] = a;
2172 ctm[1] = b;
2173 ctm[2] = c;
2174 ctm[3] = d;
2175 ctm[4] = e;
2176 ctm[5] = f;
2177
2178 // avoid FP exceptions on badly messed up PDF files
2179 for (i = 0; i < 6; ++i) {
2180 if (ctm[i] > 1e10) {
2181 ctm[i] = 1e10;
2182 } else if (ctm[i] < -1e10) {
2183 ctm[i] = -1e10;
2184 }
2185 }
2186}
2187
2188void GfxState::concatCTM(double a, double b, double c,
2189 double d, double e, double f) {
2190 double a1 = ctm[0];
2191 double b1 = ctm[1];
2192 double c1 = ctm[2];
2193 double d1 = ctm[3];
2194 int i;
2195
2196 ctm[0] = a * a1 + b * c1;
2197 ctm[1] = a * b1 + b * d1;
2198 ctm[2] = c * a1 + d * c1;
2199 ctm[3] = c * b1 + d * d1;
2200 ctm[4] = e * a1 + f * c1 + ctm[4];
2201 ctm[5] = e * b1 + f * d1 + ctm[5];
2202
2203 // avoid FP exceptions on badly messed up PDF files
2204 for (i = 0; i < 6; ++i) {
2205 if (ctm[i] > 1e10) {
2206 ctm[i] = 1e10;
2207 } else if (ctm[i] < -1e10) {
2208 ctm[i] = -1e10;
2209 }
2210 }
2211}
2212
2213void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) {
2214 if (fillColorSpace) {
2215 delete fillColorSpace;
2216 }
2217 fillColorSpace = colorSpace;
2218}
2219
2220void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) {
2221 if (strokeColorSpace) {
2222 delete strokeColorSpace;
2223 }
2224 strokeColorSpace = colorSpace;
2225}
2226
2227void GfxState::setFillPattern(GfxPattern *pattern) {
2228 if (fillPattern) {
2229 delete fillPattern;
2230 }
2231 fillPattern = pattern;
2232}
2233
2234void GfxState::setStrokePattern(GfxPattern *pattern) {
2235 if (strokePattern) {
2236 delete strokePattern;
2237 }
2238 strokePattern = pattern;
2239}
2240
2241void GfxState::setLineDash(double *dash, int length, double start) {
2242 if (lineDash)
2243 gfree(lineDash);
2244 lineDash = dash;
2245 lineDashLength = length;
2246 lineDashStart = start;
2247}
2248
2249void GfxState::clearPath() {
2250 delete path;
2251 path = new GfxPath();
2252}
2253
2254void GfxState::clip() {
2255 double xMin, yMin, xMax, yMax, x, y;
2256 GfxSubpath *subpath;
2257 int i, j;
2258
2259 xMin = xMax = yMin = yMax = 0; // make gcc happy
2260 for (i = 0; i < path->getNumSubpaths(); ++i) {
2261 subpath = path->getSubpath(i);
2262 for (j = 0; j < subpath->getNumPoints(); ++j) {
2263 transform(subpath->getX(j), subpath->getY(j), &x, &y);
2264 if (i == 0 && j == 0) {
2265 xMin = xMax = x;
2266 yMin = yMax = y;
2267 } else {
2268 if (x < xMin) {
2269 xMin = x;
2270 } else if (x > xMax) {
2271 xMax = x;
2272 }
2273 if (y < yMin) {
2274 yMin = y;
2275 } else if (y > yMax) {
2276 yMax = y;
2277 }
2278 }
2279 }
2280 }
2281 if (xMin > clipXMin) {
2282 clipXMin = xMin;
2283 }
2284 if (yMin > clipYMin) {
2285 clipYMin = yMin;
2286 }
2287 if (xMax < clipXMax) {
2288 clipXMax = xMax;
2289 }
2290 if (yMax < clipYMax) {
2291 clipYMax = yMax;
2292 }
2293}
2294
2295void GfxState::textShift(double tx, double ty) {
2296 double dx, dy;
2297
2298 textTransformDelta(tx, ty, &dx, &dy);
2299 curX += dx;
2300 curY += dy;
2301}
2302
2303void GfxState::shift(double dx, double dy) {
2304 curX += dx;
2305 curY += dy;
2306}
2307
2308GfxState *GfxState::save() {
2309 GfxState *newState;
2310
2311 newState = copy();
2312 newState->saved = this;
2313 return newState;
2314}
2315
2316GfxState *GfxState::restore() {
2317 GfxState *oldState;
2318
2319 if (saved) {
2320 oldState = saved;
2321
2322 // these attributes aren't saved/restored by the q/Q operators
2323 oldState->path = path;
2324 oldState->curX = curX;
2325 oldState->curY = curY;
2326 oldState->lineX = lineX;
2327 oldState->lineY = lineY;
2328
2329 path = NULL;
2330 saved = NULL;
2331 delete this;
2332
2333 } else {
2334 oldState = this;
2335 }
2336
2337 return oldState;
2338}