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