Back to index

tetex-bin  3.0
GfxState.cc
Go to the documentation of this file.
00001 //========================================================================
00002 //
00003 // GfxState.cc
00004 //
00005 // Copyright 1996-2003 Glyph & Cog, LLC
00006 //
00007 //========================================================================
00008 
00009 #include <aconf.h>
00010 
00011 #ifdef USE_GCC_PRAGMAS
00012 #pragma implementation
00013 #endif
00014 
00015 #include <stddef.h>
00016 #include <math.h>
00017 #include <string.h> // for memcpy()
00018 #include "gmem.h"
00019 #include "Error.h"
00020 #include "Object.h"
00021 #include "Array.h"
00022 #include "Page.h"
00023 #include "GfxState.h"
00024 
00025 //------------------------------------------------------------------------
00026 
00027 static inline double clip01(double x) {
00028   return (x < 0) ? 0 : ((x > 1) ? 1 : x);
00029 }
00030 
00031 //------------------------------------------------------------------------
00032 
00033 static char *gfxColorSpaceModeNames[] = {
00034   "DeviceGray",
00035   "CalGray",
00036   "DeviceRGB",
00037   "CalRGB",
00038   "DeviceCMYK",
00039   "Lab",
00040   "ICCBased",
00041   "Indexed",
00042   "Separation",
00043   "DeviceN",
00044   "Pattern"
00045 };
00046 
00047 #define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))
00048 
00049 //------------------------------------------------------------------------
00050 // GfxColorSpace
00051 //------------------------------------------------------------------------
00052 
00053 GfxColorSpace::GfxColorSpace() {
00054 }
00055 
00056 GfxColorSpace::~GfxColorSpace() {
00057 }
00058 
00059 GfxColorSpace *GfxColorSpace::parse(Object *csObj) {
00060   GfxColorSpace *cs;
00061   Object obj1;
00062 
00063   cs = NULL;
00064   if (csObj->isName()) {
00065     if (csObj->isName("DeviceGray") || csObj->isName("G")) {
00066       cs = new GfxDeviceGrayColorSpace();
00067     } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {
00068       cs = new GfxDeviceRGBColorSpace();
00069     } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {
00070       cs = new GfxDeviceCMYKColorSpace();
00071     } else if (csObj->isName("Pattern")) {
00072       cs = new GfxPatternColorSpace(NULL);
00073     } else {
00074       error(-1, "Bad color space '%s'", csObj->getName());
00075     }
00076   } else if (csObj->isArray()) {
00077     csObj->arrayGet(0, &obj1);
00078     if (obj1.isName("DeviceGray") || obj1.isName("G")) {
00079       cs = new GfxDeviceGrayColorSpace();
00080     } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
00081       cs = new GfxDeviceRGBColorSpace();
00082     } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
00083       cs = new GfxDeviceCMYKColorSpace();
00084     } else if (obj1.isName("CalGray")) {
00085       cs = GfxCalGrayColorSpace::parse(csObj->getArray());
00086     } else if (obj1.isName("CalRGB")) {
00087       cs = GfxCalRGBColorSpace::parse(csObj->getArray());
00088     } else if (obj1.isName("Lab")) {
00089       cs = GfxLabColorSpace::parse(csObj->getArray());
00090     } else if (obj1.isName("ICCBased")) {
00091       cs = GfxICCBasedColorSpace::parse(csObj->getArray());
00092     } else if (obj1.isName("Indexed") || obj1.isName("I")) {
00093       cs = GfxIndexedColorSpace::parse(csObj->getArray());
00094     } else if (obj1.isName("Separation")) {
00095       cs = GfxSeparationColorSpace::parse(csObj->getArray());
00096     } else if (obj1.isName("DeviceN")) {
00097       cs = GfxDeviceNColorSpace::parse(csObj->getArray());
00098     } else if (obj1.isName("Pattern")) {
00099       cs = GfxPatternColorSpace::parse(csObj->getArray());
00100     } else {
00101       error(-1, "Bad color space");
00102     }
00103     obj1.free();
00104   } else {
00105     error(-1, "Bad color space - expected name or array");
00106   }
00107   return cs;
00108 }
00109 
00110 void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
00111                                  int maxImgPixel) {
00112   int i;
00113 
00114   for (i = 0; i < getNComps(); ++i) {
00115     decodeLow[i] = 0;
00116     decodeRange[i] = 1;
00117   }
00118 }
00119 
00120 int GfxColorSpace::getNumColorSpaceModes() {
00121   return nGfxColorSpaceModes;
00122 }
00123 
00124 char *GfxColorSpace::getColorSpaceModeName(int idx) {
00125   return gfxColorSpaceModeNames[idx];
00126 }
00127 
00128 //------------------------------------------------------------------------
00129 // GfxDeviceGrayColorSpace
00130 //------------------------------------------------------------------------
00131 
00132 GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() {
00133 }
00134 
00135 GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {
00136 }
00137 
00138 GfxColorSpace *GfxDeviceGrayColorSpace::copy() {
00139   return new GfxDeviceGrayColorSpace();
00140 }
00141 
00142 void GfxDeviceGrayColorSpace::getGray(GfxColor *color, double *gray) {
00143   *gray = clip01(color->c[0]);
00144 }
00145 
00146 void GfxDeviceGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
00147   rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
00148 }
00149 
00150 void GfxDeviceGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
00151   cmyk->c = cmyk->m = cmyk->y = 0;
00152   cmyk->k = clip01(1 - color->c[0]);
00153 }
00154 
00155 //------------------------------------------------------------------------
00156 // GfxCalGrayColorSpace
00157 //------------------------------------------------------------------------
00158 
00159 GfxCalGrayColorSpace::GfxCalGrayColorSpace() {
00160   whiteX = whiteY = whiteZ = 1;
00161   blackX = blackY = blackZ = 0;
00162   gamma = 1;
00163 }
00164 
00165 GfxCalGrayColorSpace::~GfxCalGrayColorSpace() {
00166 }
00167 
00168 GfxColorSpace *GfxCalGrayColorSpace::copy() {
00169   GfxCalGrayColorSpace *cs;
00170 
00171   cs = new GfxCalGrayColorSpace();
00172   cs->whiteX = whiteX;
00173   cs->whiteY = whiteY;
00174   cs->whiteZ = whiteZ;
00175   cs->blackX = blackX;
00176   cs->blackY = blackY;
00177   cs->blackZ = blackZ;
00178   cs->gamma = gamma;
00179   return cs;
00180 }
00181 
00182 GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr) {
00183   GfxCalGrayColorSpace *cs;
00184   Object obj1, obj2, obj3;
00185 
00186   arr->get(1, &obj1);
00187   if (!obj1.isDict()) {
00188     error(-1, "Bad CalGray color space");
00189     obj1.free();
00190     return NULL;
00191   }
00192   cs = new GfxCalGrayColorSpace();
00193   if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
00194       obj2.arrayGetLength() == 3) {
00195     obj2.arrayGet(0, &obj3);
00196     cs->whiteX = obj3.getNum();
00197     obj3.free();
00198     obj2.arrayGet(1, &obj3);
00199     cs->whiteY = obj3.getNum();
00200     obj3.free();
00201     obj2.arrayGet(2, &obj3);
00202     cs->whiteZ = obj3.getNum();
00203     obj3.free();
00204   }
00205   obj2.free();
00206   if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
00207       obj2.arrayGetLength() == 3) {
00208     obj2.arrayGet(0, &obj3);
00209     cs->blackX = obj3.getNum();
00210     obj3.free();
00211     obj2.arrayGet(1, &obj3);
00212     cs->blackY = obj3.getNum();
00213     obj3.free();
00214     obj2.arrayGet(2, &obj3);
00215     cs->blackZ = obj3.getNum();
00216     obj3.free();
00217   }
00218   obj2.free();
00219   if (obj1.dictLookup("Gamma", &obj2)->isNum()) {
00220     cs->gamma = obj2.getNum();
00221   }
00222   obj2.free();
00223   obj1.free();
00224   return cs;
00225 }
00226 
00227 void GfxCalGrayColorSpace::getGray(GfxColor *color, double *gray) {
00228   *gray = clip01(color->c[0]);
00229 }
00230 
00231 void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
00232   rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
00233 }
00234 
00235 void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
00236   cmyk->c = cmyk->m = cmyk->y = 0;
00237   cmyk->k = clip01(1 - color->c[0]);
00238 }
00239 
00240 //------------------------------------------------------------------------
00241 // GfxDeviceRGBColorSpace
00242 //------------------------------------------------------------------------
00243 
00244 GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() {
00245 }
00246 
00247 GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() {
00248 }
00249 
00250 GfxColorSpace *GfxDeviceRGBColorSpace::copy() {
00251   return new GfxDeviceRGBColorSpace();
00252 }
00253 
00254 void GfxDeviceRGBColorSpace::getGray(GfxColor *color, double *gray) {
00255   *gray = clip01(0.299 * color->c[0] +
00256                0.587 * color->c[1] +
00257                0.114 * color->c[2]);
00258 }
00259 
00260 void GfxDeviceRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
00261   rgb->r = clip01(color->c[0]);
00262   rgb->g = clip01(color->c[1]);
00263   rgb->b = clip01(color->c[2]);
00264 }
00265 
00266 void GfxDeviceRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
00267   double c, m, y, k;
00268 
00269   c = clip01(1 - color->c[0]);
00270   m = clip01(1 - color->c[1]);
00271   y = clip01(1 - color->c[2]);
00272   k = c;
00273   if (m < k) {
00274     k = m;
00275   }
00276   if (y < k) {
00277     k = y;
00278   }
00279   cmyk->c = c - k;
00280   cmyk->m = m - k;
00281   cmyk->y = y - k;
00282   cmyk->k = k;
00283 }
00284 
00285 //------------------------------------------------------------------------
00286 // GfxCalRGBColorSpace
00287 //------------------------------------------------------------------------
00288 
00289 GfxCalRGBColorSpace::GfxCalRGBColorSpace() {
00290   whiteX = whiteY = whiteZ = 1;
00291   blackX = blackY = blackZ = 0;
00292   gammaR = gammaG = gammaB = 1;
00293   mat[0] = 1; mat[1] = 0; mat[2] = 0;
00294   mat[3] = 0; mat[4] = 1; mat[5] = 0;
00295   mat[6] = 0; mat[7] = 0; mat[8] = 1;
00296 }
00297 
00298 GfxCalRGBColorSpace::~GfxCalRGBColorSpace() {
00299 }
00300 
00301 GfxColorSpace *GfxCalRGBColorSpace::copy() {
00302   GfxCalRGBColorSpace *cs;
00303   int i;
00304 
00305   cs = new GfxCalRGBColorSpace();
00306   cs->whiteX = whiteX;
00307   cs->whiteY = whiteY;
00308   cs->whiteZ = whiteZ;
00309   cs->blackX = blackX;
00310   cs->blackY = blackY;
00311   cs->blackZ = blackZ;
00312   cs->gammaR = gammaR;
00313   cs->gammaG = gammaG;
00314   cs->gammaB = gammaB;
00315   for (i = 0; i < 9; ++i) {
00316     cs->mat[i] = mat[i];
00317   }
00318   return cs;
00319 }
00320 
00321 GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr) {
00322   GfxCalRGBColorSpace *cs;
00323   Object obj1, obj2, obj3;
00324   int i;
00325 
00326   arr->get(1, &obj1);
00327   if (!obj1.isDict()) {
00328     error(-1, "Bad CalRGB color space");
00329     obj1.free();
00330     return NULL;
00331   }
00332   cs = new GfxCalRGBColorSpace();
00333   if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
00334       obj2.arrayGetLength() == 3) {
00335     obj2.arrayGet(0, &obj3);
00336     cs->whiteX = obj3.getNum();
00337     obj3.free();
00338     obj2.arrayGet(1, &obj3);
00339     cs->whiteY = obj3.getNum();
00340     obj3.free();
00341     obj2.arrayGet(2, &obj3);
00342     cs->whiteZ = obj3.getNum();
00343     obj3.free();
00344   }
00345   obj2.free();
00346   if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
00347       obj2.arrayGetLength() == 3) {
00348     obj2.arrayGet(0, &obj3);
00349     cs->blackX = obj3.getNum();
00350     obj3.free();
00351     obj2.arrayGet(1, &obj3);
00352     cs->blackY = obj3.getNum();
00353     obj3.free();
00354     obj2.arrayGet(2, &obj3);
00355     cs->blackZ = obj3.getNum();
00356     obj3.free();
00357   }
00358   obj2.free();
00359   if (obj1.dictLookup("Gamma", &obj2)->isArray() &&
00360       obj2.arrayGetLength() == 3) {
00361     obj2.arrayGet(0, &obj3);
00362     cs->gammaR = obj3.getNum();
00363     obj3.free();
00364     obj2.arrayGet(1, &obj3);
00365     cs->gammaG = obj3.getNum();
00366     obj3.free();
00367     obj2.arrayGet(2, &obj3);
00368     cs->gammaB = obj3.getNum();
00369     obj3.free();
00370   }
00371   obj2.free();
00372   if (obj1.dictLookup("Matrix", &obj2)->isArray() &&
00373       obj2.arrayGetLength() == 9) {
00374     for (i = 0; i < 9; ++i) {
00375       obj2.arrayGet(i, &obj3);
00376       cs->mat[i] = obj3.getNum();
00377       obj3.free();
00378     }
00379   }
00380   obj2.free();
00381   obj1.free();
00382   return cs;
00383 }
00384 
00385 void GfxCalRGBColorSpace::getGray(GfxColor *color, double *gray) {
00386   *gray = clip01(0.299 * color->c[0] +
00387                0.587 * color->c[1] +
00388                0.114 * color->c[2]);
00389 }
00390 
00391 void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
00392   rgb->r = clip01(color->c[0]);
00393   rgb->g = clip01(color->c[1]);
00394   rgb->b = clip01(color->c[2]);
00395 }
00396 
00397 void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
00398   double c, m, y, k;
00399 
00400   c = clip01(1 - color->c[0]);
00401   m = clip01(1 - color->c[1]);
00402   y = clip01(1 - color->c[2]);
00403   k = c;
00404   if (m < k) {
00405     k = m;
00406   }
00407   if (y < k) {
00408     k = y;
00409   }
00410   cmyk->c = c - k;
00411   cmyk->m = m - k;
00412   cmyk->y = y - k;
00413   cmyk->k = k;
00414 }
00415 
00416 //------------------------------------------------------------------------
00417 // GfxDeviceCMYKColorSpace
00418 //------------------------------------------------------------------------
00419 
00420 GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() {
00421 }
00422 
00423 GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() {
00424 }
00425 
00426 GfxColorSpace *GfxDeviceCMYKColorSpace::copy() {
00427   return new GfxDeviceCMYKColorSpace();
00428 }
00429 
00430 void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, double *gray) {
00431   *gray = clip01(1 - color->c[3]
00432                - 0.299 * color->c[0]
00433                - 0.587 * color->c[1]
00434                - 0.114 * color->c[2]);
00435 }
00436 
00437 void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
00438   double c, m, y, aw, ac, am, ay, ar, ag, ab;
00439 
00440   c = clip01(color->c[0] + color->c[3]);
00441   m = clip01(color->c[1] + color->c[3]);
00442   y = clip01(color->c[2] + color->c[3]);
00443   aw = (1-c) * (1-m) * (1-y);
00444   ac = c * (1-m) * (1-y);
00445   am = (1-c) * m * (1-y);
00446   ay = (1-c) * (1-m) * y;
00447   ar = (1-c) * m * y;
00448   ag = c * (1-m) * y;
00449   ab = c * m * (1-y);
00450   rgb->r = clip01(aw + 0.9137*am + 0.9961*ay + 0.9882*ar);
00451   rgb->g = clip01(aw + 0.6196*ac + ay + 0.5176*ag);
00452   rgb->b = clip01(aw + 0.7804*ac + 0.5412*am + 0.0667*ar + 0.2118*ag +
00453                 0.4863*ab);
00454 }
00455 
00456 void GfxDeviceCMYKColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
00457   cmyk->c = clip01(color->c[0]);
00458   cmyk->m = clip01(color->c[1]);
00459   cmyk->y = clip01(color->c[2]);
00460   cmyk->k = clip01(color->c[3]);
00461 }
00462 
00463 //------------------------------------------------------------------------
00464 // GfxLabColorSpace
00465 //------------------------------------------------------------------------
00466 
00467 // This is the inverse of MatrixLMN in Example 4.10 from the PostScript
00468 // Language Reference, Third Edition.
00469 static double xyzrgb[3][3] = {
00470   {  3.240449, -1.537136, -0.498531 },
00471   { -0.969265,  1.876011,  0.041556 },
00472   {  0.055643, -0.204026,  1.057229 }
00473 };
00474 
00475 GfxLabColorSpace::GfxLabColorSpace() {
00476   whiteX = whiteY = whiteZ = 1;
00477   blackX = blackY = blackZ = 0;
00478   aMin = bMin = -100;
00479   aMax = bMax = 100;
00480 }
00481 
00482 GfxLabColorSpace::~GfxLabColorSpace() {
00483 }
00484 
00485 GfxColorSpace *GfxLabColorSpace::copy() {
00486   GfxLabColorSpace *cs;
00487 
00488   cs = new GfxLabColorSpace();
00489   cs->whiteX = whiteX;
00490   cs->whiteY = whiteY;
00491   cs->whiteZ = whiteZ;
00492   cs->blackX = blackX;
00493   cs->blackY = blackY;
00494   cs->blackZ = blackZ;
00495   cs->aMin = aMin;
00496   cs->aMax = aMax;
00497   cs->bMin = bMin;
00498   cs->bMax = bMax;
00499   cs->kr = kr;
00500   cs->kg = kg;
00501   cs->kb = kb;
00502   return cs;
00503 }
00504 
00505 GfxColorSpace *GfxLabColorSpace::parse(Array *arr) {
00506   GfxLabColorSpace *cs;
00507   Object obj1, obj2, obj3;
00508 
00509   arr->get(1, &obj1);
00510   if (!obj1.isDict()) {
00511     error(-1, "Bad Lab color space");
00512     obj1.free();
00513     return NULL;
00514   }
00515   cs = new GfxLabColorSpace();
00516   if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
00517       obj2.arrayGetLength() == 3) {
00518     obj2.arrayGet(0, &obj3);
00519     cs->whiteX = obj3.getNum();
00520     obj3.free();
00521     obj2.arrayGet(1, &obj3);
00522     cs->whiteY = obj3.getNum();
00523     obj3.free();
00524     obj2.arrayGet(2, &obj3);
00525     cs->whiteZ = obj3.getNum();
00526     obj3.free();
00527   }
00528   obj2.free();
00529   if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
00530       obj2.arrayGetLength() == 3) {
00531     obj2.arrayGet(0, &obj3);
00532     cs->blackX = obj3.getNum();
00533     obj3.free();
00534     obj2.arrayGet(1, &obj3);
00535     cs->blackY = obj3.getNum();
00536     obj3.free();
00537     obj2.arrayGet(2, &obj3);
00538     cs->blackZ = obj3.getNum();
00539     obj3.free();
00540   }
00541   obj2.free();
00542   if (obj1.dictLookup("Range", &obj2)->isArray() &&
00543       obj2.arrayGetLength() == 4) {
00544     obj2.arrayGet(0, &obj3);
00545     cs->aMin = obj3.getNum();
00546     obj3.free();
00547     obj2.arrayGet(1, &obj3);
00548     cs->aMax = obj3.getNum();
00549     obj3.free();
00550     obj2.arrayGet(2, &obj3);
00551     cs->bMin = obj3.getNum();
00552     obj3.free();
00553     obj2.arrayGet(3, &obj3);
00554     cs->bMax = obj3.getNum();
00555     obj3.free();
00556   }
00557   obj2.free();
00558   obj1.free();
00559 
00560   cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
00561               xyzrgb[0][1] * cs->whiteY +
00562               xyzrgb[0][2] * cs->whiteZ);
00563   cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
00564               xyzrgb[1][1] * cs->whiteY +
00565               xyzrgb[1][2] * cs->whiteZ);
00566   cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
00567               xyzrgb[2][1] * cs->whiteY +
00568               xyzrgb[2][2] * cs->whiteZ);
00569 
00570   return cs;
00571 }
00572 
00573 void GfxLabColorSpace::getGray(GfxColor *color, double *gray) {
00574   GfxRGB rgb;
00575 
00576   getRGB(color, &rgb);
00577   *gray = clip01(0.299 * rgb.r +
00578                0.587 * rgb.g +
00579                0.114 * rgb.b);
00580 }
00581 
00582 void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
00583   double X, Y, Z;
00584   double t1, t2;
00585   double r, g, b;
00586 
00587   // convert L*a*b* to CIE 1931 XYZ color space
00588   t1 = (color->c[0] + 16) / 116;
00589   t2 = t1 + color->c[1] / 500;
00590   if (t2 >= (6.0 / 29.0)) {
00591     X = t2 * t2 * t2;
00592   } else {
00593     X = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
00594   }
00595   X *= whiteX;
00596   if (t1 >= (6.0 / 29.0)) {
00597     Y = t1 * t1 * t1;
00598   } else {
00599     Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0));
00600   }
00601   Y *= whiteY;
00602   t2 = t1 - color->c[2] / 200;
00603   if (t2 >= (6.0 / 29.0)) {
00604     Z = t2 * t2 * t2;
00605   } else {
00606     Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
00607   }
00608   Z *= whiteZ;
00609 
00610   // convert XYZ to RGB, including gamut mapping and gamma correction
00611   r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
00612   g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
00613   b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
00614   rgb->r = pow(clip01(r * kr), 0.5);
00615   rgb->g = pow(clip01(g * kg), 0.5);
00616   rgb->b = pow(clip01(b * kb), 0.5);
00617 }
00618 
00619 void GfxLabColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
00620   GfxRGB rgb;
00621   double c, m, y, k;
00622 
00623   getRGB(color, &rgb);
00624   c = clip01(1 - rgb.r);
00625   m = clip01(1 - rgb.g);
00626   y = clip01(1 - rgb.b);
00627   k = c;
00628   if (m < k) {
00629     k = m;
00630   }
00631   if (y < k) {
00632     k = y;
00633   }
00634   cmyk->c = c - k;
00635   cmyk->m = m - k;
00636   cmyk->y = y - k;
00637   cmyk->k = k;
00638 }
00639 
00640 void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
00641                                    int maxImgPixel) {
00642   decodeLow[0] = 0;
00643   decodeRange[0] = 100;
00644   decodeLow[1] = aMin;
00645   decodeRange[1] = aMax - aMin;
00646   decodeLow[2] = bMin;
00647   decodeRange[2] = bMax - bMin;
00648 }
00649 
00650 //------------------------------------------------------------------------
00651 // GfxICCBasedColorSpace
00652 //------------------------------------------------------------------------
00653 
00654 GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
00655                                         Ref *iccProfileStreamA) {
00656   nComps = nCompsA;
00657   alt = altA;
00658   iccProfileStream = *iccProfileStreamA;
00659   rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0;
00660   rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1;
00661 }
00662 
00663 GfxICCBasedColorSpace::~GfxICCBasedColorSpace() {
00664   delete alt;
00665 }
00666 
00667 GfxColorSpace *GfxICCBasedColorSpace::copy() {
00668   GfxICCBasedColorSpace *cs;
00669   int i;
00670 
00671   cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream);
00672   for (i = 0; i < 4; ++i) {
00673     cs->rangeMin[i] = rangeMin[i];
00674     cs->rangeMax[i] = rangeMax[i];
00675   }
00676   return cs;
00677 }
00678 
00679 GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
00680   GfxICCBasedColorSpace *cs;
00681   Ref iccProfileStreamA;
00682   int nCompsA;
00683   GfxColorSpace *altA;
00684   Dict *dict;
00685   Object obj1, obj2, obj3;
00686   int i;
00687 
00688   arr->getNF(1, &obj1);
00689   if (obj1.isRef()) {
00690     iccProfileStreamA = obj1.getRef();
00691   } else {
00692     iccProfileStreamA.num = 0;
00693     iccProfileStreamA.gen = 0;
00694   }
00695   obj1.free();
00696   arr->get(1, &obj1);
00697   if (!obj1.isStream()) {
00698     error(-1, "Bad ICCBased color space (stream)");
00699     obj1.free();
00700     return NULL;
00701   }
00702   dict = obj1.streamGetDict();
00703   if (!dict->lookup("N", &obj2)->isInt()) {
00704     error(-1, "Bad ICCBased color space (N)");
00705     obj2.free();
00706     obj1.free();
00707     return NULL;
00708   }
00709   nCompsA = obj2.getInt();
00710   obj2.free();
00711   if (nCompsA > gfxColorMaxComps) {
00712     error(-1, "ICCBased color space with too many (%d > %d) components",
00713          nCompsA, gfxColorMaxComps);
00714     nCompsA = gfxColorMaxComps;
00715   }
00716   if (dict->lookup("Alternate", &obj2)->isNull() ||
00717       !(altA = GfxColorSpace::parse(&obj2))) {
00718     switch (nCompsA) {
00719     case 1:
00720       altA = new GfxDeviceGrayColorSpace();
00721       break;
00722     case 3:
00723       altA = new GfxDeviceRGBColorSpace();
00724       break;
00725     case 4:
00726       altA = new GfxDeviceCMYKColorSpace();
00727       break;
00728     default:
00729       error(-1, "Bad ICCBased color space - invalid N");
00730       obj2.free();
00731       obj1.free();
00732       return NULL;
00733     }
00734   }
00735   obj2.free();
00736   cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA);
00737   if (dict->lookup("Range", &obj2)->isArray() &&
00738       obj2.arrayGetLength() == 2 * nCompsA) {
00739     for (i = 0; i < nCompsA; ++i) {
00740       obj2.arrayGet(2*i, &obj3);
00741       cs->rangeMin[i] = obj3.getNum();
00742       obj3.free();
00743       obj2.arrayGet(2*i+1, &obj3);
00744       cs->rangeMax[i] = obj3.getNum();
00745       obj3.free();
00746     }
00747   }
00748   obj2.free();
00749   obj1.free();
00750   return cs;
00751 }
00752 
00753 void GfxICCBasedColorSpace::getGray(GfxColor *color, double *gray) {
00754   alt->getGray(color, gray);
00755 }
00756 
00757 void GfxICCBasedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
00758   alt->getRGB(color, rgb);
00759 }
00760 
00761 void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
00762   alt->getCMYK(color, cmyk);
00763 }
00764 
00765 void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow,
00766                                         double *decodeRange,
00767                                         int maxImgPixel) {
00768   alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel);
00769 
00770 #if 0
00771   // this is nominally correct, but some PDF files don't set the
00772   // correct ranges in the ICCBased dict
00773   int i;
00774 
00775   for (i = 0; i < nComps; ++i) {
00776     decodeLow[i] = rangeMin[i];
00777     decodeRange[i] = rangeMax[i] - rangeMin[i];
00778   }
00779 #endif
00780 }
00781 
00782 //------------------------------------------------------------------------
00783 // GfxIndexedColorSpace
00784 //------------------------------------------------------------------------
00785 
00786 GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA,
00787                                       int indexHighA) {
00788   base = baseA;
00789   indexHigh = indexHighA;
00790   lookup = (Guchar *)gmalloc((indexHigh + 1) * base->getNComps() *
00791                           sizeof(Guchar));
00792 }
00793 
00794 GfxIndexedColorSpace::~GfxIndexedColorSpace() {
00795   delete base;
00796   gfree(lookup);
00797 }
00798 
00799 GfxColorSpace *GfxIndexedColorSpace::copy() {
00800   GfxIndexedColorSpace *cs;
00801 
00802   cs = new GfxIndexedColorSpace(base->copy(), indexHigh);
00803   memcpy(cs->lookup, lookup,
00804         (indexHigh + 1) * base->getNComps() * sizeof(Guchar));
00805   return cs;
00806 }
00807 
00808 GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
00809   GfxIndexedColorSpace *cs;
00810   GfxColorSpace *baseA;
00811   int indexHighA;
00812   Object obj1;
00813   int x;
00814   char *s;
00815   int n, i, j;
00816 
00817   if (arr->getLength() != 4) {
00818     error(-1, "Bad Indexed color space");
00819     goto err1;
00820   }
00821   arr->get(1, &obj1);
00822   if (!(baseA = GfxColorSpace::parse(&obj1))) {
00823     error(-1, "Bad Indexed color space (base color space)");
00824     goto err2;
00825   }
00826   obj1.free();
00827   if (!arr->get(2, &obj1)->isInt()) {
00828     error(-1, "Bad Indexed color space (hival)");
00829     delete baseA;
00830     goto err2;
00831   }
00832   indexHighA = obj1.getInt();
00833   if (indexHighA < 0 || indexHighA > 255) {
00834     // the PDF spec requires indexHigh to be in [0,255] -- allowing
00835     // values larger than 255 creates a security hole: if nComps *
00836     // indexHigh is greater than 2^31, the loop below may overwrite
00837     // past the end of the array
00838     error(-1, "Bad Indexed color space (invalid indexHigh value)");
00839     delete baseA;
00840     goto err2;
00841   }
00842   obj1.free();
00843   cs = new GfxIndexedColorSpace(baseA, indexHighA);
00844   arr->get(3, &obj1);
00845   n = baseA->getNComps();
00846   if (obj1.isStream()) {
00847     obj1.streamReset();
00848     for (i = 0; i <= indexHighA; ++i) {
00849       for (j = 0; j < n; ++j) {
00850        if ((x = obj1.streamGetChar()) == EOF) {
00851          error(-1, "Bad Indexed color space (lookup table stream too short)");
00852          goto err3;
00853        }
00854        cs->lookup[i*n + j] = (Guchar)x;
00855       }
00856     }
00857     obj1.streamClose();
00858   } else if (obj1.isString()) {
00859     if (obj1.getString()->getLength() < (indexHighA + 1) * n) {
00860       error(-1, "Bad Indexed color space (lookup table string too short)");
00861       goto err3;
00862     }
00863     s = obj1.getString()->getCString();
00864     for (i = 0; i <= indexHighA; ++i) {
00865       for (j = 0; j < n; ++j) {
00866        cs->lookup[i*n + j] = (Guchar)*s++;
00867       }
00868     }
00869   } else {
00870     error(-1, "Bad Indexed color space (lookup table)");
00871     goto err3;
00872   }
00873   obj1.free();
00874   return cs;
00875 
00876  err3:
00877   delete cs;
00878  err2:
00879   obj1.free();
00880  err1:
00881   return NULL;
00882 }
00883 
00884 GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
00885                                           GfxColor *baseColor) {
00886   Guchar *p;
00887   double low[gfxColorMaxComps], range[gfxColorMaxComps];
00888   int n, i;
00889 
00890   n = base->getNComps();
00891   base->getDefaultRanges(low, range, indexHigh);
00892   p = &lookup[(int)(color->c[0] + 0.5) * n];
00893   for (i = 0; i < n; ++i) {
00894     baseColor->c[i] = low[i] + (p[i] / 255.0) * range[i];
00895   }
00896   return baseColor;
00897 }
00898 
00899 void GfxIndexedColorSpace::getGray(GfxColor *color, double *gray) {
00900   GfxColor color2;
00901 
00902   base->getGray(mapColorToBase(color, &color2), gray);
00903 }
00904 
00905 void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
00906   GfxColor color2;
00907 
00908   base->getRGB(mapColorToBase(color, &color2), rgb);
00909 }
00910 
00911 void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
00912   GfxColor color2;
00913 
00914   base->getCMYK(mapColorToBase(color, &color2), cmyk);
00915 }
00916 
00917 void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
00918                                        double *decodeRange,
00919                                        int maxImgPixel) {
00920   decodeLow[0] = 0;
00921   decodeRange[0] = maxImgPixel;
00922 }
00923 
00924 //------------------------------------------------------------------------
00925 // GfxSeparationColorSpace
00926 //------------------------------------------------------------------------
00927 
00928 GfxSeparationColorSpace::GfxSeparationColorSpace(GString *nameA,
00929                                            GfxColorSpace *altA,
00930                                            Function *funcA) {
00931   name = nameA;
00932   alt = altA;
00933   func = funcA;
00934 }
00935 
00936 GfxSeparationColorSpace::~GfxSeparationColorSpace() {
00937   delete name;
00938   delete alt;
00939   delete func;
00940 }
00941 
00942 GfxColorSpace *GfxSeparationColorSpace::copy() {
00943   return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy());
00944 }
00945 
00946 //~ handle the 'All' and 'None' colorants
00947 GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr) {
00948   GfxSeparationColorSpace *cs;
00949   GString *nameA;
00950   GfxColorSpace *altA;
00951   Function *funcA;
00952   Object obj1;
00953 
00954   if (arr->getLength() != 4) {
00955     error(-1, "Bad Separation color space");
00956     goto err1;
00957   }
00958   if (!arr->get(1, &obj1)->isName()) {
00959     error(-1, "Bad Separation color space (name)");
00960     goto err2;
00961   }
00962   nameA = new GString(obj1.getName());
00963   obj1.free();
00964   arr->get(2, &obj1);
00965   if (!(altA = GfxColorSpace::parse(&obj1))) {
00966     error(-1, "Bad Separation color space (alternate color space)");
00967     goto err3;
00968   }
00969   obj1.free();
00970   arr->get(3, &obj1);
00971   if (!(funcA = Function::parse(&obj1))) {
00972     goto err4;
00973   }
00974   obj1.free();
00975   cs = new GfxSeparationColorSpace(nameA, altA, funcA);
00976   return cs;
00977 
00978  err4:
00979   delete altA;
00980  err3:
00981   delete nameA;
00982  err2:
00983   obj1.free();
00984  err1:
00985   return NULL;
00986 }
00987 
00988 void GfxSeparationColorSpace::getGray(GfxColor *color, double *gray) {
00989   GfxColor color2;
00990 
00991   func->transform(color->c, color2.c);
00992   alt->getGray(&color2, gray);
00993 }
00994 
00995 void GfxSeparationColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
00996   GfxColor color2;
00997 
00998   func->transform(color->c, color2.c);
00999   alt->getRGB(&color2, rgb);
01000 }
01001 
01002 void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
01003   GfxColor color2;
01004 
01005   func->transform(color->c, color2.c);
01006   alt->getCMYK(&color2, cmyk);
01007 }
01008 
01009 //------------------------------------------------------------------------
01010 // GfxDeviceNColorSpace
01011 //------------------------------------------------------------------------
01012 
01013 GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
01014                                       GfxColorSpace *altA,
01015                                       Function *funcA) {
01016   nComps = nCompsA;
01017   alt = altA;
01018   func = funcA;
01019 }
01020 
01021 GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
01022   int i;
01023 
01024   for (i = 0; i < nComps; ++i) {
01025     delete names[i];
01026   }
01027   delete alt;
01028   delete func;
01029 }
01030 
01031 GfxColorSpace *GfxDeviceNColorSpace::copy() {
01032   GfxDeviceNColorSpace *cs;
01033   int i;
01034 
01035   cs = new GfxDeviceNColorSpace(nComps, alt->copy(), func->copy());
01036   for (i = 0; i < nComps; ++i) {
01037     cs->names[i] = names[i]->copy();
01038   }
01039   return cs;
01040 }
01041 
01042 //~ handle the 'None' colorant
01043 GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) {
01044   GfxDeviceNColorSpace *cs;
01045   int nCompsA;
01046   GString *namesA[gfxColorMaxComps];
01047   GfxColorSpace *altA;
01048   Function *funcA;
01049   Object obj1, obj2;
01050   int i;
01051 
01052   if (arr->getLength() != 4 && arr->getLength() != 5) {
01053     error(-1, "Bad DeviceN color space");
01054     goto err1;
01055   }
01056   if (!arr->get(1, &obj1)->isArray()) {
01057     error(-1, "Bad DeviceN color space (names)");
01058     goto err2;
01059   }
01060   nCompsA = obj1.arrayGetLength();
01061   if (nCompsA > gfxColorMaxComps) {
01062     error(-1, "DeviceN color space with too many (%d > %d) components",
01063          nCompsA, gfxColorMaxComps);
01064     nCompsA = gfxColorMaxComps;
01065   }
01066   for (i = 0; i < nCompsA; ++i) {
01067     if (!obj1.arrayGet(i, &obj2)->isName()) {
01068       error(-1, "Bad DeviceN color space (names)");
01069       obj2.free();
01070       goto err2;
01071     }
01072     namesA[i] = new GString(obj2.getName());
01073     obj2.free();
01074   }
01075   obj1.free();
01076   arr->get(2, &obj1);
01077   if (!(altA = GfxColorSpace::parse(&obj1))) {
01078     error(-1, "Bad DeviceN color space (alternate color space)");
01079     goto err3;
01080   }
01081   obj1.free();
01082   arr->get(3, &obj1);
01083   if (!(funcA = Function::parse(&obj1))) {
01084     goto err4;
01085   }
01086   obj1.free();
01087   cs = new GfxDeviceNColorSpace(nCompsA, altA, funcA);
01088   for (i = 0; i < nCompsA; ++i) {
01089     cs->names[i] = namesA[i];
01090   }
01091   return cs;
01092 
01093  err4:
01094   delete altA;
01095  err3:
01096   for (i = 0; i < nCompsA; ++i) {
01097     delete namesA[i];
01098   }
01099  err2:
01100   obj1.free();
01101  err1:
01102   return NULL;
01103 }
01104 
01105 void GfxDeviceNColorSpace::getGray(GfxColor *color, double *gray) {
01106   GfxColor color2;
01107 
01108   func->transform(color->c, color2.c);
01109   alt->getGray(&color2, gray);
01110 }
01111 
01112 void GfxDeviceNColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
01113   GfxColor color2;
01114 
01115   func->transform(color->c, color2.c);
01116   alt->getRGB(&color2, rgb);
01117 }
01118 
01119 void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
01120   GfxColor color2;
01121 
01122   func->transform(color->c, color2.c);
01123   alt->getCMYK(&color2, cmyk);
01124 }
01125 
01126 //------------------------------------------------------------------------
01127 // GfxPatternColorSpace
01128 //------------------------------------------------------------------------
01129 
01130 GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) {
01131   under = underA;
01132 }
01133 
01134 GfxPatternColorSpace::~GfxPatternColorSpace() {
01135   if (under) {
01136     delete under;
01137   }
01138 }
01139 
01140 GfxColorSpace *GfxPatternColorSpace::copy() {
01141   return new GfxPatternColorSpace(under ? under->copy() :
01142                                       (GfxColorSpace *)NULL);
01143 }
01144 
01145 GfxColorSpace *GfxPatternColorSpace::parse(Array *arr) {
01146   GfxPatternColorSpace *cs;
01147   GfxColorSpace *underA;
01148   Object obj1;
01149 
01150   if (arr->getLength() != 1 && arr->getLength() != 2) {
01151     error(-1, "Bad Pattern color space");
01152     return NULL;
01153   }
01154   underA = NULL;
01155   if (arr->getLength() == 2) {
01156     arr->get(1, &obj1);
01157     if (!(underA = GfxColorSpace::parse(&obj1))) {
01158       error(-1, "Bad Pattern color space (underlying color space)");
01159       obj1.free();
01160       return NULL;
01161     }
01162     obj1.free();
01163   }
01164   cs = new GfxPatternColorSpace(underA);
01165   return cs;
01166 }
01167 
01168 void GfxPatternColorSpace::getGray(GfxColor *color, double *gray) {
01169   *gray = 0;
01170 }
01171 
01172 void GfxPatternColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
01173   rgb->r = rgb->g = rgb->b = 0;
01174 }
01175 
01176 void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
01177   cmyk->c = cmyk->m = cmyk->y = 0;
01178   cmyk->k = 1;
01179 }
01180 
01181 //------------------------------------------------------------------------
01182 // Pattern
01183 //------------------------------------------------------------------------
01184 
01185 GfxPattern::GfxPattern(int typeA) {
01186   type = typeA;
01187 }
01188 
01189 GfxPattern::~GfxPattern() {
01190 }
01191 
01192 GfxPattern *GfxPattern::parse(Object *obj) {
01193   GfxPattern *pattern;
01194   Object obj1;
01195 
01196   if (obj->isDict()) {
01197     obj->dictLookup("PatternType", &obj1);
01198   } else if (obj->isStream()) {
01199     obj->streamGetDict()->lookup("PatternType", &obj1);
01200   } else {
01201     return NULL;
01202   }
01203   pattern = NULL;
01204   if (obj1.isInt() && obj1.getInt() == 1) {
01205     pattern = GfxTilingPattern::parse(obj);
01206   } else if (obj1.isInt() && obj1.getInt() == 2) {
01207     pattern = GfxShadingPattern::parse(obj);
01208   }
01209   obj1.free();
01210   return pattern;
01211 }
01212 
01213 //------------------------------------------------------------------------
01214 // GfxTilingPattern
01215 //------------------------------------------------------------------------
01216 
01217 GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
01218   GfxTilingPattern *pat;
01219   Dict *dict;
01220   int paintTypeA, tilingTypeA;
01221   double bboxA[4], matrixA[6];
01222   double xStepA, yStepA;
01223   Object resDictA;
01224   Object obj1, obj2;
01225   int i;
01226 
01227   if (!patObj->isStream()) {
01228     return NULL;
01229   }
01230   dict = patObj->streamGetDict();
01231 
01232   if (dict->lookup("PaintType", &obj1)->isInt()) {
01233     paintTypeA = obj1.getInt();
01234   } else {
01235     paintTypeA = 1;
01236     error(-1, "Invalid or missing PaintType in pattern");
01237   }
01238   obj1.free();
01239   if (dict->lookup("TilingType", &obj1)->isInt()) {
01240     tilingTypeA = obj1.getInt();
01241   } else {
01242     tilingTypeA = 1;
01243     error(-1, "Invalid or missing TilingType in pattern");
01244   }
01245   obj1.free();
01246   bboxA[0] = bboxA[1] = 0;
01247   bboxA[2] = bboxA[3] = 1;
01248   if (dict->lookup("BBox", &obj1)->isArray() &&
01249       obj1.arrayGetLength() == 4) {
01250     for (i = 0; i < 4; ++i) {
01251       if (obj1.arrayGet(i, &obj2)->isNum()) {
01252        bboxA[i] = obj2.getNum();
01253       }
01254       obj2.free();
01255     }
01256   } else {
01257     error(-1, "Invalid or missing BBox in pattern");
01258   }
01259   obj1.free();
01260   if (dict->lookup("XStep", &obj1)->isNum()) {
01261     xStepA = obj1.getNum();
01262   } else {
01263     xStepA = 1;
01264     error(-1, "Invalid or missing XStep in pattern");
01265   }
01266   obj1.free();
01267   if (dict->lookup("YStep", &obj1)->isNum()) {
01268     yStepA = obj1.getNum();
01269   } else {
01270     yStepA = 1;
01271     error(-1, "Invalid or missing YStep in pattern");
01272   }
01273   obj1.free();
01274   if (!dict->lookup("Resources", &resDictA)->isDict()) {
01275     resDictA.free();
01276     resDictA.initNull();
01277     error(-1, "Invalid or missing Resources in pattern");
01278   }
01279   matrixA[0] = 1; matrixA[1] = 0;
01280   matrixA[2] = 0; matrixA[3] = 1;
01281   matrixA[4] = 0; matrixA[5] = 0;
01282   if (dict->lookup("Matrix", &obj1)->isArray() &&
01283       obj1.arrayGetLength() == 6) {
01284     for (i = 0; i < 6; ++i) {
01285       if (obj1.arrayGet(i, &obj2)->isNum()) {
01286        matrixA[i] = obj2.getNum();
01287       }
01288       obj2.free();
01289     }
01290   }
01291   obj1.free();
01292 
01293   pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA,
01294                           &resDictA, matrixA, patObj);
01295   resDictA.free();
01296   return pat;
01297 }
01298 
01299 GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
01300                                double *bboxA, double xStepA, double yStepA,
01301                                Object *resDictA, double *matrixA,
01302                                Object *contentStreamA):
01303   GfxPattern(1)
01304 {
01305   int i;
01306 
01307   paintType = paintTypeA;
01308   tilingType = tilingTypeA;
01309   for (i = 0; i < 4; ++i) {
01310     bbox[i] = bboxA[i];
01311   }
01312   xStep = xStepA;
01313   yStep = yStepA;
01314   resDictA->copy(&resDict);
01315   for (i = 0; i < 6; ++i) {
01316     matrix[i] = matrixA[i];
01317   }
01318   contentStreamA->copy(&contentStream);
01319 }
01320 
01321 GfxTilingPattern::~GfxTilingPattern() {
01322   resDict.free();
01323   contentStream.free();
01324 }
01325 
01326 GfxPattern *GfxTilingPattern::copy() {
01327   return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
01328                            &resDict, matrix, &contentStream);
01329 }
01330 
01331 //------------------------------------------------------------------------
01332 // GfxShadingPattern
01333 //------------------------------------------------------------------------
01334 
01335 GfxShadingPattern *GfxShadingPattern::parse(Object *patObj) {
01336   Dict *dict;
01337   GfxShading *shadingA;
01338   double matrixA[6];
01339   Object obj1, obj2;
01340   int i;
01341 
01342   if (!patObj->isDict()) {
01343     return NULL;
01344   }
01345   dict = patObj->getDict();
01346 
01347   dict->lookup("Shading", &obj1);
01348   shadingA = GfxShading::parse(&obj1);
01349   obj1.free();
01350   if (!shadingA) {
01351     return NULL;
01352   }
01353 
01354   matrixA[0] = 1; matrixA[1] = 0;
01355   matrixA[2] = 0; matrixA[3] = 1;
01356   matrixA[4] = 0; matrixA[5] = 0;
01357   if (dict->lookup("Matrix", &obj1)->isArray() &&
01358       obj1.arrayGetLength() == 6) {
01359     for (i = 0; i < 6; ++i) {
01360       if (obj1.arrayGet(i, &obj2)->isNum()) {
01361        matrixA[i] = obj2.getNum();
01362       }
01363       obj2.free();
01364     }
01365   }
01366   obj1.free();
01367 
01368   return new GfxShadingPattern(shadingA, matrixA);
01369 }
01370 
01371 GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, double *matrixA):
01372   GfxPattern(2)
01373 {
01374   int i;
01375 
01376   shading = shadingA;
01377   for (i = 0; i < 6; ++i) {
01378     matrix[i] = matrixA[i];
01379   }
01380 }
01381 
01382 GfxShadingPattern::~GfxShadingPattern() {
01383   delete shading;
01384 }
01385 
01386 GfxPattern *GfxShadingPattern::copy() {
01387   return new GfxShadingPattern(shading->copy(), matrix);
01388 }
01389 
01390 //------------------------------------------------------------------------
01391 // GfxShading
01392 //------------------------------------------------------------------------
01393 
01394 GfxShading::GfxShading(int typeA) {
01395   type = typeA;
01396   colorSpace = NULL;
01397 }
01398 
01399 GfxShading::GfxShading(GfxShading *shading) {
01400   int i;
01401 
01402   type = shading->type;
01403   colorSpace = shading->colorSpace->copy();
01404   for (i = 0; i < gfxColorMaxComps; ++i) {
01405     background.c[i] = shading->background.c[i];
01406   }
01407   hasBackground = shading->hasBackground;
01408   xMin = shading->xMin;
01409   yMin = shading->yMin;
01410   xMax = shading->xMax;
01411   yMax = shading->yMax;
01412   hasBBox = shading->hasBBox;
01413 }
01414 
01415 GfxShading::~GfxShading() {
01416   if (colorSpace) {
01417     delete colorSpace;
01418   }
01419 }
01420 
01421 GfxShading *GfxShading::parse(Object *obj) {
01422   GfxShading *shading;
01423   Dict *dict;
01424   int typeA;
01425   Object obj1;
01426 
01427   if (obj->isDict()) {
01428     dict = obj->getDict();
01429   } else if (obj->isStream()) {
01430     dict = obj->streamGetDict();
01431   } else {
01432     return NULL;
01433   }
01434 
01435   if (!dict->lookup("ShadingType", &obj1)->isInt()) {
01436     error(-1, "Invalid ShadingType in shading dictionary");
01437     obj1.free();
01438     return NULL;
01439   }
01440   typeA = obj1.getInt();
01441   obj1.free();
01442 
01443   switch (typeA) {
01444   case 1:
01445     shading = GfxFunctionShading::parse(dict);
01446     break;
01447   case 2:
01448     shading = GfxAxialShading::parse(dict);
01449     break;
01450   case 3:
01451     shading = GfxRadialShading::parse(dict);
01452     break;
01453   default:
01454     error(-1, "Unimplemented shading type %d", typeA);
01455     goto err1;
01456   }
01457 
01458   return shading;
01459 
01460  err1:
01461   return NULL;
01462 }
01463 
01464 GBool GfxShading::init(Dict *dict) {
01465   Object obj1, obj2;
01466   int i;
01467 
01468   dict->lookup("ColorSpace", &obj1);
01469   if (!(colorSpace = GfxColorSpace::parse(&obj1))) {
01470     error(-1, "Bad color space in shading dictionary");
01471     obj1.free();
01472     return gFalse;
01473   }
01474   obj1.free();
01475 
01476   for (i = 0; i < gfxColorMaxComps; ++i) {
01477     background.c[i] = 0;
01478   }
01479   hasBackground = gFalse;
01480   if (dict->lookup("Background", &obj1)->isArray()) {
01481     if (obj1.arrayGetLength() == colorSpace->getNComps()) {
01482       hasBackground = gTrue;
01483       for (i = 0; i < colorSpace->getNComps(); ++i) {
01484        background.c[i] = obj1.arrayGet(i, &obj2)->getNum();
01485        obj2.free();
01486       }
01487     } else {
01488       error(-1, "Bad Background in shading dictionary");
01489     }
01490   }
01491   obj1.free();
01492 
01493   xMin = yMin = xMax = yMax = 0;
01494   hasBBox = gFalse;
01495   if (dict->lookup("BBox", &obj1)->isArray()) {
01496     if (obj1.arrayGetLength() == 4) {
01497       hasBBox = gTrue;
01498       xMin = obj1.arrayGet(0, &obj2)->getNum();
01499       obj2.free();
01500       yMin = obj1.arrayGet(1, &obj2)->getNum();
01501       obj2.free();
01502       xMax = obj1.arrayGet(2, &obj2)->getNum();
01503       obj2.free();
01504       yMax = obj1.arrayGet(3, &obj2)->getNum();
01505       obj2.free();
01506     } else {
01507       error(-1, "Bad BBox in shading dictionary");
01508     }
01509   }
01510   obj1.free();
01511 
01512   return gTrue;
01513 }
01514 
01515 //------------------------------------------------------------------------
01516 // GfxFunctionShading
01517 //------------------------------------------------------------------------
01518 
01519 GfxFunctionShading::GfxFunctionShading(double x0A, double y0A,
01520                                    double x1A, double y1A,
01521                                    double *matrixA,
01522                                    Function **funcsA, int nFuncsA):
01523   GfxShading(1)
01524 {
01525   int i;
01526 
01527   x0 = x0A;
01528   y0 = y0A;
01529   x1 = x1A;
01530   y1 = y1A;
01531   for (i = 0; i < 6; ++i) {
01532     matrix[i] = matrixA[i];
01533   }
01534   nFuncs = nFuncsA;
01535   for (i = 0; i < nFuncs; ++i) {
01536     funcs[i] = funcsA[i];
01537   }
01538 }
01539 
01540 GfxFunctionShading::GfxFunctionShading(GfxFunctionShading *shading):
01541   GfxShading(shading)
01542 {
01543   int i;
01544 
01545   x0 = shading->x0;
01546   y0 = shading->y0;
01547   x1 = shading->x1;
01548   y1 = shading->y1;
01549   for (i = 0; i < 6; ++i) {
01550     matrix[i] = shading->matrix[i];
01551   }
01552   nFuncs = shading->nFuncs;
01553   for (i = 0; i < nFuncs; ++i) {
01554     funcs[i] = shading->funcs[i]->copy();
01555   }
01556 }
01557 
01558 GfxFunctionShading::~GfxFunctionShading() {
01559   int i;
01560 
01561   for (i = 0; i < nFuncs; ++i) {
01562     delete funcs[i];
01563   }
01564 }
01565 
01566 GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) {
01567   GfxFunctionShading *shading;
01568   double x0A, y0A, x1A, y1A;
01569   double matrixA[6];
01570   Function *funcsA[gfxColorMaxComps];
01571   int nFuncsA;
01572   Object obj1, obj2;
01573   int i;
01574 
01575   x0A = y0A = 0;
01576   x1A = y1A = 1;
01577   if (dict->lookup("Domain", &obj1)->isArray() &&
01578       obj1.arrayGetLength() == 4) {
01579     x0A = obj1.arrayGet(0, &obj2)->getNum();
01580     obj2.free();
01581     y0A = obj1.arrayGet(1, &obj2)->getNum();
01582     obj2.free();
01583     x1A = obj1.arrayGet(2, &obj2)->getNum();
01584     obj2.free();
01585     y1A = obj1.arrayGet(3, &obj2)->getNum();
01586     obj2.free();
01587   }
01588   obj1.free();
01589 
01590   matrixA[0] = 1; matrixA[1] = 0;
01591   matrixA[2] = 0; matrixA[3] = 1;
01592   matrixA[4] = 0; matrixA[5] = 0;
01593   if (dict->lookup("Matrix", &obj1)->isArray() &&
01594       obj1.arrayGetLength() == 6) {
01595     matrixA[0] = obj1.arrayGet(0, &obj2)->getNum();
01596     obj2.free();
01597     matrixA[1] = obj1.arrayGet(1, &obj2)->getNum();
01598     obj2.free();
01599     matrixA[2] = obj1.arrayGet(2, &obj2)->getNum();
01600     obj2.free();
01601     matrixA[3] = obj1.arrayGet(3, &obj2)->getNum();
01602     obj2.free();
01603     matrixA[4] = obj1.arrayGet(4, &obj2)->getNum();
01604     obj2.free();
01605     matrixA[5] = obj1.arrayGet(5, &obj2)->getNum();
01606     obj2.free();
01607   }
01608   obj1.free();
01609 
01610   dict->lookup("Function", &obj1);
01611   if (obj1.isArray()) {
01612     nFuncsA = obj1.arrayGetLength();
01613     if (nFuncsA > gfxColorMaxComps) {
01614       error(-1, "Invalid Function array in shading dictionary");
01615       goto err1;
01616     }
01617     for (i = 0; i < nFuncsA; ++i) {
01618       obj1.arrayGet(i, &obj2);
01619       if (!(funcsA[i] = Function::parse(&obj2))) {
01620        goto err2;
01621       }
01622       obj2.free();
01623     }
01624   } else {
01625     nFuncsA = 1;
01626     if (!(funcsA[0] = Function::parse(&obj1))) {
01627       goto err1;
01628     }
01629   }
01630   obj1.free();
01631 
01632   shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,
01633                                funcsA, nFuncsA);
01634   if (!shading->init(dict)) {
01635     delete shading;
01636     return NULL;
01637   }
01638   return shading;
01639 
01640  err2:
01641   obj2.free();
01642  err1:
01643   obj1.free();
01644   return NULL;
01645 }
01646 
01647 GfxShading *GfxFunctionShading::copy() {
01648   return new GfxFunctionShading(this);
01649 }
01650 
01651 void GfxFunctionShading::getColor(double x, double y, GfxColor *color) {
01652   double in[2];
01653   int i;
01654 
01655   in[0] = x;
01656   in[1] = y;
01657   for (i = 0; i < nFuncs; ++i) {
01658     funcs[i]->transform(in, &color->c[i]);
01659   }
01660 }
01661 
01662 //------------------------------------------------------------------------
01663 // GfxAxialShading
01664 //------------------------------------------------------------------------
01665 
01666 GfxAxialShading::GfxAxialShading(double x0A, double y0A,
01667                              double x1A, double y1A,
01668                              double t0A, double t1A,
01669                              Function **funcsA, int nFuncsA,
01670                              GBool extend0A, GBool extend1A):
01671   GfxShading(2)
01672 {
01673   int i;
01674 
01675   x0 = x0A;
01676   y0 = y0A;
01677   x1 = x1A;
01678   y1 = y1A;
01679   t0 = t0A;
01680   t1 = t1A;
01681   nFuncs = nFuncsA;
01682   for (i = 0; i < nFuncs; ++i) {
01683     funcs[i] = funcsA[i];
01684   }
01685   extend0 = extend0A;
01686   extend1 = extend1A;
01687 }
01688 
01689 GfxAxialShading::GfxAxialShading(GfxAxialShading *shading):
01690   GfxShading(shading)
01691 {
01692   int i;
01693 
01694   x0 = shading->x0;
01695   y0 = shading->y0;
01696   x1 = shading->x1;
01697   y1 = shading->y1;
01698   t0 = shading->t0;
01699   y1 = shading->t1;
01700   nFuncs = shading->nFuncs;
01701   for (i = 0; i < nFuncs; ++i) {
01702     funcs[i] = shading->funcs[i]->copy();
01703   }
01704   extend0 = shading->extend0;
01705   extend1 = shading->extend1;
01706 }
01707 
01708 GfxAxialShading::~GfxAxialShading() {
01709   int i;
01710 
01711   for (i = 0; i < nFuncs; ++i) {
01712     delete funcs[i];
01713   }
01714 }
01715 
01716 GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
01717   GfxAxialShading *shading;
01718   double x0A, y0A, x1A, y1A;
01719   double t0A, t1A;
01720   Function *funcsA[gfxColorMaxComps];
01721   int nFuncsA;
01722   GBool extend0A, extend1A;
01723   Object obj1, obj2;
01724   int i;
01725 
01726   x0A = y0A = x1A = y1A = 0;
01727   if (dict->lookup("Coords", &obj1)->isArray() &&
01728       obj1.arrayGetLength() == 4) {
01729     x0A = obj1.arrayGet(0, &obj2)->getNum();
01730     obj2.free();
01731     y0A = obj1.arrayGet(1, &obj2)->getNum();
01732     obj2.free();
01733     x1A = obj1.arrayGet(2, &obj2)->getNum();
01734     obj2.free();
01735     y1A = obj1.arrayGet(3, &obj2)->getNum();
01736     obj2.free();
01737   } else {
01738     error(-1, "Missing or invalid Coords in shading dictionary");
01739     goto err1;
01740   }
01741   obj1.free();
01742 
01743   t0A = 0;
01744   t1A = 1;
01745   if (dict->lookup("Domain", &obj1)->isArray() &&
01746       obj1.arrayGetLength() == 2) {
01747     t0A = obj1.arrayGet(0, &obj2)->getNum();
01748     obj2.free();
01749     t1A = obj1.arrayGet(1, &obj2)->getNum();
01750     obj2.free();
01751   }
01752   obj1.free();
01753 
01754   dict->lookup("Function", &obj1);
01755   if (obj1.isArray()) {
01756     nFuncsA = obj1.arrayGetLength();
01757     if (nFuncsA > gfxColorMaxComps) {
01758       error(-1, "Invalid Function array in shading dictionary");
01759       goto err1;
01760     }
01761     for (i = 0; i < nFuncsA; ++i) {
01762       obj1.arrayGet(i, &obj2);
01763       if (!(funcsA[i] = Function::parse(&obj2))) {
01764        obj1.free();
01765        obj2.free();
01766        goto err1;
01767       }
01768       obj2.free();
01769     }
01770   } else {
01771     nFuncsA = 1;
01772     if (!(funcsA[0] = Function::parse(&obj1))) {
01773       obj1.free();
01774       goto err1;
01775     }
01776   }
01777   obj1.free();
01778 
01779   extend0A = extend1A = gFalse;
01780   if (dict->lookup("Extend", &obj1)->isArray() &&
01781       obj1.arrayGetLength() == 2) {
01782     extend0A = obj1.arrayGet(0, &obj2)->getBool();
01783     obj2.free();
01784     extend1A = obj1.arrayGet(1, &obj2)->getBool();
01785     obj2.free();
01786   }
01787   obj1.free();
01788 
01789   shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
01790                             funcsA, nFuncsA, extend0A, extend1A);
01791   if (!shading->init(dict)) {
01792     delete shading;
01793     return NULL;
01794   }
01795   return shading;
01796 
01797  err1:
01798   return NULL;
01799 }
01800 
01801 GfxShading *GfxAxialShading::copy() {
01802   return new GfxAxialShading(this);
01803 }
01804 
01805 void GfxAxialShading::getColor(double t, GfxColor *color) {
01806   int i;
01807 
01808   // NB: there can be one function with n outputs or n functions with
01809   // one output each (where n = number of color components)
01810   for (i = 0; i < nFuncs; ++i) {
01811     funcs[i]->transform(&t, &color->c[i]);
01812   }
01813 }
01814 
01815 //------------------------------------------------------------------------
01816 // GfxRadialShading
01817 //------------------------------------------------------------------------
01818 
01819 GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
01820                                double x1A, double y1A, double r1A,
01821                                double t0A, double t1A,
01822                                Function **funcsA, int nFuncsA,
01823                                GBool extend0A, GBool extend1A):
01824   GfxShading(3)
01825 {
01826   int i;
01827 
01828   x0 = x0A;
01829   y0 = y0A;
01830   r0 = r0A;
01831   x1 = x1A;
01832   y1 = y1A;
01833   r1 = r1A;
01834   t0 = t0A;
01835   t1 = t1A;
01836   nFuncs = nFuncsA;
01837   for (i = 0; i < nFuncs; ++i) {
01838     funcs[i] = funcsA[i];
01839   }
01840   extend0 = extend0A;
01841   extend1 = extend1A;
01842 }
01843 
01844 GfxRadialShading::GfxRadialShading(GfxRadialShading *shading):
01845   GfxShading(shading)
01846 {
01847   int i;
01848 
01849   x0 = shading->x0;
01850   y0 = shading->y0;
01851   r0 = shading->r0;
01852   x1 = shading->x1;
01853   y1 = shading->y1;
01854   r1 = shading->r1;
01855   t0 = shading->t0;
01856   y1 = shading->t1;
01857   nFuncs = shading->nFuncs;
01858   for (i = 0; i < nFuncs; ++i) {
01859     funcs[i] = shading->funcs[i]->copy();
01860   }
01861   extend0 = shading->extend0;
01862   extend1 = shading->extend1;
01863 }
01864 
01865 GfxRadialShading::~GfxRadialShading() {
01866   int i;
01867 
01868   for (i = 0; i < nFuncs; ++i) {
01869     delete funcs[i];
01870   }
01871 }
01872 
01873 GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
01874   GfxRadialShading *shading;
01875   double x0A, y0A, r0A, x1A, y1A, r1A;
01876   double t0A, t1A;
01877   Function *funcsA[gfxColorMaxComps];
01878   int nFuncsA;
01879   GBool extend0A, extend1A;
01880   Object obj1, obj2;
01881   int i;
01882 
01883   x0A = y0A = r0A = x1A = y1A = r1A = 0;
01884   if (dict->lookup("Coords", &obj1)->isArray() &&
01885       obj1.arrayGetLength() == 6) {
01886     x0A = obj1.arrayGet(0, &obj2)->getNum();
01887     obj2.free();
01888     y0A = obj1.arrayGet(1, &obj2)->getNum();
01889     obj2.free();
01890     r0A = obj1.arrayGet(2, &obj2)->getNum();
01891     obj2.free();
01892     x1A = obj1.arrayGet(3, &obj2)->getNum();
01893     obj2.free();
01894     y1A = obj1.arrayGet(4, &obj2)->getNum();
01895     obj2.free();
01896     r1A = obj1.arrayGet(5, &obj2)->getNum();
01897     obj2.free();
01898   } else {
01899     error(-1, "Missing or invalid Coords in shading dictionary");
01900     goto err1;
01901   }
01902   obj1.free();
01903 
01904   t0A = 0;
01905   t1A = 1;
01906   if (dict->lookup("Domain", &obj1)->isArray() &&
01907       obj1.arrayGetLength() == 2) {
01908     t0A = obj1.arrayGet(0, &obj2)->getNum();
01909     obj2.free();
01910     t1A = obj1.arrayGet(1, &obj2)->getNum();
01911     obj2.free();
01912   }
01913   obj1.free();
01914 
01915   dict->lookup("Function", &obj1);
01916   if (obj1.isArray()) {
01917     nFuncsA = obj1.arrayGetLength();
01918     if (nFuncsA > gfxColorMaxComps) {
01919       error(-1, "Invalid Function array in shading dictionary");
01920       goto err1;
01921     }
01922     for (i = 0; i < nFuncsA; ++i) {
01923       obj1.arrayGet(i, &obj2);
01924       if (!(funcsA[i] = Function::parse(&obj2))) {
01925        obj1.free();
01926        obj2.free();
01927        goto err1;
01928       }
01929       obj2.free();
01930     }
01931   } else {
01932     nFuncsA = 1;
01933     if (!(funcsA[0] = Function::parse(&obj1))) {
01934       obj1.free();
01935       goto err1;
01936     }
01937   }
01938   obj1.free();
01939 
01940   extend0A = extend1A = gFalse;
01941   if (dict->lookup("Extend", &obj1)->isArray() &&
01942       obj1.arrayGetLength() == 2) {
01943     extend0A = obj1.arrayGet(0, &obj2)->getBool();
01944     obj2.free();
01945     extend1A = obj1.arrayGet(1, &obj2)->getBool();
01946     obj2.free();
01947   }
01948   obj1.free();
01949 
01950   shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
01951                              funcsA, nFuncsA, extend0A, extend1A);
01952   if (!shading->init(dict)) {
01953     delete shading;
01954     return NULL;
01955   }
01956   return shading;
01957 
01958  err1:
01959   return NULL;
01960 }
01961 
01962 GfxShading *GfxRadialShading::copy() {
01963   return new GfxRadialShading(this);
01964 }
01965 
01966 void GfxRadialShading::getColor(double t, GfxColor *color) {
01967   int i;
01968 
01969   // NB: there can be one function with n outputs or n functions with
01970   // one output each (where n = number of color components)
01971   for (i = 0; i < nFuncs; ++i) {
01972     funcs[i]->transform(&t, &color->c[i]);
01973   }
01974 }
01975 
01976 //------------------------------------------------------------------------
01977 // GfxImageColorMap
01978 //------------------------------------------------------------------------
01979 
01980 GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
01981                                GfxColorSpace *colorSpaceA) {
01982   GfxIndexedColorSpace *indexedCS;
01983   GfxSeparationColorSpace *sepCS;
01984   int maxPixel, indexHigh;
01985   Guchar *lookup2;
01986   Function *sepFunc;
01987   Object obj;
01988   double x[gfxColorMaxComps];
01989   double y[gfxColorMaxComps];
01990   int i, j, k;
01991 
01992   ok = gTrue;
01993 
01994   // bits per component and color space
01995   bits = bitsA;
01996   maxPixel = (1 << bits) - 1;
01997   colorSpace = colorSpaceA;
01998 
01999   // get decode map
02000   if (decode->isNull()) {
02001     nComps = colorSpace->getNComps();
02002     colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel);
02003   } else if (decode->isArray()) {
02004     nComps = decode->arrayGetLength() / 2;
02005     if (nComps != colorSpace->getNComps()) {
02006       goto err1;
02007     }
02008     for (i = 0; i < nComps; ++i) {
02009       decode->arrayGet(2*i, &obj);
02010       if (!obj.isNum()) {
02011        goto err2;
02012       }
02013       decodeLow[i] = obj.getNum();
02014       obj.free();
02015       decode->arrayGet(2*i+1, &obj);
02016       if (!obj.isNum()) {
02017        goto err2;
02018       }
02019       decodeRange[i] = obj.getNum() - decodeLow[i];
02020       obj.free();
02021     }
02022   } else {
02023     goto err1;
02024   }
02025 
02026   // Construct a lookup table -- this stores pre-computed decoded
02027   // values for each component, i.e., the result of applying the
02028   // decode mapping to each possible image pixel component value.
02029   //
02030   // Optimization: for Indexed and Separation color spaces (which have
02031   // only one component), we store color values in the lookup table
02032   // rather than component values.
02033   colorSpace2 = NULL;
02034   nComps2 = 0;
02035   if (colorSpace->getMode() == csIndexed) {
02036     // Note that indexHigh may not be the same as maxPixel --
02037     // Distiller will remove unused palette entries, resulting in
02038     // indexHigh < maxPixel.
02039     indexedCS = (GfxIndexedColorSpace *)colorSpace;
02040     colorSpace2 = indexedCS->getBase();
02041     indexHigh = indexedCS->getIndexHigh();
02042     nComps2 = colorSpace2->getNComps();
02043     lookup = (double *)gmalloc((maxPixel + 1) * nComps2 * sizeof(double));
02044     lookup2 = indexedCS->getLookup();
02045     colorSpace2->getDefaultRanges(x, y, indexHigh);
02046     for (i = 0; i <= maxPixel; ++i) {
02047       j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5);
02048       if (j < 0) {
02049        j = 0;
02050       } else if (j > indexHigh) {
02051        j = indexHigh;
02052       }
02053       for (k = 0; k < nComps2; ++k) {
02054        lookup[i*nComps2 + k] = x[k] + (lookup2[j*nComps2 + k] / 255.0) * y[k];
02055       }
02056     }
02057   } else if (colorSpace->getMode() == csSeparation) {
02058     sepCS = (GfxSeparationColorSpace *)colorSpace;
02059     colorSpace2 = sepCS->getAlt();
02060     nComps2 = colorSpace2->getNComps();
02061     lookup = (double *)gmalloc((maxPixel + 1) * nComps2 * sizeof(double));
02062     sepFunc = sepCS->getFunc();
02063     for (i = 0; i <= maxPixel; ++i) {
02064       x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
02065       sepFunc->transform(x, y);
02066       for (k = 0; k < nComps2; ++k) {
02067        lookup[i*nComps2 + k] = y[k];
02068       }
02069     }
02070   } else {
02071     lookup = (double *)gmalloc((maxPixel + 1) * nComps * sizeof(double));
02072     for (i = 0; i <= maxPixel; ++i) {
02073       for (k = 0; k < nComps; ++k) {
02074        lookup[i*nComps + k] = decodeLow[k] +
02075                                 (i * decodeRange[k]) / maxPixel;
02076       }
02077     }
02078   }
02079 
02080   return;
02081 
02082  err2:
02083   obj.free();
02084  err1:
02085   ok = gFalse;
02086 }
02087 
02088 GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) {
02089   int n, i;
02090 
02091   colorSpace = colorMap->colorSpace->copy();
02092   bits = colorMap->bits;
02093   nComps = colorMap->nComps;
02094   nComps2 = colorMap->nComps2;
02095   colorSpace2 = NULL;
02096   lookup = NULL;
02097   n = 1 << bits;
02098   if (colorSpace->getMode() == csIndexed) {
02099     colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase();
02100     n = n * nComps2 * sizeof(double);
02101   } else if (colorSpace->getMode() == csSeparation) {
02102     colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt();
02103     n = n * nComps2 * sizeof(double);
02104   } else {
02105     n = n * nComps * sizeof(double);
02106   }
02107   lookup = (double *)gmalloc(n);
02108   memcpy(lookup, colorMap->lookup, n);
02109   for (i = 0; i < nComps; ++i) {
02110     decodeLow[i] = colorMap->decodeLow[i];
02111     decodeRange[i] = colorMap->decodeRange[i];
02112   }
02113   ok = gTrue;
02114 }
02115 
02116 GfxImageColorMap::~GfxImageColorMap() {
02117   delete colorSpace;
02118   gfree(lookup);
02119 }
02120 
02121 void GfxImageColorMap::getGray(Guchar *x, double *gray) {
02122   GfxColor color;
02123   double *p;
02124   int i;
02125 
02126   if (colorSpace2) {
02127     p = &lookup[x[0] * nComps2];
02128     for (i = 0; i < nComps2; ++i) {
02129       color.c[i] = *p++;
02130     }
02131     colorSpace2->getGray(&color, gray);
02132   } else {
02133     for (i = 0; i < nComps; ++i) {
02134       color.c[i] = lookup[x[i] * nComps + i];
02135     }
02136     colorSpace->getGray(&color, gray);
02137   }
02138 }
02139 
02140 void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {
02141   GfxColor color;
02142   double *p;
02143   int i;
02144 
02145   if (colorSpace2) {
02146     p = &lookup[x[0] * nComps2];
02147     for (i = 0; i < nComps2; ++i) {
02148       color.c[i] = *p++;
02149     }
02150     colorSpace2->getRGB(&color, rgb);
02151   } else {
02152     for (i = 0; i < nComps; ++i) {
02153       color.c[i] = lookup[x[i] * nComps + i];
02154     }
02155     colorSpace->getRGB(&color, rgb);
02156   }
02157 }
02158 
02159 void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
02160   GfxColor color;
02161   double *p;
02162   int i;
02163 
02164   if (colorSpace2) {
02165     p = &lookup[x[0] * nComps2];
02166     for (i = 0; i < nComps2; ++i) {
02167       color.c[i] = *p++;
02168     }
02169     colorSpace2->getCMYK(&color, cmyk);
02170   } else {
02171     for (i = 0; i < nComps; ++i) {
02172       color.c[i] = lookup[x[i] * nComps + i];
02173     }
02174     colorSpace->getCMYK(&color, cmyk);
02175   }
02176 }
02177 
02178 void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
02179   int maxPixel, i;
02180 
02181   maxPixel = (1 << bits) - 1;
02182   for (i = 0; i < nComps; ++i) {
02183     color->c[i] = decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel;
02184   }
02185 }
02186 
02187 //------------------------------------------------------------------------
02188 // GfxSubpath and GfxPath
02189 //------------------------------------------------------------------------
02190 
02191 GfxSubpath::GfxSubpath(double x1, double y1) {
02192   size = 16;
02193   x = (double *)gmalloc(size * sizeof(double));
02194   y = (double *)gmalloc(size * sizeof(double));
02195   curve = (GBool *)gmalloc(size * sizeof(GBool));
02196   n = 1;
02197   x[0] = x1;
02198   y[0] = y1;
02199   curve[0] = gFalse;
02200   closed = gFalse;
02201 }
02202 
02203 GfxSubpath::~GfxSubpath() {
02204   gfree(x);
02205   gfree(y);
02206   gfree(curve);
02207 }
02208 
02209 // Used for copy().
02210 GfxSubpath::GfxSubpath(GfxSubpath *subpath) {
02211   size = subpath->size;
02212   n = subpath->n;
02213   x = (double *)gmalloc(size * sizeof(double));
02214   y = (double *)gmalloc(size * sizeof(double));
02215   curve = (GBool *)gmalloc(size * sizeof(GBool));
02216   memcpy(x, subpath->x, n * sizeof(double));
02217   memcpy(y, subpath->y, n * sizeof(double));
02218   memcpy(curve, subpath->curve, n * sizeof(GBool));
02219   closed = subpath->closed;
02220 }
02221 
02222 void GfxSubpath::lineTo(double x1, double y1) {
02223   if (n >= size) {
02224     size += 16;
02225     x = (double *)grealloc(x, size * sizeof(double));
02226     y = (double *)grealloc(y, size * sizeof(double));
02227     curve = (GBool *)grealloc(curve, size * sizeof(GBool));
02228   }
02229   x[n] = x1;
02230   y[n] = y1;
02231   curve[n] = gFalse;
02232   ++n;
02233 }
02234 
02235 void GfxSubpath::curveTo(double x1, double y1, double x2, double y2,
02236                       double x3, double y3) {
02237   if (n+3 > size) {
02238     size += 16;
02239     x = (double *)grealloc(x, size * sizeof(double));
02240     y = (double *)grealloc(y, size * sizeof(double));
02241     curve = (GBool *)grealloc(curve, size * sizeof(GBool));
02242   }
02243   x[n] = x1;
02244   y[n] = y1;
02245   x[n+1] = x2;
02246   y[n+1] = y2;
02247   x[n+2] = x3;
02248   y[n+2] = y3;
02249   curve[n] = curve[n+1] = gTrue;
02250   curve[n+2] = gFalse;
02251   n += 3;
02252 }
02253 
02254 void GfxSubpath::close() {
02255   if (x[n-1] != x[0] || y[n-1] != y[0]) {
02256     lineTo(x[0], y[0]);
02257   }
02258   closed = gTrue;
02259 }
02260 
02261 void GfxSubpath::offset(double dx, double dy) {
02262   int i;
02263 
02264   for (i = 0; i < n; ++i) {
02265     x[i] += dx;
02266     y[i] += dy;
02267   }
02268 }
02269 
02270 GfxPath::GfxPath() {
02271   justMoved = gFalse;
02272   size = 16;
02273   n = 0;
02274   firstX = firstY = 0;
02275   subpaths = (GfxSubpath **)gmalloc(size * sizeof(GfxSubpath *));
02276 }
02277 
02278 GfxPath::~GfxPath() {
02279   int i;
02280 
02281   for (i = 0; i < n; ++i)
02282     delete subpaths[i];
02283   gfree(subpaths);
02284 }
02285 
02286 // Used for copy().
02287 GfxPath::GfxPath(GBool justMoved1, double firstX1, double firstY1,
02288                GfxSubpath **subpaths1, int n1, int size1) {
02289   int i;
02290 
02291   justMoved = justMoved1;
02292   firstX = firstX1;
02293   firstY = firstY1;
02294   size = size1;
02295   n = n1;
02296   subpaths = (GfxSubpath **)gmalloc(size * sizeof(GfxSubpath *));
02297   for (i = 0; i < n; ++i)
02298     subpaths[i] = subpaths1[i]->copy();
02299 }
02300 
02301 void GfxPath::moveTo(double x, double y) {
02302   justMoved = gTrue;
02303   firstX = x;
02304   firstY = y;
02305 }
02306 
02307 void GfxPath::lineTo(double x, double y) {
02308   if (justMoved) {
02309     if (n >= size) {
02310       size += 16;
02311       subpaths = (GfxSubpath **)
02312                   grealloc(subpaths, size * sizeof(GfxSubpath *));
02313     }
02314     subpaths[n] = new GfxSubpath(firstX, firstY);
02315     ++n;
02316     justMoved = gFalse;
02317   }
02318   subpaths[n-1]->lineTo(x, y);
02319 }
02320 
02321 void GfxPath::curveTo(double x1, double y1, double x2, double y2,
02322             double x3, double y3) {
02323   if (justMoved) {
02324     if (n >= size) {
02325       size += 16;
02326       subpaths = (GfxSubpath **)
02327                   grealloc(subpaths, size * sizeof(GfxSubpath *));
02328     }
02329     subpaths[n] = new GfxSubpath(firstX, firstY);
02330     ++n;
02331     justMoved = gFalse;
02332   }
02333   subpaths[n-1]->curveTo(x1, y1, x2, y2, x3, y3);
02334 }
02335 
02336 void GfxPath::close() {
02337   // this is necessary to handle the pathological case of
02338   // moveto/closepath/clip, which defines an empty clipping region
02339   if (justMoved) {
02340     if (n >= size) {
02341       size += 16;
02342       subpaths = (GfxSubpath **)
02343                   grealloc(subpaths, size * sizeof(GfxSubpath *));
02344     }
02345     subpaths[n] = new GfxSubpath(firstX, firstY);
02346     ++n;
02347     justMoved = gFalse;
02348   }
02349   subpaths[n-1]->close();
02350 }
02351 
02352 void GfxPath::append(GfxPath *path) {
02353   int i;
02354 
02355   if (n + path->n > size) {
02356     size = n + path->n;
02357     subpaths = (GfxSubpath **)
02358                  grealloc(subpaths, size * sizeof(GfxSubpath *));
02359   }
02360   for (i = 0; i < path->n; ++i) {
02361     subpaths[n++] = path->subpaths[i]->copy();
02362   }
02363   justMoved = gFalse;
02364 }
02365 
02366 void GfxPath::offset(double dx, double dy) {
02367   int i;
02368 
02369   for (i = 0; i < n; ++i) {
02370     subpaths[i]->offset(dx, dy);
02371   }
02372 }
02373 
02374 //------------------------------------------------------------------------
02375 // GfxState
02376 //------------------------------------------------------------------------
02377 
02378 GfxState::GfxState(double hDPI, double vDPI, PDFRectangle *pageBox,
02379                  int rotate, GBool upsideDown) {
02380   double kx, ky;
02381 
02382   px1 = pageBox->x1;
02383   py1 = pageBox->y1;
02384   px2 = pageBox->x2;
02385   py2 = pageBox->y2;
02386   kx = hDPI / 72.0;
02387   ky = vDPI / 72.0;
02388   if (rotate == 90) {
02389     ctm[0] = 0;
02390     ctm[1] = upsideDown ? ky : -ky;
02391     ctm[2] = kx;
02392     ctm[3] = 0;
02393     ctm[4] = -kx * py1;
02394     ctm[5] = ky * (upsideDown ? -px1 : px2);
02395     pageWidth = kx * (py2 - py1);
02396     pageHeight = ky * (px2 - px1);
02397   } else if (rotate == 180) {
02398     ctm[0] = -kx;
02399     ctm[1] = 0;
02400     ctm[2] = 0;
02401     ctm[3] = upsideDown ? ky : -ky;
02402     ctm[4] = kx * px2;
02403     ctm[5] = ky * (upsideDown ? -py1 : py2);
02404     pageWidth = kx * (px2 - px1);
02405     pageHeight = ky * (py2 - py1);
02406   } else if (rotate == 270) {
02407     ctm[0] = 0;
02408     ctm[1] = upsideDown ? -ky : ky;
02409     ctm[2] = -kx;
02410     ctm[3] = 0;
02411     ctm[4] = kx * py2;
02412     ctm[5] = ky * (upsideDown ? px2 : -px1);
02413     pageWidth = kx * (py2 - py1);
02414     pageHeight = ky * (px2 - px1);
02415   } else {
02416     ctm[0] = kx;
02417     ctm[1] = 0;
02418     ctm[2] = 0;
02419     ctm[3] = upsideDown ? -ky : ky;
02420     ctm[4] = -kx * px1;
02421     ctm[5] = ky * (upsideDown ? py2 : -py1);
02422     pageWidth = kx * (px2 - px1);
02423     pageHeight = ky * (py2 - py1);
02424   }
02425 
02426   fillColorSpace = new GfxDeviceGrayColorSpace();
02427   strokeColorSpace = new GfxDeviceGrayColorSpace();
02428   fillColor.c[0] = 0;
02429   strokeColor.c[0] = 0;
02430   fillPattern = NULL;
02431   strokePattern = NULL;
02432   fillOpacity = 1;
02433   strokeOpacity = 1;
02434 
02435   lineWidth = 1;
02436   lineDash = NULL;
02437   lineDashLength = 0;
02438   lineDashStart = 0;
02439   flatness = 1;
02440   lineJoin = 0;
02441   lineCap = 0;
02442   miterLimit = 10;
02443 
02444   font = NULL;
02445   fontSize = 0;
02446   textMat[0] = 1; textMat[1] = 0;
02447   textMat[2] = 0; textMat[3] = 1;
02448   textMat[4] = 0; textMat[5] = 0;
02449   charSpace = 0;
02450   wordSpace = 0;
02451   horizScaling = 1;
02452   leading = 0;
02453   rise = 0;
02454   render = 0;
02455 
02456   path = new GfxPath();
02457   curX = curY = 0;
02458   lineX = lineY = 0;
02459 
02460   clipXMin = 0;
02461   clipYMin = 0;
02462   clipXMax = pageWidth;
02463   clipYMax = pageHeight;
02464 
02465   saved = NULL;
02466 }
02467 
02468 GfxState::~GfxState() {
02469   if (fillColorSpace) {
02470     delete fillColorSpace;
02471   }
02472   if (strokeColorSpace) {
02473     delete strokeColorSpace;
02474   }
02475   if (fillPattern) {
02476     delete fillPattern;
02477   }
02478   if (strokePattern) {
02479     delete strokePattern;
02480   }
02481   gfree(lineDash);
02482   if (path) {
02483     // this gets set to NULL by restore()
02484     delete path;
02485   }
02486   if (saved) {
02487     delete saved;
02488   }
02489 }
02490 
02491 // Used for copy();
02492 GfxState::GfxState(GfxState *state) {
02493   memcpy(this, state, sizeof(GfxState));
02494   if (fillColorSpace) {
02495     fillColorSpace = state->fillColorSpace->copy();
02496   }
02497   if (strokeColorSpace) {
02498     strokeColorSpace = state->strokeColorSpace->copy();
02499   }
02500   if (fillPattern) {
02501     fillPattern = state->fillPattern->copy();
02502   }
02503   if (strokePattern) {
02504     strokePattern = state->strokePattern->copy();
02505   }
02506   if (lineDashLength > 0) {
02507     lineDash = (double *)gmalloc(lineDashLength * sizeof(double));
02508     memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double));
02509   }
02510   saved = NULL;
02511 }
02512 
02513 void GfxState::setPath(GfxPath *pathA) {
02514   delete path;
02515   path = pathA;
02516 }
02517 
02518 void GfxState::getUserClipBBox(double *xMin, double *yMin,
02519                             double *xMax, double *yMax) {
02520   double ictm[6];
02521   double xMin1, yMin1, xMax1, yMax1, det, tx, ty;
02522 
02523   // invert the CTM
02524   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
02525   ictm[0] = ctm[3] * det;
02526   ictm[1] = -ctm[1] * det;
02527   ictm[2] = -ctm[2] * det;
02528   ictm[3] = ctm[0] * det;
02529   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
02530   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
02531 
02532   // transform all four corners of the clip bbox; find the min and max
02533   // x and y values
02534   xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4];
02535   yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5];
02536   tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4];
02537   ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5];
02538   if (tx < xMin1) {
02539     xMin1 = tx;
02540   } else if (tx > xMax1) {
02541     xMax1 = tx;
02542   }
02543   if (ty < yMin1) {
02544     yMin1 = ty;
02545   } else if (ty > yMax1) {
02546     yMax1 = ty;
02547   }
02548   tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4];
02549   ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5];
02550   if (tx < xMin1) {
02551     xMin1 = tx;
02552   } else if (tx > xMax1) {
02553     xMax1 = tx;
02554   }
02555   if (ty < yMin1) {
02556     yMin1 = ty;
02557   } else if (ty > yMax1) {
02558     yMax1 = ty;
02559   }
02560   tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4];
02561   ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5];
02562   if (tx < xMin1) {
02563     xMin1 = tx;
02564   } else if (tx > xMax1) {
02565     xMax1 = tx;
02566   }
02567   if (ty < yMin1) {
02568     yMin1 = ty;
02569   } else if (ty > yMax1) {
02570     yMax1 = ty;
02571   }
02572 
02573   *xMin = xMin1;
02574   *yMin = yMin1;
02575   *xMax = xMax1;
02576   *yMax = yMax1;
02577 }
02578 
02579 double GfxState::transformWidth(double w) {
02580   double x, y;
02581 
02582   x = ctm[0] + ctm[2];
02583   y = ctm[1] + ctm[3];
02584   return w * sqrt(0.5 * (x * x + y * y));
02585 }
02586 
02587 double GfxState::getTransformedFontSize() {
02588   double x1, y1, x2, y2;
02589 
02590   x1 = textMat[2] * fontSize;
02591   y1 = textMat[3] * fontSize;
02592   x2 = ctm[0] * x1 + ctm[2] * y1;
02593   y2 = ctm[1] * x1 + ctm[3] * y1;
02594   return sqrt(x2 * x2 + y2 * y2);
02595 }
02596 
02597 void GfxState::getFontTransMat(double *m11, double *m12,
02598                             double *m21, double *m22) {
02599   *m11 = (textMat[0] * ctm[0] + textMat[1] * ctm[2]) * fontSize;
02600   *m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize;
02601   *m21 = (textMat[2] * ctm[0] + textMat[3] * ctm[2]) * fontSize;
02602   *m22 = (textMat[2] * ctm[1] + textMat[3] * ctm[3]) * fontSize;
02603 }
02604 
02605 void GfxState::setCTM(double a, double b, double c,
02606                     double d, double e, double f) {
02607   int i;
02608 
02609   ctm[0] = a;
02610   ctm[1] = b;
02611   ctm[2] = c;
02612   ctm[3] = d;
02613   ctm[4] = e;
02614   ctm[5] = f;
02615 
02616   // avoid FP exceptions on badly messed up PDF files
02617   for (i = 0; i < 6; ++i) {
02618     if (ctm[i] > 1e10) {
02619       ctm[i] = 1e10;
02620     } else if (ctm[i] < -1e10) {
02621       ctm[i] = -1e10;
02622     }
02623   }
02624 }
02625 
02626 void GfxState::concatCTM(double a, double b, double c,
02627                       double d, double e, double f) {
02628   double a1 = ctm[0];
02629   double b1 = ctm[1];
02630   double c1 = ctm[2];
02631   double d1 = ctm[3];
02632   int i;
02633 
02634   ctm[0] = a * a1 + b * c1;
02635   ctm[1] = a * b1 + b * d1;
02636   ctm[2] = c * a1 + d * c1;
02637   ctm[3] = c * b1 + d * d1;
02638   ctm[4] = e * a1 + f * c1 + ctm[4];
02639   ctm[5] = e * b1 + f * d1 + ctm[5];
02640 
02641   // avoid FP exceptions on badly messed up PDF files
02642   for (i = 0; i < 6; ++i) {
02643     if (ctm[i] > 1e10) {
02644       ctm[i] = 1e10;
02645     } else if (ctm[i] < -1e10) {
02646       ctm[i] = -1e10;
02647     }
02648   }
02649 }
02650 
02651 void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) {
02652   if (fillColorSpace) {
02653     delete fillColorSpace;
02654   }
02655   fillColorSpace = colorSpace;
02656 }
02657 
02658 void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) {
02659   if (strokeColorSpace) {
02660     delete strokeColorSpace;
02661   }
02662   strokeColorSpace = colorSpace;
02663 }
02664 
02665 void GfxState::setFillPattern(GfxPattern *pattern) {
02666   if (fillPattern) {
02667     delete fillPattern;
02668   }
02669   fillPattern = pattern;
02670 }
02671 
02672 void GfxState::setStrokePattern(GfxPattern *pattern) {
02673   if (strokePattern) {
02674     delete strokePattern;
02675   }
02676   strokePattern = pattern;
02677 }
02678 
02679 void GfxState::setLineDash(double *dash, int length, double start) {
02680   if (lineDash)
02681     gfree(lineDash);
02682   lineDash = dash;
02683   lineDashLength = length;
02684   lineDashStart = start;
02685 }
02686 
02687 void GfxState::clearPath() {
02688   delete path;
02689   path = new GfxPath();
02690 }
02691 
02692 void GfxState::clip() {
02693   double xMin, yMin, xMax, yMax, x, y;
02694   GfxSubpath *subpath;
02695   int i, j;
02696 
02697   xMin = xMax = yMin = yMax = 0; // make gcc happy
02698   for (i = 0; i < path->getNumSubpaths(); ++i) {
02699     subpath = path->getSubpath(i);
02700     for (j = 0; j < subpath->getNumPoints(); ++j) {
02701       transform(subpath->getX(j), subpath->getY(j), &x, &y);
02702       if (i == 0 && j == 0) {
02703        xMin = xMax = x;
02704        yMin = yMax = y;
02705       } else {
02706        if (x < xMin) {
02707          xMin = x;
02708        } else if (x > xMax) {
02709          xMax = x;
02710        }
02711        if (y < yMin) {
02712          yMin = y;
02713        } else if (y > yMax) {
02714          yMax = y;
02715        }
02716       }
02717     }
02718   }
02719   if (xMin > clipXMin) {
02720     clipXMin = xMin;
02721   }
02722   if (yMin > clipYMin) {
02723     clipYMin = yMin;
02724   }
02725   if (xMax < clipXMax) {
02726     clipXMax = xMax;
02727   }
02728   if (yMax < clipYMax) {
02729     clipYMax = yMax;
02730   }
02731 }
02732 
02733 void GfxState::textShift(double tx, double ty) {
02734   double dx, dy;
02735 
02736   textTransformDelta(tx, ty, &dx, &dy);
02737   curX += dx;
02738   curY += dy;
02739 }
02740 
02741 void GfxState::shift(double dx, double dy) {
02742   curX += dx;
02743   curY += dy;
02744 }
02745 
02746 GfxState *GfxState::save() {
02747   GfxState *newState;
02748 
02749   newState = copy();
02750   newState->saved = this;
02751   return newState;
02752 }
02753 
02754 GfxState *GfxState::restore() {
02755   GfxState *oldState;
02756 
02757   if (saved) {
02758     oldState = saved;
02759 
02760     // these attributes aren't saved/restored by the q/Q operators
02761     oldState->path = path;
02762     oldState->curX = curX;
02763     oldState->curY = curY;
02764     oldState->lineX = lineX;
02765     oldState->lineY = lineY;
02766 
02767     path = NULL;
02768     saved = NULL;
02769     delete this;
02770 
02771   } else {
02772     oldState = this;
02773   }
02774 
02775   return oldState;
02776 }