From: kramm Date: Sat, 3 Dec 2005 14:47:09 +0000 (+0000) Subject: upgraded to xpdf-3.01pl1 X-Git-Tag: xpdf-3-01~2 X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=commitdiff_plain;h=297cc1806d04fad24755c0e51eef94c85981bace upgraded to xpdf-3.01pl1 --- diff --git a/pdf2swf/xpdf/FoFiType1C.cc b/pdf2swf/xpdf/FoFiType1C.cc index a1cec31..499414b 100644 --- a/pdf2swf/xpdf/FoFiType1C.cc +++ b/pdf2swf/xpdf/FoFiType1C.cc @@ -120,7 +120,7 @@ Gushort *FoFiType1C::getCIDToGIDMap(int *nCIDs) { } } ++n; - map = (Gushort *)gmalloc(n * sizeof(Gushort)); + map = (Gushort *)gmallocn(n, sizeof(Gushort)); memset(map, 0, n * sizeof(Gushort)); for (i = 0; i < nGlyphs; ++i) { map[charset[i]] = i; @@ -231,11 +231,8 @@ void FoFiType1C::convertToType1(char **newEncoding, GBool ascii, (*outputFunc)(outputStream, "0 1 255 {1 index exch /.notdef put} for\n", 40); enc = newEncoding ? newEncoding : encoding; - if(!enc) { - fprintf(stderr, "convertToType1: Warning: No Encoding\n"); - } for (i = 0; i < 256; ++i) { - if (enc && enc[i]) { + if (enc[i]) { sprintf(buf, "dup %d /%s put\n", i, enc[i]); (*outputFunc)(outputStream, buf, strlen(buf)); } @@ -406,7 +403,7 @@ void FoFiType1C::convertToCIDType0(char *psName, nCIDs = charset[i] + 1; } } - cidMap = (int *)gmalloc(nCIDs * sizeof(int)); + cidMap = (int *)gmallocn(nCIDs, sizeof(int)); for (i = 0; i < nCIDs; ++i) { cidMap[i] = -1; } @@ -416,7 +413,7 @@ void FoFiType1C::convertToCIDType0(char *psName, // build the charstrings charStrings = new GString(); - charStringOffsets = (int *)gmalloc((nCIDs + 1) * sizeof(int)); + charStringOffsets = (int *)gmallocn(nCIDs + 1, sizeof(int)); for (i = 0; i < nCIDs; ++i) { charStringOffsets[i] = charStrings->getLength(); if ((gid = cidMap[i]) >= 0) { @@ -479,10 +476,18 @@ void FoFiType1C::convertToCIDType0(char *psName, sprintf(buf, " /Supplement %d def\n", topDict.supplement); (*outputFunc)(outputStream, buf, strlen(buf)); (*outputFunc)(outputStream, "end def\n", 8); - sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n", - topDict.fontMatrix[0], topDict.fontMatrix[1], topDict.fontMatrix[2], - topDict.fontMatrix[3], topDict.fontMatrix[4], topDict.fontMatrix[5]); - (*outputFunc)(outputStream, buf, strlen(buf)); + if (topDict.hasFontMatrix) { + sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n", + topDict.fontMatrix[0], topDict.fontMatrix[1], + topDict.fontMatrix[2], topDict.fontMatrix[3], + topDict.fontMatrix[4], topDict.fontMatrix[5]); + (*outputFunc)(outputStream, buf, strlen(buf)); + } else if (privateDicts[0].hasFontMatrix) { + (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); + } else { + (*outputFunc)(outputStream, + "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38); + } sprintf(buf, "/FontBBox [%g %g %g %g] def\n", topDict.fontBBox[0], topDict.fontBBox[1], topDict.fontBBox[2], topDict.fontBBox[3]); @@ -512,7 +517,18 @@ void FoFiType1C::convertToCIDType0(char *psName, sprintf(buf, "dup %d 10 dict begin\n", i); (*outputFunc)(outputStream, buf, strlen(buf)); (*outputFunc)(outputStream, "/FontType 1 def\n", 16); - (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); + if (privateDicts[i].hasFontMatrix) { + sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n", + privateDicts[i].fontMatrix[0], + privateDicts[i].fontMatrix[1], + privateDicts[i].fontMatrix[2], + privateDicts[i].fontMatrix[3], + privateDicts[i].fontMatrix[4], + privateDicts[i].fontMatrix[5]); + (*outputFunc)(outputStream, buf, strlen(buf)); + } else { + (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); + } sprintf(buf, "/PaintType %d def\n", topDict.paintType); (*outputFunc)(outputStream, buf, strlen(buf)); (*outputFunc)(outputStream, "/Private 32 dict begin\n", 23); @@ -673,7 +689,7 @@ void FoFiType1C::convertToType0(char *psName, nCIDs = charset[i] + 1; } } - cidMap = (int *)gmalloc(nCIDs * sizeof(int)); + cidMap = (int *)gmallocn(nCIDs, sizeof(int)); for (i = 0; i < nCIDs; ++i) { cidMap[i] = -1; } @@ -702,11 +718,21 @@ void FoFiType1C::convertToType0(char *psName, sprintf(buf, "_%02x def\n", i >> 8); (*outputFunc)(outputStream, buf, strlen(buf)); (*outputFunc)(outputStream, "/FontType 1 def\n", 16); - sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n", - topDict.fontMatrix[0], topDict.fontMatrix[1], - topDict.fontMatrix[2], topDict.fontMatrix[3], - topDict.fontMatrix[4], topDict.fontMatrix[5]); - (*outputFunc)(outputStream, buf, strlen(buf)); + if (privateDicts[fd].hasFontMatrix) { + sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n", + privateDicts[fd].fontMatrix[0], + privateDicts[fd].fontMatrix[1], + privateDicts[fd].fontMatrix[2], + privateDicts[fd].fontMatrix[3], + privateDicts[fd].fontMatrix[4], + privateDicts[fd].fontMatrix[5]); + (*outputFunc)(outputStream, buf, strlen(buf)); + } else if (topDict.hasFontMatrix) { + (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); + } else { + (*outputFunc)(outputStream, + "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38); + } sprintf(buf, "/FontBBox [%g %g %g %g] def\n", topDict.fontBBox[0], topDict.fontBBox[1], topDict.fontBBox[2], topDict.fontBBox[3]); @@ -890,7 +916,15 @@ void FoFiType1C::convertToType0(char *psName, (*outputFunc)(outputStream, psName, strlen(psName)); (*outputFunc)(outputStream, " def\n", 5); (*outputFunc)(outputStream, "/FontType 0 def\n", 16); - (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); + if (topDict.hasFontMatrix) { + sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n", + topDict.fontMatrix[0], topDict.fontMatrix[1], + topDict.fontMatrix[2], topDict.fontMatrix[3], + topDict.fontMatrix[4], topDict.fontMatrix[5]); + (*outputFunc)(outputStream, buf, strlen(buf)); + } else { + (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); + } (*outputFunc)(outputStream, "/FMapType 2 def\n", 16); (*outputFunc)(outputStream, "/Encoding [\n", 12); for (i = 0; i < nCIDs; i += 256) { @@ -950,6 +984,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, nOps = 0; nHints = 0; firstOp = gTrue; + openPath = gFalse; } pos = offset; @@ -973,6 +1008,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, d = 0; dFP = gFalse; for (k = 0; k < nOps; k += 2) { + // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints if (ops[k+1].num < 0) { d += ops[k].num + ops[k+1].num; dFP |= ops[k].isFP | ops[k+1].isFP; @@ -1002,6 +1038,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, d = 0; dFP = gFalse; for (k = 0; k < nOps; k += 2) { + // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints if (ops[k+1].num < 0) { d += ops[k].num + ops[k+1].num; dFP |= ops[k].isFP | ops[k+1].isFP; @@ -1025,6 +1062,10 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, cvtGlyphWidth(nOps == 2, charBuf, pDict); firstOp = gFalse; } + if (openPath) { + charBuf->append((char)9); + openPath = gFalse; + } if (nOps != 1) { //~ error(-1, "Wrong number of args (%d) to Type 2 vmoveto", nOps); } @@ -1042,6 +1083,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, charBuf->append((char)5); } nOps = 0; + openPath = gTrue; break; case 0x0006: // hlineto if (nOps < 1) { @@ -1052,6 +1094,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, charBuf->append((char)((k & 1) ? 7 : 6)); } nOps = 0; + openPath = gTrue; break; case 0x0007: // vlineto if (nOps < 1) { @@ -1062,6 +1105,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, charBuf->append((char)((k & 1) ? 6 : 7)); } nOps = 0; + openPath = gTrue; break; case 0x0008: // rrcurveto if (nOps < 6 || nOps % 6 != 0) { @@ -1077,6 +1121,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, charBuf->append((char)8); } nOps = 0; + openPath = gTrue; break; case 0x000a: // callsubr if (nOps >= 1) { @@ -1102,6 +1147,10 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, cvtGlyphWidth(nOps == 1 || nOps == 5, charBuf, pDict); firstOp = gFalse; } + if (openPath) { + charBuf->append((char)9); + openPath = gFalse; + } if (nOps == 4) { cvtNum(0, gFalse, charBuf); cvtNum(ops[0].num, ops[0].isFP, charBuf); @@ -1177,6 +1226,10 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, cvtGlyphWidth(nOps == 3, charBuf, pDict); firstOp = gFalse; } + if (openPath) { + charBuf->append((char)9); + openPath = gFalse; + } if (nOps != 2) { //~ error(-1, "Wrong number of args (%d) to Type 2 rmoveto", nOps); } @@ -1190,6 +1243,10 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, cvtGlyphWidth(nOps == 2, charBuf, pDict); firstOp = gFalse; } + if (openPath) { + charBuf->append((char)9); + openPath = gFalse; + } if (nOps != 1) { //~ error(-1, "Wrong number of args (%d) to Type 2 hmoveto", nOps); } @@ -1226,6 +1283,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, cvtNum(ops[k+1].num, ops[k].isFP, charBuf); charBuf->append((char)5); nOps = 0; + openPath = gTrue; break; case 0x0019: // rlinecurve if (nOps < 8 || (nOps - 6) % 2 != 0) { @@ -1244,6 +1302,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf); charBuf->append((char)8); nOps = 0; + openPath = gTrue; break; case 0x001a: // vvcurveto if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) { @@ -1271,6 +1330,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, charBuf->append((char)8); } nOps = 0; + openPath = gTrue; break; case 0x001b: // hhcurveto if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) { @@ -1298,6 +1358,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, charBuf->append((char)8); } nOps = 0; + openPath = gTrue; break; case 0x001d: // callgsubr if (nOps >= 1) { @@ -1351,6 +1412,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, charBuf->append((char)8); } nOps = 0; + openPath = gTrue; break; case 0x001f: // hvcurveto if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) { @@ -1390,6 +1452,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, charBuf->append((char)8); } nOps = 0; + openPath = gTrue; break; case 0x0c00: // dotsection (should be Type 1 only?) // ignored @@ -1439,6 +1502,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, cvtNum(0, gFalse, charBuf); charBuf->append((char)8); nOps = 0; + openPath = gTrue; break; case 0x0c23: // flex if (nOps != 13) { @@ -1459,6 +1523,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, cvtNum(ops[11].num, ops[11].isFP, charBuf); charBuf->append((char)8); nOps = 0; + openPath = gTrue; break; case 0x0c24: // hflex1 if (nOps != 9) { @@ -1480,6 +1545,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, ops[1].isFP | ops[3].isFP | ops[7].isFP, charBuf); charBuf->append((char)8); nOps = 0; + openPath = gTrue; break; case 0x0c25: // flex1 if (nOps != 11) { @@ -1509,6 +1575,7 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf, } charBuf->append((char)8); nOps = 0; + openPath = gTrue; break; default: //~ error(-1, "Illegal Type 2 charstring op: %04x", @@ -1695,7 +1762,7 @@ GBool FoFiType1C::parse() { } nFDs = fdIdx.len; privateDicts = (Type1CPrivateDict *) - gmalloc(nFDs * sizeof(Type1CPrivateDict)); + gmallocn(nFDs, sizeof(Type1CPrivateDict)); for (i = 0; i < nFDs; ++i) { getIndexVal(&fdIdx, i, &val, &parsedOk); if (!parsedOk) { @@ -1776,6 +1843,7 @@ void FoFiType1C::readTopDict() { topDict.fontMatrix[3] = 0.001; topDict.fontMatrix[4] = 0; topDict.fontMatrix[5] = 0; + topDict.hasFontMatrix = gFalse; topDict.uniqueID = 0; topDict.fontBBox[0] = 0; topDict.fontBBox[1] = 0; @@ -1824,7 +1892,8 @@ void FoFiType1C::readTopDict() { topDict.fontMatrix[2] = ops[2].num; topDict.fontMatrix[3] = ops[3].num; topDict.fontMatrix[4] = ops[4].num; - topDict.fontMatrix[5] = ops[5].num; break; + topDict.fontMatrix[5] = ops[5].num; + topDict.hasFontMatrix = gTrue; break; case 0x000d: topDict.uniqueID = (int)ops[0].num; break; case 0x0005: topDict.fontBBox[0] = ops[0].num; topDict.fontBBox[1] = ops[1].num; @@ -1848,10 +1917,14 @@ void FoFiType1C::readTopDict() { } // Read a CID font dict (FD) - this pulls out the private dict -// pointer, and reads the private dict. +// pointer, and reads the private dict. It also pulls the FontMatrix +// (if any) out of the FD. void FoFiType1C::readFD(int offset, int length, Type1CPrivateDict *pDict) { int pos, pSize, pOffset; + double fontMatrix[6]; + GBool hasFontMatrix; + hasFontMatrix = gFalse; pSize = pOffset = 0; pos = offset; nOps = 0; @@ -1869,17 +1942,35 @@ void FoFiType1C::readFD(int offset, int length, Type1CPrivateDict *pDict) { pSize = (int)ops[0].num; pOffset = (int)ops[1].num; break; + } else if (ops[nOps - 1].op == 0x0c07) { + fontMatrix[0] = ops[0].num; + fontMatrix[1] = ops[1].num; + fontMatrix[2] = ops[2].num; + fontMatrix[3] = ops[3].num; + fontMatrix[4] = ops[4].num; + fontMatrix[5] = ops[5].num; + hasFontMatrix = gTrue; } nOps = 0; } } readPrivateDict(pOffset, pSize, pDict); + if (hasFontMatrix) { + pDict->fontMatrix[0] = fontMatrix[0]; + pDict->fontMatrix[1] = fontMatrix[1]; + pDict->fontMatrix[2] = fontMatrix[2]; + pDict->fontMatrix[3] = fontMatrix[3]; + pDict->fontMatrix[4] = fontMatrix[4]; + pDict->fontMatrix[5] = fontMatrix[5]; + pDict->hasFontMatrix = gTrue; + } } void FoFiType1C::readPrivateDict(int offset, int length, Type1CPrivateDict *pDict) { int pos; + pDict->hasFontMatrix = gFalse; pDict->nBlueValues = 0; pDict->nOtherBlues = 0; pDict->nFamilyBlues = 0; @@ -1898,9 +1989,9 @@ void FoFiType1C::readPrivateDict(int offset, int length, pDict->initialRandomSeed = 0; pDict->subrsOffset = 0; pDict->defaultWidthX = 0; - pDict->defaultWidthXFP = 0; + pDict->defaultWidthXFP = gFalse; pDict->nominalWidthX = 0; - pDict->nominalWidthXFP = 0; + pDict->nominalWidthXFP = gFalse; // no dictionary if (offset == 0 || length == 0) { @@ -1979,9 +2070,11 @@ void FoFiType1C::readPrivateDict(int offset, int length, break; case 0x0014: pDict->defaultWidthX = ops[0].num; + pDict->defaultWidthXFP = ops[0].isFP; break; case 0x0015: pDict->nominalWidthX = ops[0].num; + pDict->nominalWidthXFP = ops[0].isFP; break; } nOps = 0; @@ -2052,7 +2145,7 @@ void FoFiType1C::buildEncoding() { encoding = fofiType1ExpertEncoding; } else { - encoding = (char **)gmalloc(256 * sizeof(char *)); + encoding = (char **)gmallocn(256, sizeof(char *)); for (i = 0; i < 256; ++i) { encoding[i] = NULL; } @@ -2139,7 +2232,7 @@ GBool FoFiType1C::readCharset() { } else if (topDict.charsetOffset == 2) { charset = fofiType1CExpertSubsetCharset; } else { - charset = (Gushort *)gmalloc(nGlyphs * sizeof(Gushort)); + charset = (Gushort *)gmallocn(nGlyphs, sizeof(Gushort)); for (i = 0; i < nGlyphs; ++i) { charset[i] = 0; } @@ -2324,9 +2417,9 @@ void FoFiType1C::getIndex(int pos, Type1CIndex *idx, GBool *ok) { idx->pos = pos; idx->len = getU16BE(pos, ok); if (idx->len == 0) { - // empty indexes are legal + // empty indexes are legal and contain just the length field idx->offSize = 0; - idx->startPos = idx->endPos = 0; + idx->startPos = idx->endPos = pos + 2; } else { idx->offSize = getU8(pos + 2, ok); if (idx->offSize < 1 || idx->offSize > 4) { @@ -2356,7 +2449,7 @@ void FoFiType1C::getIndexVal(Type1CIndex *idx, int i, idx->offSize, ok); pos1 = idx->startPos + getUVarBE(idx->pos + 3 + (i + 1) * idx->offSize, idx->offSize, ok); - if (pos0 < idx->startPos || pos0 >= idx->endPos || + if (pos0 < idx->startPos || pos0 > idx->endPos || pos1 <= idx->startPos || pos1 > idx->endPos || pos1 < pos0) { *ok = gFalse; @@ -2374,7 +2467,7 @@ char *FoFiType1C::getString(int sid, char *buf, GBool *ok) { } else { sid -= 391; getIndexVal(&stringIdx, sid, &val, ok); - if (ok) { + if (*ok) { if ((n = val.len) > 255) { n = 255; } diff --git a/pdf2swf/xpdf/Gfx.cc b/pdf2swf/xpdf/Gfx.cc index e86d6b1..a13c615 100644 --- a/pdf2swf/xpdf/Gfx.cc +++ b/pdf2swf/xpdf/Gfx.cc @@ -12,6 +12,7 @@ #pragma implementation #endif +#include #include #include #include @@ -45,19 +46,33 @@ #define functionMaxDepth 6 // Max delta allowed in any color component for a function shading fill. -#define functionColorDelta (1 / 256.0) +#define functionColorDelta (dblToCol(1 / 256.0)) // Max number of splits along the t axis for an axial shading fill. #define axialMaxSplits 256 // Max delta allowed in any color component for an axial shading fill. -#define axialColorDelta (1 / 256.0) +#define axialColorDelta (dblToCol(1 / 256.0)) // Max number of splits along the t axis for a radial shading fill. #define radialMaxSplits 256 // Max delta allowed in any color component for a radial shading fill. -#define radialColorDelta (1 / 256.0) +#define radialColorDelta (dblToCol(1 / 256.0)) + +// Max recursive depth for a Gouraud triangle shading fill. +#define gouraudMaxDepth 4 + +// Max delta allowed in any color component for a Gouraud triangle +// shading fill. +#define gouraudColorDelta (dblToCol(1 / 256.0)) + +// Max recursive depth for a patch mesh shading fill. +#define patchMaxDepth 6 + +// Max delta allowed in any color component for a patch mesh shading +// fill. +#define patchColorDelta (dblToCol(1 / 256.0)) //------------------------------------------------------------------------ // Operator table @@ -405,7 +420,7 @@ GBool GfxResources::lookupGState(char *name, Object *obj) { //------------------------------------------------------------------------ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, - double hDPI, double vDPI, PDFRectangle *box, GBool crop, + double hDPI, double vDPI, PDFRectangle *box, PDFRectangle *cropBox, int rotate, GBool (*abortCheckCbkA)(void *data), void *abortCheckCbkDataA) { @@ -424,7 +439,7 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, fontChanged = gFalse; clip = clipNone; ignoreUndef = 0; - out->startPage(pageNum, state, cropBox->x1,cropBox->y1,cropBox->x2,cropBox->y2); + out->startPage(pageNum, state); out->setDefaultCTM(state->getCTM()); out->updateAll(state); for (i = 0; i < 6; ++i) { @@ -435,7 +450,7 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, abortCheckCbkData = abortCheckCbkDataA; // set crop box - /*if (crop) { + if (cropBox) { state->moveTo(cropBox->x1, cropBox->y1); state->lineTo(cropBox->x2, cropBox->y1); state->lineTo(cropBox->x2, cropBox->y2); @@ -444,11 +459,11 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, state->clip(); out->clip(state); state->clearPath(); - }*/ + } } Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict, - PDFRectangle *box, GBool crop, PDFRectangle *cropBox, + PDFRectangle *box, PDFRectangle *cropBox, GBool (*abortCheckCbkA)(void *data), void *abortCheckCbkDataA) { int i; @@ -474,7 +489,7 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict, abortCheckCbkData = abortCheckCbkDataA; // set crop box - if (crop) { + if (cropBox) { state->moveTo(cropBox->x1, cropBox->y1); state->lineTo(cropBox->x2, cropBox->y1); state->lineTo(cropBox->x2, cropBox->y2); @@ -734,7 +749,7 @@ void Gfx::opSetDash(Object args[], int numArgs) { if (length == 0) { dash = NULL; } else { - dash = (double *)gmalloc(length * sizeof(double)); + dash = (double *)gmallocn(length, sizeof(double)); for (i = 0; i < length; ++i) { dash[i] = a->get(i, &obj)->getNum(); obj.free(); @@ -771,6 +786,8 @@ void Gfx::opSetLineWidth(Object args[], int numArgs) { void Gfx::opSetExtGState(Object args[], int numArgs) { Object obj1, obj2; + GfxBlendMode mode; + GBool haveFillOP; if (!res->lookupGState(args[0].getName(), &obj1)) { return; @@ -780,6 +797,17 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { obj1.free(); return; } + + // transparency support: blend mode, fill/stroke opacity + if (!obj1.dictLookup("BM", &obj2)->isNull()) { + if (state->parseBlendMode(&obj2, &mode)) { + state->setBlendMode(mode); + out->updateBlendMode(state); + } else { + error(getPos(), "Invalid blend mode in ExtGState"); + } + } + obj2.free(); if (obj1.dictLookup("ca", &obj2)->isNum()) { state->setFillOpacity(obj2.getNum()); out->updateFillOpacity(state); @@ -790,6 +818,23 @@ void Gfx::opSetExtGState(Object args[], int numArgs) { out->updateStrokeOpacity(state); } obj2.free(); + + // fill/stroke overprint + if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) { + state->setFillOverprint(obj2.getBool()); + out->updateFillOverprint(state); + } + obj2.free(); + if (obj1.dictLookup("OP", &obj2)->isBool()) { + state->setStrokeOverprint(obj2.getBool()); + out->updateStrokeOverprint(state); + if (!haveFillOP) { + state->setFillOverprint(obj2.getBool()); + out->updateFillOverprint(state); + } + } + obj2.free(); + obj1.free(); } @@ -805,7 +850,8 @@ void Gfx::opSetFillGray(Object args[], int numArgs) { state->setFillPattern(NULL); state->setFillColorSpace(new GfxDeviceGrayColorSpace()); - color.c[0] = args[0].getNum(); + out->updateFillColorSpace(state); + color.c[0] = dblToCol(args[0].getNum()); state->setFillColor(&color); out->updateFillColor(state); } @@ -815,7 +861,8 @@ void Gfx::opSetStrokeGray(Object args[], int numArgs) { state->setStrokePattern(NULL); state->setStrokeColorSpace(new GfxDeviceGrayColorSpace()); - color.c[0] = args[0].getNum(); + out->updateStrokeColorSpace(state); + color.c[0] = dblToCol(args[0].getNum()); state->setStrokeColor(&color); out->updateStrokeColor(state); } @@ -826,8 +873,9 @@ void Gfx::opSetFillCMYKColor(Object args[], int numArgs) { state->setFillPattern(NULL); state->setFillColorSpace(new GfxDeviceCMYKColorSpace()); + out->updateFillColorSpace(state); for (i = 0; i < 4; ++i) { - color.c[i] = args[i].getNum(); + color.c[i] = dblToCol(args[i].getNum()); } state->setFillColor(&color); out->updateFillColor(state); @@ -839,8 +887,9 @@ void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) { state->setStrokePattern(NULL); state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace()); + out->updateStrokeColorSpace(state); for (i = 0; i < 4; ++i) { - color.c[i] = args[i].getNum(); + color.c[i] = dblToCol(args[i].getNum()); } state->setStrokeColor(&color); out->updateStrokeColor(state); @@ -852,8 +901,9 @@ void Gfx::opSetFillRGBColor(Object args[], int numArgs) { state->setFillPattern(NULL); state->setFillColorSpace(new GfxDeviceRGBColorSpace()); + out->updateFillColorSpace(state); for (i = 0; i < 3; ++i) { - color.c[i] = args[i].getNum(); + color.c[i] = dblToCol(args[i].getNum()); } state->setFillColor(&color); out->updateFillColor(state); @@ -865,8 +915,9 @@ void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) { state->setStrokePattern(NULL); state->setStrokeColorSpace(new GfxDeviceRGBColorSpace()); + out->updateStrokeColorSpace(state); for (i = 0; i < 3; ++i) { - color.c[i] = args[i].getNum(); + color.c[i] = dblToCol(args[i].getNum()); } state->setStrokeColor(&color); out->updateStrokeColor(state); @@ -888,6 +939,7 @@ void Gfx::opSetFillColorSpace(Object args[], int numArgs) { obj.free(); if (colorSpace) { state->setFillColorSpace(colorSpace); + out->updateFillColorSpace(state); } else { error(getPos(), "Bad color space (fill)"); } @@ -914,6 +966,7 @@ void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) { obj.free(); if (colorSpace) { state->setStrokeColorSpace(colorSpace); + out->updateStrokeColorSpace(state); } else { error(getPos(), "Bad color space (stroke)"); } @@ -930,7 +983,7 @@ void Gfx::opSetFillColor(Object args[], int numArgs) { state->setFillPattern(NULL); for (i = 0; i < numArgs; ++i) { - color.c[i] = args[i].getNum(); + color.c[i] = dblToCol(args[i].getNum()); } state->setFillColor(&color); out->updateFillColor(state); @@ -942,7 +995,7 @@ void Gfx::opSetStrokeColor(Object args[], int numArgs) { state->setStrokePattern(NULL); for (i = 0; i < numArgs; ++i) { - color.c[i] = args[i].getNum(); + color.c[i] = dblToCol(args[i].getNum()); } state->setStrokeColor(&color); out->updateStrokeColor(state); @@ -957,7 +1010,7 @@ void Gfx::opSetFillColorN(Object args[], int numArgs) { if (numArgs > 1) { for (i = 0; i < numArgs && i < 4; ++i) { if (args[i].isNum()) { - color.c[i] = args[i].getNum(); + color.c[i] = dblToCol(args[i].getNum()); } } state->setFillColor(&color); @@ -972,7 +1025,7 @@ void Gfx::opSetFillColorN(Object args[], int numArgs) { state->setFillPattern(NULL); for (i = 0; i < numArgs && i < 4; ++i) { if (args[i].isNum()) { - color.c[i] = args[i].getNum(); + color.c[i] = dblToCol(args[i].getNum()); } } state->setFillColor(&color); @@ -989,7 +1042,7 @@ void Gfx::opSetStrokeColorN(Object args[], int numArgs) { if (numArgs > 1) { for (i = 0; i < numArgs && i < 4; ++i) { if (args[i].isNum()) { - color.c[i] = args[i].getNum(); + color.c[i] = dblToCol(args[i].getNum()); } } state->setStrokeColor(&color); @@ -1004,7 +1057,7 @@ void Gfx::opSetStrokeColorN(Object args[], int numArgs) { state->setStrokePattern(NULL); for (i = 0; i < numArgs && i < 4; ++i) { if (args[i].isNum()) { - color.c[i] = args[i].getNum(); + color.c[i] = dblToCol(args[i].getNum()); } } state->setStrokeColor(&color); @@ -1294,7 +1347,7 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) { m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4]; m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5]; - // construct a (base space) -> (pattern space) transform matrix + // construct a (device space) -> (pattern space) transform matrix det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]); imb[0] = m1[3] * det; imb[1] = -m1[1] * det; @@ -1312,11 +1365,15 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) { // Adobe's behavior if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) { state->setFillColorSpace(cs->copy()); + out->updateFillColorSpace(state); state->setStrokeColorSpace(cs->copy()); + out->updateStrokeColorSpace(state); state->setStrokeColor(state->getFillColor()); } else { state->setFillColorSpace(new GfxDeviceGrayColorSpace()); + out->updateFillColorSpace(state); state->setStrokeColorSpace(new GfxDeviceGrayColorSpace()); + out->updateStrokeColorSpace(state); } state->setFillPattern(NULL); out->updateFillColor(state); @@ -1334,8 +1391,13 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) { } state->clearPath(); - // transform clip region bbox to pattern space + // get the clip region, check for empty state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax); + if (cxMin > cxMax || cyMin > cyMax) { + goto err; + } + + // transform clip region bbox to pattern space xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4]; yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5]; x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4]; @@ -1387,18 +1449,28 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) { for (i = 0; i < 4; ++i) { m1[i] = m[i]; } - for (yi = yi0; yi < yi1; ++yi) { - for (xi = xi0; xi < xi1; ++xi) { - x = xi * xstep; - y = yi * ystep; - m1[4] = x * m[0] + y * m[2] + m[4]; - m1[5] = x * m[1] + y * m[3] + m[5]; - doForm1(tPat->getContentStream(), tPat->getResDict(), - m1, tPat->getBBox()); + if (out->useTilingPatternFill()) { + m1[4] = m[4]; + m1[5] = m[5]; + out->tilingPatternFill(state, tPat->getContentStream(), + tPat->getPaintType(), tPat->getResDict(), + m1, tPat->getBBox(), + xi0, yi0, xi1, yi1, xstep, ystep); + } else { + for (yi = yi0; yi < yi1; ++yi) { + for (xi = xi0; xi < xi1; ++xi) { + x = xi * xstep; + y = yi * ystep; + m1[4] = x * m[0] + y * m[2] + m[4]; + m1[5] = x * m[1] + y * m[3] + m[5]; + doForm1(tPat->getContentStream(), tPat->getResDict(), + m1, tPat->getBBox()); + } } } // restore graphics state + err: restoreState(); state->setPath(savedPath); } @@ -1427,7 +1499,7 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) { state->closePath(); state->clip(); out->clip(state); - state->clearPath(); + state->setPath(savedPath->copy()); } // clip to current path @@ -1437,6 +1509,17 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) { } else { out->clip(state); } + + // set the color space + state->setFillColorSpace(shading->getColorSpace()->copy()); + out->updateFillColorSpace(state); + + // background color fill + if (shading->getHasBackground()) { + state->setFillColor(shading->getBackground()); + out->updateFillColor(state); + out->fill(state); + } state->clearPath(); // construct a (pattern space) -> (current space) transform matrix @@ -1470,9 +1553,6 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) { state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]); out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]); - // set the color space - state->setFillColorSpace(shading->getColorSpace()->copy()); - // do shading type-specific operations switch (shading->getType()) { case 1: @@ -1484,6 +1564,14 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) { case 3: doRadialShFill((GfxRadialShading *)shading); break; + case 4: + case 5: + doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading); + break; + case 6: + case 7: + doPatchMeshShFill((GfxPatchMeshShading *)shading); + break; } // restore graphics state @@ -1519,6 +1607,7 @@ void Gfx::opShFill(Object args[], int numArgs) { // set the color space state->setFillColorSpace(shading->getColorSpace()->copy()); + out->updateFillColorSpace(state); // do shading type-specific operations switch (shading->getType()) { @@ -1531,6 +1620,14 @@ void Gfx::opShFill(Object args[], int numArgs) { case 3: doRadialShFill((GfxRadialShading *)shading); break; + case 4: + case 5: + doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading); + break; + case 6: + case 7: + doPatchMeshShFill((GfxPatchMeshShading *)shading); + break; } // restore graphics state @@ -1544,12 +1641,16 @@ void Gfx::doFunctionShFill(GfxFunctionShading *shading) { double x0, y0, x1, y1; GfxColor colors[4]; - shading->getDomain(&x0, &y0, &x1, &y1); - shading->getColor(x0, y0, &colors[0]); - shading->getColor(x0, y1, &colors[1]); - shading->getColor(x1, y0, &colors[2]); - shading->getColor(x1, y1, &colors[3]); - doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0); + if (out->useShadedFills()) { + out->functionShadedFill(state, shading); + } else { + shading->getDomain(&x0, &y0, &x1, &y1); + shading->getColor(x0, y0, &colors[0]); + shading->getColor(x0, y1, &colors[1]); + shading->getColor(x1, y0, &colors[2]); + shading->getColor(x1, y1, &colors[3]); + doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0); + } } void Gfx::doFunctionShFill1(GfxFunctionShading *shading, @@ -1569,7 +1670,7 @@ void Gfx::doFunctionShFill1(GfxFunctionShading *shading, // compare the four corner colors for (i = 0; i < 4; ++i) { for (j = 0; j < nComps; ++j) { - if (fabs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) { + if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) { break; } } @@ -1581,8 +1682,6 @@ void Gfx::doFunctionShFill1(GfxFunctionShading *shading, // center of the rectangle xM = 0.5 * (x0 + x1); yM = 0.5 * (y0 + y1); - - out->useGradients(); // the four corner colors are close (or we hit the recursive limit) // -- fill the rectangle; but require at least one subdivision @@ -1666,6 +1765,7 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) { double xMin, yMin, xMax, yMax; double x0, y0, x1, y1; double dx, dy, mul; + GBool dxZero, dyZero; double tMin, tMax, t, tx, ty; double s[4], sMin, sMax, tmp; double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1; @@ -1676,180 +1776,113 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) { int nComps; int i, j, k, kk; - // get the clip region bbox - state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); - - // compute min and max t values, based on the four corners of the - // clip region bbox - shading->getCoords(&x0, &y0, &x1, &y1); - dx = x1 - x0; - dy = y1 - y0; - mul = 1 / (dx * dx + dy * dy); - tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul; - t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul; - if (t < tMin) { - tMin = t; - } else if (t > tMax) { - tMax = t; - } - t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul; - if (t < tMin) { - tMin = t; - } else if (t > tMax) { - tMax = t; - } - t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul; - if (t < tMin) { - tMin = t; - } else if (t > tMax) { - tMax = t; - } - if (tMin < 0 && !shading->getExtend0()) { - tMin = 0; - } - if (tMax > 1 && !shading->getExtend1()) { - tMax = 1; - } - - // get the function domain - t0 = shading->getDomain0(); - t1 = shading->getDomain1(); - - // Traverse the t axis and do the shading. - // - // For each point (tx, ty) on the t axis, consider a line through - // that point perpendicular to the t axis: - // - // x(s) = tx + s * -dy --> s = (x - tx) / -dy - // y(s) = ty + s * dx --> s = (y - ty) / dx - // - // Then look at the intersection of this line with the bounding box - // (xMin, yMin, xMax, yMax). In the general case, there are four - // intersection points: - // - // s0 = (xMin - tx) / -dy - // s1 = (xMax - tx) / -dy - // s2 = (yMin - ty) / dx - // s3 = (yMax - ty) / dx - // - // and we want the middle two s values. - // - // In the case where dx = 0, take s0 and s1; in the case where dy = - // 0, take s2 and s3. - // - // Each filled polygon is bounded by two of these line segments - // perpdendicular to the t axis. - // - // The t axis is bisected into smaller regions until the color - // difference across a region is small enough, and then the region - // is painted with a single color. - - // set up: require at least one split to avoid problems when the two - // ends of the t axis have the same color - nComps = shading->getColorSpace()->getNComps(); - ta[0] = tMin; - next[0] = axialMaxSplits / 2; - ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax); - next[axialMaxSplits / 2] = axialMaxSplits; - ta[axialMaxSplits] = tMax; - - // compute the color at t = tMin - if (tMin < 0) { - tt = t0; - } else if (tMin > 1) { - tt = t1; - } else { - tt = t0 + (t1 - t0) * tMin; - } - shading->getColor(tt, &color0); - - // compute the coordinates of the point on the t axis at t = tMin; - // then compute the intersection of the perpendicular line with the - // bounding box - tx = x0 + tMin * dx; - ty = y0 + tMin * dy; - if (dx == 0 && dy == 0) { - sMin = sMax = 0; - } if (dx == 0) { - sMin = (xMin - tx) / -dy; - sMax = (xMax - tx) / -dy; - if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; } - } else if (dy == 0) { - sMin = (yMin - ty) / dx; - sMax = (yMax - ty) / dx; - if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; } + if (out->useShadedFills()) { + + out->axialShadedFill(state, shading); + } else { - s[0] = (yMin - ty) / dx; - s[1] = (yMax - ty) / dx; - s[2] = (xMin - tx) / -dy; - s[3] = (xMax - tx) / -dy; - for (j = 0; j < 3; ++j) { - kk = j; - for (k = j + 1; k < 4; ++k) { - if (s[k] < s[kk]) { - kk = k; - } - } - tmp = s[j]; s[j] = s[kk]; s[kk] = tmp; - } - sMin = s[1]; - sMax = s[2]; - } - ux0 = tx - sMin * dy; - uy0 = ty + sMin * dx; - vx0 = tx - sMax * dy; - vy0 = ty + sMax * dx; - - i = 0; - if(i < axialMaxSplits) - out->useGradients(); - - while (i < axialMaxSplits) { - - // bisect until color difference is small enough or we hit the - // bisection limit - j = next[i]; - while (j > i + 1) { - if (ta[j] < 0) { - tt = t0; - } else if (ta[j] > 1) { - tt = t1; - } else { - tt = t0 + (t1 - t0) * ta[j]; - } - shading->getColor(tt, &color1); - for (k = 0; k < nComps; ++k) { - if (fabs(color1.c[k] - color0.c[k]) > axialColorDelta) { - break; - } - } - if (k == nComps) { - break; - } - k = (i + j) / 2; - ta[k] = 0.5 * (ta[i] + ta[j]); - next[i] = k; - next[k] = j; - j = k; + + // get the clip region bbox + state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); + + // compute min and max t values, based on the four corners of the + // clip region bbox + shading->getCoords(&x0, &y0, &x1, &y1); + dx = x1 - x0; + dy = y1 - y0; + dxZero = fabs(dx) < 0.001; + dyZero = fabs(dy) < 0.001; + mul = 1 / (dx * dx + dy * dy); + tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul; + t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul; + if (t < tMin) { + tMin = t; + } else if (t > tMax) { + tMax = t; + } + t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul; + if (t < tMin) { + tMin = t; + } else if (t > tMax) { + tMax = t; + } + t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul; + if (t < tMin) { + tMin = t; + } else if (t > tMax) { + tMax = t; + } + if (tMin < 0 && !shading->getExtend0()) { + tMin = 0; + } + if (tMax > 1 && !shading->getExtend1()) { + tMax = 1; } - // use the average of the colors of the two sides of the region - for (k = 0; k < nComps; ++k) { - color0.c[k] = 0.5 * (color0.c[k] + color1.c[k]); + // get the function domain + t0 = shading->getDomain0(); + t1 = shading->getDomain1(); + + // Traverse the t axis and do the shading. + // + // For each point (tx, ty) on the t axis, consider a line through + // that point perpendicular to the t axis: + // + // x(s) = tx + s * -dy --> s = (x - tx) / -dy + // y(s) = ty + s * dx --> s = (y - ty) / dx + // + // Then look at the intersection of this line with the bounding box + // (xMin, yMin, xMax, yMax). In the general case, there are four + // intersection points: + // + // s0 = (xMin - tx) / -dy + // s1 = (xMax - tx) / -dy + // s2 = (yMin - ty) / dx + // s3 = (yMax - ty) / dx + // + // and we want the middle two s values. + // + // In the case where dx = 0, take s0 and s1; in the case where dy = + // 0, take s2 and s3. + // + // Each filled polygon is bounded by two of these line segments + // perpdendicular to the t axis. + // + // The t axis is bisected into smaller regions until the color + // difference across a region is small enough, and then the region + // is painted with a single color. + + // set up: require at least one split to avoid problems when the two + // ends of the t axis have the same color + nComps = shading->getColorSpace()->getNComps(); + ta[0] = tMin; + next[0] = axialMaxSplits / 2; + ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax); + next[axialMaxSplits / 2] = axialMaxSplits; + ta[axialMaxSplits] = tMax; + + // compute the color at t = tMin + if (tMin < 0) { + tt = t0; + } else if (tMin > 1) { + tt = t1; + } else { + tt = t0 + (t1 - t0) * tMin; } + shading->getColor(tt, &color0); - // compute the coordinates of the point on the t axis; then - // compute the intersection of the perpendicular line with the + // compute the coordinates of the point on the t axis at t = tMin; + // then compute the intersection of the perpendicular line with the // bounding box - tx = x0 + ta[j] * dx; - ty = y0 + ta[j] * dy; - if (dx == 0 && dy == 0) { + tx = x0 + tMin * dx; + ty = y0 + tMin * dy; + if (dxZero && dyZero) { sMin = sMax = 0; - } if (dx == 0) { + } if (dxZero) { sMin = (xMin - tx) / -dy; sMax = (xMax - tx) / -dy; if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; } - } else if (dy == 0) { + } else if (dyZero) { sMin = (yMin - ty) / dx; sMax = (yMax - ty) / dx; if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; } @@ -1870,31 +1903,104 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) { sMin = s[1]; sMax = s[2]; } - ux1 = tx - sMin * dy; - uy1 = ty + sMin * dx; - vx1 = tx - sMax * dy; - vy1 = ty + sMax * dx; + ux0 = tx - sMin * dy; + uy0 = ty + sMin * dx; + vx0 = tx - sMax * dy; + vy0 = ty + sMax * dx; + + i = 0; + while (i < axialMaxSplits) { + + // bisect until color difference is small enough or we hit the + // bisection limit + j = next[i]; + while (j > i + 1) { + if (ta[j] < 0) { + tt = t0; + } else if (ta[j] > 1) { + tt = t1; + } else { + tt = t0 + (t1 - t0) * ta[j]; + } + shading->getColor(tt, &color1); + for (k = 0; k < nComps; ++k) { + if (abs(color1.c[k] - color0.c[k]) > axialColorDelta) { + break; + } + } + if (k == nComps) { + break; + } + k = (i + j) / 2; + ta[k] = 0.5 * (ta[i] + ta[j]); + next[i] = k; + next[k] = j; + j = k; + } - // set the color - state->setFillColor(&color0); - out->updateFillColor(state); + // use the average of the colors of the two sides of the region + for (k = 0; k < nComps; ++k) { + color0.c[k] = (color0.c[k] + color1.c[k]) / 2; + } - // fill the region - state->moveTo(ux0, uy0); - state->lineTo(vx0, vy0); - state->lineTo(vx1, vy1); - state->lineTo(ux1, uy1); - state->closePath(); - out->fill(state); - state->clearPath(); + // compute the coordinates of the point on the t axis; then + // compute the intersection of the perpendicular line with the + // bounding box + tx = x0 + ta[j] * dx; + ty = y0 + ta[j] * dy; + if (dxZero && dyZero) { + sMin = sMax = 0; + } if (dxZero) { + sMin = (xMin - tx) / -dy; + sMax = (xMax - tx) / -dy; + if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; } + } else if (dyZero) { + sMin = (yMin - ty) / dx; + sMax = (yMax - ty) / dx; + if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; } + } else { + s[0] = (yMin - ty) / dx; + s[1] = (yMax - ty) / dx; + s[2] = (xMin - tx) / -dy; + s[3] = (xMax - tx) / -dy; + for (j = 0; j < 3; ++j) { + kk = j; + for (k = j + 1; k < 4; ++k) { + if (s[k] < s[kk]) { + kk = k; + } + } + tmp = s[j]; s[j] = s[kk]; s[kk] = tmp; + } + sMin = s[1]; + sMax = s[2]; + } + ux1 = tx - sMin * dy; + uy1 = ty + sMin * dx; + vx1 = tx - sMax * dy; + vy1 = ty + sMax * dx; - // set up for next region - ux0 = ux1; - uy0 = uy1; - vx0 = vx1; - vy0 = vy1; - color0 = color1; - i = next[i]; + // set the color + state->setFillColor(&color0); + out->updateFillColor(state); + + // fill the region + state->moveTo(ux0, uy0); + state->lineTo(vx0, vy0); + state->lineTo(vx1, vy1); + state->lineTo(ux1, uy1); + state->closePath(); + out->fill(state); + state->clearPath(); + + // set up for next region + ux0 = ux1; + uy0 = uy1; + vx0 = vx1; + vy0 = vy1; + color0 = color1; + i = next[i]; + } } } @@ -1907,144 +2013,134 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) { double ta, tb, sa, sb; int ia, ib, k, n; double *ctm; - double angle, t; + double angle, t, d0, d1; - // get the shading info - shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); - t0 = shading->getDomain0(); - t1 = shading->getDomain1(); - nComps = shading->getColorSpace()->getNComps(); + if (out->useShadedFills()) { - // compute the (possibly extended) s range - sMin = 0; - sMax = 1; - if (shading->getExtend0()) { - if (r0 < r1) { - // extend the smaller end - sMin = -r0 / (r1 - r0); - } else { - // extend the larger end - //~ this computes the diagonal of the bounding box -- we should - //~ really compute the intersection of the moving/expanding - //~ circles with each of the four corners and look for the max - //~ radius - state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); - sMin = (sqrt((xMax - xMin) * (xMax - xMin) + - (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); - if (sMin > 0) { - sMin = 0; - } else if (sMin < -20) { - // sanity check - sMin = -20; + out->radialShadedFill(state, shading); + + } else { + + // get the shading info + shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); + t0 = shading->getDomain0(); + t1 = shading->getDomain1(); + nComps = shading->getColorSpace()->getNComps(); + + // compute the (possibly extended) s range + sMin = 0; + sMax = 1; + if (shading->getExtend0()) { + if (r0 < r1) { + // extend the smaller end + sMin = -r0 / (r1 - r0); + } else { + // extend the larger end + state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); + d0 = (x0 - xMin) * (x0 - xMin); + d1 = (x0 - xMax) * (x0 - xMax); + sMin = d0 > d1 ? d0 : d1; + d0 = (y0 - yMin) * (y0 - yMin); + d1 = (y0 - yMax) * (y0 - yMax); + sMin += d0 > d1 ? d0 : d1; + sMin = (sqrt(sMin) - r0) / (r1 - r0); + if (sMin > 0) { + sMin = 0; + } else if (sMin < -20) { + // sanity check + sMin = -20; + } } } - } - if (shading->getExtend1()) { - if (r1 < r0) { - // extend the smaller end - sMax = -r0 / (r1 - r0); - } else if (r1 > r0) { - // extend the larger end - state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); - sMax = (sqrt((xMax - xMin) * (xMax - xMin) + - (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); - if (sMax < 1) { - sMin = 1; - } else if (sMax > 20) { - // sanity check - sMax = 20; + if (shading->getExtend1()) { + if (r1 < r0) { + // extend the smaller end + sMax = -r0 / (r1 - r0); + } else if (r1 > r0) { + // extend the larger end + state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); + d0 = (x1 - xMin) * (x1 - xMin); + d1 = (x1 - xMax) * (x1 - xMax); + sMax = d0 > d1 ? d0 : d1; + d0 = (y1 - yMin) * (y1 - yMin); + d1 = (y1 - yMax) * (y1 - yMax); + sMax += d0 > d1 ? d0 : d1; + sMax = (sqrt(sMax) - r0) / (r1 - r0); + if (sMax < 1) { + sMax = 1; + } else if (sMax > 20) { + // sanity check + sMax = 20; + } } } - } - // compute the number of steps into which circles must be divided to - // achieve a curve flatness of 0.1 pixel in device space for the - // largest circle (note that "device space" is 72 dpi when generating - // PostScript, hence the relatively small 0.1 pixel accuracy) - ctm = state->getCTM(); - t = fabs(ctm[0]); - if (fabs(ctm[1]) > t) { - t = fabs(ctm[1]); - } - if (fabs(ctm[2]) > t) { - t = fabs(ctm[2]); - } - if (fabs(ctm[3]) > t) { - t = fabs(ctm[3]); - } - if (r0 > r1) { - t *= r0; - } else { - t *= r1; - } - if (t < 1) { - n = 3; - } else { - n = (int)(M_PI / acos(1 - 0.1 / t)); - if (n < 3) { - n = 3; - } else if (n > 200) { - n = 200; - } - } - - // Traverse the t axis and do the shading. - // - // This generates and fills a series of rings. Each ring is defined - // by two circles: - // sa, ta, xa, ya, ra, colorA - // sb, tb, xb, yb, rb, colorB - // - // The s/t axis is divided into radialMaxSplits parts; these parts - // are combined as much as possible while respecting the - // radialColorDelta parameter. - - // setup for the start circle - ia = 0; - sa = sMin; - ta = t0 + sa * (t1 - t0); - xa = x0 + sa * (x1 - x0); - ya = y0 + sa * (y1 - y0); - ra = r0 + sa * (r1 - r0); - if (ta < t0) { - shading->getColor(t0, &colorA); - } else if (ta > t1) { - shading->getColor(t1, &colorA); - } else { - shading->getColor(ta, &colorA); - } - - if(ia < radialMaxSplits) - out->useGradients(); - - while (ia < radialMaxSplits) { - - // go as far along the t axis (toward t1) as we can, such that the - // color difference is within the tolerance (radialColorDelta) -- - // this uses bisection (between the current value, t, and t1), - // limited to radialMaxSplits points along the t axis; require at - // least one split to avoid problems when the innermost and - // outermost colors are the same - ib = radialMaxSplits; - sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin); - tb = t0 + sb * (t1 - t0); - if (tb < t0) { - shading->getColor(t0, &colorB); - } else if (tb > t1) { - shading->getColor(t1, &colorB); + // compute the number of steps into which circles must be divided to + // achieve a curve flatness of 0.1 pixel in device space for the + // largest circle (note that "device space" is 72 dpi when generating + // PostScript, hence the relatively small 0.1 pixel accuracy) + ctm = state->getCTM(); + t = fabs(ctm[0]); + if (fabs(ctm[1]) > t) { + t = fabs(ctm[1]); + } + if (fabs(ctm[2]) > t) { + t = fabs(ctm[2]); + } + if (fabs(ctm[3]) > t) { + t = fabs(ctm[3]); + } + if (r0 > r1) { + t *= r0; } else { - shading->getColor(tb, &colorB); + t *= r1; } - while (ib - ia > 1) { - for (k = 0; k < nComps; ++k) { - if (fabs(colorB.c[k] - colorA.c[k]) > radialColorDelta) { - break; - } - } - if (k == nComps && ib < radialMaxSplits) { - break; + if (t < 1) { + n = 3; + } else { + n = (int)(M_PI / acos(1 - 0.1 / t)); + if (n < 3) { + n = 3; + } else if (n > 200) { + n = 200; } - ib = (ia + ib) / 2; + } + + // Traverse the t axis and do the shading. + // + // This generates and fills a series of rings. Each ring is defined + // by two circles: + // sa, ta, xa, ya, ra, colorA + // sb, tb, xb, yb, rb, colorB + // + // The s/t axis is divided into radialMaxSplits parts; these parts + // are combined as much as possible while respecting the + // radialColorDelta parameter. + + // setup for the start circle + ia = 0; + sa = sMin; + ta = t0 + sa * (t1 - t0); + xa = x0 + sa * (x1 - x0); + ya = y0 + sa * (y1 - y0); + ra = r0 + sa * (r1 - r0); + if (ta < t0) { + shading->getColor(t0, &colorA); + } else if (ta > t1) { + shading->getColor(t1, &colorA); + } else { + shading->getColor(ta, &colorA); + } + + while (ia < radialMaxSplits) { + + // go as far along the t axis (toward t1) as we can, such that the + // color difference is within the tolerance (radialColorDelta) -- + // this uses bisection (between the current value, t, and t1), + // limited to radialMaxSplits points along the t axis; require at + // least one split to avoid problems when the innermost and + // outermost colors are the same + ib = radialMaxSplits; sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin); tb = t0 + sb * (t1 - t0); if (tb < t0) { @@ -2054,48 +2150,276 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) { } else { shading->getColor(tb, &colorB); } - } + while (ib - ia > 1) { + for (k = 0; k < nComps; ++k) { + if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) { + break; + } + } + if (k == nComps && ib < radialMaxSplits) { + break; + } + ib = (ia + ib) / 2; + sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin); + tb = t0 + sb * (t1 - t0); + if (tb < t0) { + shading->getColor(t0, &colorB); + } else if (tb > t1) { + shading->getColor(t1, &colorB); + } else { + shading->getColor(tb, &colorB); + } + } + + // compute center and radius of the circle + xb = x0 + sb * (x1 - x0); + yb = y0 + sb * (y1 - y0); + rb = r0 + sb * (r1 - r0); + + // use the average of the colors at the two circles + for (k = 0; k < nComps; ++k) { + colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2; + } + state->setFillColor(&colorA); + out->updateFillColor(state); + + // construct path for first circle + state->moveTo(xa + ra, ya); + for (k = 1; k < n; ++k) { + angle = ((double)k / (double)n) * 2 * M_PI; + state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); + } + state->closePath(); - // compute center and radius of the circle - xb = x0 + sb * (x1 - x0); - yb = y0 + sb * (y1 - y0); - rb = r0 + sb * (r1 - r0); + // construct and append path for second circle + state->moveTo(xb + rb, yb); + for (k = 1; k < n; ++k) { + angle = ((double)k / (double)n) * 2 * M_PI; + state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); + } + state->closePath(); - // use the average of the colors at the two circles - for (k = 0; k < nComps; ++k) { - colorA.c[k] = 0.5 * (colorA.c[k] + colorB.c[k]); + // fill the ring + out->eoFill(state); + state->clearPath(); + + // step to the next value of t + ia = ib; + sa = sb; + ta = tb; + xa = xb; + ya = yb; + ra = rb; + colorA = colorB; } - state->setFillColor(&colorA); - out->updateFillColor(state); + } +} + +void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) { + double x0, y0, x1, y1, x2, y2; + GfxColor color0, color1, color2; + int i; + + for (i = 0; i < shading->getNTriangles(); ++i) { + shading->getTriangle(i, &x0, &y0, &color0, + &x1, &y1, &color1, + &x2, &y2, &color2); + gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2, + shading->getColorSpace()->getNComps(), 0); + } +} + +void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0, + double x1, double y1, GfxColor *color1, + double x2, double y2, GfxColor *color2, + int nComps, int depth) { + double x01, y01, x12, y12, x20, y20; + GfxColor color01, color12, color20; + int i; - // construct path for first circle - state->moveTo(xa + ra, ya); - for (k = 1; k < n; ++k) { - angle = ((double)k / (double)n) * 2 * M_PI; - state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); + for (i = 0; i < nComps; ++i) { + if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta || + abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) { + break; } + } + if (i == nComps || depth == gouraudMaxDepth) { + state->setFillColor(color0); + out->updateFillColor(state); + state->moveTo(x0, y0); + state->lineTo(x1, y1); + state->lineTo(x2, y2); state->closePath(); + out->fill(state); + state->clearPath(); + } else { + x01 = 0.5 * (x0 + x1); + y01 = 0.5 * (y0 + y1); + x12 = 0.5 * (x1 + x2); + y12 = 0.5 * (y1 + y2); + x20 = 0.5 * (x2 + x0); + y20 = 0.5 * (y2 + y0); + //~ if the shading has a Function, this should interpolate on the + //~ function parameter, not on the color components + for (i = 0; i < nComps; ++i) { + color01.c[i] = (color0->c[i] + color1->c[i]) / 2; + color12.c[i] = (color1->c[i] + color2->c[i]) / 2; + color20.c[i] = (color2->c[i] + color0->c[i]) / 2; + } + gouraudFillTriangle(x0, y0, color0, x01, y01, &color01, + x20, y20, &color20, nComps, depth + 1); + gouraudFillTriangle(x01, y01, &color01, x1, y1, color1, + x12, y12, &color12, nComps, depth + 1); + gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12, + x20, y20, &color20, nComps, depth + 1); + gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12, + x2, y2, color2, nComps, depth + 1); + } +} + +void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) { + int start, i; + + if (shading->getNPatches() > 128) { + start = 3; + } else if (shading->getNPatches() > 64) { + start = 2; + } else if (shading->getNPatches() > 16) { + start = 1; + } else { + start = 0; + } + for (i = 0; i < shading->getNPatches(); ++i) { + fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(), + start); + } +} - // construct and append path for second circle - state->moveTo(xb + rb, yb); - for (k = 1; k < n; ++k) { - angle = ((double)k / (double)n) * 2 * M_PI; - state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); +void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) { + GfxPatch patch00, patch01, patch10, patch11; + double xx[4][8], yy[4][8]; + double xxm, yym; + int i; + + for (i = 0; i < nComps; ++i) { + if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i]) + > patchColorDelta || + abs(patch->color[0][1].c[i] - patch->color[1][1].c[i]) + > patchColorDelta || + abs(patch->color[1][1].c[i] - patch->color[1][0].c[i]) + > patchColorDelta || + abs(patch->color[1][0].c[i] - patch->color[0][0].c[i]) + > patchColorDelta) { + break; } + } + if (i == nComps || depth == patchMaxDepth) { + state->setFillColor(&patch->color[0][0]); + out->updateFillColor(state); + state->moveTo(patch->x[0][0], patch->y[0][0]); + state->curveTo(patch->x[0][1], patch->y[0][1], + patch->x[0][2], patch->y[0][2], + patch->x[0][3], patch->y[0][3]); + state->curveTo(patch->x[1][3], patch->y[1][3], + patch->x[2][3], patch->y[2][3], + patch->x[3][3], patch->y[3][3]); + state->curveTo(patch->x[3][2], patch->y[3][2], + patch->x[3][1], patch->y[3][1], + patch->x[3][0], patch->y[3][0]); + state->curveTo(patch->x[2][0], patch->y[2][0], + patch->x[1][0], patch->y[1][0], + patch->x[0][0], patch->y[0][0]); state->closePath(); - - // fill the ring - out->eoFill(state); + out->fill(state); state->clearPath(); - - // step to the next value of t - ia = ib; - sa = sb; - ta = tb; - xa = xb; - ya = yb; - ra = rb; - colorA = colorB; + } else { + for (i = 0; i < 4; ++i) { + xx[i][0] = patch->x[i][0]; + yy[i][0] = patch->y[i][0]; + xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]); + yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]); + xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]); + yym = 0.5 * (patch->y[i][1] + patch->y[i][2]); + xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]); + yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]); + xx[i][2] = 0.5 * (xx[i][1] + xxm); + yy[i][2] = 0.5 * (yy[i][1] + yym); + xx[i][5] = 0.5 * (xxm + xx[i][6]); + yy[i][5] = 0.5 * (yym + yy[i][6]); + xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]); + yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]); + xx[i][7] = patch->x[i][3]; + yy[i][7] = patch->y[i][3]; + } + for (i = 0; i < 4; ++i) { + patch00.x[0][i] = xx[0][i]; + patch00.y[0][i] = yy[0][i]; + patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]); + patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]); + xxm = 0.5 * (xx[1][i] + xx[2][i]); + yym = 0.5 * (yy[1][i] + yy[2][i]); + patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]); + patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]); + patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm); + patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym); + patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]); + patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]); + patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]); + patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]); + patch10.x[0][i] = patch00.x[3][i]; + patch10.y[0][i] = patch00.y[3][i]; + patch10.x[3][i] = xx[3][i]; + patch10.y[3][i] = yy[3][i]; + } + for (i = 4; i < 8; ++i) { + patch01.x[0][i-4] = xx[0][i]; + patch01.y[0][i-4] = yy[0][i]; + patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]); + patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]); + xxm = 0.5 * (xx[1][i] + xx[2][i]); + yym = 0.5 * (yy[1][i] + yy[2][i]); + patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]); + patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]); + patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm); + patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym); + patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]); + patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]); + patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]); + patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]); + patch11.x[0][i-4] = patch01.x[3][i-4]; + patch11.y[0][i-4] = patch01.y[3][i-4]; + patch11.x[3][i-4] = xx[3][i]; + patch11.y[3][i-4] = yy[3][i]; + } + //~ if the shading has a Function, this should interpolate on the + //~ function parameter, not on the color components + for (i = 0; i < nComps; ++i) { + patch00.color[0][0].c[i] = patch->color[0][0].c[i]; + patch00.color[0][1].c[i] = (patch->color[0][0].c[i] + + patch->color[0][1].c[i]) / 2; + patch01.color[0][0].c[i] = patch00.color[0][1].c[i]; + patch01.color[0][1].c[i] = patch->color[0][1].c[i]; + patch01.color[1][1].c[i] = (patch->color[0][1].c[i] + + patch->color[1][1].c[i]) / 2; + patch11.color[0][1].c[i] = patch01.color[1][1].c[i]; + patch11.color[1][1].c[i] = patch->color[1][1].c[i]; + patch11.color[1][0].c[i] = (patch->color[1][1].c[i] + + patch->color[1][0].c[i]) / 2; + patch10.color[1][1].c[i] = patch11.color[1][0].c[i]; + patch10.color[1][0].c[i] = patch->color[1][0].c[i]; + patch10.color[0][0].c[i] = (patch->color[1][0].c[i] + + patch->color[0][0].c[i]) / 2; + patch00.color[1][0].c[i] = patch10.color[0][0].c[i]; + patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] + + patch01.color[1][1].c[i]) / 2; + patch01.color[1][0].c[i] = patch00.color[1][1].c[i]; + patch11.color[0][0].c[i] = patch00.color[1][1].c[i]; + patch10.color[0][1].c[i] = patch00.color[1][1].c[i]; + } + fillPatch(&patch00, nComps, depth + 1); + fillPatch(&patch10, nComps, depth + 1); + fillPatch(&patch01, nComps, depth + 1); + fillPatch(&patch11, nComps, depth + 1); } } @@ -2243,7 +2567,13 @@ void Gfx::opShowText(Object args[], int numArgs) { error(getPos(), "No font in show"); return; } + if (fontChanged) { + out->updateFont(state); + fontChanged = gFalse; + } + out->beginStringOp(state); doShowText(args[0].getString()); + out->endStringOp(state); } void Gfx::opMoveShowText(Object args[], int numArgs) { @@ -2253,11 +2583,17 @@ void Gfx::opMoveShowText(Object args[], int numArgs) { error(getPos(), "No font in move/show"); return; } + if (fontChanged) { + out->updateFont(state); + fontChanged = gFalse; + } tx = state->getLineX(); ty = state->getLineY() - state->getLeading(); state->textMoveTo(tx, ty); out->updateTextPos(state); + out->beginStringOp(state); doShowText(args[0].getString()); + out->endStringOp(state); } void Gfx::opMoveSetShowText(Object args[], int numArgs) { @@ -2267,6 +2603,10 @@ void Gfx::opMoveSetShowText(Object args[], int numArgs) { error(getPos(), "No font in move/set/show"); return; } + if (fontChanged) { + out->updateFont(state); + fontChanged = gFalse; + } state->setWordSpace(args[0].getNum()); state->setCharSpace(args[1].getNum()); tx = state->getLineX(); @@ -2275,7 +2615,9 @@ void Gfx::opMoveSetShowText(Object args[], int numArgs) { out->updateWordSpace(state); out->updateCharSpace(state); out->updateTextPos(state); + out->beginStringOp(state); doShowText(args[2].getString()); + out->endStringOp(state); } void Gfx::opShowSpaceText(Object args[], int numArgs) { @@ -2288,15 +2630,24 @@ void Gfx::opShowSpaceText(Object args[], int numArgs) { error(getPos(), "No font in show/space"); return; } + if (fontChanged) { + out->updateFont(state); + fontChanged = gFalse; + } + out->beginStringOp(state); wMode = state->getFont()->getWMode(); a = args[0].getArray(); for (i = 0; i < a->getLength(); ++i) { a->get(i, &obj); if (obj.isNum()) { + // this uses the absolute value of the font size to match + // Acrobat's behavior if (wMode) { - state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize()); + state->textShift(0, -obj.getNum() * 0.001 * + fabs(state->getFontSize())); } else { - state->textShift(-obj.getNum() * 0.001 * state->getFontSize(), 0); + state->textShift(-obj.getNum() * 0.001 * + fabs(state->getFontSize()), 0); } out->updateTextShift(state, obj.getNum()); } else if (obj.isString()) { @@ -2306,6 +2657,7 @@ void Gfx::opShowSpaceText(Object args[], int numArgs) { } obj.free(); } + out->endStringOp(state); } void Gfx::doShowText(GString *s) { @@ -2324,10 +2676,6 @@ void Gfx::doShowText(GString *s) { char *p; int len, n, uLen, nChars, nSpaces, i; - if (fontChanged) { - out->updateFont(state); - fontChanged = gFalse; - } font = state->getFont(); wMode = font->getWMode(); @@ -2436,7 +2784,7 @@ void Gfx::doShowText(GString *s) { originY *= state->getFontSize(); state->textTransformDelta(originX, originY, &tOriginX, &tOriginY); out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY, - tdx, tdy, tOriginX, tOriginY, code, u, uLen); + tdx, tdy, tOriginX, tOriginY, code, n, u, uLen); state->shift(tdx, tdy); p += n; len -= n; @@ -2510,9 +2858,11 @@ void Gfx::opXObject(Object args[], int numArgs) { #endif obj1.streamGetDict()->lookup("Subtype", &obj2); if (obj2.isName("Image")) { - res->lookupXObjectNF(args[0].getName(), &refObj); - doImage(&refObj, obj1.getStream(), gFalse); - refObj.free(); + if (out->needNonText()) { + res->lookupXObjectNF(args[0].getName(), &refObj); + doImage(&refObj, obj1.getStream(), gFalse); + refObj.free(); + } } else if (obj2.isName("Form")) { doForm(&obj1); } else if (obj2.isName("PS")) { @@ -2535,19 +2885,28 @@ void Gfx::opXObject(Object args[], int numArgs) { } void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { - Dict *dict; + Dict *dict, *maskDict; int width, height; - int bits; + int bits, maskBits; + StreamColorSpaceMode csMode; GBool mask; GBool invert; - GfxColorSpace *colorSpace; - GfxImageColorMap *colorMap; - Object maskObj; - GBool haveMask; + GfxColorSpace *colorSpace, *maskColorSpace; + GfxImageColorMap *colorMap, *maskColorMap; + Object maskObj, smaskObj; + GBool haveColorKeyMask, haveExplicitMask, haveSoftMask; int maskColors[2*gfxColorMaxComps]; + int maskWidth, maskHeight; + GBool maskInvert; + Stream *maskStr; Object obj1, obj2; int i; + // get info from the stream + bits = 0; + csMode = streamCSNone; + str->getImageParams(&bits, &csMode); + // get stream dict dict = str->getDict(); @@ -2585,19 +2944,21 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { obj1.free(); // bit depth - dict->lookup("BitsPerComponent", &obj1); - if (obj1.isNull()) { + if (bits == 0) { + dict->lookup("BitsPerComponent", &obj1); + if (obj1.isNull()) { + obj1.free(); + dict->lookup("BPC", &obj1); + } + if (obj1.isInt()) { + bits = obj1.getInt(); + } else if (mask) { + bits = 1; + } else { + goto err2; + } obj1.free(); - dict->lookup("BPC", &obj1); } - if (obj1.isInt()) { - bits = obj1.getInt(); - } else if (mask) { - bits = 1; - } else { - goto err2; - } - obj1.free(); // display a mask if (mask) { @@ -2641,7 +3002,17 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { obj2.free(); } } - colorSpace = GfxColorSpace::parse(&obj1); + if (!obj1.isNull()) { + colorSpace = GfxColorSpace::parse(&obj1); + } else if (csMode == streamCSDeviceGray) { + colorSpace = new GfxDeviceGrayColorSpace(); + } else if (csMode == streamCSDeviceRGB) { + colorSpace = new GfxDeviceRGBColorSpace(); + } else if (csMode == streamCSDeviceCMYK) { + colorSpace = new GfxDeviceCMYKColorSpace(); + } else { + colorSpace = NULL; + } obj1.free(); if (!colorSpace) { goto err1; @@ -2659,23 +3030,163 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { } // get the mask - haveMask = gFalse; + haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse; + maskStr = NULL; // make gcc happy + maskWidth = maskHeight = 0; // make gcc happy + maskInvert = gFalse; // make gcc happy + maskColorMap = NULL; // make gcc happy dict->lookup("Mask", &maskObj); - if (maskObj.isArray()) { - for (i = 0; i < maskObj.arrayGetLength(); ++i) { + dict->lookup("SMask", &smaskObj); + if (smaskObj.isStream()) { + // soft mask + if (inlineImg) { + goto err1; + } + maskStr = smaskObj.getStream(); + maskDict = smaskObj.streamGetDict(); + maskDict->lookup("Width", &obj1); + if (obj1.isNull()) { + obj1.free(); + maskDict->lookup("W", &obj1); + } + if (!obj1.isInt()) { + goto err2; + } + maskWidth = obj1.getInt(); + obj1.free(); + maskDict->lookup("Height", &obj1); + if (obj1.isNull()) { + obj1.free(); + maskDict->lookup("H", &obj1); + } + if (!obj1.isInt()) { + goto err2; + } + maskHeight = obj1.getInt(); + obj1.free(); + maskDict->lookup("BitsPerComponent", &obj1); + if (obj1.isNull()) { + obj1.free(); + maskDict->lookup("BPC", &obj1); + } + if (!obj1.isInt()) { + goto err2; + } + maskBits = obj1.getInt(); + obj1.free(); + maskDict->lookup("ColorSpace", &obj1); + if (obj1.isNull()) { + obj1.free(); + maskDict->lookup("CS", &obj1); + } + if (obj1.isName()) { + res->lookupColorSpace(obj1.getName(), &obj2); + if (!obj2.isNull()) { + obj1.free(); + obj1 = obj2; + } else { + obj2.free(); + } + } + maskColorSpace = GfxColorSpace::parse(&obj1); + obj1.free(); + if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) { + goto err1; + } + maskDict->lookup("Decode", &obj1); + if (obj1.isNull()) { + obj1.free(); + maskDict->lookup("D", &obj1); + } + maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace); + obj1.free(); + if (!maskColorMap->isOk()) { + delete maskColorMap; + goto err1; + } + //~ handle the Matte entry + haveSoftMask = gTrue; + } else if (maskObj.isArray()) { + // color key mask + for (i = 0; + i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps; + ++i) { maskObj.arrayGet(i, &obj1); maskColors[i] = obj1.getInt(); obj1.free(); } - haveMask = gTrue; + haveColorKeyMask = gTrue; + } else if (maskObj.isStream()) { + // explicit mask + if (inlineImg) { + goto err1; + } + maskStr = maskObj.getStream(); + maskDict = maskObj.streamGetDict(); + maskDict->lookup("Width", &obj1); + if (obj1.isNull()) { + obj1.free(); + maskDict->lookup("W", &obj1); + } + if (!obj1.isInt()) { + goto err2; + } + maskWidth = obj1.getInt(); + obj1.free(); + maskDict->lookup("Height", &obj1); + if (obj1.isNull()) { + obj1.free(); + maskDict->lookup("H", &obj1); + } + if (!obj1.isInt()) { + goto err2; + } + maskHeight = obj1.getInt(); + obj1.free(); + maskDict->lookup("ImageMask", &obj1); + if (obj1.isNull()) { + obj1.free(); + maskDict->lookup("IM", &obj1); + } + if (!obj1.isBool() || !obj1.getBool()) { + goto err2; + } + obj1.free(); + maskInvert = gFalse; + maskDict->lookup("Decode", &obj1); + if (obj1.isNull()) { + obj1.free(); + maskDict->lookup("D", &obj1); + } + if (obj1.isArray()) { + obj1.arrayGet(0, &obj2); + if (obj2.isInt() && obj2.getInt() == 1) { + maskInvert = gTrue; + } + obj2.free(); + } else if (!obj1.isNull()) { + goto err2; + } + obj1.free(); + haveExplicitMask = gTrue; } // draw it - out->drawImage(state, ref, str, width, height, colorMap, - haveMask ? maskColors : (int *)NULL, inlineImg); + if (haveSoftMask) { + out->drawSoftMaskedImage(state, ref, str, width, height, colorMap, + maskStr, maskWidth, maskHeight, maskColorMap); + delete maskColorMap; + } else if (haveExplicitMask) { + out->drawMaskedImage(state, ref, str, width, height, colorMap, + maskStr, maskWidth, maskHeight, maskInvert); + } else { + out->drawImage(state, ref, str, width, height, colorMap, + haveColorKeyMask ? maskColors : (int *)NULL, inlineImg); + } delete colorMap; maskObj.free(); + smaskObj.free(); } if ((i = width * height) > 1000) { @@ -2710,7 +3221,7 @@ void Gfx::doForm(Object *str) { // check form type dict->lookup("FormType", &obj1); - if (!(obj1.isInt() && obj1.getInt() == 1)) { + if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) { error(getPos(), "Unknown form type"); } obj1.free(); diff --git a/pdf2swf/xpdf/GfxFont.h b/pdf2swf/xpdf/GfxFont.h index df1e00c..62dfd08 100644 --- a/pdf2swf/xpdf/GfxFont.h +++ b/pdf2swf/xpdf/GfxFont.h @@ -207,7 +207,7 @@ public: CharCodeToUnicode *getToUnicode(); // Return the character name associated with . - char *getCharName(int code) { return code>=256?0:enc[code]; } + char *getCharName(int code) { return enc[code]; } // Returns true if the PDF font specified an encoding. GBool getHasEncoding() { return hasEncoding; }