mirror of
https://invent.kde.org/graphics/okular
synced 2024-09-19 07:51:27 +00:00
82cd404f88
svn path=/trunk/kdegraphics/kpdf/; revision=340184
2772 lines
65 KiB
C++
2772 lines
65 KiB
C++
//========================================================================
|
|
//
|
|
// GfxState.cc
|
|
//
|
|
// Copyright 1996-2003 Glyph & Cog, LLC
|
|
//
|
|
//========================================================================
|
|
|
|
#include <aconf.h>
|
|
|
|
#ifdef USE_GCC_PRAGMAS
|
|
#pragma implementation
|
|
#endif
|
|
|
|
#include <stddef.h>
|
|
#include <math.h>
|
|
#include <string.h> // for memcpy()
|
|
#include "gmem.h"
|
|
#include "Error.h"
|
|
#include "Object.h"
|
|
#include "Array.h"
|
|
#include "Page.h"
|
|
#include "GfxState.h"
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
static inline double clip01(double x) {
|
|
return (x < 0) ? 0 : ((x > 1) ? 1 : x);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
static const char *gfxColorSpaceModeNames[] = {
|
|
"DeviceGray",
|
|
"CalGray",
|
|
"DeviceRGB",
|
|
"CalRGB",
|
|
"DeviceCMYK",
|
|
"Lab",
|
|
"ICCBased",
|
|
"Indexed",
|
|
"Separation",
|
|
"DeviceN",
|
|
"Pattern"
|
|
};
|
|
|
|
#define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxColorSpace
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxColorSpace::GfxColorSpace() {
|
|
}
|
|
|
|
GfxColorSpace::~GfxColorSpace() {
|
|
}
|
|
|
|
GfxColorSpace *GfxColorSpace::parse(Object *csObj) {
|
|
GfxColorSpace *cs;
|
|
Object obj1;
|
|
|
|
cs = NULL;
|
|
if (csObj->isName()) {
|
|
if (csObj->isName("DeviceGray") || csObj->isName("G")) {
|
|
cs = new GfxDeviceGrayColorSpace();
|
|
} else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {
|
|
cs = new GfxDeviceRGBColorSpace();
|
|
} else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {
|
|
cs = new GfxDeviceCMYKColorSpace();
|
|
} else if (csObj->isName("Pattern")) {
|
|
cs = new GfxPatternColorSpace(NULL);
|
|
} else {
|
|
error(-1, "Bad color space '%s'", csObj->getName());
|
|
}
|
|
} else if (csObj->isArray()) {
|
|
csObj->arrayGet(0, &obj1);
|
|
if (obj1.isName("DeviceGray") || obj1.isName("G")) {
|
|
cs = new GfxDeviceGrayColorSpace();
|
|
} else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
|
|
cs = new GfxDeviceRGBColorSpace();
|
|
} else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
|
|
cs = new GfxDeviceCMYKColorSpace();
|
|
} else if (obj1.isName("CalGray")) {
|
|
cs = GfxCalGrayColorSpace::parse(csObj->getArray());
|
|
} else if (obj1.isName("CalRGB")) {
|
|
cs = GfxCalRGBColorSpace::parse(csObj->getArray());
|
|
} else if (obj1.isName("Lab")) {
|
|
cs = GfxLabColorSpace::parse(csObj->getArray());
|
|
} else if (obj1.isName("ICCBased")) {
|
|
cs = GfxICCBasedColorSpace::parse(csObj->getArray());
|
|
} else if (obj1.isName("Indexed") || obj1.isName("I")) {
|
|
cs = GfxIndexedColorSpace::parse(csObj->getArray());
|
|
} else if (obj1.isName("Separation")) {
|
|
cs = GfxSeparationColorSpace::parse(csObj->getArray());
|
|
} else if (obj1.isName("DeviceN")) {
|
|
cs = GfxDeviceNColorSpace::parse(csObj->getArray());
|
|
} else if (obj1.isName("Pattern")) {
|
|
cs = GfxPatternColorSpace::parse(csObj->getArray());
|
|
} else {
|
|
error(-1, "Bad color space");
|
|
}
|
|
obj1.free();
|
|
} else {
|
|
error(-1, "Bad color space - expected name or array");
|
|
}
|
|
return cs;
|
|
}
|
|
|
|
void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
|
|
int /*maxImgPixel*/) {
|
|
int i;
|
|
|
|
for (i = 0; i < getNComps(); ++i) {
|
|
decodeLow[i] = 0;
|
|
decodeRange[i] = 1;
|
|
}
|
|
}
|
|
|
|
int GfxColorSpace::getNumColorSpaceModes() {
|
|
return nGfxColorSpaceModes;
|
|
}
|
|
|
|
const char *GfxColorSpace::getColorSpaceModeName(int idx) {
|
|
return gfxColorSpaceModeNames[idx];
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxDeviceGrayColorSpace
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() {
|
|
}
|
|
|
|
GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {
|
|
}
|
|
|
|
GfxColorSpace *GfxDeviceGrayColorSpace::copy() {
|
|
return new GfxDeviceGrayColorSpace();
|
|
}
|
|
|
|
void GfxDeviceGrayColorSpace::getGray(GfxColor *color, double *gray) {
|
|
*gray = clip01(color->c[0]);
|
|
}
|
|
|
|
void GfxDeviceGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
|
|
rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
|
|
}
|
|
|
|
void GfxDeviceGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
|
|
cmyk->c = cmyk->m = cmyk->y = 0;
|
|
cmyk->k = clip01(1 - color->c[0]);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxCalGrayColorSpace
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxCalGrayColorSpace::GfxCalGrayColorSpace() {
|
|
whiteX = whiteY = whiteZ = 1;
|
|
blackX = blackY = blackZ = 0;
|
|
gamma = 1;
|
|
}
|
|
|
|
GfxCalGrayColorSpace::~GfxCalGrayColorSpace() {
|
|
}
|
|
|
|
GfxColorSpace *GfxCalGrayColorSpace::copy() {
|
|
GfxCalGrayColorSpace *cs;
|
|
|
|
cs = new GfxCalGrayColorSpace();
|
|
cs->whiteX = whiteX;
|
|
cs->whiteY = whiteY;
|
|
cs->whiteZ = whiteZ;
|
|
cs->blackX = blackX;
|
|
cs->blackY = blackY;
|
|
cs->blackZ = blackZ;
|
|
cs->gamma = gamma;
|
|
return cs;
|
|
}
|
|
|
|
GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr) {
|
|
GfxCalGrayColorSpace *cs;
|
|
Object obj1, obj2, obj3;
|
|
|
|
arr->get(1, &obj1);
|
|
if (!obj1.isDict()) {
|
|
error(-1, "Bad CalGray color space");
|
|
obj1.free();
|
|
return NULL;
|
|
}
|
|
cs = new GfxCalGrayColorSpace();
|
|
if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
|
|
obj2.arrayGetLength() == 3) {
|
|
obj2.arrayGet(0, &obj3);
|
|
cs->whiteX = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(1, &obj3);
|
|
cs->whiteY = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(2, &obj3);
|
|
cs->whiteZ = obj3.getNum();
|
|
obj3.free();
|
|
}
|
|
obj2.free();
|
|
if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
|
|
obj2.arrayGetLength() == 3) {
|
|
obj2.arrayGet(0, &obj3);
|
|
cs->blackX = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(1, &obj3);
|
|
cs->blackY = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(2, &obj3);
|
|
cs->blackZ = obj3.getNum();
|
|
obj3.free();
|
|
}
|
|
obj2.free();
|
|
if (obj1.dictLookup("Gamma", &obj2)->isNum()) {
|
|
cs->gamma = obj2.getNum();
|
|
}
|
|
obj2.free();
|
|
obj1.free();
|
|
return cs;
|
|
}
|
|
|
|
void GfxCalGrayColorSpace::getGray(GfxColor *color, double *gray) {
|
|
*gray = clip01(color->c[0]);
|
|
}
|
|
|
|
void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
|
|
rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
|
|
}
|
|
|
|
void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
|
|
cmyk->c = cmyk->m = cmyk->y = 0;
|
|
cmyk->k = clip01(1 - color->c[0]);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxDeviceRGBColorSpace
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() {
|
|
}
|
|
|
|
GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() {
|
|
}
|
|
|
|
GfxColorSpace *GfxDeviceRGBColorSpace::copy() {
|
|
return new GfxDeviceRGBColorSpace();
|
|
}
|
|
|
|
void GfxDeviceRGBColorSpace::getGray(GfxColor *color, double *gray) {
|
|
*gray = clip01(0.299 * color->c[0] +
|
|
0.587 * color->c[1] +
|
|
0.114 * color->c[2]);
|
|
}
|
|
|
|
void GfxDeviceRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
|
|
rgb->r = clip01(color->c[0]);
|
|
rgb->g = clip01(color->c[1]);
|
|
rgb->b = clip01(color->c[2]);
|
|
}
|
|
|
|
void GfxDeviceRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
|
|
double c, m, y, k;
|
|
|
|
c = clip01(1 - color->c[0]);
|
|
m = clip01(1 - color->c[1]);
|
|
y = clip01(1 - color->c[2]);
|
|
k = c;
|
|
if (m < k) {
|
|
k = m;
|
|
}
|
|
if (y < k) {
|
|
k = y;
|
|
}
|
|
cmyk->c = c - k;
|
|
cmyk->m = m - k;
|
|
cmyk->y = y - k;
|
|
cmyk->k = k;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxCalRGBColorSpace
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxCalRGBColorSpace::GfxCalRGBColorSpace() {
|
|
whiteX = whiteY = whiteZ = 1;
|
|
blackX = blackY = blackZ = 0;
|
|
gammaR = gammaG = gammaB = 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() {
|
|
}
|
|
|
|
GfxColorSpace *GfxCalRGBColorSpace::copy() {
|
|
GfxCalRGBColorSpace *cs;
|
|
int i;
|
|
|
|
cs = new GfxCalRGBColorSpace();
|
|
cs->whiteX = whiteX;
|
|
cs->whiteY = whiteY;
|
|
cs->whiteZ = whiteZ;
|
|
cs->blackX = blackX;
|
|
cs->blackY = blackY;
|
|
cs->blackZ = blackZ;
|
|
cs->gammaR = gammaR;
|
|
cs->gammaG = gammaG;
|
|
cs->gammaB = gammaB;
|
|
for (i = 0; i < 9; ++i) {
|
|
cs->mat[i] = mat[i];
|
|
}
|
|
return cs;
|
|
}
|
|
|
|
GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr) {
|
|
GfxCalRGBColorSpace *cs;
|
|
Object obj1, obj2, obj3;
|
|
int i;
|
|
|
|
arr->get(1, &obj1);
|
|
if (!obj1.isDict()) {
|
|
error(-1, "Bad CalRGB color space");
|
|
obj1.free();
|
|
return NULL;
|
|
}
|
|
cs = new GfxCalRGBColorSpace();
|
|
if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
|
|
obj2.arrayGetLength() == 3) {
|
|
obj2.arrayGet(0, &obj3);
|
|
cs->whiteX = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(1, &obj3);
|
|
cs->whiteY = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(2, &obj3);
|
|
cs->whiteZ = obj3.getNum();
|
|
obj3.free();
|
|
}
|
|
obj2.free();
|
|
if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
|
|
obj2.arrayGetLength() == 3) {
|
|
obj2.arrayGet(0, &obj3);
|
|
cs->blackX = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(1, &obj3);
|
|
cs->blackY = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(2, &obj3);
|
|
cs->blackZ = obj3.getNum();
|
|
obj3.free();
|
|
}
|
|
obj2.free();
|
|
if (obj1.dictLookup("Gamma", &obj2)->isArray() &&
|
|
obj2.arrayGetLength() == 3) {
|
|
obj2.arrayGet(0, &obj3);
|
|
cs->gammaR = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(1, &obj3);
|
|
cs->gammaG = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(2, &obj3);
|
|
cs->gammaB = obj3.getNum();
|
|
obj3.free();
|
|
}
|
|
obj2.free();
|
|
if (obj1.dictLookup("Matrix", &obj2)->isArray() &&
|
|
obj2.arrayGetLength() == 9) {
|
|
for (i = 0; i < 9; ++i) {
|
|
obj2.arrayGet(i, &obj3);
|
|
cs->mat[i] = obj3.getNum();
|
|
obj3.free();
|
|
}
|
|
}
|
|
obj2.free();
|
|
obj1.free();
|
|
return cs;
|
|
}
|
|
|
|
void GfxCalRGBColorSpace::getGray(GfxColor *color, double *gray) {
|
|
*gray = clip01(0.299 * color->c[0] +
|
|
0.587 * color->c[1] +
|
|
0.114 * color->c[2]);
|
|
}
|
|
|
|
void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
|
|
rgb->r = clip01(color->c[0]);
|
|
rgb->g = clip01(color->c[1]);
|
|
rgb->b = clip01(color->c[2]);
|
|
}
|
|
|
|
void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
|
|
double c, m, y, k;
|
|
|
|
c = clip01(1 - color->c[0]);
|
|
m = clip01(1 - color->c[1]);
|
|
y = clip01(1 - color->c[2]);
|
|
k = c;
|
|
if (m < k) {
|
|
k = m;
|
|
}
|
|
if (y < k) {
|
|
k = y;
|
|
}
|
|
cmyk->c = c - k;
|
|
cmyk->m = m - k;
|
|
cmyk->y = y - k;
|
|
cmyk->k = k;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxDeviceCMYKColorSpace
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() {
|
|
}
|
|
|
|
GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() {
|
|
}
|
|
|
|
GfxColorSpace *GfxDeviceCMYKColorSpace::copy() {
|
|
return new GfxDeviceCMYKColorSpace();
|
|
}
|
|
|
|
void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, double *gray) {
|
|
*gray = clip01(1 - color->c[3]
|
|
- 0.299 * color->c[0]
|
|
- 0.587 * color->c[1]
|
|
- 0.114 * color->c[2]);
|
|
}
|
|
|
|
void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
|
|
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) {
|
|
cmyk->c = clip01(color->c[0]);
|
|
cmyk->m = clip01(color->c[1]);
|
|
cmyk->y = clip01(color->c[2]);
|
|
cmyk->k = clip01(color->c[3]);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxLabColorSpace
|
|
//------------------------------------------------------------------------
|
|
|
|
// This is the inverse of MatrixLMN in Example 4.10 from the PostScript
|
|
// Language Reference, Third Edition.
|
|
static double xyzrgb[3][3] = {
|
|
{ 3.240449, -1.537136, -0.498531 },
|
|
{ -0.969265, 1.876011, 0.041556 },
|
|
{ 0.055643, -0.204026, 1.057229 }
|
|
};
|
|
|
|
GfxLabColorSpace::GfxLabColorSpace() {
|
|
whiteX = whiteY = whiteZ = 1;
|
|
blackX = blackY = blackZ = 0;
|
|
aMin = bMin = -100;
|
|
aMax = bMax = 100;
|
|
}
|
|
|
|
GfxLabColorSpace::~GfxLabColorSpace() {
|
|
}
|
|
|
|
GfxColorSpace *GfxLabColorSpace::copy() {
|
|
GfxLabColorSpace *cs;
|
|
|
|
cs = new GfxLabColorSpace();
|
|
cs->whiteX = whiteX;
|
|
cs->whiteY = whiteY;
|
|
cs->whiteZ = whiteZ;
|
|
cs->blackX = blackX;
|
|
cs->blackY = blackY;
|
|
cs->blackZ = blackZ;
|
|
cs->aMin = aMin;
|
|
cs->aMax = aMax;
|
|
cs->bMin = bMin;
|
|
cs->bMax = bMax;
|
|
cs->kr = kr;
|
|
cs->kg = kg;
|
|
cs->kb = kb;
|
|
return cs;
|
|
}
|
|
|
|
GfxColorSpace *GfxLabColorSpace::parse(Array *arr) {
|
|
GfxLabColorSpace *cs;
|
|
Object obj1, obj2, obj3;
|
|
|
|
arr->get(1, &obj1);
|
|
if (!obj1.isDict()) {
|
|
error(-1, "Bad Lab color space");
|
|
obj1.free();
|
|
return NULL;
|
|
}
|
|
cs = new GfxLabColorSpace();
|
|
if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
|
|
obj2.arrayGetLength() == 3) {
|
|
obj2.arrayGet(0, &obj3);
|
|
cs->whiteX = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(1, &obj3);
|
|
cs->whiteY = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(2, &obj3);
|
|
cs->whiteZ = obj3.getNum();
|
|
obj3.free();
|
|
}
|
|
obj2.free();
|
|
if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
|
|
obj2.arrayGetLength() == 3) {
|
|
obj2.arrayGet(0, &obj3);
|
|
cs->blackX = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(1, &obj3);
|
|
cs->blackY = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(2, &obj3);
|
|
cs->blackZ = obj3.getNum();
|
|
obj3.free();
|
|
}
|
|
obj2.free();
|
|
if (obj1.dictLookup("Range", &obj2)->isArray() &&
|
|
obj2.arrayGetLength() == 4) {
|
|
obj2.arrayGet(0, &obj3);
|
|
cs->aMin = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(1, &obj3);
|
|
cs->aMax = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(2, &obj3);
|
|
cs->bMin = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(3, &obj3);
|
|
cs->bMax = obj3.getNum();
|
|
obj3.free();
|
|
}
|
|
obj2.free();
|
|
obj1.free();
|
|
|
|
cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
|
|
xyzrgb[0][1] * cs->whiteY +
|
|
xyzrgb[0][2] * cs->whiteZ);
|
|
cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
|
|
xyzrgb[1][1] * cs->whiteY +
|
|
xyzrgb[1][2] * cs->whiteZ);
|
|
cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
|
|
xyzrgb[2][1] * cs->whiteY +
|
|
xyzrgb[2][2] * cs->whiteZ);
|
|
|
|
return cs;
|
|
}
|
|
|
|
void GfxLabColorSpace::getGray(GfxColor *color, double *gray) {
|
|
GfxRGB rgb;
|
|
|
|
getRGB(color, &rgb);
|
|
*gray = clip01(0.299 * rgb.r +
|
|
0.587 * rgb.g +
|
|
0.114 * rgb.b);
|
|
}
|
|
|
|
void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
|
|
double X, Y, Z;
|
|
double t1, t2;
|
|
double r, g, b;
|
|
|
|
// convert L*a*b* to CIE 1931 XYZ color space
|
|
t1 = (color->c[0] + 16) / 116;
|
|
t2 = t1 + color->c[1] / 500;
|
|
if (t2 >= (6.0 / 29.0)) {
|
|
X = t2 * t2 * t2;
|
|
} else {
|
|
X = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
|
|
}
|
|
X *= whiteX;
|
|
if (t1 >= (6.0 / 29.0)) {
|
|
Y = t1 * t1 * t1;
|
|
} else {
|
|
Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0));
|
|
}
|
|
Y *= whiteY;
|
|
t2 = t1 - color->c[2] / 200;
|
|
if (t2 >= (6.0 / 29.0)) {
|
|
Z = t2 * t2 * t2;
|
|
} else {
|
|
Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
|
|
}
|
|
Z *= whiteZ;
|
|
|
|
// convert XYZ to RGB, including gamut mapping and gamma correction
|
|
r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
|
|
g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
|
|
b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
|
|
rgb->r = pow(clip01(r * kr), 0.5);
|
|
rgb->g = pow(clip01(g * kg), 0.5);
|
|
rgb->b = pow(clip01(b * kb), 0.5);
|
|
}
|
|
|
|
void GfxLabColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
|
|
GfxRGB rgb;
|
|
double c, m, y, k;
|
|
|
|
getRGB(color, &rgb);
|
|
c = clip01(1 - rgb.r);
|
|
m = clip01(1 - rgb.g);
|
|
y = clip01(1 - rgb.b);
|
|
k = c;
|
|
if (m < k) {
|
|
k = m;
|
|
}
|
|
if (y < k) {
|
|
k = y;
|
|
}
|
|
cmyk->c = c - k;
|
|
cmyk->m = m - k;
|
|
cmyk->y = y - k;
|
|
cmyk->k = k;
|
|
}
|
|
|
|
void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
|
|
int /*maxImgPixel*/) {
|
|
decodeLow[0] = 0;
|
|
decodeRange[0] = 100;
|
|
decodeLow[1] = aMin;
|
|
decodeRange[1] = aMax - aMin;
|
|
decodeLow[2] = bMin;
|
|
decodeRange[2] = bMax - bMin;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxICCBasedColorSpace
|
|
//------------------------------------------------------------------------
|
|
|
|
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;
|
|
}
|
|
|
|
GfxICCBasedColorSpace::~GfxICCBasedColorSpace() {
|
|
delete alt;
|
|
}
|
|
|
|
GfxColorSpace *GfxICCBasedColorSpace::copy() {
|
|
GfxICCBasedColorSpace *cs;
|
|
int i;
|
|
|
|
cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream);
|
|
for (i = 0; i < 4; ++i) {
|
|
cs->rangeMin[i] = rangeMin[i];
|
|
cs->rangeMax[i] = rangeMax[i];
|
|
}
|
|
return cs;
|
|
}
|
|
|
|
GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
|
|
GfxICCBasedColorSpace *cs;
|
|
Ref iccProfileStreamA;
|
|
int nCompsA;
|
|
GfxColorSpace *altA;
|
|
Dict *dict;
|
|
Object obj1, obj2, obj3;
|
|
int i;
|
|
|
|
arr->getNF(1, &obj1);
|
|
if (obj1.isRef()) {
|
|
iccProfileStreamA = obj1.getRef();
|
|
} else {
|
|
iccProfileStreamA.num = 0;
|
|
iccProfileStreamA.gen = 0;
|
|
}
|
|
obj1.free();
|
|
arr->get(1, &obj1);
|
|
if (!obj1.isStream()) {
|
|
error(-1, "Bad ICCBased color space (stream)");
|
|
obj1.free();
|
|
return NULL;
|
|
}
|
|
dict = obj1.streamGetDict();
|
|
if (!dict->lookup("N", &obj2)->isInt()) {
|
|
error(-1, "Bad ICCBased color space (N)");
|
|
obj2.free();
|
|
obj1.free();
|
|
return NULL;
|
|
}
|
|
nCompsA = obj2.getInt();
|
|
obj2.free();
|
|
if (dict->lookup("Alternate", &obj2)->isNull() ||
|
|
!(altA = GfxColorSpace::parse(&obj2))) {
|
|
switch (nCompsA) {
|
|
case 1:
|
|
altA = new GfxDeviceGrayColorSpace();
|
|
break;
|
|
case 3:
|
|
altA = new GfxDeviceRGBColorSpace();
|
|
break;
|
|
case 4:
|
|
altA = new GfxDeviceCMYKColorSpace();
|
|
break;
|
|
default:
|
|
error(-1, "Bad ICCBased color space - invalid N");
|
|
obj2.free();
|
|
obj1.free();
|
|
return NULL;
|
|
}
|
|
}
|
|
obj2.free();
|
|
cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA);
|
|
if (dict->lookup("Range", &obj2)->isArray() &&
|
|
obj2.arrayGetLength() == 2 * nCompsA) {
|
|
for (i = 0; i < nCompsA; ++i) {
|
|
obj2.arrayGet(2*i, &obj3);
|
|
cs->rangeMin[i] = obj3.getNum();
|
|
obj3.free();
|
|
obj2.arrayGet(2*i+1, &obj3);
|
|
cs->rangeMax[i] = obj3.getNum();
|
|
obj3.free();
|
|
}
|
|
}
|
|
obj2.free();
|
|
obj1.free();
|
|
return cs;
|
|
}
|
|
|
|
void GfxICCBasedColorSpace::getGray(GfxColor *color, double *gray) {
|
|
alt->getGray(color, gray);
|
|
}
|
|
|
|
void GfxICCBasedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
|
|
alt->getRGB(color, rgb);
|
|
}
|
|
|
|
void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
|
|
alt->getCMYK(color, 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 *baseA,
|
|
int indexHighA) {
|
|
base = baseA;
|
|
indexHigh = indexHighA;
|
|
lookup = (Guchar *)gmalloc((indexHigh + 1) * base->getNComps() *
|
|
sizeof(Guchar));
|
|
}
|
|
|
|
GfxIndexedColorSpace::~GfxIndexedColorSpace() {
|
|
delete base;
|
|
gfree(lookup);
|
|
}
|
|
|
|
GfxColorSpace *GfxIndexedColorSpace::copy() {
|
|
GfxIndexedColorSpace *cs;
|
|
|
|
cs = new GfxIndexedColorSpace(base->copy(), indexHigh);
|
|
memcpy(cs->lookup, lookup,
|
|
(indexHigh + 1) * base->getNComps() * sizeof(Guchar));
|
|
return cs;
|
|
}
|
|
|
|
GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
|
|
GfxIndexedColorSpace *cs;
|
|
GfxColorSpace *baseA;
|
|
int indexHighA;
|
|
Object obj1;
|
|
int x;
|
|
char *s;
|
|
int n, i, j;
|
|
|
|
if (arr->getLength() != 4) {
|
|
error(-1, "Bad Indexed color space");
|
|
goto err1;
|
|
}
|
|
arr->get(1, &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;
|
|
}
|
|
obj1.free();
|
|
cs = new GfxIndexedColorSpace(baseA, indexHighA);
|
|
arr->get(3, &obj1);
|
|
n = baseA->getNComps();
|
|
if (obj1.isStream()) {
|
|
obj1.streamReset();
|
|
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)");
|
|
goto err3;
|
|
}
|
|
cs->lookup[i*n + j] = (Guchar)x;
|
|
}
|
|
}
|
|
obj1.streamClose();
|
|
} else if (obj1.isString()) {
|
|
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 <= indexHighA; ++i) {
|
|
for (j = 0; j < n; ++j) {
|
|
cs->lookup[i*n + j] = (Guchar)*s++;
|
|
}
|
|
}
|
|
} else {
|
|
error(-1, "Bad Indexed color space (lookup table)");
|
|
goto err3;
|
|
}
|
|
obj1.free();
|
|
return cs;
|
|
|
|
err3:
|
|
delete cs;
|
|
err2:
|
|
obj1.free();
|
|
err1:
|
|
return NULL;
|
|
}
|
|
|
|
GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
|
|
GfxColor *baseColor) {
|
|
Guchar *p;
|
|
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) {
|
|
baseColor->c[i] = low[i] + (p[i] / 255.0) * range[i];
|
|
}
|
|
return baseColor;
|
|
}
|
|
|
|
void GfxIndexedColorSpace::getGray(GfxColor *color, double *gray) {
|
|
GfxColor color2;
|
|
|
|
base->getGray(mapColorToBase(color, &color2), gray);
|
|
}
|
|
|
|
void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
|
|
GfxColor color2;
|
|
|
|
base->getRGB(mapColorToBase(color, &color2), rgb);
|
|
}
|
|
|
|
void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
|
|
GfxColor color2;
|
|
|
|
base->getCMYK(mapColorToBase(color, &color2), cmyk);
|
|
}
|
|
|
|
void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
|
|
double *decodeRange,
|
|
int maxImgPixel) {
|
|
decodeLow[0] = 0;
|
|
decodeRange[0] = maxImgPixel;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxSeparationColorSpace
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxSeparationColorSpace::GfxSeparationColorSpace(GString *nameA,
|
|
GfxColorSpace *altA,
|
|
Function *funcA) {
|
|
name = nameA;
|
|
alt = altA;
|
|
func = funcA;
|
|
}
|
|
|
|
GfxSeparationColorSpace::~GfxSeparationColorSpace() {
|
|
delete name;
|
|
delete alt;
|
|
delete func;
|
|
}
|
|
|
|
GfxColorSpace *GfxSeparationColorSpace::copy() {
|
|
return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy());
|
|
}
|
|
|
|
//~ handle the 'All' and 'None' colorants
|
|
GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr) {
|
|
GfxSeparationColorSpace *cs;
|
|
GString *nameA;
|
|
GfxColorSpace *altA;
|
|
Function *funcA;
|
|
Object obj1;
|
|
|
|
if (arr->getLength() != 4) {
|
|
error(-1, "Bad Separation color space");
|
|
goto err1;
|
|
}
|
|
if (!arr->get(1, &obj1)->isName()) {
|
|
error(-1, "Bad Separation color space (name)");
|
|
goto err2;
|
|
}
|
|
nameA = new GString(obj1.getName());
|
|
obj1.free();
|
|
arr->get(2, &obj1);
|
|
if (!(altA = GfxColorSpace::parse(&obj1))) {
|
|
error(-1, "Bad Separation color space (alternate color space)");
|
|
goto err3;
|
|
}
|
|
obj1.free();
|
|
arr->get(3, &obj1);
|
|
if (!(funcA = Function::parse(&obj1))) {
|
|
goto err4;
|
|
}
|
|
obj1.free();
|
|
cs = new GfxSeparationColorSpace(nameA, altA, funcA);
|
|
return cs;
|
|
|
|
err4:
|
|
delete altA;
|
|
err3:
|
|
delete nameA;
|
|
err2:
|
|
obj1.free();
|
|
err1:
|
|
return NULL;
|
|
}
|
|
|
|
void GfxSeparationColorSpace::getGray(GfxColor *color, double *gray) {
|
|
GfxColor color2;
|
|
|
|
func->transform(color->c, color2.c);
|
|
alt->getGray(&color2, gray);
|
|
}
|
|
|
|
void GfxSeparationColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
|
|
GfxColor color2;
|
|
|
|
func->transform(color->c, color2.c);
|
|
alt->getRGB(&color2, rgb);
|
|
}
|
|
|
|
void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
|
|
GfxColor color2;
|
|
|
|
func->transform(color->c, color2.c);
|
|
alt->getCMYK(&color2, cmyk);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxDeviceNColorSpace
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
|
|
GfxColorSpace *altA,
|
|
Function *funcA) {
|
|
nComps = nCompsA;
|
|
alt = altA;
|
|
func = funcA;
|
|
}
|
|
|
|
GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
|
|
int i;
|
|
|
|
for (i = 0; i < nComps; ++i) {
|
|
delete names[i];
|
|
}
|
|
delete alt;
|
|
delete func;
|
|
}
|
|
|
|
GfxColorSpace *GfxDeviceNColorSpace::copy() {
|
|
GfxDeviceNColorSpace *cs;
|
|
int i;
|
|
|
|
cs = new GfxDeviceNColorSpace(nComps, alt->copy(), func->copy());
|
|
for (i = 0; i < nComps; ++i) {
|
|
cs->names[i] = names[i]->copy();
|
|
}
|
|
return cs;
|
|
}
|
|
|
|
//~ handle the 'None' colorant
|
|
GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) {
|
|
GfxDeviceNColorSpace *cs;
|
|
int nCompsA;
|
|
GString *namesA[gfxColorMaxComps];
|
|
GfxColorSpace *altA;
|
|
Function *funcA;
|
|
Object obj1, obj2;
|
|
int i;
|
|
|
|
if (arr->getLength() != 4 && arr->getLength() != 5) {
|
|
error(-1, "Bad DeviceN color space");
|
|
goto err1;
|
|
}
|
|
if (!arr->get(1, &obj1)->isArray()) {
|
|
error(-1, "Bad DeviceN color space (names)");
|
|
goto err2;
|
|
}
|
|
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;
|
|
}
|
|
namesA[i] = new GString(obj2.getName());
|
|
obj2.free();
|
|
}
|
|
obj1.free();
|
|
arr->get(2, &obj1);
|
|
if (!(altA = GfxColorSpace::parse(&obj1))) {
|
|
error(-1, "Bad DeviceN color space (alternate color space)");
|
|
goto err3;
|
|
}
|
|
obj1.free();
|
|
arr->get(3, &obj1);
|
|
if (!(funcA = Function::parse(&obj1))) {
|
|
goto err4;
|
|
}
|
|
obj1.free();
|
|
cs = new GfxDeviceNColorSpace(nCompsA, altA, funcA);
|
|
for (i = 0; i < nCompsA; ++i) {
|
|
cs->names[i] = namesA[i];
|
|
}
|
|
return cs;
|
|
|
|
err4:
|
|
delete altA;
|
|
err3:
|
|
for (i = 0; i < nCompsA; ++i) {
|
|
delete namesA[i];
|
|
}
|
|
err2:
|
|
obj1.free();
|
|
err1:
|
|
return NULL;
|
|
}
|
|
|
|
void GfxDeviceNColorSpace::getGray(GfxColor *color, double *gray) {
|
|
GfxColor color2;
|
|
|
|
func->transform(color->c, color2.c);
|
|
alt->getGray(&color2, gray);
|
|
}
|
|
|
|
void GfxDeviceNColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
|
|
GfxColor color2;
|
|
|
|
func->transform(color->c, color2.c);
|
|
alt->getRGB(&color2, rgb);
|
|
}
|
|
|
|
void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
|
|
GfxColor color2;
|
|
|
|
func->transform(color->c, color2.c);
|
|
alt->getCMYK(&color2, cmyk);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxPatternColorSpace
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) {
|
|
under = underA;
|
|
}
|
|
|
|
GfxPatternColorSpace::~GfxPatternColorSpace() {
|
|
if (under) {
|
|
delete under;
|
|
}
|
|
}
|
|
|
|
GfxColorSpace *GfxPatternColorSpace::copy() {
|
|
return new GfxPatternColorSpace(under ? under->copy() :
|
|
(GfxColorSpace *)NULL);
|
|
}
|
|
|
|
GfxColorSpace *GfxPatternColorSpace::parse(Array *arr) {
|
|
GfxPatternColorSpace *cs;
|
|
GfxColorSpace *underA;
|
|
Object obj1;
|
|
|
|
if (arr->getLength() != 1 && arr->getLength() != 2) {
|
|
error(-1, "Bad Pattern color space");
|
|
return NULL;
|
|
}
|
|
underA = NULL;
|
|
if (arr->getLength() == 2) {
|
|
arr->get(1, &obj1);
|
|
if (!(underA = GfxColorSpace::parse(&obj1))) {
|
|
error(-1, "Bad Pattern color space (underlying color space)");
|
|
obj1.free();
|
|
return NULL;
|
|
}
|
|
obj1.free();
|
|
}
|
|
cs = new GfxPatternColorSpace(underA);
|
|
return cs;
|
|
}
|
|
|
|
void GfxPatternColorSpace::getGray(GfxColor */*color*/, double *gray) {
|
|
*gray = 0;
|
|
}
|
|
|
|
void GfxPatternColorSpace::getRGB(GfxColor */*color*/, GfxRGB *rgb) {
|
|
rgb->r = rgb->g = rgb->b = 0;
|
|
}
|
|
|
|
void GfxPatternColorSpace::getCMYK(GfxColor */*color*/, GfxCMYK *cmyk) {
|
|
cmyk->c = cmyk->m = cmyk->y = 0;
|
|
cmyk->k = 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// Pattern
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxPattern::GfxPattern(int typeA) {
|
|
type = typeA;
|
|
}
|
|
|
|
GfxPattern::~GfxPattern() {
|
|
}
|
|
|
|
GfxPattern *GfxPattern::parse(Object *obj) {
|
|
GfxPattern *pattern;
|
|
Object obj1;
|
|
|
|
if (obj->isDict()) {
|
|
obj->dictLookup("PatternType", &obj1);
|
|
} else if (obj->isStream()) {
|
|
obj->streamGetDict()->lookup("PatternType", &obj1);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
pattern = NULL;
|
|
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;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxTilingPattern
|
|
//------------------------------------------------------------------------
|
|
|
|
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 (!patObj->isStream()) {
|
|
return NULL;
|
|
}
|
|
dict = patObj->streamGetDict();
|
|
|
|
if (dict->lookup("PaintType", &obj1)->isInt()) {
|
|
paintTypeA = obj1.getInt();
|
|
} else {
|
|
paintTypeA = 1;
|
|
error(-1, "Invalid or missing PaintType in pattern");
|
|
}
|
|
obj1.free();
|
|
if (dict->lookup("TilingType", &obj1)->isInt()) {
|
|
tilingTypeA = obj1.getInt();
|
|
} else {
|
|
tilingTypeA = 1;
|
|
error(-1, "Invalid or missing TilingType in pattern");
|
|
}
|
|
obj1.free();
|
|
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()) {
|
|
bboxA[i] = obj2.getNum();
|
|
}
|
|
obj2.free();
|
|
}
|
|
} else {
|
|
error(-1, "Invalid or missing BBox in pattern");
|
|
}
|
|
obj1.free();
|
|
if (dict->lookup("XStep", &obj1)->isNum()) {
|
|
xStepA = obj1.getNum();
|
|
} else {
|
|
xStepA = 1;
|
|
error(-1, "Invalid or missing XStep in pattern");
|
|
}
|
|
obj1.free();
|
|
if (dict->lookup("YStep", &obj1)->isNum()) {
|
|
yStepA = obj1.getNum();
|
|
} else {
|
|
yStepA = 1;
|
|
error(-1, "Invalid or missing YStep in pattern");
|
|
}
|
|
obj1.free();
|
|
if (!dict->lookup("Resources", &resDictA)->isDict()) {
|
|
resDictA.free();
|
|
resDictA.initNull();
|
|
error(-1, "Invalid or missing Resources in pattern");
|
|
}
|
|
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();
|
|
|
|
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() {
|
|
resDict.free();
|
|
contentStream.free();
|
|
}
|
|
|
|
GfxPattern *GfxTilingPattern::copy() {
|
|
return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
|
|
&resDict, matrix, &contentStream);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// 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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxShading
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxShading::GfxShading(int typeA) {
|
|
type = typeA;
|
|
colorSpace = NULL;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
GfxShading::~GfxShading() {
|
|
if (colorSpace) {
|
|
delete colorSpace;
|
|
}
|
|
}
|
|
|
|
GfxShading *GfxShading::parse(Object *obj) {
|
|
GfxShading *shading;
|
|
Dict *dict;
|
|
int typeA;
|
|
Object obj1;
|
|
|
|
if (obj->isDict()) {
|
|
dict = obj->getDict();
|
|
} else if (obj->isStream()) {
|
|
dict = obj->streamGetDict();
|
|
} else {
|
|
return NULL;
|
|
}
|
|
|
|
if (!dict->lookup("ShadingType", &obj1)->isInt()) {
|
|
error(-1, "Invalid ShadingType in shading dictionary");
|
|
obj1.free();
|
|
return NULL;
|
|
}
|
|
typeA = obj1.getInt();
|
|
obj1.free();
|
|
|
|
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 shading;
|
|
|
|
err1:
|
|
return NULL;
|
|
}
|
|
|
|
GBool GfxShading::init(Dict *dict) {
|
|
Object obj1, obj2;
|
|
int i;
|
|
|
|
dict->lookup("ColorSpace", &obj1);
|
|
if (!(colorSpace = GfxColorSpace::parse(&obj1))) {
|
|
error(-1, "Bad color space in shading dictionary");
|
|
obj1.free();
|
|
return gFalse;
|
|
}
|
|
obj1.free();
|
|
|
|
for (i = 0; i < gfxColorMaxComps; ++i) {
|
|
background.c[i] = 0;
|
|
}
|
|
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");
|
|
}
|
|
}
|
|
obj1.free();
|
|
|
|
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();
|
|
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();
|
|
|
|
return gTrue;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxFunctionShading
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxFunctionShading::GfxFunctionShading(double x0A, double y0A,
|
|
double x1A, double y1A,
|
|
double *matrixA,
|
|
Function **funcsA, int nFuncsA):
|
|
GfxShading(1)
|
|
{
|
|
int i;
|
|
|
|
x0 = x0A;
|
|
y0 = y0A;
|
|
x1 = x1A;
|
|
y1 = y1A;
|
|
for (i = 0; i < 6; ++i) {
|
|
matrix[i] = matrixA[i];
|
|
}
|
|
nFuncs = nFuncsA;
|
|
for (i = 0; i < nFuncs; ++i) {
|
|
funcs[i] = funcsA[i];
|
|
}
|
|
}
|
|
|
|
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];
|
|
}
|
|
nFuncs = shading->nFuncs;
|
|
for (i = 0; i < nFuncs; ++i) {
|
|
funcs[i] = shading->funcs[i]->copy();
|
|
}
|
|
}
|
|
|
|
GfxFunctionShading::~GfxFunctionShading() {
|
|
int i;
|
|
|
|
for (i = 0; i < nFuncs; ++i) {
|
|
delete funcs[i];
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
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();
|
|
}
|
|
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))) {
|
|
goto err2;
|
|
}
|
|
obj2.free();
|
|
}
|
|
} else {
|
|
nFuncsA = 1;
|
|
if (!(funcsA[0] = Function::parse(&obj1))) {
|
|
goto err1;
|
|
}
|
|
}
|
|
obj1.free();
|
|
|
|
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;
|
|
}
|
|
obj2.free();
|
|
}
|
|
} else {
|
|
nFuncsA = 1;
|
|
if (!(funcsA[0] = Function::parse(&obj1))) {
|
|
obj1.free();
|
|
goto err1;
|
|
}
|
|
}
|
|
obj1.free();
|
|
|
|
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();
|
|
}
|
|
obj1.free();
|
|
|
|
shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
|
|
funcsA, nFuncsA, extend0A, extend1A);
|
|
if (!shading->init(dict)) {
|
|
delete shading;
|
|
return NULL;
|
|
}
|
|
return shading;
|
|
|
|
err1:
|
|
return NULL;
|
|
}
|
|
|
|
GfxShading *GfxAxialShading::copy() {
|
|
return new GfxAxialShading(this);
|
|
}
|
|
|
|
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]);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxRadialShading
|
|
//------------------------------------------------------------------------
|
|
|
|
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;
|
|
|
|
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];
|
|
}
|
|
extend0 = extend0A;
|
|
extend1 = extend1A;
|
|
}
|
|
|
|
GfxRadialShading::GfxRadialShading(GfxRadialShading *shading):
|
|
GfxShading(shading)
|
|
{
|
|
int i;
|
|
|
|
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;
|
|
}
|
|
|
|
GfxRadialShading::~GfxRadialShading() {
|
|
int i;
|
|
|
|
for (i = 0; i < nFuncs; ++i) {
|
|
delete funcs[i];
|
|
}
|
|
}
|
|
|
|
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;
|
|
int i;
|
|
|
|
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();
|
|
|
|
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;
|
|
}
|
|
obj2.free();
|
|
}
|
|
} else {
|
|
nFuncsA = 1;
|
|
if (!(funcsA[0] = Function::parse(&obj1))) {
|
|
obj1.free();
|
|
goto err1;
|
|
}
|
|
}
|
|
obj1.free();
|
|
|
|
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();
|
|
}
|
|
obj1.free();
|
|
|
|
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;
|
|
|
|
err1:
|
|
return NULL;
|
|
}
|
|
|
|
GfxShading *GfxRadialShading::copy() {
|
|
return new GfxRadialShading(this);
|
|
}
|
|
|
|
void GfxRadialShading::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]);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// GfxImageColorMap
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
|
|
GfxColorSpace *colorSpaceA) {
|
|
GfxIndexedColorSpace *indexedCS;
|
|
GfxSeparationColorSpace *sepCS;
|
|
int maxPixel, indexHigh;
|
|
Guchar *lookup2;
|
|
Function *sepFunc;
|
|
Object obj;
|
|
double x[gfxColorMaxComps];
|
|
double y[gfxColorMaxComps];
|
|
int i, j, k;
|
|
|
|
ok = gTrue;
|
|
|
|
// bits per component and color space
|
|
bits = bitsA;
|
|
maxPixel = (1 << bits) - 1;
|
|
colorSpace = colorSpaceA;
|
|
|
|
// get decode map
|
|
if (decode->isNull()) {
|
|
nComps = colorSpace->getNComps();
|
|
colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel);
|
|
} else if (decode->isArray()) {
|
|
nComps = decode->arrayGetLength() / 2;
|
|
if (nComps != colorSpace->getNComps()) {
|
|
goto err1;
|
|
}
|
|
for (i = 0; i < nComps; ++i) {
|
|
decode->arrayGet(2*i, &obj);
|
|
if (!obj.isNum()) {
|
|
goto err2;
|
|
}
|
|
decodeLow[i] = obj.getNum();
|
|
obj.free();
|
|
decode->arrayGet(2*i+1, &obj);
|
|
if (!obj.isNum()) {
|
|
goto err2;
|
|
}
|
|
decodeRange[i] = obj.getNum() - decodeLow[i];
|
|
obj.free();
|
|
}
|
|
} else {
|
|
goto err1;
|
|
}
|
|
|
|
// 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.
|
|
//
|
|
// Optimization: for Indexed and Separation color spaces (which have
|
|
// only one component), we store color values in the lookup table
|
|
// rather than component values.
|
|
colorSpace2 = NULL;
|
|
nComps2 = 0;
|
|
if (colorSpace->getMode() == csIndexed) {
|
|
// Note that indexHigh may not be the same as maxPixel --
|
|
// Distiller will remove unused palette entries, resulting in
|
|
// indexHigh < maxPixel.
|
|
indexedCS = (GfxIndexedColorSpace *)colorSpace;
|
|
colorSpace2 = indexedCS->getBase();
|
|
indexHigh = indexedCS->getIndexHigh();
|
|
nComps2 = colorSpace2->getNComps();
|
|
lookup = (double *)gmalloc((maxPixel + 1) * nComps2 * sizeof(double));
|
|
lookup2 = indexedCS->getLookup();
|
|
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] = 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));
|
|
sepFunc = sepCS->getFunc();
|
|
for (i = 0; i <= maxPixel; ++i) {
|
|
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));
|
|
for (i = 0; i <= maxPixel; ++i) {
|
|
for (k = 0; k < nComps; ++k) {
|
|
lookup[i*nComps + k] = decodeLow[k] +
|
|
(i * decodeRange[k]) / maxPixel;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
err2:
|
|
obj.free();
|
|
err1:
|
|
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);
|
|
}
|
|
|
|
void GfxImageColorMap::getGray(Guchar *x, double *gray) {
|
|
GfxColor color;
|
|
double *p;
|
|
int i;
|
|
|
|
if (colorSpace2) {
|
|
p = &lookup[x[0] * nComps2];
|
|
for (i = 0; i < nComps2; ++i) {
|
|
color.c[i] = *p++;
|
|
}
|
|
colorSpace2->getGray(&color, gray);
|
|
} else {
|
|
for (i = 0; i < nComps; ++i) {
|
|
color.c[i] = lookup[x[i] * nComps + i];
|
|
}
|
|
colorSpace->getGray(&color, gray);
|
|
}
|
|
}
|
|
|
|
void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {
|
|
GfxColor color;
|
|
double *p;
|
|
int i;
|
|
|
|
if (colorSpace2) {
|
|
p = &lookup[x[0] * nComps2];
|
|
for (i = 0; i < nComps2; ++i) {
|
|
color.c[i] = *p++;
|
|
}
|
|
colorSpace2->getRGB(&color, rgb);
|
|
} else {
|
|
for (i = 0; i < nComps; ++i) {
|
|
color.c[i] = lookup[x[i] * nComps + i];
|
|
}
|
|
colorSpace->getRGB(&color, rgb);
|
|
}
|
|
}
|
|
|
|
void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
|
|
GfxColor color;
|
|
double *p;
|
|
int i;
|
|
|
|
if (colorSpace2) {
|
|
p = &lookup[x[0] * nComps2];
|
|
for (i = 0; i < nComps2; ++i) {
|
|
color.c[i] = *p++;
|
|
}
|
|
colorSpace2->getCMYK(&color, cmyk);
|
|
} else {
|
|
for (i = 0; i < nComps; ++i) {
|
|
color.c[i] = lookup[x[i] * nComps + i];
|
|
}
|
|
colorSpace->getCMYK(&color, 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
|
|
//------------------------------------------------------------------------
|
|
|
|
GfxSubpath::GfxSubpath(double x1, double y1) {
|
|
size = 16;
|
|
x = (double *)gmalloc(size * sizeof(double));
|
|
y = (double *)gmalloc(size * sizeof(double));
|
|
curve = (GBool *)gmalloc(size * sizeof(GBool));
|
|
n = 1;
|
|
x[0] = x1;
|
|
y[0] = y1;
|
|
curve[0] = gFalse;
|
|
closed = gFalse;
|
|
}
|
|
|
|
GfxSubpath::~GfxSubpath() {
|
|
gfree(x);
|
|
gfree(y);
|
|
gfree(curve);
|
|
}
|
|
|
|
// Used for copy().
|
|
GfxSubpath::GfxSubpath(GfxSubpath *subpath) {
|
|
size = subpath->size;
|
|
n = subpath->n;
|
|
x = (double *)gmalloc(size * sizeof(double));
|
|
y = (double *)gmalloc(size * sizeof(double));
|
|
curve = (GBool *)gmalloc(size * sizeof(GBool));
|
|
memcpy(x, subpath->x, n * sizeof(double));
|
|
memcpy(y, subpath->y, n * sizeof(double));
|
|
memcpy(curve, subpath->curve, n * sizeof(GBool));
|
|
closed = subpath->closed;
|
|
}
|
|
|
|
void GfxSubpath::lineTo(double x1, double y1) {
|
|
if (n >= size) {
|
|
size += 16;
|
|
x = (double *)grealloc(x, size * sizeof(double));
|
|
y = (double *)grealloc(y, size * sizeof(double));
|
|
curve = (GBool *)grealloc(curve, size * sizeof(GBool));
|
|
}
|
|
x[n] = x1;
|
|
y[n] = y1;
|
|
curve[n] = gFalse;
|
|
++n;
|
|
}
|
|
|
|
void GfxSubpath::curveTo(double x1, double y1, double x2, double y2,
|
|
double x3, double y3) {
|
|
if (n+3 > size) {
|
|
size += 16;
|
|
x = (double *)grealloc(x, size * sizeof(double));
|
|
y = (double *)grealloc(y, size * sizeof(double));
|
|
curve = (GBool *)grealloc(curve, size * sizeof(GBool));
|
|
}
|
|
x[n] = x1;
|
|
y[n] = y1;
|
|
x[n+1] = x2;
|
|
y[n+1] = y2;
|
|
x[n+2] = x3;
|
|
y[n+2] = y3;
|
|
curve[n] = curve[n+1] = gTrue;
|
|
curve[n+2] = gFalse;
|
|
n += 3;
|
|
}
|
|
|
|
void GfxSubpath::close() {
|
|
if (x[n-1] != x[0] || y[n-1] != y[0]) {
|
|
lineTo(x[0], y[0]);
|
|
}
|
|
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;
|
|
n = 0;
|
|
firstX = firstY = 0;
|
|
subpaths = (GfxSubpath **)gmalloc(size * sizeof(GfxSubpath *));
|
|
}
|
|
|
|
GfxPath::~GfxPath() {
|
|
int i;
|
|
|
|
for (i = 0; i < n; ++i)
|
|
delete subpaths[i];
|
|
gfree(subpaths);
|
|
}
|
|
|
|
// Used for copy().
|
|
GfxPath::GfxPath(GBool justMoved1, double firstX1, double firstY1,
|
|
GfxSubpath **subpaths1, int n1, int size1) {
|
|
int i;
|
|
|
|
justMoved = justMoved1;
|
|
firstX = firstX1;
|
|
firstY = firstY1;
|
|
size = size1;
|
|
n = n1;
|
|
subpaths = (GfxSubpath **)gmalloc(size * sizeof(GfxSubpath *));
|
|
for (i = 0; i < n; ++i)
|
|
subpaths[i] = subpaths1[i]->copy();
|
|
}
|
|
|
|
void GfxPath::moveTo(double x, double y) {
|
|
justMoved = gTrue;
|
|
firstX = x;
|
|
firstY = y;
|
|
}
|
|
|
|
void GfxPath::lineTo(double x, double y) {
|
|
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]->lineTo(x, y);
|
|
}
|
|
|
|
void GfxPath::curveTo(double x1, double y1, double x2, double y2,
|
|
double x3, double y3) {
|
|
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]->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 hDPI, double vDPI, PDFRectangle *pageBox,
|
|
int rotate, GBool upsideDown) {
|
|
double kx, ky;
|
|
|
|
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 ? ky : -ky;
|
|
ctm[2] = kx;
|
|
ctm[3] = 0;
|
|
ctm[4] = -kx * py1;
|
|
ctm[5] = ky * (upsideDown ? -px1 : px2);
|
|
pageWidth = kx * (py2 - py1);
|
|
pageHeight = ky * (px2 - px1);
|
|
} else if (rotate == 180) {
|
|
ctm[0] = -kx;
|
|
ctm[1] = 0;
|
|
ctm[2] = 0;
|
|
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 ? -ky : ky;
|
|
ctm[2] = -kx;
|
|
ctm[3] = 0;
|
|
ctm[4] = kx * py2;
|
|
ctm[5] = ky * (upsideDown ? px2 : -px1);
|
|
pageWidth = kx * (py2 - py1);
|
|
pageHeight = ky * (px2 - px1);
|
|
} else {
|
|
ctm[0] = kx;
|
|
ctm[1] = 0;
|
|
ctm[2] = 0;
|
|
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();
|
|
strokeColorSpace = new GfxDeviceGrayColorSpace();
|
|
fillColor.c[0] = 0;
|
|
strokeColor.c[0] = 0;
|
|
fillPattern = NULL;
|
|
strokePattern = NULL;
|
|
fillOpacity = 1;
|
|
strokeOpacity = 1;
|
|
|
|
lineWidth = 1;
|
|
lineDash = NULL;
|
|
lineDashLength = 0;
|
|
lineDashStart = 0;
|
|
flatness = 1;
|
|
lineJoin = 0;
|
|
lineCap = 0;
|
|
miterLimit = 10;
|
|
|
|
font = NULL;
|
|
fontSize = 0;
|
|
textMat[0] = 1; textMat[1] = 0;
|
|
textMat[2] = 0; textMat[3] = 1;
|
|
textMat[4] = 0; textMat[5] = 0;
|
|
charSpace = 0;
|
|
wordSpace = 0;
|
|
horizScaling = 1;
|
|
leading = 0;
|
|
rise = 0;
|
|
render = 0;
|
|
|
|
path = new GfxPath();
|
|
curX = curY = 0;
|
|
lineX = lineY = 0;
|
|
|
|
clipXMin = 0;
|
|
clipYMin = 0;
|
|
clipXMax = pageWidth;
|
|
clipYMax = pageHeight;
|
|
|
|
saved = NULL;
|
|
}
|
|
|
|
GfxState::~GfxState() {
|
|
if (fillColorSpace) {
|
|
delete fillColorSpace;
|
|
}
|
|
if (strokeColorSpace) {
|
|
delete strokeColorSpace;
|
|
}
|
|
if (fillPattern) {
|
|
delete fillPattern;
|
|
}
|
|
if (strokePattern) {
|
|
delete strokePattern;
|
|
}
|
|
gfree(lineDash);
|
|
if (path) {
|
|
// this gets set to NULL by restore()
|
|
delete path;
|
|
}
|
|
if (saved) {
|
|
delete saved;
|
|
}
|
|
}
|
|
|
|
// Used for copy();
|
|
GfxState::GfxState(GfxState *state) {
|
|
memcpy(this, state, sizeof(GfxState));
|
|
if (fillColorSpace) {
|
|
fillColorSpace = state->fillColorSpace->copy();
|
|
}
|
|
if (strokeColorSpace) {
|
|
strokeColorSpace = state->strokeColorSpace->copy();
|
|
}
|
|
if (fillPattern) {
|
|
fillPattern = state->fillPattern->copy();
|
|
}
|
|
if (strokePattern) {
|
|
strokePattern = state->strokePattern->copy();
|
|
}
|
|
if (lineDashLength > 0) {
|
|
lineDash = (double *)gmalloc(lineDashLength * sizeof(double));
|
|
memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double));
|
|
}
|
|
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;
|
|
|
|
x = ctm[0] + ctm[2];
|
|
y = ctm[1] + ctm[3];
|
|
return w * sqrt(0.5 * (x * x + y * y));
|
|
}
|
|
|
|
double GfxState::getTransformedFontSize() {
|
|
double x1, y1, x2, y2;
|
|
|
|
x1 = textMat[2] * fontSize;
|
|
y1 = textMat[3] * fontSize;
|
|
x2 = ctm[0] * x1 + ctm[2] * y1;
|
|
y2 = ctm[1] * x1 + ctm[3] * y1;
|
|
return sqrt(x2 * x2 + y2 * y2);
|
|
}
|
|
|
|
void GfxState::getFontTransMat(double *m11, double *m12,
|
|
double *m21, double *m22) {
|
|
*m11 = (textMat[0] * ctm[0] + textMat[1] * ctm[2]) * fontSize;
|
|
*m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize;
|
|
*m21 = (textMat[2] * ctm[0] + textMat[3] * ctm[2]) * fontSize;
|
|
*m22 = (textMat[2] * ctm[1] + textMat[3] * ctm[3]) * fontSize;
|
|
}
|
|
|
|
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,
|
|
double d, double e, double f) {
|
|
double a1 = ctm[0];
|
|
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;
|
|
ctm[2] = c * a1 + d * c1;
|
|
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) {
|
|
if (fillColorSpace) {
|
|
delete fillColorSpace;
|
|
}
|
|
fillColorSpace = colorSpace;
|
|
}
|
|
|
|
void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) {
|
|
if (strokeColorSpace) {
|
|
delete strokeColorSpace;
|
|
}
|
|
strokeColorSpace = colorSpace;
|
|
}
|
|
|
|
void GfxState::setFillPattern(GfxPattern *pattern) {
|
|
if (fillPattern) {
|
|
delete fillPattern;
|
|
}
|
|
fillPattern = pattern;
|
|
}
|
|
|
|
void GfxState::setStrokePattern(GfxPattern *pattern) {
|
|
if (strokePattern) {
|
|
delete strokePattern;
|
|
}
|
|
strokePattern = pattern;
|
|
}
|
|
|
|
void GfxState::setLineDash(double *dash, int length, double start) {
|
|
if (lineDash)
|
|
gfree(lineDash);
|
|
lineDash = dash;
|
|
lineDashLength = length;
|
|
lineDashStart = start;
|
|
}
|
|
|
|
void GfxState::clearPath() {
|
|
delete path;
|
|
path = new GfxPath();
|
|
}
|
|
|
|
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) {
|
|
double dx, dy;
|
|
|
|
textTransformDelta(tx, ty, &dx, &dy);
|
|
curX += dx;
|
|
curY += dy;
|
|
}
|
|
|
|
void GfxState::shift(double dx, double dy) {
|
|
curX += dx;
|
|
curY += dy;
|
|
}
|
|
|
|
GfxState *GfxState::save() {
|
|
GfxState *newState;
|
|
|
|
newState = copy();
|
|
newState->saved = this;
|
|
return newState;
|
|
}
|
|
|
|
GfxState *GfxState::restore() {
|
|
GfxState *oldState;
|
|
|
|
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;
|
|
}
|