//======================================================================== // // GfxState.cc // // Copyright 1996 Derek B. Noonburg // //======================================================================== #ifdef __GNUC__ #pragma implementation #endif #include #include #include // for memcpy() #include "gmem.h" #include "Error.h" #include "Object.h" #include "GfxState.h" //------------------------------------------------------------------------ // GfxColor //------------------------------------------------------------------------ void GfxColor::setCMYK(double c, double m, double y, double k) { if ((r = 1 - (c + k)) < 0) r = 0; if ((g = 1 - (m + k)) < 0) g = 0; if ((b = 1 - (y + k)) < 0) b = 0; } //------------------------------------------------------------------------ // GfxColorSpace //------------------------------------------------------------------------ GfxColorSpace::GfxColorSpace(Object *colorSpace) { Object csObj; Object obj, obj2; char *s; int x; int i, j; ok = gTrue; lookup = NULL; // check for Separation colorspace colorSpace->copy(&csObj); sepFunc = NULL; if (colorSpace->isArray()) { colorSpace->arrayGet(0, &obj); if (obj.isName("Separation")) { csObj.free(); colorSpace->arrayGet(2, &csObj); sepFunc = new Function(colorSpace->arrayGet(3, &obj2)); obj2.free(); if (!sepFunc->isOk()) { delete sepFunc; sepFunc = NULL; } } obj.free(); } // get mode indexed = gFalse; if (csObj.isName()) { setMode(&csObj); } else if (csObj.isArray()) { csObj.arrayGet(0, &obj); if (obj.isName("Indexed") || obj.isName("I")) { indexed = gTrue; setMode(csObj.arrayGet(1, &obj2)); obj2.free(); } else { setMode(&csObj); } obj.free(); } else { goto err1; } if (!ok) return; // get lookup table for indexed colorspace if (indexed) { csObj.arrayGet(2, &obj); if (!obj.isInt()) goto err2; indexHigh = obj.getInt(); obj.free(); lookup = (Guchar (*)[4])gmalloc((indexHigh + 1) * 4 * sizeof(Guchar)); csObj.arrayGet(3, &obj); if (obj.isStream()) { obj.streamReset(); for (i = 0; i <= indexHigh; ++i) { for (j = 0; j < numComps; ++j) { if ((x = obj.streamGetChar()) == EOF) goto err2; lookup[i][j] = (Guchar)x; } } } else if (obj.isString()) { s = obj.getString()->getCString(); for (i = 0; i <= indexHigh; ++i) for (j = 0; j < numComps; ++j) lookup[i][j] = (Guchar)*s++; } else { goto err2; } obj.free(); } csObj.free(); return; err2: obj.free(); err1: csObj.free(); ok = gFalse; } GfxColorSpace::GfxColorSpace(GfxColorMode mode1) { sepFunc = NULL; mode = mode1; indexed = gFalse; switch (mode) { case colorGray: numComps = 1; break; case colorCMYK: numComps = 4; break; case colorRGB: numComps = 3; break; } lookup = NULL; ok = gTrue; } GfxColorSpace::~GfxColorSpace() { if (sepFunc) delete sepFunc; gfree(lookup); } GfxColorSpace::GfxColorSpace(GfxColorSpace *colorSpace) { int size; if (colorSpace->sepFunc) sepFunc = colorSpace->sepFunc->copy(); else sepFunc = NULL; mode = colorSpace->mode; indexed = colorSpace->indexed; numComps = colorSpace->numComps; indexHigh = colorSpace->indexHigh; if (indexed) { size = (indexHigh + 1) * 4 * sizeof(Guchar); lookup = (Guchar (*)[4])gmalloc(size); memcpy(lookup, colorSpace->lookup, size); } else { lookup = NULL; } ok = gTrue; } void GfxColorSpace::setMode(Object *colorSpace) { Object obj; if (colorSpace->isName("DeviceGray") || colorSpace->isName("G")) { mode = colorGray; numComps = 1; } else if (colorSpace->isName("DeviceRGB") || colorSpace->isName("RGB")) { mode = colorRGB; numComps = 3; } else if (colorSpace->isName("DeviceCMYK") || colorSpace->isName("CMYK")) { mode = colorCMYK; numComps = 4; } else if (colorSpace->isArray()) { colorSpace->arrayGet(0, &obj); if (obj.isName("CalGray")) { mode = colorGray; numComps = 1; } else if (obj.isName("CalRGB")) { mode = colorRGB; numComps = 3; } else if (obj.isName("CalCMYK")) { mode = colorCMYK; numComps = 4; } else { ok = gFalse; } obj.free(); } else { ok = gFalse; } } void GfxColorSpace::getColor(double x[4], GfxColor *color) { double y[4]; Guchar *p; if (sepFunc) { sepFunc->transform(x, y); } else { y[0] = x[0]; y[1] = x[1]; y[2] = x[2]; y[3] = x[3]; } if (indexed) { p = lookup[(int)(y[0] + 0.5)]; switch (mode) { case colorGray: color->setGray(p[0] / 255.0); break; case colorCMYK: color->setCMYK(p[0] / 255.0, p[1] / 255.0, p[2] / 255.0, p[3] / 255.0); break; case colorRGB: color->setRGB(p[0] / 255.0, p[1] / 255.0, p[2] / 255.0); break; } } else { switch (mode) { case colorGray: color->setGray(y[0]); break; case colorCMYK: color->setCMYK(y[0], y[1], y[2], y[3]); break; case colorRGB: color->setRGB(y[0], y[1], y[2]); break; } } } //------------------------------------------------------------------------ // Function //------------------------------------------------------------------------ Function::Function(Object *funcObj) { Stream *str; Dict *dict; int nSamples, sampleBits; double sampleMul; Object obj1, obj2; Guint buf, bitMask; int bits; int s; int i; ok = gFalse; samples = NULL; if (!funcObj->isStream()) { error(-1, "Expected function dictionary"); goto err3; } str = funcObj->getStream(); dict = str->getDict(); //----- FunctionType if (!dict->lookup("FunctionType", &obj1)->isInt() || obj1.getInt() != 0) { error(-1, "Unknown function type"); goto err2; } obj1.free(); //----- Domain if (!dict->lookup("Domain", &obj1)->isArray()) { error(-1, "Function is missing domain"); goto err2; } m = obj1.arrayGetLength() / 2; if (m > 4) { error(-1, "Functions with more than 1 input are unsupported"); goto err2; } 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; } domain[i][1] = obj2.getNum(); obj2.free(); } obj1.free(); //----- Range if (!dict->lookup("Range", &obj1)->isArray()) { error(-1, "Function is missing range"); goto err2; } n = obj1.arrayGetLength() / 2; if (n > 4) { error(-1, "Functions with more than 4 outputs are unsupported"); 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(); 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(); obj2.free(); } obj1.free(); //----- Size if (!dict->lookup("Size", &obj1)->isArray() || obj1.arrayGetLength() != m) { error(-1, "Function has missing or invalid size array"); goto err2; } for (i = 0; i < m; ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isInt()) { error(-1, "Illegal value in function size array"); goto err1; } sampleSize[i] = obj2.getInt(); obj2.free(); } obj1.free(); //----- BitsPerSample if (!dict->lookup("BitsPerSample", &obj1)->isInt()) { error(-1, "Function has missing or invalid BitsPerSample"); goto err2; } 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 err1; } 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 err1; } encode[i][1] = obj2.getNum(); obj2.free(); } } else { for (i = 0; i < m; ++i) { encode[i][0] = 0; encode[i][1] = sampleSize[i] - 1; } } 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 err1; } 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 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]; } } 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; } ok = gTrue; return; err1: obj2.free(); err2: obj1.free(); err3: return; } Function::Function(Function *func) { int nSamples, i; m = func->m; n = func->n; memcpy(domain, func->domain, sizeof(domain)); memcpy(range, func->range, sizeof(range)); memcpy(sampleSize, func->sampleSize, sizeof(sampleSize)); memcpy(encode, func->encode, sizeof(encode)); memcpy(decode, func->decode, sizeof(decode)); nSamples = n; for (i = 0; i < m; ++i) nSamples *= sampleSize[i]; samples = (double *)gmalloc(nSamples * sizeof(double)); memcpy(samples, func->samples, nSamples * sizeof(double)); ok = gTrue; } Function::~Function() { if (samples) gfree(samples); } void Function::transform(double *in, double *out) { double e[4]; double s; double x0, x1; int e0, e1; double efrac; 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; } for (i = 0; i < n; ++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]; } } //------------------------------------------------------------------------ // GfxImageColorMap //------------------------------------------------------------------------ GfxImageColorMap::GfxImageColorMap(int bits1, Object *decode, GfxColorSpace *colorSpace1) { GfxColor color; double x[4]; int maxPixel; Object obj; int i, j; ok = gTrue; // bits per component and colorspace bits = bits1; maxPixel = (1 << bits) - 1; colorSpace = colorSpace1; mode = colorSpace->getMode(); // get decode map if (decode->isNull()) { if (colorSpace->isIndexed()) { indexed = gTrue; numComps = 1; decodeLow[0] = 0; decodeRange[0] = maxPixel; } else { indexed = gFalse; numComps = colorSpace->getNumPixelComps(); for (i = 0; i < numComps; ++i) { decodeLow[i] = 0; decodeRange[i] = 1; } } } else if (decode->isArray()) { numComps = decode->arrayGetLength() / 2; if (numComps != colorSpace->getNumPixelComps()) goto err1; indexed = colorSpace->isIndexed(); for (i = 0; i < numComps; ++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 lookup table lookup = (double (*)[4])gmalloc((maxPixel + 1) * 4 * sizeof(double)); if (indexed) { for (i = 0; i <= maxPixel; ++i) { x[0] = (double)i; colorSpace->getColor(x, &color); lookup[i][0] = color.getR(); lookup[i][1] = color.getG(); lookup[i][2] = color.getB(); } } else { for (i = 0; i <= maxPixel; ++i) for (j = 0; j < numComps; ++j) lookup[i][j] = decodeLow[j] + (i * decodeRange[j]) / maxPixel; } return; err2: obj.free(); err1: ok = gFalse; } GfxImageColorMap::~GfxImageColorMap() { delete colorSpace; gfree(lookup); } void GfxImageColorMap::getColor(Guchar x[4], GfxColor *color) { double *p; if (indexed) { p = lookup[x[0]]; color->setRGB(p[0], p[1], p[2]); } else { switch (mode) { case colorGray: color->setGray(lookup[x[0]][0]); break; case colorCMYK: color->setCMYK(lookup[x[0]][0], lookup[x[1]][1], lookup[x[2]][2], lookup[x[3]][3]); break; case colorRGB: color->setRGB(lookup[x[0]][0], lookup[x[1]][1], lookup[x[2]][2]); break; } } } //------------------------------------------------------------------------ // 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; } 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)); } 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; } GfxPath::GfxPath() { justMoved = gFalse; size = 16; n = 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); } //------------------------------------------------------------------------ // GfxState //------------------------------------------------------------------------ GfxState::GfxState(int dpi, double px1a, double py1a, double px2a, double py2a, int rotate, GBool upsideDown) { double k; px1 = px1a; py1 = py1a; px2 = px2a; py2 = py2a; k = (double)dpi / 72.0; if (rotate == 90) { ctm[0] = 0; ctm[1] = upsideDown ? k : -k; ctm[2] = k; ctm[3] = 0; ctm[4] = -k * py1; ctm[5] = k * (upsideDown ? -px1 : px2); pageWidth = (int)(k * (py2 - py1)); pageHeight = (int)(k * (px2 - px1)); } else if (rotate == 180) { ctm[0] = -k; ctm[1] = 0; ctm[2] = 0; ctm[3] = upsideDown ? k : -k; ctm[4] = k * px2; ctm[5] = k * (upsideDown ? -py1 : py2); pageWidth = (int)(k * (px2 - px1)); pageHeight = (int)(k * (py2 - py1)); } else if (rotate == 270) { ctm[0] = 0; ctm[1] = upsideDown ? -k : k; ctm[2] = -k; ctm[3] = 0; ctm[4] = k * py2; ctm[5] = k * (upsideDown ? px2 : -px1); pageWidth = (int)(k * (py2 - py1)); pageHeight = (int)(k * (px2 - px1)); } else { ctm[0] = k; ctm[1] = 0; ctm[2] = 0; ctm[3] = upsideDown ? -k : k; ctm[4] = -k * px1; ctm[5] = k * (upsideDown ? py2 : -py1); pageWidth = (int)(k * (px2 - px1)); pageHeight = (int)(k * (py2 - py1)); } fillColorSpace = new GfxColorSpace(colorGray); strokeColorSpace = new GfxColorSpace(colorGray); fillColor.setGray(0); strokeColor.setGray(0); lineWidth = 1; lineDash = NULL; lineDashLength = 0; lineDashStart = 0; flatness = 0; 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; saved = NULL; } GfxState::~GfxState() { if (fillColorSpace) delete fillColorSpace; if (strokeColorSpace) delete strokeColorSpace; gfree(lineDash); 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 (lineDashLength > 0) { lineDash = (double *)gmalloc(lineDashLength * sizeof(double)); memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double)); } path = state->path->copy(); saved = NULL; } 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::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]; 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]; } void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) { if (fillColorSpace) delete fillColorSpace; fillColorSpace = colorSpace; } void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) { if (strokeColorSpace) delete strokeColorSpace; strokeColorSpace = colorSpace; } 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::textShift(double tx) { double dx, dy; textTransformDelta(tx, 0, &dx, &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; saved = NULL; delete this; } else { oldState = this; } return oldState; }