a6018be456be57a3ace956788b3a9b1c10715ec9
[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
1616   ok = gTrue;
1617
1618   // bits per component and color space
1619   bits = bitsA;
1620   maxPixel = (1 << bits) - 1;
1621   colorSpace = colorSpaceA;
1622
1623   // get decode map
1624   if (decode->isNull()) {
1625     nComps = colorSpace->getNComps();
1626     colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel);
1627   } else if (decode->isArray()) {
1628     nComps = decode->arrayGetLength() / 2;
1629     if (nComps != colorSpace->getNComps()) {
1630       goto err1;
1631     }
1632     for (i = 0; i < nComps; ++i) {
1633       decode->arrayGet(2*i, &obj);
1634       if (!obj.isNum()) {
1635         goto err2;
1636       }
1637       decodeLow[i] = obj.getNum();
1638       obj.free();
1639       decode->arrayGet(2*i+1, &obj);
1640       if (!obj.isNum()) {
1641         goto err2;
1642       }
1643       decodeRange[i] = obj.getNum() - decodeLow[i];
1644       obj.free();
1645     }
1646   } else {
1647     goto err1;
1648   }
1649
1650   // Construct a lookup table -- this stores pre-computed decoded
1651   // values for each component, i.e., the result of applying the
1652   // decode mapping to each possible image pixel component value.
1653   //
1654   // Optimization: for Indexed and Separation color spaces (which have
1655   // only one component), we store color values in the lookup table
1656   // rather than component values.
1657   colorSpace2 = NULL;
1658   nComps2 = 0;
1659   if (colorSpace->getMode() == csIndexed) {
1660     // Note that indexHigh may not be the same as maxPixel --
1661     // Distiller will remove unused palette entries, resulting in
1662     // indexHigh < maxPixel.
1663     indexedCS = (GfxIndexedColorSpace *)colorSpace;
1664     colorSpace2 = indexedCS->getBase();
1665     indexHigh = indexedCS->getIndexHigh();
1666     nComps2 = colorSpace2->getNComps();
1667     lookup = (double *)gmalloc((maxPixel + 1) * nComps2 * sizeof(double));
1668     lookup2 = indexedCS->getLookup();
1669     for (i = 0; i <= indexHigh; ++i) {
1670       j = (int)(decodeLow[0] +(i * decodeRange[0]) / maxPixel + 0.5);
1671       for (k = 0; k < nComps2; ++k) {
1672         lookup[i*nComps2 + k] = lookup2[i*nComps2 + k] / 255.0;
1673       }
1674     }
1675   } else if (colorSpace->getMode() == csSeparation) {
1676     sepCS = (GfxSeparationColorSpace *)colorSpace;
1677     colorSpace2 = sepCS->getAlt();
1678     nComps2 = colorSpace2->getNComps();
1679     lookup = (double *)gmalloc((maxPixel + 1) * nComps2 * sizeof(double));
1680     sepFunc = sepCS->getFunc();
1681     for (i = 0; i <= maxPixel; ++i) {
1682       x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
1683       sepFunc->transform(x, y);
1684       for (k = 0; k < nComps2; ++k) {
1685         lookup[i*nComps2 + k] = y[k];
1686       }
1687     }
1688   } else {
1689     lookup = (double *)gmalloc((maxPixel + 1) * nComps * sizeof(double));
1690     for (i = 0; i <= maxPixel; ++i) {
1691       for (k = 0; k < nComps; ++k) {
1692         lookup[i*nComps + k] = decodeLow[k] +
1693                                  (i * decodeRange[k]) / maxPixel;
1694       }
1695     }
1696   }
1697
1698   return;
1699
1700  err2:
1701   obj.free();
1702  err1:
1703   ok = gFalse;
1704 }
1705
1706 GfxImageColorMap::~GfxImageColorMap() {
1707   delete colorSpace;
1708   gfree(lookup);
1709 }
1710
1711 void GfxImageColorMap::getGray(Guchar *x, double *gray) {
1712   GfxColor color;
1713   double *p;
1714   int i;
1715
1716   if (colorSpace2) {
1717     p = &lookup[x[0] * nComps2];
1718     for (i = 0; i < nComps2; ++i) {
1719       color.c[i] = *p++;
1720     }
1721     colorSpace2->getGray(&color, gray);
1722   } else {
1723     for (i = 0; i < nComps; ++i) {
1724       color.c[i] = lookup[x[i] * nComps + i];
1725     }
1726     colorSpace->getGray(&color, gray);
1727   }
1728 }
1729
1730 void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {
1731
1732   GfxColor color;
1733   double *p;
1734   int i;
1735
1736   if (colorSpace2) {
1737     p = &lookup[x[0] * nComps2];
1738     for (i = 0; i < nComps2; ++i) {
1739       color.c[i] = *p++;
1740     }
1741     colorSpace2->getRGB(&color, rgb);
1742   } else {
1743     for (i = 0; i < nComps; ++i) {
1744       color.c[i] = lookup[x[i] * nComps + i];
1745     }
1746     colorSpace->getRGB(&color, rgb);
1747   }
1748 }
1749
1750 void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
1751   GfxColor color;
1752   double *p;
1753   int i;
1754
1755   if (colorSpace2) {
1756     p = &lookup[x[0] * nComps2];
1757     for (i = 0; i < nComps2; ++i) {
1758       color.c[i] = *p++;
1759     }
1760     colorSpace2->getCMYK(&color, cmyk);
1761   } else {
1762     for (i = 0; i < nComps; ++i) {
1763       color.c[i] = lookup[x[i] * nComps + i];
1764     }
1765     colorSpace->getCMYK(&color, cmyk);
1766   }
1767 }
1768
1769 //------------------------------------------------------------------------
1770 // GfxSubpath and GfxPath
1771 //------------------------------------------------------------------------
1772
1773 GfxSubpath::GfxSubpath(double x1, double y1) {
1774   size = 16;
1775   x = (double *)gmalloc(size * sizeof(double));
1776   y = (double *)gmalloc(size * sizeof(double));
1777   curve = (GBool *)gmalloc(size * sizeof(GBool));
1778   n = 1;
1779   x[0] = x1;
1780   y[0] = y1;
1781   curve[0] = gFalse;
1782   closed = gFalse;
1783 }
1784
1785 GfxSubpath::~GfxSubpath() {
1786   gfree(x);
1787   gfree(y);
1788   gfree(curve);
1789 }
1790
1791 // Used for copy().
1792 GfxSubpath::GfxSubpath(GfxSubpath *subpath) {
1793   size = subpath->size;
1794   n = subpath->n;
1795   x = (double *)gmalloc(size * sizeof(double));
1796   y = (double *)gmalloc(size * sizeof(double));
1797   curve = (GBool *)gmalloc(size * sizeof(GBool));
1798   memcpy(x, subpath->x, n * sizeof(double));
1799   memcpy(y, subpath->y, n * sizeof(double));
1800   memcpy(curve, subpath->curve, n * sizeof(GBool));
1801   closed = subpath->closed;
1802 }
1803
1804 void GfxSubpath::lineTo(double x1, double y1) {
1805   if (n >= size) {
1806     size += 16;
1807     x = (double *)grealloc(x, size * sizeof(double));
1808     y = (double *)grealloc(y, size * sizeof(double));
1809     curve = (GBool *)grealloc(curve, size * sizeof(GBool));
1810   }
1811   x[n] = x1;
1812   y[n] = y1;
1813   curve[n] = gFalse;
1814   ++n;
1815 }
1816
1817 void GfxSubpath::curveTo(double x1, double y1, double x2, double y2,
1818                          double x3, double y3) {
1819   if (n+3 > size) {
1820     size += 16;
1821     x = (double *)grealloc(x, size * sizeof(double));
1822     y = (double *)grealloc(y, size * sizeof(double));
1823     curve = (GBool *)grealloc(curve, size * sizeof(GBool));
1824   }
1825   x[n] = x1;
1826   y[n] = y1;
1827   x[n+1] = x2;
1828   y[n+1] = y2;
1829   x[n+2] = x3;
1830   y[n+2] = y3;
1831   curve[n] = curve[n+1] = gTrue;
1832   curve[n+2] = gFalse;
1833   n += 3;
1834 }
1835
1836 void GfxSubpath::close() {
1837   if (x[n-1] != x[0] || y[n-1] != y[0]) {
1838     lineTo(x[0], y[0]);
1839   }
1840   closed = gTrue;
1841 }
1842
1843 GfxPath::GfxPath() {
1844   justMoved = gFalse;
1845   size = 16;
1846   n = 0;
1847   firstX = firstY = 0;
1848   subpaths = (GfxSubpath **)gmalloc(size * sizeof(GfxSubpath *));
1849 }
1850
1851 GfxPath::~GfxPath() {
1852   int i;
1853
1854   for (i = 0; i < n; ++i)
1855     delete subpaths[i];
1856   gfree(subpaths);
1857 }
1858
1859 // Used for copy().
1860 GfxPath::GfxPath(GBool justMoved1, double firstX1, double firstY1,
1861                  GfxSubpath **subpaths1, int n1, int size1) {
1862   int i;
1863
1864   justMoved = justMoved1;
1865   firstX = firstX1;
1866   firstY = firstY1;
1867   size = size1;
1868   n = n1;
1869   subpaths = (GfxSubpath **)gmalloc(size * sizeof(GfxSubpath *));
1870   for (i = 0; i < n; ++i)
1871     subpaths[i] = subpaths1[i]->copy();
1872 }
1873
1874 void GfxPath::moveTo(double x, double y) {
1875   justMoved = gTrue;
1876   firstX = x;
1877   firstY = y;
1878 }
1879
1880 void GfxPath::lineTo(double x, double y) {
1881   if (justMoved) {
1882     if (n >= size) {
1883       size += 16;
1884       subpaths = (GfxSubpath **)
1885                    grealloc(subpaths, size * sizeof(GfxSubpath *));
1886     }
1887     subpaths[n] = new GfxSubpath(firstX, firstY);
1888     ++n;
1889     justMoved = gFalse;
1890   }
1891   subpaths[n-1]->lineTo(x, y);
1892 }
1893
1894 void GfxPath::curveTo(double x1, double y1, double x2, double y2,
1895              double x3, double y3) {
1896   if (justMoved) {
1897     if (n >= size) {
1898       size += 16;
1899       subpaths = (GfxSubpath **)
1900                    grealloc(subpaths, size * sizeof(GfxSubpath *));
1901     }
1902     subpaths[n] = new GfxSubpath(firstX, firstY);
1903     ++n;
1904     justMoved = gFalse;
1905   }
1906   subpaths[n-1]->curveTo(x1, y1, x2, y2, x3, y3);
1907 }
1908
1909 void GfxPath::close() {
1910   // this is necessary to handle the pathological case of
1911   // moveto/closepath/clip, which defines an empty clipping region
1912   if (justMoved) {
1913     if (n >= size) {
1914       size += 16;
1915       subpaths = (GfxSubpath **)
1916                    grealloc(subpaths, size * sizeof(GfxSubpath *));
1917     }
1918     subpaths[n] = new GfxSubpath(firstX, firstY);
1919     ++n;
1920     justMoved = gFalse;
1921   }
1922   subpaths[n-1]->close();
1923 }
1924
1925 //------------------------------------------------------------------------
1926 // GfxState
1927 //------------------------------------------------------------------------
1928
1929 GfxState::GfxState(double dpi, PDFRectangle *pageBox, int rotate,
1930                    GBool upsideDown) {
1931   double k;
1932
1933   px1 = pageBox->x1;
1934   py1 = pageBox->y1;
1935   px2 = pageBox->x2;
1936   py2 = pageBox->y2;
1937   k = dpi / 72.0;
1938   if (rotate == 90) {
1939     ctm[0] = 0;
1940     ctm[1] = upsideDown ? k : -k;
1941     ctm[2] = k;
1942     ctm[3] = 0;
1943     ctm[4] = -k * py1;
1944     ctm[5] = k * (upsideDown ? -px1 : px2);
1945     pageWidth = k * (py2 - py1);
1946     pageHeight = k * (px2 - px1);
1947   } else if (rotate == 180) {
1948     ctm[0] = -k;
1949     ctm[1] = 0;
1950     ctm[2] = 0;
1951     ctm[3] = upsideDown ? k : -k;
1952     ctm[4] = k * px2;
1953     ctm[5] = k * (upsideDown ? -py1 : py2);
1954     pageWidth = k * (px2 - px1);
1955     pageHeight = k * (py2 - py1);
1956   } else if (rotate == 270) {
1957     ctm[0] = 0;
1958     ctm[1] = upsideDown ? -k : k;
1959     ctm[2] = -k;
1960     ctm[3] = 0;
1961     ctm[4] = k * py2;
1962     ctm[5] = k * (upsideDown ? px2 : -px1);
1963     pageWidth = k * (py2 - py1);
1964     pageHeight = k * (px2 - px1);
1965   } else {
1966     ctm[0] = k;
1967     ctm[1] = 0;
1968     ctm[2] = 0;
1969     ctm[3] = upsideDown ? -k : k;
1970     ctm[4] = -k * px1;
1971     ctm[5] = k * (upsideDown ? py2 : -py1);
1972     pageWidth = k * (px2 - px1);
1973     pageHeight = k * (py2 - py1);
1974   }
1975
1976   fillColorSpace = new GfxDeviceGrayColorSpace();
1977   strokeColorSpace = new GfxDeviceGrayColorSpace();
1978   fillColor.c[0] = 0;
1979   strokeColor.c[0] = 0;
1980   fillPattern = NULL;
1981   strokePattern = NULL;
1982   fillOpacity = 1;
1983   strokeOpacity = 1;
1984
1985   lineWidth = 1;
1986   lineDash = NULL;
1987   lineDashLength = 0;
1988   lineDashStart = 0;
1989   flatness = 0;
1990   lineJoin = 0;
1991   lineCap = 0;
1992   miterLimit = 10;
1993
1994   font = NULL;
1995   fontSize = 0;
1996   textMat[0] = 1; textMat[1] = 0;
1997   textMat[2] = 0; textMat[3] = 1;
1998   textMat[4] = 0; textMat[5] = 0;
1999   charSpace = 0;
2000   wordSpace = 0;
2001   horizScaling = 1;
2002   leading = 0;
2003   rise = 0;
2004   render = 0;
2005
2006   path = new GfxPath();
2007   curX = curY = 0;
2008   lineX = lineY = 0;
2009
2010   clipXMin = 0;
2011   clipYMin = 0;
2012   clipXMax = pageWidth;
2013   clipYMax = pageHeight;
2014
2015   saved = NULL;
2016 }
2017
2018 GfxState::~GfxState() {
2019   if (fillColorSpace) {
2020     delete fillColorSpace;
2021   }
2022   if (strokeColorSpace) {
2023     delete strokeColorSpace;
2024   }
2025   if (fillPattern) {
2026     delete fillPattern;
2027   }
2028   if (strokePattern) {
2029     delete strokePattern;
2030   }
2031   gfree(lineDash);
2032   if (path) {
2033     // this gets set to NULL by restore()
2034     delete path;
2035   }
2036   if (saved) {
2037     delete saved;
2038   }
2039 }
2040
2041 // Used for copy();
2042 GfxState::GfxState(GfxState *state) {
2043   memcpy(this, state, sizeof(GfxState));
2044   if (fillColorSpace) {
2045     fillColorSpace = state->fillColorSpace->copy();
2046   }
2047   if (strokeColorSpace) {
2048     strokeColorSpace = state->strokeColorSpace->copy();
2049   }
2050   if (fillPattern) {
2051     fillPattern = state->fillPattern->copy();
2052   }
2053   if (strokePattern) {
2054     strokePattern = state->strokePattern->copy();
2055   }
2056   if (lineDashLength > 0) {
2057     lineDash = (double *)gmalloc(lineDashLength * sizeof(double));
2058     memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double));
2059   }
2060   saved = NULL;
2061 }
2062
2063 void GfxState::getUserClipBBox(double *xMin, double *yMin,
2064                                double *xMax, double *yMax) {
2065   double ictm[6];
2066   double xMin1, yMin1, xMax1, yMax1, det, tx, ty;
2067
2068   // invert the CTM
2069   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
2070   ictm[0] = ctm[3] * det;
2071   ictm[1] = -ctm[1] * det;
2072   ictm[2] = -ctm[2] * det;
2073   ictm[3] = ctm[0] * det;
2074   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2075   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2076
2077   // transform all four corners of the clip bbox; find the min and max
2078   // x and y values
2079   xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4];
2080   yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5];
2081   tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4];
2082   ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5];
2083   if (tx < xMin1) {
2084     xMin1 = tx;
2085   } else if (tx > xMax1) {
2086     xMax1 = tx;
2087   }
2088   if (ty < yMin1) {
2089     yMin1 = ty;
2090   } else if (ty > yMax1) {
2091     yMax1 = ty;
2092   }
2093   tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4];
2094   ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5];
2095   if (tx < xMin1) {
2096     xMin1 = tx;
2097   } else if (tx > xMax1) {
2098     xMax1 = tx;
2099   }
2100   if (ty < yMin1) {
2101     yMin1 = ty;
2102   } else if (ty > yMax1) {
2103     yMax1 = ty;
2104   }
2105   tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4];
2106   ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5];
2107   if (tx < xMin1) {
2108     xMin1 = tx;
2109   } else if (tx > xMax1) {
2110     xMax1 = tx;
2111   }
2112   if (ty < yMin1) {
2113     yMin1 = ty;
2114   } else if (ty > yMax1) {
2115     yMax1 = ty;
2116   }
2117
2118   *xMin = xMin1;
2119   *yMin = yMin1;
2120   *xMax = xMax1;
2121   *yMax = yMax1;
2122 }
2123
2124 double GfxState::transformWidth(double w) {
2125   double x, y;
2126
2127   x = ctm[0] + ctm[2];
2128   y = ctm[1] + ctm[3];
2129   return w * sqrt(0.5 * (x * x + y * y));
2130 }
2131
2132 double GfxState::getTransformedFontSize() {
2133   double x1, y1, x2, y2;
2134
2135   x1 = textMat[2] * fontSize;
2136   y1 = textMat[3] * fontSize;
2137   x2 = ctm[0] * x1 + ctm[2] * y1;
2138   y2 = ctm[1] * x1 + ctm[3] * y1;
2139   return sqrt(x2 * x2 + y2 * y2);
2140 }
2141
2142 void GfxState::getFontTransMat(double *m11, double *m12,
2143                                double *m21, double *m22) {
2144   *m11 = (textMat[0] * ctm[0] + textMat[1] * ctm[2]) * fontSize;
2145   *m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize;
2146   *m21 = (textMat[2] * ctm[0] + textMat[3] * ctm[2]) * fontSize;
2147   *m22 = (textMat[2] * ctm[1] + textMat[3] * ctm[3]) * fontSize;
2148 }
2149
2150 void GfxState::setCTM(double a, double b, double c,
2151                       double d, double e, double f) {
2152   int i;
2153
2154   ctm[0] = a;
2155   ctm[1] = b;
2156   ctm[2] = c;
2157   ctm[3] = d;
2158   ctm[4] = e;
2159   ctm[5] = f;
2160
2161   // avoid FP exceptions on badly messed up PDF files
2162   for (i = 0; i < 6; ++i) {
2163     if (ctm[i] > 1e10) {
2164       ctm[i] = 1e10;
2165     } else if (ctm[i] < -1e10) {
2166       ctm[i] = -1e10;
2167     }
2168   }
2169 }
2170
2171 void GfxState::concatCTM(double a, double b, double c,
2172                          double d, double e, double f) {
2173   double a1 = ctm[0];
2174   double b1 = ctm[1];
2175   double c1 = ctm[2];
2176   double d1 = ctm[3];
2177   int i;
2178
2179   ctm[0] = a * a1 + b * c1;
2180   ctm[1] = a * b1 + b * d1;
2181   ctm[2] = c * a1 + d * c1;
2182   ctm[3] = c * b1 + d * d1;
2183   ctm[4] = e * a1 + f * c1 + ctm[4];
2184   ctm[5] = e * b1 + f * d1 + ctm[5];
2185
2186   // avoid FP exceptions on badly messed up PDF files
2187   for (i = 0; i < 6; ++i) {
2188     if (ctm[i] > 1e10) {
2189       ctm[i] = 1e10;
2190     } else if (ctm[i] < -1e10) {
2191       ctm[i] = -1e10;
2192     }
2193   }
2194 }
2195
2196 void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) {
2197   if (fillColorSpace) {
2198     delete fillColorSpace;
2199   }
2200   fillColorSpace = colorSpace;
2201 }
2202
2203 void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) {
2204   if (strokeColorSpace) {
2205     delete strokeColorSpace;
2206   }
2207   strokeColorSpace = colorSpace;
2208 }
2209
2210 void GfxState::setFillPattern(GfxPattern *pattern) {
2211   if (fillPattern) {
2212     delete fillPattern;
2213   }
2214   fillPattern = pattern;
2215 }
2216
2217 void GfxState::setStrokePattern(GfxPattern *pattern) {
2218   if (strokePattern) {
2219     delete strokePattern;
2220   }
2221   strokePattern = pattern;
2222 }
2223
2224 void GfxState::setLineDash(double *dash, int length, double start) {
2225   if (lineDash)
2226     gfree(lineDash);
2227   lineDash = dash;
2228   lineDashLength = length;
2229   lineDashStart = start;
2230 }
2231
2232 void GfxState::clearPath() {
2233   delete path;
2234   path = new GfxPath();
2235 }
2236
2237 void GfxState::clip() {
2238   double xMin, yMin, xMax, yMax, x, y;
2239   GfxSubpath *subpath;
2240   int i, j;
2241
2242   xMin = xMax = yMin = yMax = 0; // make gcc happy
2243   for (i = 0; i < path->getNumSubpaths(); ++i) {
2244     subpath = path->getSubpath(i);
2245     for (j = 0; j < subpath->getNumPoints(); ++j) {
2246       transform(subpath->getX(j), subpath->getY(j), &x, &y);
2247       if (i == 0 && j == 0) {
2248         xMin = xMax = x;
2249         yMin = yMax = y;
2250       } else {
2251         if (x < xMin) {
2252           xMin = x;
2253         } else if (x > xMax) {
2254           xMax = x;
2255         }
2256         if (y < yMin) {
2257           yMin = y;
2258         } else if (y > yMax) {
2259           yMax = y;
2260         }
2261       }
2262     }
2263   }
2264   if (xMin > clipXMin) {
2265     clipXMin = xMin;
2266   }
2267   if (yMin > clipYMin) {
2268     clipYMin = yMin;
2269   }
2270   if (xMax < clipXMax) {
2271     clipXMax = xMax;
2272   }
2273   if (yMax < clipYMax) {
2274     clipYMax = yMax;
2275   }
2276 }
2277
2278 void GfxState::textShift(double tx, double ty) {
2279   double dx, dy;
2280
2281   textTransformDelta(tx, ty, &dx, &dy);
2282   curX += dx;
2283   curY += dy;
2284 }
2285
2286 void GfxState::shift(double dx, double dy) {
2287   curX += dx;
2288   curY += dy;
2289 }
2290
2291 GfxState *GfxState::save() {
2292   GfxState *newState;
2293
2294   newState = copy();
2295   newState->saved = this;
2296   return newState;
2297 }
2298
2299 GfxState *GfxState::restore() {
2300   GfxState *oldState;
2301
2302   if (saved) {
2303     oldState = saved;
2304
2305     // these attributes aren't saved/restored by the q/Q operators
2306     oldState->path = path;
2307     oldState->curX = curX;
2308     oldState->curY = curY;
2309     oldState->lineX = lineX;
2310     oldState->lineY = lineY;
2311
2312     path = NULL;
2313     saved = NULL;
2314     delete this;
2315
2316   } else {
2317     oldState = this;
2318   }
2319
2320   return oldState;
2321 }