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