X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=pdf2swf%2Fxpdf%2FGfxState.cc;h=5aa9c28c1d415c16be58247ae416eb65a47de54f;hb=ff0eecc9f0cbe4ecdaac00b5b04660777ad7c630;hp=1abf9a56ec47025632d26f02c6ad3f769bbccbde;hpb=fc554a43712b76d16b41ec77dd311b4a78b1ef6b;p=swftools.git diff --git a/pdf2swf/xpdf/GfxState.cc b/pdf2swf/xpdf/GfxState.cc index 1abf9a5..5aa9c28 100644 --- a/pdf2swf/xpdf/GfxState.cc +++ b/pdf2swf/xpdf/GfxState.cc @@ -2,11 +2,13 @@ // // GfxState.cc // -// Copyright 1996 Derek B. Noonburg +// Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== -#ifdef __GNUC__ +#include + +#ifdef USE_GCC_PRAGMAS #pragma implementation #endif @@ -17,6 +19,7 @@ #include "Error.h" #include "Object.h" #include "Array.h" +#include "Page.h" #include "GfxState.h" //------------------------------------------------------------------------ @@ -26,6 +29,24 @@ static inline double clip01(double x) { } //------------------------------------------------------------------------ + +static char *gfxColorSpaceModeNames[] = { + "DeviceGray", + "CalGray", + "DeviceRGB", + "CalRGB", + "DeviceCMYK", + "Lab", + "ICCBased", + "Indexed", + "Separation", + "DeviceN", + "Pattern" +}; + +#define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *))) + +//------------------------------------------------------------------------ // GfxColorSpace //------------------------------------------------------------------------ @@ -77,7 +98,7 @@ GfxColorSpace *GfxColorSpace::parse(Object *csObj) { } else if (obj1.isName("Pattern")) { cs = GfxPatternColorSpace::parse(csObj->getArray()); } else { - error(-1, "Bad color space '%s'", csObj->getName()); + error(-1, "Bad color space"); } obj1.free(); } else { @@ -96,6 +117,14 @@ void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, } } +int GfxColorSpace::getNumColorSpaceModes() { + return nGfxColorSpaceModes; +} + +char *GfxColorSpace::getColorSpaceModeName(int idx) { + return gfxColorSpaceModeNames[idx]; +} + //------------------------------------------------------------------------ // GfxDeviceGrayColorSpace //------------------------------------------------------------------------ @@ -261,9 +290,9 @@ GfxCalRGBColorSpace::GfxCalRGBColorSpace() { whiteX = whiteY = whiteZ = 1; blackX = blackY = blackZ = 0; gammaR = gammaG = gammaB = 1; - m[0] = 1; m[1] = 0; m[2] = 0; - m[3] = 0; m[4] = 1; m[5] = 0; - m[6] = 0; m[7] = 0; m[8] = 1; + mat[0] = 1; mat[1] = 0; mat[2] = 0; + mat[3] = 0; mat[4] = 1; mat[5] = 0; + mat[6] = 0; mat[7] = 0; mat[8] = 1; } GfxCalRGBColorSpace::~GfxCalRGBColorSpace() { @@ -284,7 +313,7 @@ GfxColorSpace *GfxCalRGBColorSpace::copy() { cs->gammaG = gammaG; cs->gammaB = gammaB; for (i = 0; i < 9; ++i) { - cs->m[i] = m[i]; + cs->mat[i] = mat[i]; } return cs; } @@ -344,7 +373,7 @@ GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr) { obj2.arrayGetLength() == 9) { for (i = 0; i < 9; ++i) { obj2.arrayGet(i, &obj3); - cs->m[i] = obj3.getNum(); + cs->mat[i] = obj3.getNum(); obj3.free(); } } @@ -406,9 +435,22 @@ void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, double *gray) { } void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { - rgb->r = clip01(1 - (color->c[0] + color->c[3])); - rgb->g = clip01(1 - (color->c[1] + color->c[3])); - rgb->b = clip01(1 - (color->c[2] + color->c[3])); + double c, m, y, aw, ac, am, ay, ar, ag, ab; + + c = clip01(color->c[0] + color->c[3]); + m = clip01(color->c[1] + color->c[3]); + y = clip01(color->c[2] + color->c[3]); + aw = (1-c) * (1-m) * (1-y); + ac = c * (1-m) * (1-y); + am = (1-c) * m * (1-y); + ay = (1-c) * (1-m) * y; + ar = (1-c) * m * y; + ag = c * (1-m) * y; + ab = c * m * (1-y); + rgb->r = clip01(aw + 0.9137*am + 0.9961*ay + 0.9882*ar); + rgb->g = clip01(aw + 0.6196*ac + ay + 0.5176*ag); + rgb->b = clip01(aw + 0.7804*ac + 0.5412*am + 0.0667*ar + 0.2118*ag + + 0.4863*ab); } void GfxDeviceCMYKColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { @@ -609,11 +651,11 @@ void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, // GfxICCBasedColorSpace //------------------------------------------------------------------------ -GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nComps, GfxColorSpace *alt, - Ref *iccProfileStream) { - this->nComps = nComps; - this->alt = alt; - this->iccProfileStream = *iccProfileStream; +GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA, + Ref *iccProfileStreamA) { + nComps = nCompsA; + alt = altA; + iccProfileStream = *iccProfileStreamA; rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0; rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1; } @@ -636,19 +678,19 @@ GfxColorSpace *GfxICCBasedColorSpace::copy() { GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) { GfxICCBasedColorSpace *cs; - Ref iccProfileStream; - int nComps; - GfxColorSpace *alt; + Ref iccProfileStreamA; + int nCompsA; + GfxColorSpace *altA; Dict *dict; Object obj1, obj2, obj3; int i; arr->getNF(1, &obj1); if (obj1.isRef()) { - iccProfileStream = obj1.getRef(); + iccProfileStreamA = obj1.getRef(); } else { - iccProfileStream.num = 0; - iccProfileStream.gen = 0; + iccProfileStreamA.num = 0; + iccProfileStreamA.gen = 0; } obj1.free(); arr->get(1, &obj1); @@ -664,19 +706,19 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) { obj1.free(); return NULL; } - nComps = obj2.getInt(); + nCompsA = obj2.getInt(); obj2.free(); if (dict->lookup("Alternate", &obj2)->isNull() || - !(alt = GfxColorSpace::parse(&obj2))) { - switch (nComps) { + !(altA = GfxColorSpace::parse(&obj2))) { + switch (nCompsA) { case 1: - alt = new GfxDeviceGrayColorSpace(); + altA = new GfxDeviceGrayColorSpace(); break; case 3: - alt = new GfxDeviceRGBColorSpace(); + altA = new GfxDeviceRGBColorSpace(); break; case 4: - alt = new GfxDeviceCMYKColorSpace(); + altA = new GfxDeviceCMYKColorSpace(); break; default: error(-1, "Bad ICCBased color space - invalid N"); @@ -686,10 +728,10 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) { } } obj2.free(); - cs = new GfxICCBasedColorSpace(nComps, alt, &iccProfileStream); + cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA); if (dict->lookup("Range", &obj2)->isArray() && - obj2.arrayGetLength() == 2 * nComps) { - for (i = 0; i < nComps; ++i) { + obj2.arrayGetLength() == 2 * nCompsA) { + for (i = 0; i < nCompsA; ++i) { obj2.arrayGet(2*i, &obj3); cs->rangeMin[i] = obj3.getNum(); obj3.free(); @@ -718,24 +760,30 @@ void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) { + alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel); + +#if 0 + // this is nominally correct, but some PDF files don't set the + // correct ranges in the ICCBased dict int i; for (i = 0; i < nComps; ++i) { decodeLow[i] = rangeMin[i]; decodeRange[i] = rangeMax[i] - rangeMin[i]; } +#endif } //------------------------------------------------------------------------ // GfxIndexedColorSpace //------------------------------------------------------------------------ -GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *base, - int indexHigh) { - this->base = base; - this->indexHigh = indexHigh; - this->lookup = (Guchar *)gmalloc((indexHigh + 1) * base->getNComps() * - sizeof(Guchar)); +GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA, + int indexHighA) { + base = baseA; + indexHigh = indexHighA; + lookup = (Guchar *)gmalloc((indexHigh + 1) * base->getNComps() * + sizeof(Guchar)); } GfxIndexedColorSpace::~GfxIndexedColorSpace() { @@ -754,8 +802,8 @@ GfxColorSpace *GfxIndexedColorSpace::copy() { GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) { GfxIndexedColorSpace *cs; - GfxColorSpace *base; - int indexHigh; + GfxColorSpace *baseA; + int indexHighA; Object obj1; int x; char *s; @@ -766,23 +814,33 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) { goto err1; } arr->get(1, &obj1); - if (!(base = GfxColorSpace::parse(&obj1))) { + if (!(baseA = GfxColorSpace::parse(&obj1))) { error(-1, "Bad Indexed color space (base color space)"); goto err2; } obj1.free(); if (!arr->get(2, &obj1)->isInt()) { error(-1, "Bad Indexed color space (hival)"); + delete baseA; + goto err2; + } + indexHighA = obj1.getInt(); + if (indexHighA < 0 || indexHighA > 255) { + // the PDF spec requires indexHigh to be in [0,255] -- allowing + // values larger than 255 creates a security hole: if nComps * + // indexHigh is greater than 2^31, the loop below may overwrite + // past the end of the array + error(-1, "Bad Indexed color space (invalid indexHigh value)"); + delete baseA; goto err2; } - indexHigh = obj1.getInt(); obj1.free(); - cs = new GfxIndexedColorSpace(base, indexHigh); + cs = new GfxIndexedColorSpace(baseA, indexHighA); arr->get(3, &obj1); - n = base->getNComps(); + n = baseA->getNComps(); if (obj1.isStream()) { obj1.streamReset(); - for (i = 0; i <= indexHigh; ++i) { + for (i = 0; i <= indexHighA; ++i) { for (j = 0; j < n; ++j) { if ((x = obj1.streamGetChar()) == EOF) { error(-1, "Bad Indexed color space (lookup table stream too short)"); @@ -793,12 +851,12 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) { } obj1.streamClose(); } else if (obj1.isString()) { - if (obj1.getString()->getLength() < (indexHigh + 1) * n) { + if (obj1.getString()->getLength() < (indexHighA + 1) * n) { error(-1, "Bad Indexed color space (lookup table string too short)"); goto err3; } s = obj1.getString()->getCString(); - for (i = 0; i <= indexHigh; ++i) { + for (i = 0; i <= indexHighA; ++i) { for (j = 0; j < n; ++j) { cs->lookup[i*n + j] = (Guchar)*s++; } @@ -818,43 +876,37 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) { return NULL; } -void GfxIndexedColorSpace::getGray(GfxColor *color, double *gray) { +GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color, + GfxColor *baseColor) { Guchar *p; - GfxColor color2; + double low[gfxColorMaxComps], range[gfxColorMaxComps]; int n, i; n = base->getNComps(); + base->getDefaultRanges(low, range, indexHigh); p = &lookup[(int)(color->c[0] + 0.5) * n]; for (i = 0; i < n; ++i) { - color2.c[i] = p[i] / 255.0; + baseColor->c[i] = low[i] + (p[i] / 255.0) * range[i]; } - base->getGray(&color2, gray); + return baseColor; +} + +void GfxIndexedColorSpace::getGray(GfxColor *color, double *gray) { + GfxColor color2; + + base->getGray(mapColorToBase(color, &color2), gray); } void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { - Guchar *p; GfxColor color2; - int n, i; - n = base->getNComps(); - p = &lookup[(int)(color->c[0] + 0.5) * n]; - for (i = 0; i < n; ++i) { - color2.c[i] = p[i] / 255.0; - } - base->getRGB(&color2, rgb); + base->getRGB(mapColorToBase(color, &color2), rgb); } void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { - Guchar *p; GfxColor color2; - int n, i; - n = base->getNComps(); - p = &lookup[(int)(color->c[0] + 0.5) * n]; - for (i = 0; i < n; ++i) { - color2.c[i] = p[i] / 255.0; - } - base->getCMYK(&color2, cmyk); + base->getCMYK(mapColorToBase(color, &color2), cmyk); } void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow, @@ -868,12 +920,12 @@ void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow, // GfxSeparationColorSpace //------------------------------------------------------------------------ -GfxSeparationColorSpace::GfxSeparationColorSpace(GString *name, - GfxColorSpace *alt, - Function *func) { - this->name = name; - this->alt = alt; - this->func = func; +GfxSeparationColorSpace::GfxSeparationColorSpace(GString *nameA, + GfxColorSpace *altA, + Function *funcA) { + name = nameA; + alt = altA; + func = funcA; } GfxSeparationColorSpace::~GfxSeparationColorSpace() { @@ -889,9 +941,9 @@ GfxColorSpace *GfxSeparationColorSpace::copy() { //~ handle the 'All' and 'None' colorants GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr) { GfxSeparationColorSpace *cs; - GString *name; - GfxColorSpace *alt; - Function *func; + GString *nameA; + GfxColorSpace *altA; + Function *funcA; Object obj1; if (arr->getLength() != 4) { @@ -902,27 +954,26 @@ GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr) { error(-1, "Bad Separation color space (name)"); goto err2; } - name = new GString(obj1.getName()); + nameA = new GString(obj1.getName()); obj1.free(); arr->get(2, &obj1); - if (!(alt = GfxColorSpace::parse(&obj1))) { + if (!(altA = GfxColorSpace::parse(&obj1))) { error(-1, "Bad Separation color space (alternate color space)"); goto err3; } obj1.free(); - func = Function::parse(arr->get(3, &obj1)); - obj1.free(); - if (!func->isOk()) { + arr->get(3, &obj1); + if (!(funcA = Function::parse(&obj1))) { goto err4; } - cs = new GfxSeparationColorSpace(name, alt, func); + obj1.free(); + cs = new GfxSeparationColorSpace(nameA, altA, funcA); return cs; err4: - delete func; - delete alt; + delete altA; err3: - delete name; + delete nameA; err2: obj1.free(); err1: @@ -954,12 +1005,12 @@ void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { // GfxDeviceNColorSpace //------------------------------------------------------------------------ -GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nComps, - GfxColorSpace *alt, - Function *func) { - this->nComps = nComps; - this->alt = alt; - this->func = func; +GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA, + GfxColorSpace *altA, + Function *funcA) { + nComps = nCompsA; + alt = altA; + func = funcA; } GfxDeviceNColorSpace::~GfxDeviceNColorSpace() { @@ -986,10 +1037,10 @@ GfxColorSpace *GfxDeviceNColorSpace::copy() { //~ handle the 'None' colorant GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) { GfxDeviceNColorSpace *cs; - int nComps; - GString *names[gfxColorMaxComps]; - GfxColorSpace *alt; - Function *func; + int nCompsA; + GString *namesA[gfxColorMaxComps]; + GfxColorSpace *altA; + Function *funcA; Object obj1, obj2; int i; @@ -1001,40 +1052,44 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) { error(-1, "Bad DeviceN color space (names)"); goto err2; } - nComps = obj1.arrayGetLength(); - for (i = 0; i < nComps; ++i) { + nCompsA = obj1.arrayGetLength(); + if (nCompsA > gfxColorMaxComps) { + error(-1, "DeviceN color space with more than %d > %d components", + nCompsA, gfxColorMaxComps); + nCompsA = gfxColorMaxComps; + } + for (i = 0; i < nCompsA; ++i) { if (!obj1.arrayGet(i, &obj2)->isName()) { error(-1, "Bad DeviceN color space (names)"); obj2.free(); goto err2; } - names[i] = new GString(obj2.getName()); + namesA[i] = new GString(obj2.getName()); obj2.free(); } obj1.free(); arr->get(2, &obj1); - if (!(alt = GfxColorSpace::parse(&obj1))) { + if (!(altA = GfxColorSpace::parse(&obj1))) { error(-1, "Bad DeviceN color space (alternate color space)"); goto err3; } obj1.free(); - func = Function::parse(arr->get(3, &obj1)); - obj1.free(); - if (!func->isOk()) { + arr->get(3, &obj1); + if (!(funcA = Function::parse(&obj1))) { goto err4; } - cs = new GfxDeviceNColorSpace(nComps, alt, func); - for (i = 0; i < nComps; ++i) { - cs->names[i] = names[i]; + obj1.free(); + cs = new GfxDeviceNColorSpace(nCompsA, altA, funcA); + for (i = 0; i < nCompsA; ++i) { + cs->names[i] = namesA[i]; } return cs; err4: - delete func; - delete alt; + delete altA; err3: - for (i = 0; i < nComps; ++i) { - delete names[i]; + for (i = 0; i < nCompsA; ++i) { + delete namesA[i]; } err2: obj1.free(); @@ -1067,8 +1122,8 @@ void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { // GfxPatternColorSpace //------------------------------------------------------------------------ -GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *under) { - this->under = under; +GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) { + under = underA; } GfxPatternColorSpace::~GfxPatternColorSpace() { @@ -1084,24 +1139,24 @@ GfxColorSpace *GfxPatternColorSpace::copy() { GfxColorSpace *GfxPatternColorSpace::parse(Array *arr) { GfxPatternColorSpace *cs; - GfxColorSpace *under; + GfxColorSpace *underA; Object obj1; if (arr->getLength() != 1 && arr->getLength() != 2) { error(-1, "Bad Pattern color space"); return NULL; } - under = NULL; + underA = NULL; if (arr->getLength() == 2) { arr->get(1, &obj1); - if (!(under = GfxColorSpace::parse(&obj1))) { + if (!(underA = GfxColorSpace::parse(&obj1))) { error(-1, "Bad Pattern color space (underlying color space)"); obj1.free(); return NULL; } obj1.free(); } - cs = new GfxPatternColorSpace(under); + cs = new GfxPatternColorSpace(underA); return cs; } @@ -1122,8 +1177,8 @@ void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { // Pattern //------------------------------------------------------------------------ -GfxPattern::GfxPattern(int type) { - this->type = type; +GfxPattern::GfxPattern(int typeA) { + type = typeA; } GfxPattern::~GfxPattern() { @@ -1131,18 +1186,22 @@ GfxPattern::~GfxPattern() { GfxPattern *GfxPattern::parse(Object *obj) { GfxPattern *pattern; - Dict *dict; Object obj1; + if (obj->isDict()) { + obj->dictLookup("PatternType", &obj1); + } else if (obj->isStream()) { + obj->streamGetDict()->lookup("PatternType", &obj1); + } else { + return NULL; + } pattern = NULL; - if (obj->isStream()) { - dict = obj->streamGetDict(); - dict->lookup("PatternType", &obj1); - if (obj1.isInt() && obj1.getInt() == 1) { - pattern = new GfxTilingPattern(dict, obj); - } - obj1.free(); + if (obj1.isInt() && obj1.getInt() == 1) { + pattern = GfxTilingPattern::parse(obj); + } else if (obj1.isInt() && obj1.getInt() == 2) { + pattern = GfxShadingPattern::parse(obj); } + obj1.free(); return pattern; } @@ -1150,33 +1209,42 @@ GfxPattern *GfxPattern::parse(Object *obj) { // GfxTilingPattern //------------------------------------------------------------------------ -GfxTilingPattern::GfxTilingPattern(Dict *streamDict, Object *stream): - GfxPattern(1) -{ +GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) { + GfxTilingPattern *pat; + Dict *dict; + int paintTypeA, tilingTypeA; + double bboxA[4], matrixA[6]; + double xStepA, yStepA; + Object resDictA; Object obj1, obj2; int i; - if (streamDict->lookup("PaintType", &obj1)->isInt()) { - paintType = obj1.getInt(); + if (!patObj->isStream()) { + return NULL; + } + dict = patObj->streamGetDict(); + + if (dict->lookup("PaintType", &obj1)->isInt()) { + paintTypeA = obj1.getInt(); } else { - paintType = 1; + paintTypeA = 1; error(-1, "Invalid or missing PaintType in pattern"); } obj1.free(); - if (streamDict->lookup("TilingType", &obj1)->isInt()) { - tilingType = obj1.getInt(); + if (dict->lookup("TilingType", &obj1)->isInt()) { + tilingTypeA = obj1.getInt(); } else { - tilingType = 1; + tilingTypeA = 1; error(-1, "Invalid or missing TilingType in pattern"); } obj1.free(); - bbox[0] = bbox[1] = 0; - bbox[2] = bbox[3] = 1; - if (streamDict->lookup("BBox", &obj1)->isArray() && + bboxA[0] = bboxA[1] = 0; + bboxA[2] = bboxA[3] = 1; + if (dict->lookup("BBox", &obj1)->isArray() && obj1.arrayGetLength() == 4) { for (i = 0; i < 4; ++i) { if (obj1.arrayGet(i, &obj2)->isNum()) { - bbox[i] = obj2.getNum(); + bboxA[i] = obj2.getNum(); } obj2.free(); } @@ -1184,39 +1252,65 @@ GfxTilingPattern::GfxTilingPattern(Dict *streamDict, Object *stream): error(-1, "Invalid or missing BBox in pattern"); } obj1.free(); - if (streamDict->lookup("XStep", &obj1)->isNum()) { - xStep = obj1.getNum(); + if (dict->lookup("XStep", &obj1)->isNum()) { + xStepA = obj1.getNum(); } else { - xStep = 1; + xStepA = 1; error(-1, "Invalid or missing XStep in pattern"); } obj1.free(); - if (streamDict->lookup("YStep", &obj1)->isNum()) { - yStep = obj1.getNum(); + if (dict->lookup("YStep", &obj1)->isNum()) { + yStepA = obj1.getNum(); } else { - yStep = 1; + yStepA = 1; error(-1, "Invalid or missing YStep in pattern"); } obj1.free(); - if (!streamDict->lookup("Resources", &resDict)->isDict()) { - resDict.free(); - resDict.initNull(); + if (!dict->lookup("Resources", &resDictA)->isDict()) { + resDictA.free(); + resDictA.initNull(); error(-1, "Invalid or missing Resources in pattern"); } - matrix[0] = 1; matrix[1] = 0; - matrix[2] = 0; matrix[3] = 1; - matrix[4] = 0; matrix[5] = 0; - if (streamDict->lookup("Matrix", &obj1)->isArray() && + matrixA[0] = 1; matrixA[1] = 0; + matrixA[2] = 0; matrixA[3] = 1; + matrixA[4] = 0; matrixA[5] = 0; + if (dict->lookup("Matrix", &obj1)->isArray() && obj1.arrayGetLength() == 6) { for (i = 0; i < 6; ++i) { if (obj1.arrayGet(i, &obj2)->isNum()) { - matrix[i] = obj2.getNum(); + matrixA[i] = obj2.getNum(); } obj2.free(); } } obj1.free(); - stream->copy(&contentStream); + + pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA, + &resDictA, matrixA, patObj); + resDictA.free(); + return pat; +} + +GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA, + double *bboxA, double xStepA, double yStepA, + Object *resDictA, double *matrixA, + Object *contentStreamA): + GfxPattern(1) +{ + int i; + + paintType = paintTypeA; + tilingType = tilingTypeA; + for (i = 0; i < 4; ++i) { + bbox[i] = bboxA[i]; + } + xStep = xStepA; + yStep = yStepA; + resDictA->copy(&resDict); + for (i = 0; i < 6; ++i) { + matrix[i] = matrixA[i]; + } + contentStreamA->copy(&contentStream); } GfxTilingPattern::~GfxTilingPattern() { @@ -1225,492 +1319,679 @@ GfxTilingPattern::~GfxTilingPattern() { } GfxPattern *GfxTilingPattern::copy() { - return new GfxTilingPattern(this); + return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep, + &resDict, matrix, &contentStream); } -GfxTilingPattern::GfxTilingPattern(GfxTilingPattern *pat): - GfxPattern(1) +//------------------------------------------------------------------------ +// GfxShadingPattern +//------------------------------------------------------------------------ + +GfxShadingPattern *GfxShadingPattern::parse(Object *patObj) { + Dict *dict; + GfxShading *shadingA; + double matrixA[6]; + Object obj1, obj2; + int i; + + if (!patObj->isDict()) { + return NULL; + } + dict = patObj->getDict(); + + dict->lookup("Shading", &obj1); + shadingA = GfxShading::parse(&obj1); + obj1.free(); + if (!shadingA) { + return NULL; + } + + matrixA[0] = 1; matrixA[1] = 0; + matrixA[2] = 0; matrixA[3] = 1; + matrixA[4] = 0; matrixA[5] = 0; + if (dict->lookup("Matrix", &obj1)->isArray() && + obj1.arrayGetLength() == 6) { + for (i = 0; i < 6; ++i) { + if (obj1.arrayGet(i, &obj2)->isNum()) { + matrixA[i] = obj2.getNum(); + } + obj2.free(); + } + } + obj1.free(); + + return new GfxShadingPattern(shadingA, matrixA); +} + +GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, double *matrixA): + GfxPattern(2) { - memcpy(this, pat, sizeof(GfxTilingPattern)); - pat->resDict.copy(&resDict); - pat->contentStream.copy(&contentStream); + int i; + + shading = shadingA; + for (i = 0; i < 6; ++i) { + matrix[i] = matrixA[i]; + } +} + +GfxShadingPattern::~GfxShadingPattern() { + delete shading; +} + +GfxPattern *GfxShadingPattern::copy() { + return new GfxShadingPattern(shading->copy(), matrix); } //------------------------------------------------------------------------ -// Function +// GfxShading //------------------------------------------------------------------------ -Function::Function() { +GfxShading::GfxShading(int typeA) { + type = typeA; + colorSpace = NULL; } -Function::~Function() { +GfxShading::GfxShading(GfxShading *shading) { + int i; + + type = shading->type; + colorSpace = shading->colorSpace->copy(); + for (i = 0; i < gfxColorMaxComps; ++i) { + background.c[i] = shading->background.c[i]; + } + hasBackground = shading->hasBackground; + xMin = shading->xMin; + yMin = shading->yMin; + xMax = shading->xMax; + yMax = shading->yMax; + hasBBox = shading->hasBBox; } -Function *Function::parse(Object *funcObj) { - Function *func; +GfxShading::~GfxShading() { + if (colorSpace) { + delete colorSpace; + } +} + +GfxShading *GfxShading::parse(Object *obj) { + GfxShading *shading; Dict *dict; - int funcType; + int typeA; Object obj1; - if (funcObj->isStream()) { - dict = funcObj->streamGetDict(); - } else if (funcObj->isDict()) { - dict = funcObj->getDict(); + if (obj->isDict()) { + dict = obj->getDict(); + } else if (obj->isStream()) { + dict = obj->streamGetDict(); } else { - error(-1, "Expected function dictionary or stream"); return NULL; } - if (!dict->lookup("FunctionType", &obj1)->isInt()) { - error(-1, "Function type is missing or wrong type"); + if (!dict->lookup("ShadingType", &obj1)->isInt()) { + error(-1, "Invalid ShadingType in shading dictionary"); obj1.free(); return NULL; } - funcType = obj1.getInt(); + typeA = obj1.getInt(); obj1.free(); - if (funcType == 0) { - func = new SampledFunction(funcObj, dict); - } else if (funcType == 2) { - func = new ExponentialFunction(funcObj, dict); - } else { - error(-1, "Unimplemented function type"); - return NULL; - } - if (!func->isOk()) { - delete func; - return NULL; + switch (typeA) { + case 1: + shading = GfxFunctionShading::parse(dict); + break; + case 2: + shading = GfxAxialShading::parse(dict); + break; + case 3: + shading = GfxRadialShading::parse(dict); + break; + default: + error(-1, "Unimplemented shading type %d", typeA); + goto err1; } - return func; + return shading; + + err1: + return NULL; } -GBool Function::init(Dict *dict) { +GBool GfxShading::init(Dict *dict) { Object obj1, obj2; int i; - //----- Domain - if (!dict->lookup("Domain", &obj1)->isArray()) { - error(-1, "Function is missing domain"); - goto err2; + dict->lookup("ColorSpace", &obj1); + if (!(colorSpace = GfxColorSpace::parse(&obj1))) { + error(-1, "Bad color space in shading dictionary"); + obj1.free(); + return gFalse; } - m = obj1.arrayGetLength() / 2; - if (m > funcMaxInputs) { - error(-1, "Functions with more than %d inputs are unsupported", - funcMaxInputs); - goto err2; + obj1.free(); + + for (i = 0; i < gfxColorMaxComps; ++i) { + background.c[i] = 0; } - for (i = 0; i < m; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(-1, "Illegal value in function domain array"); - goto err1; - } - domain[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(-1, "Illegal value in function domain array"); - goto err1; + hasBackground = gFalse; + if (dict->lookup("Background", &obj1)->isArray()) { + if (obj1.arrayGetLength() == colorSpace->getNComps()) { + hasBackground = gTrue; + for (i = 0; i < colorSpace->getNComps(); ++i) { + background.c[i] = obj1.arrayGet(i, &obj2)->getNum(); + obj2.free(); + } + } else { + error(-1, "Bad Background in shading dictionary"); } - domain[i][1] = obj2.getNum(); - obj2.free(); } obj1.free(); - //----- Range - hasRange = gFalse; - n = 0; - if (dict->lookup("Range", &obj1)->isArray()) { - hasRange = gTrue; - n = obj1.arrayGetLength() / 2; - if (n > funcMaxOutputs) { - error(-1, "Functions with more than %d outputs are unsupported", - funcMaxOutputs); - goto err2; - } - for (i = 0; i < n; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(-1, "Illegal value in function range array"); - goto err1; - } - range[i][0] = obj2.getNum(); + xMin = yMin = xMax = yMax = 0; + hasBBox = gFalse; + if (dict->lookup("BBox", &obj1)->isArray()) { + if (obj1.arrayGetLength() == 4) { + hasBBox = gTrue; + xMin = obj1.arrayGet(0, &obj2)->getNum(); obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(-1, "Illegal value in function range array"); - goto err1; - } - range[i][1] = obj2.getNum(); + yMin = obj1.arrayGet(1, &obj2)->getNum(); + obj2.free(); + xMax = obj1.arrayGet(2, &obj2)->getNum(); obj2.free(); + yMax = obj1.arrayGet(3, &obj2)->getNum(); + obj2.free(); + } else { + error(-1, "Bad BBox in shading dictionary"); } - obj1.free(); } + obj1.free(); return gTrue; - - err1: - obj2.free(); - err2: - obj1.free(); - return gFalse; } //------------------------------------------------------------------------ -// SampledFunction +// GfxFunctionShading //------------------------------------------------------------------------ -SampledFunction::SampledFunction(Object *funcObj, Dict *dict) { - Stream *str; - int nSamples, sampleBits; - double sampleMul; - Object obj1, obj2; - Guint buf, bitMask; - int bits; - int s; +GfxFunctionShading::GfxFunctionShading(double x0A, double y0A, + double x1A, double y1A, + double *matrixA, + Function **funcsA, int nFuncsA): + GfxShading(1) +{ int i; - samples = NULL; - ok = gFalse; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; + x0 = x0A; + y0 = y0A; + x1 = x1A; + y1 = y1A; + for (i = 0; i < 6; ++i) { + matrix[i] = matrixA[i]; } - if (!hasRange) { - error(-1, "Type 0 function is missing range"); - goto err1; + nFuncs = nFuncsA; + for (i = 0; i < nFuncs; ++i) { + funcs[i] = funcsA[i]; } +} - //----- get the stream - if (!funcObj->isStream()) { - error(-1, "Type 0 function isn't a stream"); - goto err1; +GfxFunctionShading::GfxFunctionShading(GfxFunctionShading *shading): + GfxShading(shading) +{ + int i; + + x0 = shading->x0; + y0 = shading->y0; + x1 = shading->x1; + y1 = shading->y1; + for (i = 0; i < 6; ++i) { + matrix[i] = shading->matrix[i]; } - str = funcObj->getStream(); + nFuncs = shading->nFuncs; + for (i = 0; i < nFuncs; ++i) { + funcs[i] = shading->funcs[i]->copy(); + } +} - //----- Size - if (!dict->lookup("Size", &obj1)->isArray() || - obj1.arrayGetLength() != m) { - error(-1, "Function has missing or invalid size array"); - goto err2; +GfxFunctionShading::~GfxFunctionShading() { + int i; + + for (i = 0; i < nFuncs; ++i) { + delete funcs[i]; } - for (i = 0; i < m; ++i) { - obj1.arrayGet(i, &obj2); - if (!obj2.isInt()) { - error(-1, "Illegal value in function size array"); - goto err3; - } - sampleSize[i] = obj2.getInt(); +} + +GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) { + GfxFunctionShading *shading; + double x0A, y0A, x1A, y1A; + double matrixA[6]; + Function *funcsA[gfxColorMaxComps]; + int nFuncsA; + Object obj1, obj2; + int i; + + x0A = y0A = 0; + x1A = y1A = 1; + if (dict->lookup("Domain", &obj1)->isArray() && + obj1.arrayGetLength() == 4) { + x0A = obj1.arrayGet(0, &obj2)->getNum(); + obj2.free(); + y0A = obj1.arrayGet(1, &obj2)->getNum(); + obj2.free(); + x1A = obj1.arrayGet(2, &obj2)->getNum(); + obj2.free(); + y1A = obj1.arrayGet(3, &obj2)->getNum(); obj2.free(); } obj1.free(); - //----- BitsPerSample - if (!dict->lookup("BitsPerSample", &obj1)->isInt()) { - error(-1, "Function has missing or invalid BitsPerSample"); - goto err2; + matrixA[0] = 1; matrixA[1] = 0; + matrixA[2] = 0; matrixA[3] = 1; + matrixA[4] = 0; matrixA[5] = 0; + if (dict->lookup("Matrix", &obj1)->isArray() && + obj1.arrayGetLength() == 6) { + matrixA[0] = obj1.arrayGet(0, &obj2)->getNum(); + obj2.free(); + matrixA[1] = obj1.arrayGet(1, &obj2)->getNum(); + obj2.free(); + matrixA[2] = obj1.arrayGet(2, &obj2)->getNum(); + obj2.free(); + matrixA[3] = obj1.arrayGet(3, &obj2)->getNum(); + obj2.free(); + matrixA[4] = obj1.arrayGet(4, &obj2)->getNum(); + obj2.free(); + matrixA[5] = obj1.arrayGet(5, &obj2)->getNum(); + obj2.free(); } - sampleBits = obj1.getInt(); - sampleMul = 1.0 / (double)((1 << sampleBits) - 1); obj1.free(); - //----- Encode - if (dict->lookup("Encode", &obj1)->isArray() && - obj1.arrayGetLength() == 2*m) { - for (i = 0; i < m; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(-1, "Illegal value in function encode array"); - goto err3; - } - encode[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(-1, "Illegal value in function encode array"); - goto err3; + dict->lookup("Function", &obj1); + if (obj1.isArray()) { + nFuncsA = obj1.arrayGetLength(); + if (nFuncsA > gfxColorMaxComps) { + error(-1, "Invalid Function array in shading dictionary"); + goto err1; + } + for (i = 0; i < nFuncsA; ++i) { + obj1.arrayGet(i, &obj2); + if (!(funcsA[i] = Function::parse(&obj2))) { + goto err2; } - encode[i][1] = obj2.getNum(); obj2.free(); } } else { - for (i = 0; i < m; ++i) { - encode[i][0] = 0; - encode[i][1] = sampleSize[i] - 1; + nFuncsA = 1; + if (!(funcsA[0] = Function::parse(&obj1))) { + goto err1; } } obj1.free(); - //----- Decode - if (dict->lookup("Decode", &obj1)->isArray() && - obj1.arrayGetLength() == 2*n) { - for (i = 0; i < n; ++i) { - obj1.arrayGet(2*i, &obj2); - if (!obj2.isNum()) { - error(-1, "Illegal value in function decode array"); - goto err3; - } - decode[i][0] = obj2.getNum(); - obj2.free(); - obj1.arrayGet(2*i+1, &obj2); - if (!obj2.isNum()) { - error(-1, "Illegal value in function decode array"); - goto err3; + shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA, + funcsA, nFuncsA); + if (!shading->init(dict)) { + delete shading; + return NULL; + } + return shading; + + err2: + obj2.free(); + err1: + obj1.free(); + return NULL; +} + +GfxShading *GfxFunctionShading::copy() { + return new GfxFunctionShading(this); +} + +void GfxFunctionShading::getColor(double x, double y, GfxColor *color) { + double in[2]; + int i; + + in[0] = x; + in[1] = y; + for (i = 0; i < nFuncs; ++i) { + funcs[i]->transform(in, &color->c[i]); + } +} + +//------------------------------------------------------------------------ +// GfxAxialShading +//------------------------------------------------------------------------ + +GfxAxialShading::GfxAxialShading(double x0A, double y0A, + double x1A, double y1A, + double t0A, double t1A, + Function **funcsA, int nFuncsA, + GBool extend0A, GBool extend1A): + GfxShading(2) +{ + int i; + + x0 = x0A; + y0 = y0A; + x1 = x1A; + y1 = y1A; + t0 = t0A; + t1 = t1A; + nFuncs = nFuncsA; + for (i = 0; i < nFuncs; ++i) { + funcs[i] = funcsA[i]; + } + extend0 = extend0A; + extend1 = extend1A; +} + +GfxAxialShading::GfxAxialShading(GfxAxialShading *shading): + GfxShading(shading) +{ + int i; + + x0 = shading->x0; + y0 = shading->y0; + x1 = shading->x1; + y1 = shading->y1; + t0 = shading->t0; + y1 = shading->t1; + nFuncs = shading->nFuncs; + for (i = 0; i < nFuncs; ++i) { + funcs[i] = shading->funcs[i]->copy(); + } + extend0 = shading->extend0; + extend1 = shading->extend1; +} + +GfxAxialShading::~GfxAxialShading() { + int i; + + for (i = 0; i < nFuncs; ++i) { + delete funcs[i]; + } +} + +GfxAxialShading *GfxAxialShading::parse(Dict *dict) { + GfxAxialShading *shading; + double x0A, y0A, x1A, y1A; + double t0A, t1A; + Function *funcsA[gfxColorMaxComps]; + int nFuncsA; + GBool extend0A, extend1A; + Object obj1, obj2; + int i; + + x0A = y0A = x1A = y1A = 0; + if (dict->lookup("Coords", &obj1)->isArray() && + obj1.arrayGetLength() == 4) { + x0A = obj1.arrayGet(0, &obj2)->getNum(); + obj2.free(); + y0A = obj1.arrayGet(1, &obj2)->getNum(); + obj2.free(); + x1A = obj1.arrayGet(2, &obj2)->getNum(); + obj2.free(); + y1A = obj1.arrayGet(3, &obj2)->getNum(); + obj2.free(); + } else { + error(-1, "Missing or invalid Coords in shading dictionary"); + goto err1; + } + obj1.free(); + + t0A = 0; + t1A = 1; + if (dict->lookup("Domain", &obj1)->isArray() && + obj1.arrayGetLength() == 2) { + t0A = obj1.arrayGet(0, &obj2)->getNum(); + obj2.free(); + t1A = obj1.arrayGet(1, &obj2)->getNum(); + obj2.free(); + } + obj1.free(); + + dict->lookup("Function", &obj1); + if (obj1.isArray()) { + nFuncsA = obj1.arrayGetLength(); + if (nFuncsA > gfxColorMaxComps) { + error(-1, "Invalid Function array in shading dictionary"); + goto err1; + } + for (i = 0; i < nFuncsA; ++i) { + obj1.arrayGet(i, &obj2); + if (!(funcsA[i] = Function::parse(&obj2))) { + obj1.free(); + obj2.free(); + goto err1; } - decode[i][1] = obj2.getNum(); obj2.free(); } } else { - for (i = 0; i < n; ++i) { - decode[i][0] = range[i][0]; - decode[i][1] = range[i][1]; + nFuncsA = 1; + if (!(funcsA[0] = Function::parse(&obj1))) { + obj1.free(); + goto err1; } } obj1.free(); - //----- samples - nSamples = n; - for (i = 0; i < m; ++i) - nSamples *= sampleSize[i]; - samples = (double *)gmalloc(nSamples * sizeof(double)); - buf = 0; - bits = 0; - bitMask = (1 << sampleBits) - 1; - str->reset(); - for (i = 0; i < nSamples; ++i) { - if (sampleBits == 8) { - s = str->getChar(); - } else if (sampleBits == 16) { - s = str->getChar(); - s = (s << 8) + str->getChar(); - } else if (sampleBits == 32) { - s = str->getChar(); - s = (s << 8) + str->getChar(); - s = (s << 8) + str->getChar(); - s = (s << 8) + str->getChar(); - } else { - while (bits < sampleBits) { - buf = (buf << 8) | (str->getChar() & 0xff); - bits += 8; - } - s = (buf >> (bits - sampleBits)) & bitMask; - bits -= sampleBits; - } - samples[i] = (double)s * sampleMul; + extend0A = extend1A = gFalse; + if (dict->lookup("Extend", &obj1)->isArray() && + obj1.arrayGetLength() == 2) { + extend0A = obj1.arrayGet(0, &obj2)->getBool(); + obj2.free(); + extend1A = obj1.arrayGet(1, &obj2)->getBool(); + obj2.free(); } - str->close(); + obj1.free(); - ok = gTrue; - return; + shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A, + funcsA, nFuncsA, extend0A, extend1A); + if (!shading->init(dict)) { + delete shading; + return NULL; + } + return shading; - err3: - obj2.free(); - err2: - obj1.free(); err1: - return; + return NULL; +} + +GfxShading *GfxAxialShading::copy() { + return new GfxAxialShading(this); } -SampledFunction::~SampledFunction() { - if (samples) { - gfree(samples); +void GfxAxialShading::getColor(double t, GfxColor *color) { + int i; + + // NB: there can be one function with n outputs or n functions with + // one output each (where n = number of color components) + for (i = 0; i < nFuncs; ++i) { + funcs[i]->transform(&t, &color->c[i]); } } -SampledFunction::SampledFunction(SampledFunction *func) { - int nSamples, i; +//------------------------------------------------------------------------ +// GfxRadialShading +//------------------------------------------------------------------------ - memcpy(this, func, sizeof(SampledFunction)); +GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A, + double x1A, double y1A, double r1A, + double t0A, double t1A, + Function **funcsA, int nFuncsA, + GBool extend0A, GBool extend1A): + GfxShading(3) +{ + int i; - nSamples = n; - for (i = 0; i < m; ++i) { - nSamples *= sampleSize[i]; + x0 = x0A; + y0 = y0A; + r0 = r0A; + x1 = x1A; + y1 = y1A; + r1 = r1A; + t0 = t0A; + t1 = t1A; + nFuncs = nFuncsA; + for (i = 0; i < nFuncs; ++i) { + funcs[i] = funcsA[i]; } - samples = (double *)gmalloc(nSamples * sizeof(double)); - memcpy(samples, func->samples, nSamples * sizeof(double)); + extend0 = extend0A; + extend1 = extend1A; } -void SampledFunction::transform(double *in, double *out) { - double e[4]; - double s; - double x0, x1; - int e0, e1; - double efrac; +GfxRadialShading::GfxRadialShading(GfxRadialShading *shading): + GfxShading(shading) +{ int i; - // map input values into sample array - for (i = 0; i < m; ++i) { - e[i] = ((in[i] - domain[i][0]) / (domain[i][1] - domain[i][0])) * - (encode[i][1] - encode[i][0]) + encode[i][0]; - if (e[i] < 0) { - e[i] = 0; - } else if (e[i] > sampleSize[i] - 1) { - e[i] = sampleSize[i] - 1; - } + x0 = shading->x0; + y0 = shading->y0; + r0 = shading->r0; + x1 = shading->x1; + y1 = shading->y1; + r1 = shading->r1; + t0 = shading->t0; + y1 = shading->t1; + nFuncs = shading->nFuncs; + for (i = 0; i < nFuncs; ++i) { + funcs[i] = shading->funcs[i]->copy(); } + extend0 = shading->extend0; + extend1 = shading->extend1; +} - for (i = 0; i < n; ++i) { +GfxRadialShading::~GfxRadialShading() { + int i; - // m-linear interpolation - // (only m=1 is currently supported) - e0 = (int)floor(e[0]); - e1 = (int)ceil(e[0]); - efrac = e[0] - e0; - x0 = samples[e0 * n + i]; - x1 = samples[e1 * n + i]; - s = (1 - efrac) * x0 + efrac * x1; - - // map output values to range - out[i] = s * (decode[i][1] - decode[i][0]) + decode[i][0]; - if (out[i] < range[i][0]) { - out[i] = range[i][0]; - } else if (out[i] > range[i][1]) { - out[i] = range[i][1]; - } + for (i = 0; i < nFuncs; ++i) { + delete funcs[i]; } } -//------------------------------------------------------------------------ -// ExponentialFunction -//------------------------------------------------------------------------ - -ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) { +GfxRadialShading *GfxRadialShading::parse(Dict *dict) { + GfxRadialShading *shading; + double x0A, y0A, r0A, x1A, y1A, r1A; + double t0A, t1A; + Function *funcsA[gfxColorMaxComps]; + int nFuncsA; + GBool extend0A, extend1A; Object obj1, obj2; - GBool hasN; int i; - ok = gFalse; - hasN = gFalse; - - //----- initialize the generic stuff - if (!init(dict)) { - goto err1; - } - if (m != 1) { - error(-1, "Exponential function with more than one input"); + x0A = y0A = r0A = x1A = y1A = r1A = 0; + if (dict->lookup("Coords", &obj1)->isArray() && + obj1.arrayGetLength() == 6) { + x0A = obj1.arrayGet(0, &obj2)->getNum(); + obj2.free(); + y0A = obj1.arrayGet(1, &obj2)->getNum(); + obj2.free(); + r0A = obj1.arrayGet(2, &obj2)->getNum(); + obj2.free(); + x1A = obj1.arrayGet(3, &obj2)->getNum(); + obj2.free(); + y1A = obj1.arrayGet(4, &obj2)->getNum(); + obj2.free(); + r1A = obj1.arrayGet(5, &obj2)->getNum(); + obj2.free(); + } else { + error(-1, "Missing or invalid Coords in shading dictionary"); goto err1; } + obj1.free(); - //----- default values - for (i = 0; i < funcMaxOutputs; ++i) { - c0[i] = 0; - c1[i] = 1; + t0A = 0; + t1A = 1; + if (dict->lookup("Domain", &obj1)->isArray() && + obj1.arrayGetLength() == 2) { + t0A = obj1.arrayGet(0, &obj2)->getNum(); + obj2.free(); + t1A = obj1.arrayGet(1, &obj2)->getNum(); + obj2.free(); } + obj1.free(); - //----- C0 - if (dict->lookup("C0", &obj1)->isArray()) { - if (!hasN) { - n = obj1.arrayGetLength(); - } else if (obj1.arrayGetLength() != n) { - error(-1, "Function's C0 array is wrong length"); - goto err2; + dict->lookup("Function", &obj1); + if (obj1.isArray()) { + nFuncsA = obj1.arrayGetLength(); + if (nFuncsA > gfxColorMaxComps) { + error(-1, "Invalid Function array in shading dictionary"); + goto err1; } - for (i = 0; i < n; ++i) { + for (i = 0; i < nFuncsA; ++i) { obj1.arrayGet(i, &obj2); - if (!obj2.isNum()) { - error(-1, "Illegal value in function C0 array"); - goto err3; + if (!(funcsA[i] = Function::parse(&obj2))) { + obj1.free(); + obj2.free(); + goto err1; } - c0[i] = obj2.getNum(); obj2.free(); } - obj1.free(); - } - - //----- C1 - if (dict->lookup("C1", &obj1)->isArray()) { - if (!hasN) { - n = obj1.arrayGetLength(); - } else if (obj1.arrayGetLength() != n) { - error(-1, "Function's C1 array is wrong length"); - goto err2; - } - for (i = 0; i < n; ++i) { - obj1.arrayGet(i, &obj2); - if (!obj2.isNum()) { - error(-1, "Illegal value in function C1 array"); - goto err3; - } - c1[i] = obj2.getNum(); - obj2.free(); + } else { + nFuncsA = 1; + if (!(funcsA[0] = Function::parse(&obj1))) { + obj1.free(); + goto err1; } - obj1.free(); } + obj1.free(); - //----- N (exponent) - if (!dict->lookup("N", &obj1)->isNum()) { - error(-1, "Function has missing or invalid N"); - goto err2; + extend0A = extend1A = gFalse; + if (dict->lookup("Extend", &obj1)->isArray() && + obj1.arrayGetLength() == 2) { + extend0A = obj1.arrayGet(0, &obj2)->getBool(); + obj2.free(); + extend1A = obj1.arrayGet(1, &obj2)->getBool(); + obj2.free(); } - e = obj1.getNum(); obj1.free(); - ok = gTrue; - return; + shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A, + funcsA, nFuncsA, extend0A, extend1A); + if (!shading->init(dict)) { + delete shading; + return NULL; + } + return shading; - err3: - obj2.free(); - err2: - obj1.free(); err1: - return; -} - -ExponentialFunction::~ExponentialFunction() { + return NULL; } -ExponentialFunction::ExponentialFunction(ExponentialFunction *func) { - memcpy(this, func, sizeof(ExponentialFunction)); +GfxShading *GfxRadialShading::copy() { + return new GfxRadialShading(this); } -void ExponentialFunction::transform(double *in, double *out) { - double x; +void GfxRadialShading::getColor(double t, GfxColor *color) { int i; - if (in[0] < domain[0][0]) { - x = domain[0][0]; - } else if (in[0] > domain[0][1]) { - x = domain[0][1]; - } else { - x = in[0]; + // NB: there can be one function with n outputs or n functions with + // one output each (where n = number of color components) + for (i = 0; i < nFuncs; ++i) { + funcs[i]->transform(&t, &color->c[i]); } - for (i = 0; i < n; ++i) { - out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]); - if (hasRange) { - if (out[i] < range[i][0]) { - out[i] = range[i][0]; - } else if (out[i] > range[i][1]) { - out[i] = range[i][1]; - } - } - } - return; } //------------------------------------------------------------------------ // GfxImageColorMap //------------------------------------------------------------------------ -GfxImageColorMap::GfxImageColorMap(int bits, Object *decode, - GfxColorSpace *colorSpace) { +GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode, + GfxColorSpace *colorSpaceA) { GfxIndexedColorSpace *indexedCS; GfxSeparationColorSpace *sepCS; int maxPixel, indexHigh; Guchar *lookup2; Function *sepFunc; Object obj; - double x; + double x[gfxColorMaxComps]; double y[gfxColorMaxComps]; int i, j, k; + int maxPixelForAlloc; ok = gTrue; // bits per component and color space - this->bits = bits; + bits = bitsA; maxPixel = (1 << bits) - 1; - this->colorSpace = colorSpace; + maxPixelForAlloc = (1 << (bits>8?bits:8)); + colorSpace = colorSpaceA; // get decode map if (decode->isNull()) { @@ -1739,18 +2020,6 @@ GfxImageColorMap::GfxImageColorMap(int bits, Object *decode, goto err1; } -#if 0 //~ - // handle the case where fewer than 2^n palette entries of an n-bit - // indexed color space are populated (this happens, e.g., in files - // optimized by Distiller) - if (colorSpace->getMode() == csIndexed) { - i = ((GfxIndexedColorSpace *)colorSpace)->getIndexHigh(); - if (i < maxPixel) { - maxPixel = i; - } - } -#endif - // Construct a lookup table -- this stores pre-computed decoded // values for each component, i.e., the result of applying the // decode mapping to each possible image pixel component value. @@ -1768,29 +2037,35 @@ GfxImageColorMap::GfxImageColorMap(int bits, Object *decode, colorSpace2 = indexedCS->getBase(); indexHigh = indexedCS->getIndexHigh(); nComps2 = colorSpace2->getNComps(); - lookup = (double *)gmalloc((indexHigh + 1) * nComps2 * sizeof(double)); + lookup = (double *)gmalloc((maxPixelForAlloc + 1) * nComps2 * sizeof(double)); lookup2 = indexedCS->getLookup(); - for (i = 0; i <= indexHigh; ++i) { - j = (int)(decodeLow[0] +(i * decodeRange[0]) / maxPixel + 0.5); + colorSpace2->getDefaultRanges(x, y, indexHigh); + for (i = 0; i <= maxPixel; ++i) { + j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5); + if (j < 0) { + j = 0; + } else if (j > indexHigh) { + j = indexHigh; + } for (k = 0; k < nComps2; ++k) { - lookup[i*nComps2 + k] = lookup2[i*nComps2 + k] / 255.0; + lookup[i*nComps2 + k] = x[k] + (lookup2[j*nComps2 + k] / 255.0) * y[k]; } } } else if (colorSpace->getMode() == csSeparation) { sepCS = (GfxSeparationColorSpace *)colorSpace; colorSpace2 = sepCS->getAlt(); nComps2 = colorSpace2->getNComps(); - lookup = (double *)gmalloc((maxPixel + 1) * nComps2 * sizeof(double)); + lookup = (double *)gmalloc((maxPixelForAlloc + 1) * nComps2 * sizeof(double)); sepFunc = sepCS->getFunc(); for (i = 0; i <= maxPixel; ++i) { - x = decodeLow[0] + (i * decodeRange[0]) / maxPixel; - sepFunc->transform(&x, y); + x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel; + sepFunc->transform(x, y); for (k = 0; k < nComps2; ++k) { lookup[i*nComps2 + k] = y[k]; } } } else { - lookup = (double *)gmalloc((maxPixel + 1) * nComps * sizeof(double)); + lookup = (double *)gmalloc((maxPixelForAlloc + 1) * nComps * sizeof(double)); for (i = 0; i <= maxPixel; ++i) { for (k = 0; k < nComps; ++k) { lookup[i*nComps + k] = decodeLow[k] + @@ -1807,6 +2082,34 @@ GfxImageColorMap::GfxImageColorMap(int bits, Object *decode, ok = gFalse; } +GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) { + int n, i; + + colorSpace = colorMap->colorSpace->copy(); + bits = colorMap->bits; + nComps = colorMap->nComps; + nComps2 = colorMap->nComps2; + colorSpace2 = NULL; + lookup = NULL; + n = 1 << bits; + if (colorSpace->getMode() == csIndexed) { + colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase(); + n = n * nComps2 * sizeof(double); + } else if (colorSpace->getMode() == csSeparation) { + colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt(); + n = n * nComps2 * sizeof(double); + } else { + n = n * nComps * sizeof(double); + } + lookup = (double *)gmalloc(n); + memcpy(lookup, colorMap->lookup, n); + for (i = 0; i < nComps; ++i) { + decodeLow[i] = colorMap->decodeLow[i]; + decodeRange[i] = colorMap->decodeRange[i]; + } + ok = gTrue; +} + GfxImageColorMap::~GfxImageColorMap() { delete colorSpace; gfree(lookup); @@ -1869,6 +2172,15 @@ void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) { } } +void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) { + int maxPixel, i; + + maxPixel = (1 << bits) - 1; + for (i = 0; i < nComps; ++i) { + color->c[i] = decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel; + } +} + //------------------------------------------------------------------------ // GfxSubpath and GfxPath //------------------------------------------------------------------------ @@ -1943,6 +2255,15 @@ void GfxSubpath::close() { closed = gTrue; } +void GfxSubpath::offset(double dx, double dy) { + int i; + + for (i = 0; i < n; ++i) { + x[i] += dx; + y[i] += dy; + } +} + GfxPath::GfxPath() { justMoved = gFalse; size = 16; @@ -2009,56 +2330,94 @@ void GfxPath::curveTo(double x1, double y1, double x2, double y2, subpaths[n-1]->curveTo(x1, y1, x2, y2, x3, y3); } +void GfxPath::close() { + // this is necessary to handle the pathological case of + // moveto/closepath/clip, which defines an empty clipping region + if (justMoved) { + if (n >= size) { + size += 16; + subpaths = (GfxSubpath **) + grealloc(subpaths, size * sizeof(GfxSubpath *)); + } + subpaths[n] = new GfxSubpath(firstX, firstY); + ++n; + justMoved = gFalse; + } + subpaths[n-1]->close(); +} + +void GfxPath::append(GfxPath *path) { + int i; + + if (n + path->n > size) { + size = n + path->n; + subpaths = (GfxSubpath **) + grealloc(subpaths, size * sizeof(GfxSubpath *)); + } + for (i = 0; i < path->n; ++i) { + subpaths[n++] = path->subpaths[i]->copy(); + } + justMoved = gFalse; +} + +void GfxPath::offset(double dx, double dy) { + int i; + + for (i = 0; i < n; ++i) { + subpaths[i]->offset(dx, dy); + } +} //------------------------------------------------------------------------ // GfxState //------------------------------------------------------------------------ -GfxState::GfxState(double dpi, double px1a, double py1a, - double px2a, double py2a, int rotate, GBool upsideDown) { - double k; +GfxState::GfxState(double hDPI, double vDPI, PDFRectangle *pageBox, + int rotate, GBool upsideDown) { + double kx, ky; - px1 = px1a; - py1 = py1a; - px2 = px2a; - py2 = py2a; - k = dpi / 72.0; + px1 = pageBox->x1; + py1 = pageBox->y1; + px2 = pageBox->x2; + py2 = pageBox->y2; + kx = hDPI / 72.0; + ky = vDPI / 72.0; if (rotate == 90) { ctm[0] = 0; - ctm[1] = upsideDown ? k : -k; - ctm[2] = k; + ctm[1] = upsideDown ? ky : -ky; + ctm[2] = kx; ctm[3] = 0; - ctm[4] = -k * py1; - ctm[5] = k * (upsideDown ? -px1 : px2); - pageWidth = k * (py2 - py1); - pageHeight = k * (px2 - px1); + ctm[4] = -kx * py1; + ctm[5] = ky * (upsideDown ? -px1 : px2); + pageWidth = kx * (py2 - py1); + pageHeight = ky * (px2 - px1); } else if (rotate == 180) { - ctm[0] = -k; + ctm[0] = -kx; ctm[1] = 0; ctm[2] = 0; - ctm[3] = upsideDown ? k : -k; - ctm[4] = k * px2; - ctm[5] = k * (upsideDown ? -py1 : py2); - pageWidth = k * (px2 - px1); - pageHeight = k * (py2 - py1); + ctm[3] = upsideDown ? ky : -ky; + ctm[4] = kx * px2; + ctm[5] = ky * (upsideDown ? -py1 : py2); + pageWidth = kx * (px2 - px1); + pageHeight = ky * (py2 - py1); } else if (rotate == 270) { ctm[0] = 0; - ctm[1] = upsideDown ? -k : k; - ctm[2] = -k; + ctm[1] = upsideDown ? -ky : ky; + ctm[2] = -kx; ctm[3] = 0; - ctm[4] = k * py2; - ctm[5] = k * (upsideDown ? px2 : -px1); - pageWidth = k * (py2 - py1); - pageHeight = k * (px2 - px1); + ctm[4] = kx * py2; + ctm[5] = ky * (upsideDown ? px2 : -px1); + pageWidth = kx * (py2 - py1); + pageHeight = ky * (px2 - px1); } else { - ctm[0] = k; + ctm[0] = kx; ctm[1] = 0; ctm[2] = 0; - ctm[3] = upsideDown ? -k : k; - ctm[4] = -k * px1; - ctm[5] = k * (upsideDown ? py2 : -py1); - pageWidth = k * (px2 - px1); - pageHeight = k * (py2 - py1); + ctm[3] = upsideDown ? -ky : ky; + ctm[4] = -kx * px1; + ctm[5] = ky * (upsideDown ? py2 : -py1); + pageWidth = kx * (px2 - px1); + pageHeight = ky * (py2 - py1); } fillColorSpace = new GfxDeviceGrayColorSpace(); @@ -2074,7 +2433,7 @@ GfxState::GfxState(double dpi, double px1a, double py1a, lineDash = NULL; lineDashLength = 0; lineDashStart = 0; - flatness = 0; + flatness = 1; lineJoin = 0; lineCap = 0; miterLimit = 10; @@ -2095,6 +2454,11 @@ GfxState::GfxState(double dpi, double px1a, double py1a, curX = curY = 0; lineX = lineY = 0; + clipXMin = 0; + clipYMin = 0; + clipXMax = pageWidth; + clipYMax = pageHeight; + saved = NULL; } @@ -2112,7 +2476,10 @@ GfxState::~GfxState() { delete strokePattern; } gfree(lineDash); - delete path; + if (path) { + // this gets set to NULL by restore() + delete path; + } if (saved) { delete saved; } @@ -2137,10 +2504,75 @@ GfxState::GfxState(GfxState *state) { lineDash = (double *)gmalloc(lineDashLength * sizeof(double)); memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double)); } - path = state->path->copy(); saved = NULL; } +void GfxState::setPath(GfxPath *pathA) { + delete path; + path = pathA; +} + +void GfxState::getUserClipBBox(double *xMin, double *yMin, + double *xMax, double *yMax) { + double ictm[6]; + double xMin1, yMin1, xMax1, yMax1, det, tx, ty; + + // invert the CTM + det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]); + ictm[0] = ctm[3] * det; + ictm[1] = -ctm[1] * det; + ictm[2] = -ctm[2] * det; + ictm[3] = ctm[0] * det; + ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; + ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; + + // transform all four corners of the clip bbox; find the min and max + // x and y values + xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4]; + yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5]; + tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4]; + ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5]; + if (tx < xMin1) { + xMin1 = tx; + } else if (tx > xMax1) { + xMax1 = tx; + } + if (ty < yMin1) { + yMin1 = ty; + } else if (ty > yMax1) { + yMax1 = ty; + } + tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4]; + ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5]; + if (tx < xMin1) { + xMin1 = tx; + } else if (tx > xMax1) { + xMax1 = tx; + } + if (ty < yMin1) { + yMin1 = ty; + } else if (ty > yMax1) { + yMax1 = ty; + } + tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4]; + ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5]; + if (tx < xMin1) { + xMin1 = tx; + } else if (tx > xMax1) { + xMax1 = tx; + } + if (ty < yMin1) { + yMin1 = ty; + } else if (ty > yMax1) { + yMax1 = ty; + } + + *xMin = xMin1; + *yMin = yMin1; + *xMax = xMax1; + *yMax = yMax1; +} + double GfxState::transformWidth(double w) { double x, y; @@ -2169,12 +2601,23 @@ void GfxState::getFontTransMat(double *m11, double *m12, void GfxState::setCTM(double a, double b, double c, double d, double e, double f) { + int i; + ctm[0] = a; ctm[1] = b; ctm[2] = c; ctm[3] = d; ctm[4] = e; ctm[5] = f; + + // avoid FP exceptions on badly messed up PDF files + for (i = 0; i < 6; ++i) { + if (ctm[i] > 1e10) { + ctm[i] = 1e10; + } else if (ctm[i] < -1e10) { + ctm[i] = -1e10; + } + } } void GfxState::concatCTM(double a, double b, double c, @@ -2183,6 +2626,7 @@ void GfxState::concatCTM(double a, double b, double c, double b1 = ctm[1]; double c1 = ctm[2]; double d1 = ctm[3]; + int i; ctm[0] = a * a1 + b * c1; ctm[1] = a * b1 + b * d1; @@ -2190,6 +2634,15 @@ void GfxState::concatCTM(double a, double b, double c, ctm[3] = c * b1 + d * d1; ctm[4] = e * a1 + f * c1 + ctm[4]; ctm[5] = e * b1 + f * d1 + ctm[5]; + + // avoid FP exceptions on badly messed up PDF files + for (i = 0; i < 6; ++i) { + if (ctm[i] > 1e10) { + ctm[i] = 1e10; + } else if (ctm[i] < -1e10) { + ctm[i] = -1e10; + } + } } void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) { @@ -2233,12 +2686,45 @@ void GfxState::clearPath() { path = new GfxPath(); } -void GfxState::textShift(double tx) { - double dx, dy; - - textTransformDelta(tx, 0, &dx, &dy); - curX += dx; - curY += dy; +void GfxState::clip() { + double xMin, yMin, xMax, yMax, x, y; + GfxSubpath *subpath; + int i, j; + + xMin = xMax = yMin = yMax = 0; // make gcc happy + for (i = 0; i < path->getNumSubpaths(); ++i) { + subpath = path->getSubpath(i); + for (j = 0; j < subpath->getNumPoints(); ++j) { + transform(subpath->getX(j), subpath->getY(j), &x, &y); + if (i == 0 && j == 0) { + xMin = xMax = x; + yMin = yMax = y; + } else { + if (x < xMin) { + xMin = x; + } else if (x > xMax) { + xMax = x; + } + if (y < yMin) { + yMin = y; + } else if (y > yMax) { + yMax = y; + } + } + } + } + if (xMin > clipXMin) { + clipXMin = xMin; + } + if (yMin > clipYMin) { + clipYMin = yMin; + } + if (xMax < clipXMax) { + clipXMax = xMax; + } + if (yMax < clipYMax) { + clipYMax = yMax; + } } void GfxState::textShift(double tx, double ty) { @@ -2249,6 +2735,11 @@ void GfxState::textShift(double tx, double ty) { curY += dy; } +void GfxState::shift(double dx, double dy) { + curX += dx; + curY += dy; +} + GfxState *GfxState::save() { GfxState *newState; @@ -2262,10 +2753,21 @@ GfxState *GfxState::restore() { if (saved) { oldState = saved; + + // these attributes aren't saved/restored by the q/Q operators + oldState->path = path; + oldState->curX = curX; + oldState->curY = curY; + oldState->lineX = lineX; + oldState->lineY = lineY; + + path = NULL; saved = NULL; delete this; + } else { oldState = this; } + return oldState; }