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