From 431fddbc14548cdce21d94cfa4d3779bc142839f Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 10 Jan 2012 23:35:45 +0100 Subject: update with Thomas merges --- ALL_DIFF | 28083 ++++++++++++++++++++++++++----------------------------------- 1 file changed, 11982 insertions(+), 16101 deletions(-) diff --git a/ALL_DIFF b/ALL_DIFF index 385d9a2..149caaa 100644 --- a/ALL_DIFF +++ b/ALL_DIFF @@ -1,16101 +1,11982 @@ -diff -ru xpdf-3.02/doc/pdftotext.1 xpdf-3.03/doc/pdftotext.1 ---- xpdf-3.02/doc/pdftotext.1 2007-02-27 23:05:51.000000000 +0100 -+++ xpdf-3.03/doc/pdftotext.1 2011-08-15 23:08:53.000000000 +0200 -@@ -49,6 +49,10 @@ - text. The default is to \'undo' physical layout (columns, - hyphenation, etc.) and output the text in reading order. - .TP -+.BI \-fixed " number" -+Assume fixed-pitch (or tabular) text, with the specified character -+width (in points). This forces physical layout mode. -+.TP - .B \-raw - Keep the text in content stream order. This is a hack which often - "undoes" column formatting, etc. Use of raw mode is no longer -diff -ru xpdf-3.02/splash/Splash.cc xpdf-3.03/splash/Splash.cc ---- xpdf-3.02/splash/Splash.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/splash/Splash.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -1772,22 +2390,10 @@ - SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, - int w, int h, SplashCoord *mat, - GBool glyphMode) { -- SplashPipe pipe; -- GBool rot; -- SplashCoord xScale, yScale, xShear, yShear, yShear1; -- int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign; -- int ulx, uly, llx, lly, urx, ury, lrx, lry; -- int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1; -- int xMin, xMax, yMin, yMax; -- SplashClipResult clipRes, clipRes2; -- int yp, yq, yt, yStep, lastYStep; -- int xp, xq, xt, xStep, xSrc; -- int k1, spanXMin, spanXMax, spanY; -- SplashColorPtr pixBuf, p; -- int pixAcc; -- int x, y, x1, x2, y2; -- SplashCoord y1; -- int n, m, i, j; -+ SplashBitmap *scaledMask; -+ SplashClipResult clipRes; -+ GBool minorAxisZero; -+ int x0, y0, x1, y1, scaledWidth, scaledHeight; - - if (debugMode) { - printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", -@@ -1796,286 +2402,729 @@ - } - - // check for singular matrix -- if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) { -+ if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { - return splashErrSingularMatrix; - } - -- // compute scale, shear, rotation, translation parameters -- rot = splashAbs(mat[1]) > splashAbs(mat[0]); -- if (rot) { -- xScale = -mat[1]; -- yScale = mat[2] - (mat[0] * mat[3]) / mat[1]; -- xShear = -mat[3] / yScale; -- yShear = -mat[0] / mat[1]; -- } else { -- xScale = mat[0]; -- yScale = mat[3] - (mat[1] * mat[2]) / mat[0]; -- xShear = mat[2] / yScale; -- yShear = mat[1] / mat[0]; -- } -- // Note 1: The PDF spec says that all pixels whose *centers* lie -- // within the region get painted -- but that doesn't seem to match -- // up with what Acrobat actually does: it ends up leaving gaps -- // between image stripes. So we use the same rule here as for -- // fills: any pixel that overlaps the region gets painted. -- // Note 2: The "glyphMode" flag is a kludge: it switches back to -- // "correct" behavior (matching the spec), for use in rendering Type -- // 3 fonts. -- // Note 3: The +/-0.01 in these computations is to avoid floating -- // point precision problems which can lead to gaps between image -- // stripes (it can cause image stripes to overlap, but that's a much -- // less visible problem). -- if (glyphMode) { -- if (xScale >= 0) { -- tx = splashRound(mat[4]); -- tx2 = splashRound(mat[4] + xScale) - 1; -- } else { -- tx = splashRound(mat[4]) - 1; -- tx2 = splashRound(mat[4] + xScale); -- } -- } else { -- if (xScale >= 0) { -- tx = splashFloor(mat[4] - 0.01); -- tx2 = splashFloor(mat[4] + xScale + 0.01); -- } else { -- tx = splashFloor(mat[4] + 0.01); -- tx2 = splashFloor(mat[4] + xScale - 0.01); -- } -- } -- scaledWidth = abs(tx2 - tx) + 1; -- if (glyphMode) { -- if (yScale >= 0) { -- ty = splashRound(mat[5]); -- ty2 = splashRound(mat[5] + yScale) - 1; -- } else { -- ty = splashRound(mat[5]) - 1; -- ty2 = splashRound(mat[5] + yScale); -- } -- } else { -- if (yScale >= 0) { -- ty = splashFloor(mat[5] - 0.01); -- ty2 = splashFloor(mat[5] + yScale + 0.01); -- } else { -- ty = splashFloor(mat[5] + 0.01); -- ty2 = splashFloor(mat[5] + yScale - 0.01); -- } -- } -- scaledHeight = abs(ty2 - ty) + 1; -- xSign = (xScale < 0) ? -1 : 1; -- ySign = (yScale < 0) ? -1 : 1; -- yShear1 = (SplashCoord)xSign * yShear; -- -- // clipping -- ulx1 = 0; -- uly1 = 0; -- urx1 = xSign * (scaledWidth - 1); -- ury1 = (int)(yShear * urx1); -- llx1 = splashRound(xShear * ySign * (scaledHeight - 1)); -- lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1); -- lrx1 = xSign * (scaledWidth - 1) + -- splashRound(xShear * ySign * (scaledHeight - 1)); -- lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1); -- if (rot) { -- ulx = tx + uly1; uly = ty - ulx1; -- urx = tx + ury1; ury = ty - urx1; -- llx = tx + lly1; lly = ty - llx1; -- lrx = tx + lry1; lry = ty - lrx1; -- } else { -- ulx = tx + ulx1; uly = ty + uly1; -- urx = tx + urx1; ury = ty + ury1; -- llx = tx + llx1; lly = ty + lly1; -- lrx = tx + lrx1; lry = ty + lry1; -- } -- xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx -- : (llx < lrx) ? llx : lrx -- : (urx < llx) ? (urx < lrx) ? urx : lrx -- : (llx < lrx) ? llx : lrx; -- xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx -- : (llx > lrx) ? llx : lrx -- : (urx > llx) ? (urx > lrx) ? urx : lrx -- : (llx > lrx) ? llx : lrx; -- yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry -- : (lly < lry) ? lly : lry -- : (ury < lly) ? (ury < lry) ? ury : lry -- : (lly < lry) ? lly : lry; -- yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry -- : (lly > lry) ? lly : lry -- : (ury > lly) ? (ury > lry) ? ury : lry -- : (lly > lry) ? lly : lry; -- clipRes = state->clip->testRect(xMin, yMin, xMax, yMax); -- opClipRes = clipRes; -- -- // compute Bresenham parameters for x and y scaling -- yp = h / scaledHeight; -- yq = h % scaledHeight; -- xp = w / scaledWidth; -- xq = w % scaledWidth; -+ minorAxisZero = mat[1] == 0 && mat[2] == 0; - -- // allocate pixel buffer -- pixBuf = (SplashColorPtr)gmalloc((yp + 1) * w); -+ // scaling only -+ if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { -+ x0 = imgCoordMungeLowerC(mat[4], glyphMode); -+ y0 = imgCoordMungeLowerC(mat[5], glyphMode); -+ x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode); -+ y1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode); -+ // make sure narrow images cover at least one pixel -+ if (x0 == x1) { -+ ++x1; -+ } -+ if (y0 == y1) { -+ ++y1; -+ } -+ clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); -+ opClipRes = clipRes; -+ if (clipRes != splashClipAllOutside) { -+ scaledWidth = x1 - x0; -+ scaledHeight = y1 - y0; -+ scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight); -+ blitMask(scaledMask, x0, y0, clipRes); -+ delete scaledMask; -+ } -+ -+ // scaling plus vertical flip -+ } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { -+ x0 = imgCoordMungeLowerC(mat[4], glyphMode); -+ y0 = imgCoordMungeLowerC(mat[3] + mat[5], glyphMode); -+ x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode); -+ y1 = imgCoordMungeUpperC(mat[5], glyphMode); -+ // make sure narrow images cover at least one pixel -+ if (x0 == x1) { -+ ++x1; -+ } -+ if (y0 == y1) { -+ ++y1; -+ } -+ clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); -+ opClipRes = clipRes; -+ if (clipRes != splashClipAllOutside) { -+ scaledWidth = x1 - x0; -+ scaledHeight = y1 - y0; -+ scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight); -+ vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); -+ blitMask(scaledMask, x0, y0, clipRes); -+ delete scaledMask; -+ } - -- // initialize the pixel pipe -- pipeInit(&pipe, 0, 0, state->fillPattern, NULL, state->fillAlpha, -- gTrue, gFalse); -- if (vectorAntialias) { -- drawAAPixelInit(); -+ // all other cases -+ } else { -+ arbitraryTransformMask(src, srcData, w, h, mat, glyphMode); - } - -- // init y scale Bresenham -- yt = 0; -- lastYStep = 1; -+ return splashOk; -+} - -- for (y = 0; y < scaledHeight; ++y) { -+void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, -+ int srcWidth, int srcHeight, -+ SplashCoord *mat, GBool glyphMode) { -+ SplashBitmap *scaledMask; -+ SplashClipResult clipRes, clipRes2; -+ SplashPipe pipe; -+ int scaledWidth, scaledHeight, t0, t1; -+ SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; -+ SplashCoord vx[4], vy[4]; -+ int xMin, yMin, xMax, yMax; -+ ImageSection section[3]; -+ int nSections; -+ int y, xa, xb, x, i, xx, yy; -+ -+ // compute the four vertices of the target quadrilateral -+ vx[0] = mat[4]; vy[0] = mat[5]; -+ vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; -+ vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; -+ vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; - -- // y scale Bresenham -- yStep = yp; -- yt += yq; -- if (yt >= scaledHeight) { -- yt -= scaledHeight; -- ++yStep; -+ // clipping -+ xMin = imgCoordMungeLowerC(vx[0], glyphMode); -+ xMax = imgCoordMungeUpperC(vx[0], glyphMode); -+ yMin = imgCoordMungeLowerC(vy[0], glyphMode); -+ yMax = imgCoordMungeUpperC(vy[0], glyphMode); -+ for (i = 1; i < 4; ++i) { -+ t0 = imgCoordMungeLowerC(vx[i], glyphMode); -+ if (t0 < xMin) { -+ xMin = t0; -+ } -+ t0 = imgCoordMungeUpperC(vx[i], glyphMode); -+ if (t0 > xMax) { -+ xMax = t0; -+ } -+ t1 = imgCoordMungeLowerC(vy[i], glyphMode); -+ if (t1 < yMin) { -+ yMin = t1; -+ } -+ t1 = imgCoordMungeUpperC(vy[i], glyphMode); -+ if (t1 > yMax) { -+ yMax = t1; - } -+ } -+ clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1); -+ opClipRes = clipRes; -+ if (clipRes == splashClipAllOutside) { -+ return; -+ } - -- // read row(s) from image -- n = (yp > 0) ? yStep : lastYStep; -- if (n > 0) { -- p = pixBuf; -- for (i = 0; i < n; ++i) { -- (*src)(srcData, p); -- p += w; -- } -+ // compute the scale factors -+ if (mat[0] >= 0) { -+ t0 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode) - -+ imgCoordMungeLowerC(mat[4], glyphMode); -+ } else { -+ t0 = imgCoordMungeUpperC(mat[4], glyphMode) - -+ imgCoordMungeLowerC(mat[0] + mat[4], glyphMode); -+ } -+ if (mat[1] >= 0) { -+ t1 = imgCoordMungeUpperC(mat[1] + mat[5], glyphMode) - -+ imgCoordMungeLowerC(mat[5], glyphMode); -+ } else { -+ t1 = imgCoordMungeUpperC(mat[5], glyphMode) - -+ imgCoordMungeLowerC(mat[1] + mat[5], glyphMode); -+ } -+ scaledWidth = t0 > t1 ? t0 : t1; -+ if (mat[2] >= 0) { -+ t0 = imgCoordMungeUpperC(mat[2] + mat[4], glyphMode) - -+ imgCoordMungeLowerC(mat[4], glyphMode); -+ } else { -+ t0 = imgCoordMungeUpperC(mat[4], glyphMode) - -+ imgCoordMungeLowerC(mat[2] + mat[4], glyphMode); -+ } -+ if (mat[3] >= 0) { -+ t1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode) - -+ imgCoordMungeLowerC(mat[5], glyphMode); -+ } else { -+ t1 = imgCoordMungeUpperC(mat[5], glyphMode) - -+ imgCoordMungeLowerC(mat[3] + mat[5], glyphMode); -+ } -+ scaledHeight = t0 > t1 ? t0 : t1; -+ if (scaledWidth == 0) { -+ scaledWidth = 1; -+ } -+ if (scaledHeight == 0) { -+ scaledHeight = 1; -+ } -+ -+ // compute the inverse transform (after scaling) matrix -+ r00 = mat[0] / scaledWidth; -+ r01 = mat[1] / scaledWidth; -+ r10 = mat[2] / scaledHeight; -+ r11 = mat[3] / scaledHeight; -+ det = r00 * r11 - r01 * r10; -+ if (splashAbs(det) < 1e-6) { -+ // this should be caught by the singular matrix check in fillImageMask -+ return; -+ } -+ ir00 = r11 / det; -+ ir01 = -r01 / det; -+ ir10 = -r10 / det; -+ ir11 = r00 / det; -+ -+ // scale the input image -+ scaledMask = scaleMask(src, srcData, srcWidth, srcHeight, -+ scaledWidth, scaledHeight); -+ -+ // construct the three sections -+ i = (vy[2] <= vy[3]) ? 2 : 3; -+ if (vy[1] <= vy[i]) { -+ i = 1; -+ } -+ if (vy[0] < vy[i] || (i != 3 && vy[0] == vy[i])) { -+ i = 0; -+ } -+ if (vy[i] == vy[(i+1) & 3]) { -+ section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode); -+ section[0].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1; -+ if (vx[i] < vx[(i+1) & 3]) { -+ section[0].ia0 = i; -+ section[0].ia1 = (i+3) & 3; -+ section[0].ib0 = (i+1) & 3; -+ section[0].ib1 = (i+2) & 3; -+ } else { -+ section[0].ia0 = (i+1) & 3; -+ section[0].ia1 = (i+2) & 3; -+ section[0].ib0 = i; -+ section[0].ib1 = (i+3) & 3; -+ } -+ nSections = 1; -+ } else { -+ section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode); -+ section[2].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1; -+ section[0].ia0 = section[0].ib0 = i; -+ section[2].ia1 = section[2].ib1 = (i+2) & 3; -+ if (vx[(i+1) & 3] < vx[(i+3) & 3]) { -+ section[0].ia1 = section[2].ia0 = (i+1) & 3; -+ section[0].ib1 = section[2].ib0 = (i+3) & 3; -+ } else { -+ section[0].ia1 = section[2].ia0 = (i+3) & 3; -+ section[0].ib1 = section[2].ib0 = (i+1) & 3; - } -- lastYStep = yStep; -+ if (vy[(i+1) & 3] < vy[(i+3) & 3]) { -+ section[1].y0 = imgCoordMungeLowerC(vy[(i+1) & 3], glyphMode); -+ section[2].y0 = imgCoordMungeUpperC(vy[(i+3) & 3], glyphMode); -+ if (vx[(i+1) & 3] < vx[(i+3) & 3]) { -+ section[1].ia0 = (i+1) & 3; -+ section[1].ia1 = (i+2) & 3; -+ section[1].ib0 = i; -+ section[1].ib1 = (i+3) & 3; -+ } else { -+ section[1].ia0 = i; -+ section[1].ia1 = (i+3) & 3; -+ section[1].ib0 = (i+1) & 3; -+ section[1].ib1 = (i+2) & 3; -+ } -+ } else { -+ section[1].y0 = imgCoordMungeLowerC(vy[(i+3) & 3], glyphMode); -+ section[2].y0 = imgCoordMungeUpperC(vy[(i+1) & 3], glyphMode); -+ if (vx[(i+1) & 3] < vx[(i+3) & 3]) { -+ section[1].ia0 = i; -+ section[1].ia1 = (i+1) & 3; -+ section[1].ib0 = (i+3) & 3; -+ section[1].ib1 = (i+2) & 3; -+ } else { -+ section[1].ia0 = (i+3) & 3; -+ section[1].ia1 = (i+2) & 3; -+ section[1].ib0 = i; -+ section[1].ib1 = (i+1) & 3; -+ } -+ } -+ section[0].y1 = section[1].y0 - 1; -+ section[1].y1 = section[2].y0 - 1; -+ nSections = 3; -+ } -+ for (i = 0; i < nSections; ++i) { -+ section[i].xa0 = vx[section[i].ia0]; -+ section[i].ya0 = vy[section[i].ia0]; -+ section[i].xa1 = vx[section[i].ia1]; -+ section[i].ya1 = vy[section[i].ia1]; -+ section[i].xb0 = vx[section[i].ib0]; -+ section[i].yb0 = vy[section[i].ib0]; -+ section[i].xb1 = vx[section[i].ib1]; -+ section[i].yb1 = vy[section[i].ib1]; -+ section[i].dxdya = (section[i].xa1 - section[i].xa0) / -+ (section[i].ya1 - section[i].ya0); -+ section[i].dxdyb = (section[i].xb1 - section[i].xb0) / -+ (section[i].yb1 - section[i].yb0); -+ } -+ -+ // initialize the pixel pipe -+ pipeInit(&pipe, 0, 0, state->fillPattern, NULL, -+ (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); -+ if (vectorAntialias) { -+ drawAAPixelInit(); -+ } - -- // loop-invariant constants -- k1 = splashRound(xShear * ySign * y); -+ // make sure narrow images cover at least one pixel -+ if (nSections == 1) { -+ if (section[0].y0 == section[0].y1) { -+ ++section[0].y1; -+ clipRes = opClipRes = splashClipPartial; -+ } -+ } else { -+ if (section[0].y0 == section[2].y1) { -+ ++section[1].y1; -+ clipRes = opClipRes = splashClipPartial; -+ } -+ } - -- // clipping test -- if (clipRes != splashClipAllInside && -- !rot && -- (int)(yShear * k1) == -- (int)(yShear * (xSign * (scaledWidth - 1) + k1))) { -- if (xSign > 0) { -- spanXMin = tx + k1; -- spanXMax = spanXMin + (scaledWidth - 1); -+ // scan all pixels inside the target region -+ for (i = 0; i < nSections; ++i) { -+ for (y = section[i].y0; y <= section[i].y1; ++y) { -+ xa = imgCoordMungeLowerC(section[i].xa0 + -+ ((SplashCoord)y + 0.5 - section[i].ya0) * -+ section[i].dxdya, -+ glyphMode); -+ xb = imgCoordMungeUpperC(section[i].xb0 + -+ ((SplashCoord)y + 0.5 - section[i].yb0) * -+ section[i].dxdyb, -+ glyphMode); -+ // make sure narrow images cover at least one pixel -+ if (xa == xb) { -+ ++xb; -+ } -+ if (clipRes != splashClipAllInside) { -+ clipRes2 = state->clip->testSpan(xa, xb - 1, y); - } else { -- spanXMax = tx + k1; -- spanXMin = spanXMax - (scaledWidth - 1); -+ clipRes2 = clipRes; - } -- spanY = ty + ySign * y + (int)(yShear * k1); -- clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY); -- if (clipRes2 == splashClipAllOutside) { -- continue; -+ for (x = xa; x < xb; ++x) { -+ // map (x+0.5, y+0.5) back to the scaled image -+ xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + -+ ((SplashCoord)y + 0.5 - mat[5]) * ir10); -+ yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + -+ ((SplashCoord)y + 0.5 - mat[5]) * ir11); -+ // xx should always be within bounds, but floating point -+ // inaccuracy can cause problems -+ if (xx < 0) { -+ xx = 0; -+ } else if (xx >= scaledWidth) { -+ xx = scaledWidth - 1; -+ } -+ if (yy < 0) { -+ yy = 0; -+ } else if (yy >= scaledHeight) { -+ yy = scaledHeight - 1; -+ } -+ pipe.shape = scaledMask->data[yy * scaledWidth + xx]; -+ if (vectorAntialias && clipRes2 != splashClipAllInside) { -+ drawAAPixel(&pipe, x, y); -+ } else { -+ drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside); -+ } - } -+ } -+ } -+ -+ delete scaledMask; -+} -+ -+// Scale an image mask into a SplashBitmap. -+SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData, -+ int srcWidth, int srcHeight, -+ int scaledWidth, int scaledHeight) { -+ SplashBitmap *dest; -+ -+ dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8, -+ gFalse); -+ if (scaledHeight < srcHeight) { -+ if (scaledWidth < srcWidth) { -+ scaleMaskYdXd(src, srcData, srcWidth, srcHeight, -+ scaledWidth, scaledHeight, dest); -+ } else { -+ scaleMaskYdXu(src, srcData, srcWidth, srcHeight, -+ scaledWidth, scaledHeight, dest); -+ } -+ } else { -+ if (scaledWidth < srcWidth) { -+ scaleMaskYuXd(src, srcData, srcWidth, srcHeight, -+ scaledWidth, scaledHeight, dest); - } else { -- clipRes2 = clipRes; -+ scaleMaskYuXu(src, srcData, srcWidth, srcHeight, -+ scaledWidth, scaledHeight, dest); - } -+ } -+ return dest; -+} - -- // init x scale Bresenham -- xt = 0; -- xSrc = 0; -+void Splash::scaleMaskYdXd(SplashImageMaskSource src, void *srcData, -+ int srcWidth, int srcHeight, -+ int scaledWidth, int scaledHeight, -+ SplashBitmap *dest) { -+ Guchar *lineBuf; -+ Guint *pixBuf; -+ Guint pix; -+ Guchar *destPtr; -+ int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; -+ int i, j; -+ -+ // Bresenham parameters for y scale -+ yp = srcHeight / scaledHeight; -+ yq = srcHeight % scaledHeight; -+ -+ // Bresenham parameters for x scale -+ xp = srcWidth / scaledWidth; -+ xq = srcWidth % scaledWidth; -+ -+ // allocate buffers -+ lineBuf = (Guchar *)gmalloc(srcWidth); -+ pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); - -- // x shear -- x1 = k1; -+ // init y scale Bresenham -+ yt = 0; -+ -+ destPtr = dest->data; -+ for (y = 0; y < scaledHeight; ++y) { -+ -+ // y scale Bresenham -+ if ((yt += yq) >= scaledHeight) { -+ yt -= scaledHeight; -+ yStep = yp + 1; -+ } else { -+ yStep = yp; -+ } - -- // y shear -- y1 = (SplashCoord)ySign * y + yShear * x1; -- // this is a kludge: if yShear1 is negative, then (int)y1 would -- // change immediately after the first pixel, which is not what we -- // want -- if (yShear1 < 0) { -- y1 += 0.999; -+ // read rows from image -+ memset(pixBuf, 0, srcWidth * sizeof(int)); -+ for (i = 0; i < yStep; ++i) { -+ (*src)(srcData, lineBuf); -+ for (j = 0; j < srcWidth; ++j) { -+ pixBuf[j] += lineBuf[j]; -+ } - } - -- // loop-invariant constants -- n = yStep > 0 ? yStep : 1; -+ // init x scale Bresenham -+ xt = 0; -+ d0 = (255 << 23) / (yStep * xp); -+ d1 = (255 << 23) / (yStep * (xp + 1)); - -+ xx = 0; - for (x = 0; x < scaledWidth; ++x) { - - // x scale Bresenham -- xStep = xp; -- xt += xq; -- if (xt >= scaledWidth) { -+ if ((xt += xq) >= scaledWidth) { - xt -= scaledWidth; -- ++xStep; -+ xStep = xp + 1; -+ d = d1; -+ } else { -+ xStep = xp; -+ d = d0; -+ } -+ -+ // compute the final pixel -+ pix = 0; -+ for (i = 0; i < xStep; ++i) { -+ pix += pixBuf[xx++]; - } -+ // (255 * pix) / xStep * yStep -+ pix = (pix * d) >> 23; -+ -+ // store the pixel -+ *destPtr++ = (Guchar)pix; -+ } -+ } - -- // rotation -- if (rot) { -- x2 = (int)y1; -- y2 = -x1; -+ gfree(pixBuf); -+ gfree(lineBuf); -+} -+ -+void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData, -+ int srcWidth, int srcHeight, -+ int scaledWidth, int scaledHeight, -+ SplashBitmap *dest) { -+ Guchar *lineBuf; -+ Guint *pixBuf; -+ Guint pix; -+ Guchar *destPtr; -+ int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; -+ int i, j; -+ -+ // Bresenham parameters for y scale -+ yp = srcHeight / scaledHeight; -+ yq = srcHeight % scaledHeight; -+ -+ // Bresenham parameters for x scale -+ xp = scaledWidth / srcWidth; -+ xq = scaledWidth % srcWidth; -+ -+ // allocate buffers -+ lineBuf = (Guchar *)gmalloc(srcWidth); -+ pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); -+ -+ // init y scale Bresenham -+ yt = 0; -+ -+ destPtr = dest->data; -+ for (y = 0; y < scaledHeight; ++y) { -+ -+ // y scale Bresenham -+ if ((yt += yq) >= scaledHeight) { -+ yt -= scaledHeight; -+ yStep = yp + 1; -+ } else { -+ yStep = yp; -+ } -+ -+ // read rows from image -+ memset(pixBuf, 0, srcWidth * sizeof(int)); -+ for (i = 0; i < yStep; ++i) { -+ (*src)(srcData, lineBuf); -+ for (j = 0; j < srcWidth; ++j) { -+ pixBuf[j] += lineBuf[j]; -+ } -+ } -+ -+ // init x scale Bresenham -+ xt = 0; -+ d = (255 << 23) / yStep; -+ -+ for (x = 0; x < srcWidth; ++x) { -+ -+ // x scale Bresenham -+ if ((xt += xq) >= srcWidth) { -+ xt -= srcWidth; -+ xStep = xp + 1; - } else { -- x2 = x1; -- y2 = (int)y1; -+ xStep = xp; - } - -- // compute the alpha value for (x,y) after the x and y scaling -- // operations -- m = xStep > 0 ? xStep : 1; -- p = pixBuf + xSrc; -- pixAcc = 0; -- for (i = 0; i < n; ++i) { -- for (j = 0; j < m; ++j) { -- pixAcc += *p++; -- } -- p += w - m; -+ // compute the final pixel -+ pix = pixBuf[x]; -+ // (255 * pix) / yStep -+ pix = (pix * d) >> 23; -+ -+ // store the pixel -+ for (i = 0; i < xStep; ++i) { -+ *destPtr++ = (Guchar)pix; - } -+ } -+ } - -- // blend fill color with background -- if (pixAcc != 0) { -- pipe.shape = (pixAcc == n * m) -- ? (SplashCoord)1 -- : (SplashCoord)pixAcc / (SplashCoord)(n * m); -- if (vectorAntialias && clipRes2 != splashClipAllInside) { -- drawAAPixel(&pipe, tx + x2, ty + y2); -- } else { -- drawPixel(&pipe, tx + x2, ty + y2, clipRes2 == splashClipAllInside); -- } -+ gfree(pixBuf); -+ gfree(lineBuf); -+} -+ -+void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData, -+ int srcWidth, int srcHeight, -+ int scaledWidth, int scaledHeight, -+ SplashBitmap *dest) { -+ Guchar *lineBuf; -+ Guint pix; -+ Guchar *destPtr0, *destPtr; -+ int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; -+ int i; -+ -+ // Bresenham parameters for y scale -+ yp = scaledHeight / srcHeight; -+ yq = scaledHeight % srcHeight; -+ -+ // Bresenham parameters for x scale -+ xp = srcWidth / scaledWidth; -+ xq = srcWidth % scaledWidth; -+ -+ // allocate buffers -+ lineBuf = (Guchar *)gmalloc(srcWidth); -+ -+ // init y scale Bresenham -+ yt = 0; -+ -+ destPtr0 = dest->data; -+ for (y = 0; y < srcHeight; ++y) { -+ -+ // y scale Bresenham -+ if ((yt += yq) >= srcHeight) { -+ yt -= srcHeight; -+ yStep = yp + 1; -+ } else { -+ yStep = yp; -+ } -+ -+ // read row from image -+ (*src)(srcData, lineBuf); -+ -+ // init x scale Bresenham -+ xt = 0; -+ d0 = (255 << 23) / xp; -+ d1 = (255 << 23) / (xp + 1); -+ -+ xx = 0; -+ for (x = 0; x < scaledWidth; ++x) { -+ -+ // x scale Bresenham -+ if ((xt += xq) >= scaledWidth) { -+ xt -= scaledWidth; -+ xStep = xp + 1; -+ d = d1; -+ } else { -+ xStep = xp; -+ d = d0; -+ } -+ -+ // compute the final pixel -+ pix = 0; -+ for (i = 0; i < xStep; ++i) { -+ pix += lineBuf[xx++]; -+ } -+ // (255 * pix) / xStep -+ pix = (pix * d) >> 23; -+ -+ // store the pixel -+ for (i = 0; i < yStep; ++i) { -+ destPtr = destPtr0 + i * scaledWidth + x; -+ *destPtr = (Guchar)pix; - } -+ } -+ -+ destPtr0 += yStep * scaledWidth; -+ } -+ -+ gfree(lineBuf); -+} -+ -+void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData, -+ int srcWidth, int srcHeight, -+ int scaledWidth, int scaledHeight, -+ SplashBitmap *dest) { -+ Guchar *lineBuf; -+ Guint pix; -+ Guchar *destPtr0, *destPtr; -+ int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx; -+ int i, j; -+ -+ // Bresenham parameters for y scale -+ yp = scaledHeight / srcHeight; -+ yq = scaledHeight % srcHeight; -+ -+ // Bresenham parameters for x scale -+ xp = scaledWidth / srcWidth; -+ xq = scaledWidth % srcWidth; -+ -+ // allocate buffers -+ lineBuf = (Guchar *)gmalloc(srcWidth); -+ -+ // init y scale Bresenham -+ yt = 0; -+ -+ destPtr0 = dest->data; -+ for (y = 0; y < srcHeight; ++y) { -+ -+ // y scale Bresenham -+ if ((yt += yq) >= srcHeight) { -+ yt -= srcHeight; -+ yStep = yp + 1; -+ } else { -+ yStep = yp; -+ } -+ -+ // read row from image -+ (*src)(srcData, lineBuf); -+ -+ // init x scale Bresenham -+ xt = 0; -+ -+ xx = 0; -+ for (x = 0; x < srcWidth; ++x) { - - // x scale Bresenham -- xSrc += xStep; -+ if ((xt += xq) >= srcWidth) { -+ xt -= srcWidth; -+ xStep = xp + 1; -+ } else { -+ xStep = xp; -+ } -+ -+ // compute the final pixel -+ pix = lineBuf[x] ? 255 : 0; - -- // x shear -- x1 += xSign; -+ // store the pixel -+ for (i = 0; i < yStep; ++i) { -+ for (j = 0; j < xStep; ++j) { -+ destPtr = destPtr0 + i * scaledWidth + xx + j; -+ *destPtr++ = (Guchar)pix; -+ } -+ } - -- // y shear -- y1 += yShear1; -+ xx += xStep; - } -+ -+ destPtr0 += yStep * scaledWidth; - } - -- // free memory -- gfree(pixBuf); -+ gfree(lineBuf); -+} - -- return splashOk; -+void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, -+ SplashClipResult clipRes) { -+ SplashPipe pipe; -+ Guchar *p; -+ int w, h, x, y; -+ -+ w = src->getWidth(); -+ h = src->getHeight(); -+ if (vectorAntialias && clipRes != splashClipAllInside) { -+ pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL, -+ (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); -+ drawAAPixelInit(); -+ p = src->getDataPtr(); -+ for (y = 0; y < h; ++y) { -+ for (x = 0; x < w; ++x) { -+ pipe.shape = *p++; -+ drawAAPixel(&pipe, xDest + x, yDest + y); -+ } -+ } -+ } else { -+ pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL, -+ (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); -+ p = src->getDataPtr(); -+ if (clipRes == splashClipAllInside) { -+ for (y = 0; y < h; ++y) { -+ pipeSetXY(&pipe, xDest, yDest + y); -+ for (x = 0; x < w; ++x) { -+ if (*p) { -+ pipe.shape = *p; -+ (this->*pipe.run)(&pipe); -+ } else { -+ pipeIncX(&pipe); -+ } -+ ++p; -+ } -+ } -+ updateModX(xDest); -+ updateModX(xDest + w - 1); -+ updateModY(yDest); -+ updateModY(yDest + h - 1); -+ } else { -+ for (y = 0; y < h; ++y) { -+ pipeSetXY(&pipe, xDest, yDest + y); -+ for (x = 0; x < w; ++x) { -+ if (*p && state->clip->test(xDest + x, yDest + y)) { -+ pipe.shape = *p; -+ (this->*pipe.run)(&pipe); -+ updateModX(xDest + x); -+ updateModY(yDest + y); -+ } else { -+ pipeIncX(&pipe); -+ } -+ ++p; -+ } -+ } -+ } -+ } - } - - SplashError Splash::drawImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, GBool srcAlpha, - int w, int h, SplashCoord *mat) { -- SplashPipe pipe; -- GBool ok, rot; -- SplashCoord xScale, yScale, xShear, yShear, yShear1; -- int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign; -- int ulx, uly, llx, lly, urx, ury, lrx, lry; -- int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1; -- int xMin, xMax, yMin, yMax; -- SplashClipResult clipRes, clipRes2; -- int yp, yq, yt, yStep, lastYStep; -- int xp, xq, xt, xStep, xSrc; -- int k1, spanXMin, spanXMax, spanY; -- SplashColorPtr colorBuf, p; -- SplashColor pix; -- Guchar *alphaBuf, *q; --#if SPLASH_CMYK -- int pixAcc0, pixAcc1, pixAcc2, pixAcc3; --#else -- int pixAcc0, pixAcc1, pixAcc2; --#endif -- int alphaAcc; -- SplashCoord pixMul, alphaMul, alpha; -- int x, y, x1, x2, y2; -- SplashCoord y1; -- int nComps, n, m, i, j; -+ GBool ok; -+ SplashBitmap *scaledImg; -+ SplashClipResult clipRes; -+ GBool minorAxisZero; -+ int x0, y0, x1, y1, scaledWidth, scaledHeight; -+ int nComps; - - if (debugMode) { - printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", -@@ -2106,666 +3155,1170 @@ - nComps = 4; - break; - #endif -+ default: -+ ok = gFalse; -+ break; - } - if (!ok) { - return splashErrModeMismatch; - } - - // check for singular matrix -- if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) { -+ if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { - return splashErrSingularMatrix; - } - -- // compute scale, shear, rotation, translation parameters -- rot = splashAbs(mat[1]) > splashAbs(mat[0]); -- if (rot) { -- xScale = -mat[1]; -- yScale = mat[2] - (mat[0] * mat[3]) / mat[1]; -- xShear = -mat[3] / yScale; -- yShear = -mat[0] / mat[1]; -- } else { -- xScale = mat[0]; -- yScale = mat[3] - (mat[1] * mat[2]) / mat[0]; -- xShear = mat[2] / yScale; -- yShear = mat[1] / mat[0]; -- } -- // Note 1: The PDF spec says that all pixels whose *centers* lie -- // within the region get painted -- but that doesn't seem to match -- // up with what Acrobat actually does: it ends up leaving gaps -- // between image stripes. So we use the same rule here as for -- // fills: any pixel that overlaps the region gets painted. -- // Note 2: The +/-0.01 in these computations is to avoid floating -- // point precision problems which can lead to gaps between image -- // stripes (it can cause image stripes to overlap, but that's a much -- // less visible problem). -- if (xScale >= 0) { -- tx = splashFloor(mat[4] - 0.01); -- tx2 = splashFloor(mat[4] + xScale + 0.01); -- } else { -- tx = splashFloor(mat[4] + 0.01); -- tx2 = splashFloor(mat[4] + xScale - 0.01); -- } -- scaledWidth = abs(tx2 - tx) + 1; -- if (yScale >= 0) { -- ty = splashFloor(mat[5] - 0.01); -- ty2 = splashFloor(mat[5] + yScale + 0.01); -- } else { -- ty = splashFloor(mat[5] + 0.01); -- ty2 = splashFloor(mat[5] + yScale - 0.01); -- } -- scaledHeight = abs(ty2 - ty) + 1; -- xSign = (xScale < 0) ? -1 : 1; -- ySign = (yScale < 0) ? -1 : 1; -- yShear1 = (SplashCoord)xSign * yShear; -+ minorAxisZero = mat[1] == 0 && mat[2] == 0; -+ -+ // scaling only -+ if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { -+ x0 = imgCoordMungeLower(mat[4]); -+ y0 = imgCoordMungeLower(mat[5]); -+ x1 = imgCoordMungeUpper(mat[0] + mat[4]); -+ y1 = imgCoordMungeUpper(mat[3] + mat[5]); -+ // make sure narrow images cover at least one pixel -+ if (x0 == x1) { -+ ++x1; -+ } -+ if (y0 == y1) { -+ ++y1; -+ } -+ clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); -+ opClipRes = clipRes; -+ if (clipRes != splashClipAllOutside) { -+ scaledWidth = x1 - x0; -+ scaledHeight = y1 - y0; -+ scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, -+ scaledWidth, scaledHeight); -+ blitImage(scaledImg, srcAlpha, x0, y0, clipRes); -+ delete scaledImg; -+ } -+ -+ // scaling plus vertical flip -+ } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { -+ x0 = imgCoordMungeLower(mat[4]); -+ y0 = imgCoordMungeLower(mat[3] + mat[5]); -+ x1 = imgCoordMungeUpper(mat[0] + mat[4]); -+ y1 = imgCoordMungeUpper(mat[5]); -+ if (x0 == x1) { -+ if (mat[4] + mat[0] * 0.5 < x0) { -+ --x0; -+ } else { -+ ++x1; -+ } -+ } -+ if (y0 == y1) { -+ if (mat[5] + mat[1] * 0.5 < y0) { -+ --y0; -+ } else { -+ ++y1; -+ } -+ } -+ clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); -+ opClipRes = clipRes; -+ if (clipRes != splashClipAllOutside) { -+ scaledWidth = x1 - x0; -+ scaledHeight = y1 - y0; -+ scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, -+ scaledWidth, scaledHeight); -+ vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); -+ blitImage(scaledImg, srcAlpha, x0, y0, clipRes); -+ delete scaledImg; -+ } -+ -+ // all other cases -+ } else { -+ arbitraryTransformImage(src, srcData, srcMode, nComps, srcAlpha, -+ w, h, mat); -+ } -+ -+ return splashOk; -+} -+ -+void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, -+ SplashColorMode srcMode, int nComps, -+ GBool srcAlpha, -+ int srcWidth, int srcHeight, -+ SplashCoord *mat) { -+ SplashBitmap *scaledImg; -+ SplashClipResult clipRes, clipRes2; -+ SplashPipe pipe; -+ SplashColor pixel; -+ int scaledWidth, scaledHeight, t0, t1; -+ SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; -+ SplashCoord vx[4], vy[4]; -+ int xMin, yMin, xMax, yMax; -+ ImageSection section[3]; -+ int nSections; -+ int y, xa, xb, x, i, xx, yy; -+ -+ // compute the four vertices of the target quadrilateral -+ vx[0] = mat[4]; vy[0] = mat[5]; -+ vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; -+ vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; -+ vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; - - // clipping -- ulx1 = 0; -- uly1 = 0; -- urx1 = xSign * (scaledWidth - 1); -- ury1 = (int)(yShear * urx1); -- llx1 = splashRound(xShear * ySign * (scaledHeight - 1)); -- lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1); -- lrx1 = xSign * (scaledWidth - 1) + -- splashRound(xShear * ySign * (scaledHeight - 1)); -- lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1); -- if (rot) { -- ulx = tx + uly1; uly = ty - ulx1; -- urx = tx + ury1; ury = ty - urx1; -- llx = tx + lly1; lly = ty - llx1; -- lrx = tx + lry1; lry = ty - lrx1; -- } else { -- ulx = tx + ulx1; uly = ty + uly1; -- urx = tx + urx1; ury = ty + ury1; -- llx = tx + llx1; lly = ty + lly1; -- lrx = tx + lrx1; lry = ty + lry1; -- } -- xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx -- : (llx < lrx) ? llx : lrx -- : (urx < llx) ? (urx < lrx) ? urx : lrx -- : (llx < lrx) ? llx : lrx; -- xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx -- : (llx > lrx) ? llx : lrx -- : (urx > llx) ? (urx > lrx) ? urx : lrx -- : (llx > lrx) ? llx : lrx; -- yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry -- : (lly < lry) ? lly : lry -- : (ury < lly) ? (ury < lry) ? ury : lry -- : (lly < lry) ? lly : lry; -- yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry -- : (lly > lry) ? lly : lry -- : (ury > lly) ? (ury > lry) ? ury : lry -- : (lly > lry) ? lly : lry; -- clipRes = state->clip->testRect(xMin, yMin, xMax, yMax); -+ xMin = imgCoordMungeLower(vx[0]); -+ xMax = imgCoordMungeUpper(vx[0]); -+ yMin = imgCoordMungeLower(vy[0]); -+ yMax = imgCoordMungeUpper(vy[0]); -+ for (i = 1; i < 4; ++i) { -+ t0 = imgCoordMungeLower(vx[i]); -+ if (t0 < xMin) { -+ xMin = t0; -+ } -+ t0 = imgCoordMungeUpper(vx[i]); -+ if (t0 > xMax) { -+ xMax = t0; -+ } -+ t1 = imgCoordMungeLower(vy[i]); -+ if (t1 < yMin) { -+ yMin = t1; -+ } -+ t1 = imgCoordMungeUpper(vy[i]); -+ if (t1 > yMax) { -+ yMax = t1; -+ } -+ } -+ clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1); - opClipRes = clipRes; - if (clipRes == splashClipAllOutside) { -- return splashOk; -+ return; - } - -- // compute Bresenham parameters for x and y scaling -- yp = h / scaledHeight; -- yq = h % scaledHeight; -- xp = w / scaledWidth; -- xq = w % scaledWidth; -- -- // allocate pixel buffers -- colorBuf = (SplashColorPtr)gmalloc((yp + 1) * w * nComps); -- if (srcAlpha) { -- alphaBuf = (Guchar *)gmalloc((yp + 1) * w); -+ // compute the scale factors -+ if (mat[0] >= 0) { -+ t0 = imgCoordMungeUpper(mat[0] + mat[4]) - imgCoordMungeLower(mat[4]); -+ } else { -+ t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[0] + mat[4]); -+ } -+ if (mat[1] >= 0) { -+ t1 = imgCoordMungeUpper(mat[1] + mat[5]) - imgCoordMungeLower(mat[5]); -+ } else { -+ t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[1] + mat[5]); -+ } -+ scaledWidth = t0 > t1 ? t0 : t1; -+ if (mat[2] >= 0) { -+ t0 = imgCoordMungeUpper(mat[2] + mat[4]) - imgCoordMungeLower(mat[4]); -+ } else { -+ t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[2] + mat[4]); -+ } -+ if (mat[3] >= 0) { -+ t1 = imgCoordMungeUpper(mat[3] + mat[5]) - imgCoordMungeLower(mat[5]); - } else { -- alphaBuf = NULL; -+ t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[3] + mat[5]); -+ } -+ scaledHeight = t0 > t1 ? t0 : t1; -+ if (scaledWidth == 0) { -+ scaledWidth = 1; -+ } -+ if (scaledHeight == 0) { -+ scaledHeight = 1; -+ } -+ -+ // compute the inverse transform (after scaling) matrix -+ r00 = mat[0] / scaledWidth; -+ r01 = mat[1] / scaledWidth; -+ r10 = mat[2] / scaledHeight; -+ r11 = mat[3] / scaledHeight; -+ det = r00 * r11 - r01 * r10; -+ if (splashAbs(det) < 1e-6) { -+ // this should be caught by the singular matrix check in drawImage -+ return; - } -+ ir00 = r11 / det; -+ ir01 = -r01 / det; -+ ir10 = -r10 / det; -+ ir11 = r00 / det; -+ -+ // scale the input image -+ scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, -+ srcWidth, srcHeight, scaledWidth, scaledHeight); - -- pixAcc0 = pixAcc1 = pixAcc2 = 0; // make gcc happy --#if SPLASH_CMYK -- pixAcc3 = 0; // make gcc happy --#endif -+ // construct the three sections -+ i = 0; -+ if (vy[1] < vy[i]) { -+ i = 1; -+ } -+ if (vy[2] < vy[i]) { -+ i = 2; -+ } -+ if (vy[3] < vy[i]) { -+ i = 3; -+ } -+ // NB: if using fixed point, 0.000001 will be truncated to zero, -+ // so these two comparisons must be <=, not < -+ if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 && -+ vy[(i-1) & 3] < vy[(i+1) & 3]) { -+ i = (i-1) & 3; -+ } -+ if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) { -+ section[0].y0 = imgCoordMungeLower(vy[i]); -+ section[0].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1; -+ if (vx[i] < vx[(i+1) & 3]) { -+ section[0].ia0 = i; -+ section[0].ia1 = (i+3) & 3; -+ section[0].ib0 = (i+1) & 3; -+ section[0].ib1 = (i+2) & 3; -+ } else { -+ section[0].ia0 = (i+1) & 3; -+ section[0].ia1 = (i+2) & 3; -+ section[0].ib0 = i; -+ section[0].ib1 = (i+3) & 3; -+ } -+ nSections = 1; -+ } else { -+ section[0].y0 = imgCoordMungeLower(vy[i]); -+ section[2].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1; -+ section[0].ia0 = section[0].ib0 = i; -+ section[2].ia1 = section[2].ib1 = (i+2) & 3; -+ if (vx[(i+1) & 3] < vx[(i+3) & 3]) { -+ section[0].ia1 = section[2].ia0 = (i+1) & 3; -+ section[0].ib1 = section[2].ib0 = (i+3) & 3; -+ } else { -+ section[0].ia1 = section[2].ia0 = (i+3) & 3; -+ section[0].ib1 = section[2].ib0 = (i+1) & 3; -+ } -+ if (vy[(i+1) & 3] < vy[(i+3) & 3]) { -+ section[1].y0 = imgCoordMungeLower(vy[(i+1) & 3]); -+ section[2].y0 = imgCoordMungeUpper(vy[(i+3) & 3]); -+ if (vx[(i+1) & 3] < vx[(i+3) & 3]) { -+ section[1].ia0 = (i+1) & 3; -+ section[1].ia1 = (i+2) & 3; -+ section[1].ib0 = i; -+ section[1].ib1 = (i+3) & 3; -+ } else { -+ section[1].ia0 = i; -+ section[1].ia1 = (i+3) & 3; -+ section[1].ib0 = (i+1) & 3; -+ section[1].ib1 = (i+2) & 3; -+ } -+ } else { -+ section[1].y0 = imgCoordMungeLower(vy[(i+3) & 3]); -+ section[2].y0 = imgCoordMungeUpper(vy[(i+1) & 3]); -+ if (vx[(i+1) & 3] < vx[(i+3) & 3]) { -+ section[1].ia0 = i; -+ section[1].ia1 = (i+1) & 3; -+ section[1].ib0 = (i+3) & 3; -+ section[1].ib1 = (i+2) & 3; -+ } else { -+ section[1].ia0 = (i+3) & 3; -+ section[1].ia1 = (i+2) & 3; -+ section[1].ib0 = i; -+ section[1].ib1 = (i+1) & 3; -+ } -+ } -+ section[0].y1 = section[1].y0 - 1; -+ section[1].y1 = section[2].y0 - 1; -+ nSections = 3; -+ } -+ for (i = 0; i < nSections; ++i) { -+ section[i].xa0 = vx[section[i].ia0]; -+ section[i].ya0 = vy[section[i].ia0]; -+ section[i].xa1 = vx[section[i].ia1]; -+ section[i].ya1 = vy[section[i].ia1]; -+ section[i].xb0 = vx[section[i].ib0]; -+ section[i].yb0 = vy[section[i].ib0]; -+ section[i].xb1 = vx[section[i].ib1]; -+ section[i].yb1 = vy[section[i].ib1]; -+ section[i].dxdya = (section[i].xa1 - section[i].xa0) / -+ (section[i].ya1 - section[i].ya0); -+ section[i].dxdyb = (section[i].xb1 - section[i].xb0) / -+ (section[i].yb1 - section[i].yb0); -+ } - - // initialize the pixel pipe -- pipeInit(&pipe, 0, 0, NULL, pix, state->fillAlpha, -+ pipeInit(&pipe, 0, 0, NULL, pixel, -+ (Guchar)splashRound(state->fillAlpha * 255), - srcAlpha || (vectorAntialias && clipRes != splashClipAllInside), - gFalse); - if (vectorAntialias) { - drawAAPixelInit(); - } - -+ // make sure narrow images cover at least one pixel -+ if (nSections == 1) { -+ if (section[0].y0 == section[0].y1) { -+ ++section[0].y1; -+ clipRes = opClipRes = splashClipPartial; -+ } -+ } else { -+ if (section[0].y0 == section[2].y1) { -+ ++section[1].y1; -+ clipRes = opClipRes = splashClipPartial; -+ } -+ } -+ -+ // scan all pixels inside the target region -+ for (i = 0; i < nSections; ++i) { -+ for (y = section[i].y0; y <= section[i].y1; ++y) { -+ xa = imgCoordMungeLower(section[i].xa0 + -+ ((SplashCoord)y + 0.5 - section[i].ya0) * -+ section[i].dxdya); -+ xb = imgCoordMungeUpper(section[i].xb0 + -+ ((SplashCoord)y + 0.5 - section[i].yb0) * -+ section[i].dxdyb); -+ // make sure narrow images cover at least one pixel -+ if (xa == xb) { -+ ++xb; -+ } -+ if (clipRes != splashClipAllInside) { -+ clipRes2 = state->clip->testSpan(xa, xb - 1, y); -+ } else { -+ clipRes2 = clipRes; -+ } -+ for (x = xa; x < xb; ++x) { -+ // map (x+0.5, y+0.5) back to the scaled image -+ xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + -+ ((SplashCoord)y + 0.5 - mat[5]) * ir10); -+ yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + -+ ((SplashCoord)y + 0.5 - mat[5]) * ir11); -+ // xx should always be within bounds, but floating point -+ // inaccuracy can cause problems -+ if (xx < 0) { -+ xx = 0; -+ } else if (xx >= scaledWidth) { -+ xx = scaledWidth - 1; -+ } -+ if (yy < 0) { -+ yy = 0; -+ } else if (yy >= scaledHeight) { -+ yy = scaledHeight - 1; -+ } -+ scaledImg->getPixel(xx, yy, pixel); -+ if (srcAlpha) { -+ pipe.shape = scaledImg->alpha[yy * scaledWidth + xx]; -+ } else { -+ pipe.shape = 255; -+ } -+ if (vectorAntialias && clipRes2 != splashClipAllInside) { -+ drawAAPixel(&pipe, x, y); -+ } else { -+ drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside); -+ } -+ } -+ } -+ } -+ -+ delete scaledImg; -+} -+ -+// Scale an image into a SplashBitmap. -+SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, -+ SplashColorMode srcMode, int nComps, -+ GBool srcAlpha, int srcWidth, int srcHeight, -+ int scaledWidth, int scaledHeight) { -+ SplashBitmap *dest; -+ -+ dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha); -+ if (scaledHeight < srcHeight) { -+ if (scaledWidth < srcWidth) { -+ scaleImageYdXd(src, srcData, srcMode, nComps, srcAlpha, -+ srcWidth, srcHeight, scaledWidth, scaledHeight, dest); -+ } else { -+ scaleImageYdXu(src, srcData, srcMode, nComps, srcAlpha, -+ srcWidth, srcHeight, scaledWidth, scaledHeight, dest); -+ } -+ } else { -+ if (scaledWidth < srcWidth) { -+ scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha, -+ srcWidth, srcHeight, scaledWidth, scaledHeight, dest); -+ } else { -+ scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha, -+ srcWidth, srcHeight, scaledWidth, scaledHeight, dest); -+ } -+ } -+ return dest; -+} -+ -+void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, -+ SplashColorMode srcMode, int nComps, -+ GBool srcAlpha, int srcWidth, int srcHeight, -+ int scaledWidth, int scaledHeight, -+ SplashBitmap *dest) { -+ Guchar *lineBuf, *alphaLineBuf; -+ Guint *pixBuf, *alphaPixBuf; -+ Guint pix0, pix1, pix2; -+#if SPLASH_CMYK -+ Guint pix3; -+#endif -+ Guint alpha; -+ Guchar *destPtr, *destAlphaPtr; -+ int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; -+ int i, j; -+ -+ // Bresenham parameters for y scale -+ yp = srcHeight / scaledHeight; -+ yq = srcHeight % scaledHeight; -+ -+ // Bresenham parameters for x scale -+ xp = srcWidth / scaledWidth; -+ xq = srcWidth % scaledWidth; -+ -+ // allocate buffers -+ lineBuf = (Guchar *)gmallocn(srcWidth, nComps); -+ pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int)); - if (srcAlpha) { -+ alphaLineBuf = (Guchar *)gmalloc(srcWidth); -+ alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); -+ } else { -+ alphaLineBuf = NULL; -+ alphaPixBuf = NULL; -+ } - -- // init y scale Bresenham -- yt = 0; -- lastYStep = 1; -+ // init y scale Bresenham -+ yt = 0; - -- for (y = 0; y < scaledHeight; ++y) { -+ destPtr = dest->data; -+ destAlphaPtr = dest->alpha; -+ for (y = 0; y < scaledHeight; ++y) { - -- // y scale Bresenham -+ // y scale Bresenham -+ if ((yt += yq) >= scaledHeight) { -+ yt -= scaledHeight; -+ yStep = yp + 1; -+ } else { - yStep = yp; -- yt += yq; -- if (yt >= scaledHeight) { -- yt -= scaledHeight; -- ++yStep; -- } -- -- // read row(s) from image -- n = (yp > 0) ? yStep : lastYStep; -- if (n > 0) { -- p = colorBuf; -- q = alphaBuf; -- for (i = 0; i < n; ++i) { -- (*src)(srcData, p, q); -- p += w * nComps; -- q += w; -- } -- } -- lastYStep = yStep; -- -- // loop-invariant constants -- k1 = splashRound(xShear * ySign * y); -- -- // clipping test -- if (clipRes != splashClipAllInside && -- !rot && -- (int)(yShear * k1) == -- (int)(yShear * (xSign * (scaledWidth - 1) + k1))) { -- if (xSign > 0) { -- spanXMin = tx + k1; -- spanXMax = spanXMin + (scaledWidth - 1); -- } else { -- spanXMax = tx + k1; -- spanXMin = spanXMax - (scaledWidth - 1); -+ } -+ -+ // read rows from image -+ memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); -+ if (srcAlpha) { -+ memset(alphaPixBuf, 0, srcWidth * sizeof(int)); -+ } -+ for (i = 0; i < yStep; ++i) { -+ (*src)(srcData, lineBuf, alphaLineBuf); -+ for (j = 0; j < srcWidth * nComps; ++j) { -+ pixBuf[j] += lineBuf[j]; -+ } -+ if (srcAlpha) { -+ for (j = 0; j < srcWidth; ++j) { -+ alphaPixBuf[j] += alphaLineBuf[j]; - } -- spanY = ty + ySign * y + (int)(yShear * k1); -- clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY); -- if (clipRes2 == splashClipAllOutside) { -- continue; -+ } -+ } -+ -+ // init x scale Bresenham -+ xt = 0; -+ d0 = (1 << 23) / (yStep * xp); -+ d1 = (1 << 23) / (yStep * (xp + 1)); -+ -+ xx = xxa = 0; -+ for (x = 0; x < scaledWidth; ++x) { -+ -+ // x scale Bresenham -+ if ((xt += xq) >= scaledWidth) { -+ xt -= scaledWidth; -+ xStep = xp + 1; -+ d = d1; -+ } else { -+ xStep = xp; -+ d = d0; -+ } -+ -+ switch (srcMode) { -+ -+ case splashModeMono8: -+ -+ // compute the final pixel -+ pix0 = 0; -+ for (i = 0; i < xStep; ++i) { -+ pix0 += pixBuf[xx++]; -+ } -+ // pix / xStep * yStep -+ pix0 = (pix0 * d) >> 23; -+ -+ // store the pixel -+ *destPtr++ = (Guchar)pix0; -+ break; -+ -+ case splashModeRGB8: -+ -+ // compute the final pixel -+ pix0 = pix1 = pix2 = 0; -+ for (i = 0; i < xStep; ++i) { -+ pix0 += pixBuf[xx]; -+ pix1 += pixBuf[xx+1]; -+ pix2 += pixBuf[xx+2]; -+ xx += 3; -+ } -+ // pix / xStep * yStep -+ pix0 = (pix0 * d) >> 23; -+ pix1 = (pix1 * d) >> 23; -+ pix2 = (pix2 * d) >> 23; -+ -+ // store the pixel -+ *destPtr++ = (Guchar)pix0; -+ *destPtr++ = (Guchar)pix1; -+ *destPtr++ = (Guchar)pix2; -+ break; -+ -+ case splashModeBGR8: -+ -+ // compute the final pixel -+ pix0 = pix1 = pix2 = 0; -+ for (i = 0; i < xStep; ++i) { -+ pix0 += pixBuf[xx]; -+ pix1 += pixBuf[xx+1]; -+ pix2 += pixBuf[xx+2]; -+ xx += 3; -+ } -+ // pix / xStep * yStep -+ pix0 = (pix0 * d) >> 23; -+ pix1 = (pix1 * d) >> 23; -+ pix2 = (pix2 * d) >> 23; -+ -+ // store the pixel -+ *destPtr++ = (Guchar)pix2; -+ *destPtr++ = (Guchar)pix1; -+ *destPtr++ = (Guchar)pix0; -+ break; -+ -+#if SPLASH_CMYK -+ case splashModeCMYK8: -+ -+ // compute the final pixel -+ pix0 = pix1 = pix2 = pix3 = 0; -+ for (i = 0; i < xStep; ++i) { -+ pix0 += pixBuf[xx]; -+ pix1 += pixBuf[xx+1]; -+ pix2 += pixBuf[xx+2]; -+ pix3 += pixBuf[xx+3]; -+ xx += 4; -+ } -+ // pix / xStep * yStep -+ pix0 = (pix0 * d) >> 23; -+ pix1 = (pix1 * d) >> 23; -+ pix2 = (pix2 * d) >> 23; -+ pix3 = (pix3 * d) >> 23; -+ -+ // store the pixel -+ *destPtr++ = (Guchar)pix0; -+ *destPtr++ = (Guchar)pix1; -+ *destPtr++ = (Guchar)pix2; -+ *destPtr++ = (Guchar)pix3; -+ break; -+#endif -+ -+ -+ case splashModeMono1: // mono1 is not allowed -+ default: -+ break; -+ } -+ -+ // process alpha -+ if (srcAlpha) { -+ alpha = 0; -+ for (i = 0; i < xStep; ++i, ++xxa) { -+ alpha += alphaPixBuf[xxa]; -+ } -+ // alpha / xStep * yStep -+ alpha = (alpha * d) >> 23; -+ *destAlphaPtr++ = (Guchar)alpha; -+ } -+ } -+ } -+ -+ gfree(alphaPixBuf); -+ gfree(alphaLineBuf); -+ gfree(pixBuf); -+ gfree(lineBuf); -+} -+ -+void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, -+ SplashColorMode srcMode, int nComps, -+ GBool srcAlpha, int srcWidth, int srcHeight, -+ int scaledWidth, int scaledHeight, -+ SplashBitmap *dest) { -+ Guchar *lineBuf, *alphaLineBuf; -+ Guint *pixBuf, *alphaPixBuf; -+ Guint pix[splashMaxColorComps]; -+ Guint alpha; -+ Guchar *destPtr, *destAlphaPtr; -+ int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; -+ int i, j; -+ -+ // Bresenham parameters for y scale -+ yp = srcHeight / scaledHeight; -+ yq = srcHeight % scaledHeight; -+ -+ // Bresenham parameters for x scale -+ xp = scaledWidth / srcWidth; -+ xq = scaledWidth % srcWidth; -+ -+ // allocate buffers -+ lineBuf = (Guchar *)gmallocn(srcWidth, nComps); -+ pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int)); -+ if (srcAlpha) { -+ alphaLineBuf = (Guchar *)gmalloc(srcWidth); -+ alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); -+ } else { -+ alphaLineBuf = NULL; -+ alphaPixBuf = NULL; -+ } -+ -+ // init y scale Bresenham -+ yt = 0; -+ -+ destPtr = dest->data; -+ destAlphaPtr = dest->alpha; -+ for (y = 0; y < scaledHeight; ++y) { -+ -+ // y scale Bresenham -+ if ((yt += yq) >= scaledHeight) { -+ yt -= scaledHeight; -+ yStep = yp + 1; -+ } else { -+ yStep = yp; -+ } -+ -+ // read rows from image -+ memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); -+ if (srcAlpha) { -+ memset(alphaPixBuf, 0, srcWidth * sizeof(int)); -+ } -+ for (i = 0; i < yStep; ++i) { -+ (*src)(srcData, lineBuf, alphaLineBuf); -+ for (j = 0; j < srcWidth * nComps; ++j) { -+ pixBuf[j] += lineBuf[j]; -+ } -+ if (srcAlpha) { -+ for (j = 0; j < srcWidth; ++j) { -+ alphaPixBuf[j] += alphaLineBuf[j]; - } -+ } -+ } -+ -+ // init x scale Bresenham -+ xt = 0; -+ d = (1 << 23) / yStep; -+ -+ for (x = 0; x < srcWidth; ++x) { -+ -+ // x scale Bresenham -+ if ((xt += xq) >= srcWidth) { -+ xt -= srcWidth; -+ xStep = xp + 1; - } else { -- clipRes2 = clipRes; -+ xStep = xp; -+ } -+ -+ // compute the final pixel -+ for (i = 0; i < nComps; ++i) { -+ // pixBuf[] / yStep -+ pix[i] = (pixBuf[x * nComps + i] * d) >> 23; -+ } -+ -+ // store the pixel -+ switch (srcMode) { -+ case splashModeMono1: // mono1 is not allowed -+ break; -+ case splashModeMono8: -+ for (i = 0; i < xStep; ++i) { -+ *destPtr++ = (Guchar)pix[0]; -+ } -+ break; -+ case splashModeRGB8: -+ for (i = 0; i < xStep; ++i) { -+ *destPtr++ = (Guchar)pix[0]; -+ *destPtr++ = (Guchar)pix[1]; -+ *destPtr++ = (Guchar)pix[2]; -+ } -+ break; -+ case splashModeBGR8: -+ for (i = 0; i < xStep; ++i) { -+ *destPtr++ = (Guchar)pix[2]; -+ *destPtr++ = (Guchar)pix[1]; -+ *destPtr++ = (Guchar)pix[0]; -+ } -+ break; -+#if SPLASH_CMYK -+ case splashModeCMYK8: -+ for (i = 0; i < xStep; ++i) { -+ *destPtr++ = (Guchar)pix[0]; -+ *destPtr++ = (Guchar)pix[1]; -+ *destPtr++ = (Guchar)pix[2]; -+ *destPtr++ = (Guchar)pix[3]; -+ } -+ break; -+#endif -+ } -+ -+ // process alpha -+ if (srcAlpha) { -+ // alphaPixBuf[] / yStep -+ alpha = (alphaPixBuf[x] * d) >> 23; -+ for (i = 0; i < xStep; ++i) { -+ *destAlphaPtr++ = (Guchar)alpha; -+ } - } -+ } -+ } - -- // init x scale Bresenham -- xt = 0; -- xSrc = 0; -- -- // x shear -- x1 = k1; -- -- // y shear -- y1 = (SplashCoord)ySign * y + yShear * x1; -- // this is a kludge: if yShear1 is negative, then (int)y1 would -- // change immediately after the first pixel, which is not what -- // we want -- if (yShear1 < 0) { -- y1 += 0.999; -- } -+ gfree(alphaPixBuf); -+ gfree(alphaLineBuf); -+ gfree(pixBuf); -+ gfree(lineBuf); -+} - -- // loop-invariant constants -- n = yStep > 0 ? yStep : 1; -+void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, -+ SplashColorMode srcMode, int nComps, -+ GBool srcAlpha, int srcWidth, int srcHeight, -+ int scaledWidth, int scaledHeight, -+ SplashBitmap *dest) { -+ Guchar *lineBuf, *alphaLineBuf; -+ Guint pix[splashMaxColorComps]; -+ Guint alpha; -+ Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; -+ int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; -+ int i, j; -+ -+ // Bresenham parameters for y scale -+ yp = scaledHeight / srcHeight; -+ yq = scaledHeight % srcHeight; -+ -+ // Bresenham parameters for x scale -+ xp = srcWidth / scaledWidth; -+ xq = srcWidth % scaledWidth; - -- switch (srcMode) { -+ // allocate buffers -+ lineBuf = (Guchar *)gmallocn(srcWidth, nComps); -+ if (srcAlpha) { -+ alphaLineBuf = (Guchar *)gmalloc(srcWidth); -+ } else { -+ alphaLineBuf = NULL; -+ } - -- case splashModeMono1: -- case splashModeMono8: -- for (x = 0; x < scaledWidth; ++x) { -+ // init y scale Bresenham -+ yt = 0; - -- // x scale Bresenham -- xStep = xp; -- xt += xq; -- if (xt >= scaledWidth) { -- xt -= scaledWidth; -- ++xStep; -- } -+ destPtr0 = dest->data; -+ destAlphaPtr0 = dest->alpha; -+ for (y = 0; y < srcHeight; ++y) { - -- // rotation -- if (rot) { -- x2 = (int)y1; -- y2 = -x1; -- } else { -- x2 = x1; -- y2 = (int)y1; -- } -+ // y scale Bresenham -+ if ((yt += yq) >= srcHeight) { -+ yt -= srcHeight; -+ yStep = yp + 1; -+ } else { -+ yStep = yp; -+ } - -- // compute the filtered pixel at (x,y) after the x and y scaling -- // operations -- m = xStep > 0 ? xStep : 1; -- alphaAcc = 0; -- p = colorBuf + xSrc; -- q = alphaBuf + xSrc; -- pixAcc0 = 0; -- for (i = 0; i < n; ++i) { -- for (j = 0; j < m; ++j) { -- pixAcc0 += *p++; -- alphaAcc += *q++; -- } -- p += w - m; -- q += w - m; -- } -- pixMul = (SplashCoord)1 / (SplashCoord)(n * m); -- alphaMul = pixMul * (1.0 / 255.0); -- alpha = (SplashCoord)alphaAcc * alphaMul; -+ // read row from image -+ (*src)(srcData, lineBuf, alphaLineBuf); - -- if (alpha > 0) { -- pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); -+ // init x scale Bresenham -+ xt = 0; -+ d0 = (1 << 23) / xp; -+ d1 = (1 << 23) / (xp + 1); - -- // set pixel -- pipe.shape = alpha; -- if (vectorAntialias && clipRes != splashClipAllInside) { -- drawAAPixel(&pipe, tx + x2, ty + y2); -- } else { -- drawPixel(&pipe, tx + x2, ty + y2, -- clipRes2 == splashClipAllInside); -- } -- } -+ xx = xxa = 0; -+ for (x = 0; x < scaledWidth; ++x) { - -- // x scale Bresenham -- xSrc += xStep; -+ // x scale Bresenham -+ if ((xt += xq) >= scaledWidth) { -+ xt -= scaledWidth; -+ xStep = xp + 1; -+ d = d1; -+ } else { -+ xStep = xp; -+ d = d0; -+ } - -- // x shear -- x1 += xSign; -+ // compute the final pixel -+ for (i = 0; i < nComps; ++i) { -+ pix[i] = 0; -+ } -+ for (i = 0; i < xStep; ++i) { -+ for (j = 0; j < nComps; ++j, ++xx) { -+ pix[j] += lineBuf[xx]; -+ } -+ } -+ for (i = 0; i < nComps; ++i) { -+ // pix[] / xStep -+ pix[i] = (pix[i] * d) >> 23; -+ } - -- // y shear -- y1 += yShear1; -+ // store the pixel -+ switch (srcMode) { -+ case splashModeMono1: // mono1 is not allowed -+ break; -+ case splashModeMono8: -+ for (i = 0; i < yStep; ++i) { -+ destPtr = destPtr0 + (i * scaledWidth + x) * nComps; -+ *destPtr++ = (Guchar)pix[0]; - } - break; -- - case splashModeRGB8: -+ for (i = 0; i < yStep; ++i) { -+ destPtr = destPtr0 + (i * scaledWidth + x) * nComps; -+ *destPtr++ = (Guchar)pix[0]; -+ *destPtr++ = (Guchar)pix[1]; -+ *destPtr++ = (Guchar)pix[2]; -+ } -+ break; - case splashModeBGR8: -- for (x = 0; x < scaledWidth; ++x) { -- -- // x scale Bresenham -- xStep = xp; -- xt += xq; -- if (xt >= scaledWidth) { -- xt -= scaledWidth; -- ++xStep; -- } -- -- // rotation -- if (rot) { -- x2 = (int)y1; -- y2 = -x1; -- } else { -- x2 = x1; -- y2 = (int)y1; -- } -- -- // compute the filtered pixel at (x,y) after the x and y scaling -- // operations -- m = xStep > 0 ? xStep : 1; -- alphaAcc = 0; -- p = colorBuf + xSrc * 3; -- q = alphaBuf + xSrc; -- pixAcc0 = pixAcc1 = pixAcc2 = 0; -- for (i = 0; i < n; ++i) { -- for (j = 0; j < m; ++j) { -- pixAcc0 += *p++; -- pixAcc1 += *p++; -- pixAcc2 += *p++; -- alphaAcc += *q++; -- } -- p += 3 * (w - m); -- q += w - m; -- } -- pixMul = (SplashCoord)1 / (SplashCoord)(n * m); -- alphaMul = pixMul * (1.0 / 255.0); -- alpha = (SplashCoord)alphaAcc * alphaMul; -- -- if (alpha > 0) { -- pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); -- pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); -- pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); -- -- // set pixel -- pipe.shape = alpha; -- if (vectorAntialias && clipRes != splashClipAllInside) { -- drawAAPixel(&pipe, tx + x2, ty + y2); -- } else { -- drawPixel(&pipe, tx + x2, ty + y2, -- clipRes2 == splashClipAllInside); -- } -- } -- -- // x scale Bresenham -- xSrc += xStep; -- -- // x shear -- x1 += xSign; -- -- // y shear -- y1 += yShear1; -+ for (i = 0; i < yStep; ++i) { -+ destPtr = destPtr0 + (i * scaledWidth + x) * nComps; -+ *destPtr++ = (Guchar)pix[2]; -+ *destPtr++ = (Guchar)pix[1]; -+ *destPtr++ = (Guchar)pix[0]; - } - break; -- - #if SPLASH_CMYK - case splashModeCMYK8: -- for (x = 0; x < scaledWidth; ++x) { -+ for (i = 0; i < yStep; ++i) { -+ destPtr = destPtr0 + (i * scaledWidth + x) * nComps; -+ *destPtr++ = (Guchar)pix[0]; -+ *destPtr++ = (Guchar)pix[1]; -+ *destPtr++ = (Guchar)pix[2]; -+ *destPtr++ = (Guchar)pix[3]; -+ } -+ break; -+#endif -+ } - -- // x scale Bresenham -- xStep = xp; -- xt += xq; -- if (xt >= scaledWidth) { -- xt -= scaledWidth; -- ++xStep; -- } -+ // process alpha -+ if (srcAlpha) { -+ alpha = 0; -+ for (i = 0; i < xStep; ++i, ++xxa) { -+ alpha += alphaLineBuf[xxa]; -+ } -+ // alpha / xStep -+ alpha = (alpha * d) >> 23; -+ for (i = 0; i < yStep; ++i) { -+ destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x; -+ *destAlphaPtr = (Guchar)alpha; -+ } -+ } -+ } - -- // rotation -- if (rot) { -- x2 = (int)y1; -- y2 = -x1; -- } else { -- x2 = x1; -- y2 = (int)y1; -- } -+ destPtr0 += yStep * scaledWidth * nComps; -+ if (srcAlpha) { -+ destAlphaPtr0 += yStep * scaledWidth; -+ } -+ } - -- // compute the filtered pixel at (x,y) after the x and y scaling -- // operations -- m = xStep > 0 ? xStep : 1; -- alphaAcc = 0; -- p = colorBuf + xSrc * 4; -- q = alphaBuf + xSrc; -- pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0; -- for (i = 0; i < n; ++i) { -- for (j = 0; j < m; ++j) { -- pixAcc0 += *p++; -- pixAcc1 += *p++; -- pixAcc2 += *p++; -- pixAcc3 += *p++; -- alphaAcc += *q++; -- } -- p += 4 * (w - m); -- q += w - m; -- } -- pixMul = (SplashCoord)1 / (SplashCoord)(n * m); -- alphaMul = pixMul * (1.0 / 255.0); -- alpha = (SplashCoord)alphaAcc * alphaMul; -- -- if (alpha > 0) { -- pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); -- pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); -- pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); -- pix[3] = (int)((SplashCoord)pixAcc3 * pixMul); -+ gfree(alphaLineBuf); -+ gfree(lineBuf); -+} -+ -+void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, -+ SplashColorMode srcMode, int nComps, -+ GBool srcAlpha, int srcWidth, int srcHeight, -+ int scaledWidth, int scaledHeight, -+ SplashBitmap *dest) { -+ Guchar *lineBuf, *alphaLineBuf; -+ Guint pix[splashMaxColorComps]; -+ Guint alpha; -+ Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; -+ int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx; -+ int i, j; -+ -+ // Bresenham parameters for y scale -+ yp = scaledHeight / srcHeight; -+ yq = scaledHeight % srcHeight; -+ -+ // Bresenham parameters for x scale -+ xp = scaledWidth / srcWidth; -+ xq = scaledWidth % srcWidth; - -- // set pixel -- pipe.shape = alpha; -- if (vectorAntialias && clipRes != splashClipAllInside) { -- drawAAPixel(&pipe, tx + x2, ty + y2); -- } else { -- drawPixel(&pipe, tx + x2, ty + y2, -- clipRes2 == splashClipAllInside); -- } -- } -+ // allocate buffers -+ lineBuf = (Guchar *)gmallocn(srcWidth, nComps); -+ if (srcAlpha) { -+ alphaLineBuf = (Guchar *)gmalloc(srcWidth); -+ } else { -+ alphaLineBuf = NULL; -+ } - -- // x scale Bresenham -- xSrc += xStep; -+ // init y scale Bresenham -+ yt = 0; - -- // x shear -- x1 += xSign; -+ destPtr0 = dest->data; -+ destAlphaPtr0 = dest->alpha; -+ for (y = 0; y < srcHeight; ++y) { - -- // y shear -- y1 += yShear1; -- } -- break; --#endif // SPLASH_CMYK -- } -+ // y scale Bresenham -+ if ((yt += yq) >= srcHeight) { -+ yt -= srcHeight; -+ yStep = yp + 1; -+ } else { -+ yStep = yp; - } - -- } else { -+ // read row from image -+ (*src)(srcData, lineBuf, alphaLineBuf); - -- // init y scale Bresenham -- yt = 0; -- lastYStep = 1; -+ // init x scale Bresenham -+ xt = 0; - -- for (y = 0; y < scaledHeight; ++y) { -+ xx = 0; -+ for (x = 0; x < srcWidth; ++x) { - -- // y scale Bresenham -- yStep = yp; -- yt += yq; -- if (yt >= scaledHeight) { -- yt -= scaledHeight; -- ++yStep; -- } -- -- // read row(s) from image -- n = (yp > 0) ? yStep : lastYStep; -- if (n > 0) { -- p = colorBuf; -- for (i = 0; i < n; ++i) { -- (*src)(srcData, p, NULL); -- p += w * nComps; -- } -- } -- lastYStep = yStep; -- -- // loop-invariant constants -- k1 = splashRound(xShear * ySign * y); -- -- // clipping test -- if (clipRes != splashClipAllInside && -- !rot && -- (int)(yShear * k1) == -- (int)(yShear * (xSign * (scaledWidth - 1) + k1))) { -- if (xSign > 0) { -- spanXMin = tx + k1; -- spanXMax = spanXMin + (scaledWidth - 1); -- } else { -- spanXMax = tx + k1; -- spanXMin = spanXMax - (scaledWidth - 1); -- } -- spanY = ty + ySign * y + (int)(yShear * k1); -- clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY); -- if (clipRes2 == splashClipAllOutside) { -- continue; -- } -+ // x scale Bresenham -+ if ((xt += xq) >= srcWidth) { -+ xt -= srcWidth; -+ xStep = xp + 1; - } else { -- clipRes2 = clipRes; -+ xStep = xp; - } - -- // init x scale Bresenham -- xt = 0; -- xSrc = 0; -- -- // x shear -- x1 = k1; -- -- // y shear -- y1 = (SplashCoord)ySign * y + yShear * x1; -- // this is a kludge: if yShear1 is negative, then (int)y1 would -- // change immediately after the first pixel, which is not what -- // we want -- if (yShear1 < 0) { -- y1 += 0.999; -+ // compute the final pixel -+ for (i = 0; i < nComps; ++i) { -+ pix[i] = lineBuf[x * nComps + i]; - } - -- // loop-invariant constants -- n = yStep > 0 ? yStep : 1; -- -+ // store the pixel - switch (srcMode) { -- -- case splashModeMono1: -+ case splashModeMono1: // mono1 is not allowed -+ break; - case splashModeMono8: -- for (x = 0; x < scaledWidth; ++x) { -- -- // x scale Bresenham -- xStep = xp; -- xt += xq; -- if (xt >= scaledWidth) { -- xt -= scaledWidth; -- ++xStep; -+ for (i = 0; i < yStep; ++i) { -+ for (j = 0; j < xStep; ++j) { -+ destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; -+ *destPtr++ = (Guchar)pix[0]; - } -- -- // rotation -- if (rot) { -- x2 = (int)y1; -- y2 = -x1; -- } else { -- x2 = x1; -- y2 = (int)y1; -+ } -+ break; -+ case splashModeRGB8: -+ for (i = 0; i < yStep; ++i) { -+ for (j = 0; j < xStep; ++j) { -+ destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; -+ *destPtr++ = (Guchar)pix[0]; -+ *destPtr++ = (Guchar)pix[1]; -+ *destPtr++ = (Guchar)pix[2]; - } -- -- // compute the filtered pixel at (x,y) after the x and y scaling -- // operations -- m = xStep > 0 ? xStep : 1; -- p = colorBuf + xSrc; -- pixAcc0 = 0; -- for (i = 0; i < n; ++i) { -- for (j = 0; j < m; ++j) { -- pixAcc0 += *p++; -- } -- p += w - m; -+ } -+ break; -+ case splashModeBGR8: -+ for (i = 0; i < yStep; ++i) { -+ for (j = 0; j < xStep; ++j) { -+ destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; -+ *destPtr++ = (Guchar)pix[2]; -+ *destPtr++ = (Guchar)pix[1]; -+ *destPtr++ = (Guchar)pix[0]; - } -- pixMul = (SplashCoord)1 / (SplashCoord)(n * m); -- -- pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); -- -- // set pixel -- if (vectorAntialias && clipRes != splashClipAllInside) { -- pipe.shape = (SplashCoord)1; -- drawAAPixel(&pipe, tx + x2, ty + y2); -- } else { -- drawPixel(&pipe, tx + x2, ty + y2, -- clipRes2 == splashClipAllInside); -+ } -+ break; -+#if SPLASH_CMYK -+ case splashModeCMYK8: -+ for (i = 0; i < yStep; ++i) { -+ for (j = 0; j < xStep; ++j) { -+ destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; -+ *destPtr++ = (Guchar)pix[0]; -+ *destPtr++ = (Guchar)pix[1]; -+ *destPtr++ = (Guchar)pix[2]; -+ *destPtr++ = (Guchar)pix[3]; - } -- -- // x scale Bresenham -- xSrc += xStep; -- -- // x shear -- x1 += xSign; -- -- // y shear -- y1 += yShear1; - } - break; -+#endif -+ } - -- case splashModeRGB8: -- case splashModeBGR8: -- for (x = 0; x < scaledWidth; ++x) { -- -- // x scale Bresenham -- xStep = xp; -- xt += xq; -- if (xt >= scaledWidth) { -- xt -= scaledWidth; -- ++xStep; -+ // process alpha -+ if (srcAlpha) { -+ alpha = alphaLineBuf[x]; -+ for (i = 0; i < yStep; ++i) { -+ for (j = 0; j < xStep; ++j) { -+ destAlphaPtr = destAlphaPtr0 + i * scaledWidth + xx + j; -+ *destAlphaPtr = (Guchar)alpha; - } -+ } -+ } - -- // rotation -- if (rot) { -- x2 = (int)y1; -- y2 = -x1; -- } else { -- x2 = x1; -- y2 = (int)y1; -- } -+ xx += xStep; -+ } - -- // compute the filtered pixel at (x,y) after the x and y scaling -- // operations -- m = xStep > 0 ? xStep : 1; -- p = colorBuf + xSrc * 3; -- pixAcc0 = pixAcc1 = pixAcc2 = 0; -- for (i = 0; i < n; ++i) { -- for (j = 0; j < m; ++j) { -- pixAcc0 += *p++; -- pixAcc1 += *p++; -- pixAcc2 += *p++; -- } -- p += 3 * (w - m); -- } -- pixMul = (SplashCoord)1 / (SplashCoord)(n * m); -+ destPtr0 += yStep * scaledWidth * nComps; -+ if (srcAlpha) { -+ destAlphaPtr0 += yStep * scaledWidth; -+ } -+ } - -- pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); -- pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); -- pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); -- -- // set pixel -- if (vectorAntialias && clipRes != splashClipAllInside) { -- pipe.shape = (SplashCoord)1; -- drawAAPixel(&pipe, tx + x2, ty + y2); -- } else { -- drawPixel(&pipe, tx + x2, ty + y2, -- clipRes2 == splashClipAllInside); -- } -+ gfree(alphaLineBuf); -+ gfree(lineBuf); -+} -+ -+void Splash::vertFlipImage(SplashBitmap *img, int width, int height, -+ int nComps) { -+ Guchar *lineBuf; -+ Guchar *p0, *p1; -+ int w; -+ -+ w = width * nComps; -+ lineBuf = (Guchar *)gmalloc(w); -+ for (p0 = img->data, p1 = img->data + (height - 1) * w; -+ p0 < p1; -+ p0 += w, p1 -= w) { -+ memcpy(lineBuf, p0, w); -+ memcpy(p0, p1, w); -+ memcpy(p1, lineBuf, w); -+ } -+ if (img->alpha) { -+ for (p0 = img->alpha, p1 = img->alpha + (height - 1) * width; -+ p0 < p1; -+ p0 += width, p1 -= width) { -+ memcpy(lineBuf, p0, width); -+ memcpy(p0, p1, width); -+ memcpy(p1, lineBuf, width); -+ } -+ } -+ gfree(lineBuf); -+} - -- // x scale Bresenham -- xSrc += xStep; -+void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, -+ SplashClipResult clipRes) { -+ SplashPipe pipe; -+ SplashColor pixel; -+ Guchar *ap; -+ int w, h, x0, y0, x1, y1, x, y; - -- // x shear -- x1 += xSign; -+ // split the image into clipped and unclipped regions -+ w = src->getWidth(); -+ h = src->getHeight(); -+ if (clipRes == splashClipAllInside) { -+ x0 = 0; -+ y0 = 0; -+ x1 = w; -+ y1 = h; -+ } else { -+ if (state->clip->getNumPaths()) { -+ x0 = x1 = w; -+ y0 = y1 = h; -+ } else { -+ if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) { -+ x0 = 0; -+ } -+ if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) { -+ y0 = 0; -+ } -+ if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) { -+ x1 = w; -+ } -+ if (x1 < x0) { -+ x1 = x0; -+ } -+ if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) { -+ y1 = h; -+ } -+ if (y1 < y0) { -+ y1 = y0; -+ } -+ } -+ } - -- // y shear -- y1 += yShear1; -+ // draw the unclipped region -+ if (x0 < w && y0 < h && x0 < x1 && y0 < y1) { -+ pipeInit(&pipe, xDest + x0, yDest + y0, NULL, pixel, -+ (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse); -+ if (srcAlpha) { -+ for (y = y0; y < y1; ++y) { -+ pipeSetXY(&pipe, xDest + x0, yDest + y); -+ ap = src->getAlphaPtr() + y * w + x0; -+ for (x = x0; x < x1; ++x) { -+ src->getPixel(x, y, pixel); -+ pipe.shape = *ap++; -+ (this->*pipe.run)(&pipe); - } -- break; -+ } -+ } else { -+ for (y = y0; y < y1; ++y) { -+ pipeSetXY(&pipe, xDest + x0, yDest + y); -+ for (x = x0; x < x1; ++x) { -+ src->getPixel(x, y, pixel); -+ (this->*pipe.run)(&pipe); -+ } -+ } -+ } -+ updateModX(xDest + x0); -+ updateModX(xDest + x1 - 1); -+ updateModY(yDest + y0); -+ updateModY(yDest + y1 - 1); -+ } - --#if SPLASH_CMYK -- case splashModeCMYK8: -- for (x = 0; x < scaledWidth; ++x) { -+ // draw the clipped regions -+ if (y0 > 0) { -+ blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0); -+ } -+ if (y1 < h) { -+ blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1); -+ } -+ if (x0 > 0 && y0 < y1) { -+ blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0); -+ } -+ if (x1 < w && y0 < y1) { -+ blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0, -+ w - x1, y1 - y0); -+ } -+} - -- // x scale Bresenham -- xStep = xp; -- xt += xq; -- if (xt >= scaledWidth) { -- xt -= scaledWidth; -- ++xStep; -- } -+void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha, -+ int xSrc, int ySrc, int xDest, int yDest, -+ int w, int h) { -+ SplashPipe pipe; -+ SplashColor pixel; -+ Guchar *ap; -+ int x, y; - -- // rotation -- if (rot) { -- x2 = (int)y1; -- y2 = -x1; -+ if (vectorAntialias) { -+ pipeInit(&pipe, xDest, yDest, NULL, pixel, -+ (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); -+ drawAAPixelInit(); -+ if (srcAlpha) { -+ for (y = 0; y < h; ++y) { -+ ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; -+ for (x = 0; x < w; ++x) { -+ src->getPixel(xSrc + x, ySrc + y, pixel); -+ pipe.shape = *ap++; -+ drawAAPixel(&pipe, xDest + x, yDest + y); -+ } -+ } -+ } else { -+ for (y = 0; y < h; ++y) { -+ for (x = 0; x < w; ++x) { -+ src->getPixel(xSrc + x, ySrc + y, pixel); -+ pipe.shape = 255; -+ drawAAPixel(&pipe, xDest + x, yDest + y); -+ } -+ } -+ } -+ } else { -+ pipeInit(&pipe, xDest, yDest, NULL, pixel, -+ (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse); -+ if (srcAlpha) { -+ for (y = 0; y < h; ++y) { -+ ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; -+ pipeSetXY(&pipe, xDest, yDest + y); -+ for (x = 0; x < w; ++x) { -+ if (state->clip->test(xDest + x, yDest + y)) { -+ src->getPixel(xSrc + x, ySrc + y, pixel); -+ pipe.shape = *ap++; -+ (this->*pipe.run)(&pipe); -+ updateModX(xDest + x); -+ updateModY(yDest + y); - } else { -- x2 = x1; -- y2 = (int)y1; -- } -- -- // compute the filtered pixel at (x,y) after the x and y scaling -- // operations -- m = xStep > 0 ? xStep : 1; -- p = colorBuf + xSrc * 4; -- pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0; -- for (i = 0; i < n; ++i) { -- for (j = 0; j < m; ++j) { -- pixAcc0 += *p++; -- pixAcc1 += *p++; -- pixAcc2 += *p++; -- pixAcc3 += *p++; -- } -- p += 4 * (w - m); -+ pipeIncX(&pipe); -+ ++ap; - } -- pixMul = (SplashCoord)1 / (SplashCoord)(n * m); -- -- pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); -- pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); -- pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); -- pix[3] = (int)((SplashCoord)pixAcc3 * pixMul); -- -- // set pixel -- if (vectorAntialias && clipRes != splashClipAllInside) { -- pipe.shape = (SplashCoord)1; -- drawAAPixel(&pipe, tx + x2, ty + y2); -+ } -+ } -+ } else { -+ for (y = 0; y < h; ++y) { -+ pipeSetXY(&pipe, xDest, yDest + y); -+ for (x = 0; x < w; ++x) { -+ if (state->clip->test(xDest + x, yDest + y)) { -+ src->getPixel(xSrc + x, ySrc + y, pixel); -+ (this->*pipe.run)(&pipe); -+ updateModX(xDest + x); -+ updateModY(yDest + y); - } else { -- drawPixel(&pipe, tx + x2, ty + y2, -- clipRes2 == splashClipAllInside); -+ pipeIncX(&pipe); - } -- -- // x scale Bresenham -- xSrc += xStep; -- -- // x shear -- x1 += xSign; -- -- // y shear -- y1 += yShear1; - } -- break; --#endif // SPLASH_CMYK - } - } -- - } -- -- gfree(colorBuf); -- gfree(alphaBuf); -- -- return splashOk; - } - - SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, -@@ -2782,39 +4335,72 @@ - } - - if (src->alpha) { -- pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha, -- gTrue, nonIsolated); -- for (y = 0; y < h; ++y) { -- pipeSetXY(&pipe, xDest, yDest + y); -- ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; -- for (x = 0; x < w; ++x) { -- src->getPixel(xSrc + x, ySrc + y, pixel); -- alpha = *ap++; -- if (noClip || state->clip->test(xDest + x, yDest + y)) { -+ pipeInit(&pipe, xDest, yDest, NULL, pixel, -+ (Guchar)splashRound(state->fillAlpha * 255), gTrue, nonIsolated); -+ if (noClip) { -+ for (y = 0; y < h; ++y) { -+ pipeSetXY(&pipe, xDest, yDest + y); -+ ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; -+ for (x = 0; x < w; ++x) { -+ src->getPixel(xSrc + x, ySrc + y, pixel); -+ alpha = *ap++; - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same -- pipe.shape = (SplashCoord)(alpha / 255.0); -- pipeRun(&pipe); -- updateModX(xDest + x); -- updateModY(yDest + y); -- } else { -- pipeIncX(&pipe); -+ pipe.shape = alpha; -+ (this->*pipe.run)(&pipe); -+ } -+ } -+ updateModX(xDest); -+ updateModX(xDest + w - 1); -+ updateModY(yDest); -+ updateModY(yDest + h - 1); -+ } else { -+ for (y = 0; y < h; ++y) { -+ pipeSetXY(&pipe, xDest, yDest + y); -+ ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; -+ for (x = 0; x < w; ++x) { -+ src->getPixel(xSrc + x, ySrc + y, pixel); -+ alpha = *ap++; -+ if (state->clip->test(xDest + x, yDest + y)) { -+ // this uses shape instead of alpha, which isn't technically -+ // correct, but works out the same -+ pipe.shape = alpha; -+ (this->*pipe.run)(&pipe); -+ updateModX(xDest + x); -+ updateModY(yDest + y); -+ } else { -+ pipeIncX(&pipe); -+ } - } - } - } - } else { -- pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha, -- gFalse, nonIsolated); -- for (y = 0; y < h; ++y) { -- pipeSetXY(&pipe, xDest, yDest + y); -- for (x = 0; x < w; ++x) { -- src->getPixel(xSrc + x, ySrc + y, pixel); -- if (noClip || state->clip->test(xDest + x, yDest + y)) { -- pipeRun(&pipe); -- updateModX(xDest + x); -- updateModY(yDest + y); -- } else { -- pipeIncX(&pipe); -+ pipeInit(&pipe, xDest, yDest, NULL, pixel, -+ (Guchar)splashRound(state->fillAlpha * 255), gFalse, nonIsolated); -+ if (noClip) { -+ for (y = 0; y < h; ++y) { -+ pipeSetXY(&pipe, xDest, yDest + y); -+ for (x = 0; x < w; ++x) { -+ src->getPixel(xSrc + x, ySrc + y, pixel); -+ (this->*pipe.run)(&pipe); -+ } -+ } -+ updateModX(xDest); -+ updateModX(xDest + w - 1); -+ updateModY(yDest); -+ updateModY(yDest + h - 1); -+ } else { -+ for (y = 0; y < h; ++y) { -+ pipeSetXY(&pipe, xDest, yDest + y); -+ for (x = 0; x < w; ++x) { -+ src->getPixel(xSrc + x, ySrc + y, pixel); -+ if (state->clip->test(xDest + x, yDest + y)) { -+ (this->*pipe.run)(&pipe); -+ updateModX(xDest + x); -+ updateModY(yDest + y); -+ } else { -+ pipeIncX(&pipe); -+ } - } - } - } -@@ -2826,7 +4412,10 @@ - void Splash::compositeBackground(SplashColorPtr color) { - SplashColorPtr p; - Guchar *q; -- Guchar alpha, alpha1, c, color0, color1, color2, color3; -+ Guchar alpha, alpha1, c, color0, color1, color2; -+#if SPLASH_CMYK -+ Guchar color3; -+#endif - int x, y, mask; - - switch (bitmap->mode) { -@@ -2911,10 +4500,8 @@ - - SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, - int xDest, int yDest, int w, int h) { -- SplashColor pixel; -- SplashColorPtr p; -- Guchar *q; -- int x, y, mask; -+ SplashColorPtr p, q; -+ int x, y, mask, srcMask; - - if (src->mode != bitmap->mode) { - return splashErrModeMismatch; -@@ -2925,9 +4512,10 @@ - for (y = 0; y < h; ++y) { - p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)]; - mask = 0x80 >> (xDest & 7); -+ q = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)]; -+ srcMask = 0x80 >> (xSrc & 7); - for (x = 0; x < w; ++x) { -- src->getPixel(xSrc + x, ySrc + y, pixel); -- if (pixel[0]) { -+ if (*q & srcMask) { - *p |= mask; - } else { - *p &= ~mask; -@@ -2936,15 +4524,19 @@ - mask = 0x80; - ++p; - } -+ if (!(srcMask >>= 1)) { -+ srcMask = 0x80; -+ ++q; -+ } - } - } - break; - case splashModeMono8: - for (y = 0; y < h; ++y) { - p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest]; -+ q = &src->data[(ySrc + y) * src->rowSize + xSrc]; - for (x = 0; x < w; ++x) { -- src->getPixel(xSrc + x, ySrc + y, pixel); -- *p++ = pixel[0]; -+ *p++ = *q++; - } - } - break; -@@ -2952,11 +4544,11 @@ - case splashModeBGR8: - for (y = 0; y < h; ++y) { - p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest]; -+ q = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc]; - for (x = 0; x < w; ++x) { -- src->getPixel(xSrc + x, ySrc + y, pixel); -- *p++ = pixel[0]; -- *p++ = pixel[1]; -- *p++ = pixel[2]; -+ *p++ = *q++; -+ *p++ = *q++; -+ *p++ = *q++; - } - } - break; -@@ -2964,12 +4556,12 @@ - case splashModeCMYK8: - for (y = 0; y < h; ++y) { - p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; -+ q = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc]; - for (x = 0; x < w; ++x) { -- src->getPixel(xSrc + x, ySrc + y, pixel); -- *p++ = pixel[0]; -- *p++ = pixel[1]; -- *p++ = pixel[2]; -- *p++ = pixel[3]; -+ *p++ = *q++; -+ *p++ = *q++; -+ *p++ = *q++; -+ *p++ = *q++; - } - } - break; -diff -ru xpdf-3.02/splash/SplashFTFont.cc xpdf-3.03/splash/SplashFTFont.cc ---- xpdf-3.02/splash/SplashFTFont.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/splash/SplashFTFont.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -20,6 +20,7 @@ - #include "SplashMath.h" - #include "SplashGlyphBitmap.h" - #include "SplashPath.h" -+#include "SplashFontEngine.h" - #include "SplashFTFontEngine.h" - #include "SplashFTFontFile.h" - #include "SplashFTFont.h" -@@ -157,6 +219,7 @@ - FT_Vector offset; - FT_GlyphSlot slot; - FT_UInt gid; -+ FT_Int32 flags; - int rowSize; - Guchar *p, *q; - int i; -@@ -174,30 +237,39 @@ - } else { - gid = (FT_UInt)c; - } - if (ff->trueType && gid < 0) { - // skip the TrueType notdef glyph - return gFalse; - } - -- // if we have the FT2 bytecode interpreter, autohinting won't be used --#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER -- if (FT_Load_Glyph(ff->face, gid, -- aa ? FT_LOAD_NO_BITMAP : FT_LOAD_DEFAULT)) { -+ flags = 0; -+ if (aa) { -+ flags |= FT_LOAD_NO_BITMAP; -+ } -+ if (ff->engine->flags & splashFTNoHinting) { -+ flags |= FT_LOAD_NO_HINTING; -+ } else if (ff->trueType) { -+ // FT2's autohinting doesn't always work very well (especially with -+ // font subsets), so turn it off if anti-aliasing is enabled; if -+ // anti-aliasing is disabled, this seems to be a tossup - some fonts -+ // look better with hinting, some without, so leave hinting on -+ if (aa) { -+ flags |= FT_LOAD_NO_AUTOHINT; -+ } -+ } else if (ff->type1) { -+ // Type 1 fonts seem to look better with 'light' hinting mode -+ flags |= FT_LOAD_TARGET_LIGHT; -+ } -+ if (FT_Load_Glyph(ff->face, gid, flags)) { - return gFalse; - } --#else -- // FT2's autohinting doesn't always work very well (especially with -- // font subsets), so turn it off if anti-aliasing is enabled; if -- // anti-aliasing is disabled, this seems to be a tossup - some fonts -- // look better with hinting, some without, so leave hinting on -- if (FT_Load_Glyph(ff->face, gid, -- aa ? FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP -- : FT_LOAD_DEFAULT)) { -+ if (FT_Render_Glyph(slot, aa ? FT_RENDER_MODE_NORMAL -+ : FT_RENDER_MODE_MONO)) { - return gFalse; - } --#endif -- if (FT_Render_Glyph(slot, aa ? ft_render_mode_normal -- : ft_render_mode_mono)) { -+ if (slot->bitmap.width == 0 || slot->bitmap.rows == 0) { -+ // this can happen if (a) the glyph is really tiny or (b) the -+ // metrics in the TrueType file are broken - return gFalse; - } - -@@ -258,7 +330,7 @@ - } else { - gid = (FT_UInt)c; - } -- if (ff->trueType && gid == 0) { -+ if (ff->trueType && gid < 0) { - // skip the TrueType notdef glyph - return NULL; - } -diff -ru xpdf-3.02/splash/Splash.h xpdf-3.03/splash/Splash.h ---- xpdf-3.02/splash/Splash.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/splash/Splash.h 2011-08-15 23:08:53.000000000 +0200 -@@ -43,11 +43,11 @@ - //------------------------------------------------------------------------ - - enum SplashPipeResultColorCtrl { -+ splashPipeResultColorNoAlphaBlendMono, -+ splashPipeResultColorNoAlphaBlendRGB, - #if SPLASH_CMYK - splashPipeResultColorNoAlphaBlendCMYK, - #endif -- splashPipeResultColorNoAlphaBlendRGB, -- splashPipeResultColorNoAlphaBlendMono, - splashPipeResultColorAlphaNoBlendMono, - splashPipeResultColorAlphaNoBlendRGB, - #if SPLASH_CMYK -@@ -232,17 +240,30 @@ - void setDebugMode(GBool debugModeA) { debugMode = debugModeA; } - - #if 1 //~tmp: turn off anti-aliasing temporarily -- GBool getVectorAntialias() { return vectorAntialias; } -- void setVectorAntialias(GBool vaa) { vectorAntialias = vaa; } - void setInShading(GBool sh) { inShading = sh; } - #endif - - private: - -@@ -283,10 +365,12 @@ - SplashBitmap *alpha0Bitmap; // for non-isolated groups, this is the - // bitmap containing the alpha0 values - int alpha0X, alpha0Y; // offset within alpha0Bitmap -- SplashCoord aaGamma[splashAASize * splashAASize + 1]; -+ Guchar aaGamma[splashAASize * splashAASize + 1]; - SplashCoord minLineWidth; -diff -ru xpdf-3.02/splash/SplashState.cc xpdf-3.03/splash/SplashState.cc ---- xpdf-3.02/splash/SplashState.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/splash/SplashState.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -50,16 +54,28 @@ - lineDashLength = 0; - lineDashPhase = 0; - strokeAdjust = gFalse; -- clip = new SplashClip(0, 0, width - 0.001, height - 0.001, vectorAntialias); -+ clip = new SplashClip(0, 0, width, height, vectorAntialias); - softMask = NULL; - deleteSoftMask = gFalse; - inNonIsolatedGroup = gFalse; -@@ -80,10 +96,21 @@ - lineDashLength = 0; - lineDashPhase = 0; - strokeAdjust = gFalse; -- clip = new SplashClip(0, 0, width - 0.001, height - 0.001, vectorAntialias); -+ clip = new SplashClip(0, 0, width, height, vectorAntialias); - softMask = NULL; - deleteSoftMask = gFalse; - inNonIsolatedGroup = gFalse; -diff -ru xpdf-3.02/splash/SplashXPath.cc xpdf-3.03/splash/SplashXPath.cc ---- xpdf-3.02/splash/SplashXPath.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/splash/SplashXPath.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -49,14 +52,13 @@ - // SplashXPath - //------------------------------------------------------------------------ - - SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, - SplashCoord flatness, GBool closeSubpaths) { - SplashPathHint *hint; - SplashXPathPoint *pts; - SplashXPathAdjust *adjusts, *adjust; - SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp; -- SplashCoord adj0, adj1, w; -- int ww; -+ SplashCoord adj0, adj1; - int curSubpath, curSubpathX, i, j; - - // transform the points -@@ -98,19 +95,24 @@ - adj0 = adj1; - adj1 = x0; - } -- w = adj1 - adj0; -- ww = splashRound(w); -- if (ww == 0) { -- ww = 1; -- } - adjusts[i].x0a = adj0 - 0.01; - adjusts[i].x0b = adj0 + 0.01; - adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - 0.01; - adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + 0.01; - adjusts[i].x1a = adj1 - 0.01; - adjusts[i].x1b = adj1 + 0.01; -- adjusts[i].x0 = (SplashCoord)splashRound(adj0); -- adjusts[i].x1 = adjusts[i].x0 + ww - 0.01; -+ // rounding both edge coordinates can result in lines of -+ // different widths (e.g., adj=10.1, adj1=11.3 --> x0=10, x1=11; -+ // adj0=10.4, adj1=11.6 --> x0=10, x1=12), but it has the -+ // benefit of making adjacent strokes/fills line up without any -+ // gaps between them -+ x0 = splashRound(adj0); -+ x1 = splashRound(adj1); -+ if (x1 == x0) { -+ x1 = x1 + 1; -+ } -+ adjusts[i].x0 = (SplashCoord)x0; -+ adjusts[i].x1 = (SplashCoord)x1 - 0.01; - adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1); - adjusts[i].firstPt = hint->firstPt; - adjusts[i].lastPt = hint->lastPt; -diff -ru xpdf-3.02/xpdf/Annot.cc xpdf-3.03/xpdf/Annot.cc ---- xpdf-3.02/xpdf/Annot.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/Annot.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -466,45 +496,41 @@ - // radio button - if (ff & fieldFlagRadio) { - //~ Acrobat doesn't draw a caption if there is no AP dict (?) -- if (fieldLookup(field, "V", &obj1)->isName()) { -- if (annot->lookup("AS", &obj2)->isName(obj1.getName())) { -- if (caption) { -- drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter, -- gFalse, gTrue); -- } else { -- if (mkDict) { -- if (mkDict->lookup("BC", &obj3)->isArray() && -- obj3.arrayGetLength() > 0) { -- dx = xMax - xMin; -- dy = yMax - yMin; -- setColor(obj3.getArray(), gTrue, 0); -- drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy), -- gTrue); -- } -- obj3.free(); -+ if (fieldLookup(field, acroForm, "V", &obj1) -+ ->isName(appearanceState->getCString())) { -+ if (caption) { -+ drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter, -+ gFalse, gTrue); -+ } else { -+ if (mkDict) { -+ if (mkDict->lookup("BC", &obj2)->isArray() && -+ obj2.arrayGetLength() > 0) { -+ dx = xMax - xMin; -+ dy = yMax - yMin; -+ setColor(obj2.getArray(), gTrue, 0); -+ drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy), -+ gTrue); - } -+ obj2.free(); - } - } -- obj2.free(); - } - obj1.free(); - // pushbutton - } else if (ff & fieldFlagPushbutton) { - if (caption) { - drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter, - gFalse, gFalse); - } - // checkbox - } else { -- // According to the PDF spec the off state must be named "Off", -- // and the on state can be named anything, but Acrobat apparently -- // looks for "Yes" and treats anything else as off. -- if (fieldLookup(field, "V", &obj1)->isName("Yes")) { -+ fieldLookup(field, acroForm, "V", &obj1); -+ if (obj1.isName() && !obj1.isName("Off")) { - if (!caption) { - caption = new GString("3"); // ZapfDingbats checkmark - } - drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter, - gFalse, gTrue); - } - obj1.free(); - } -@@ -699,10 +729,12 @@ - // Draw the variable text or caption for a field. - void Annot::drawText(GString *text, GString *da, GfxFontDict *fontDict, - GBool multiline, int comb, int quadding, - GBool txField, GBool forceZapfDingbats) { -+ GString *text2; - GList *daToks; - GString *tok; - GfxFont *font; - double dx, dy; - double fontSize, fontSize2, border, x, xPrev, y, w, w2, wMax; - int tfPos, tmPos, i, j, k, c; - -@@ -710,6 +742,23 @@ - //~ and only replace the marked content portion of it - //~ (this is only relevant for Tx fields) - -+ // check for a Unicode string -+ //~ this currently drops all non-Latin1 characters -+ if (text->getLength() >= 2 && -+ text->getChar(0) == '\xfe' && text->getChar(1) == '\xff') { -+ text2 = new GString(); -+ for (i = 2; i+1 < text->getLength(); i += 2) { -+ c = ((text->getChar(i) & 0xff) << 8) + (text->getChar(i+1) & 0xff); -+ if (c <= 0xff) { -+ text2->append((char)c); -+ } else { -+ text2->append('?'); -+ } -+ } -+ } else { -+ text2 = text; -+ } -+ - // parse the default appearance string - tfPos = tmPos = -1; - if (da) { -@@ -776,22 +826,39 @@ - // compute font autosize - if (fontSize == 0) { - for (fontSize = 20; fontSize > 1; --fontSize) { - y = dy - 3; - w2 = 0; - i = 0; -- while (i < text->getLength()) { -- getNextLine(text, i, font, fontSize, wMax, &j, &w, &k); -+ while (i < text2->getLength()) { -+ getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k); - if (w > w2) { - w2 = w; - } -@@ -840,9 +907,9 @@ - // write a series of lines of text - i = 0; - xPrev = 0; -- while (i < text->getLength()) { -+ while (i < text2->getLength()) { - -- getNextLine(text, i, font, fontSize, wMax, &j, &w, &k); -+ getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k); - - // compute text start position - switch (quadding) { -@@ -862,7 +929,7 @@ - appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize); - appearBuf->append('('); - for (; i < j; ++i) { -- c = text->getChar(i) & 0xff; -+ c = text2->getChar(i) & 0xff; - if (c == '(' || c == ')' || c == '\\') { - appearBuf->append('\\'); - appearBuf->append(c); -@@ -910,13 +977,13 @@ - x = border + 2; - break; - case fieldQuadCenter: -- x = border + 2 + 0.5 * (comb - text->getLength()) * w; -+ x = border + 2 + 0.5 * (comb - text2->getLength()) * w; - break; - case fieldQuadRight: -- x = border + 2 + (comb - text->getLength()) * w; -+ x = border + 2 + (comb - text2->getLength()) * w; - break; - } - y = 0.5 * dy - 0.4 * fontSize; - - // set the font matrix - if (tmPos >= 0) { -@@ -943,12 +1010,12 @@ - // write the text string - //~ this should center (instead of left-justify) each character within - //~ its comb cell -- for (i = 0; i < text->getLength(); ++i) { -+ for (i = 0; i < text2->getLength(); ++i) { - if (i > 0) { - appearBuf->appendf("{0:.2f} 0 Td\n", w); - } - appearBuf->append('('); -- c = text->getChar(i) & 0xff; -+ c = text2->getChar(i) & 0xff; - if (c == '(' || c == ')' || c == '\\') { - appearBuf->append('\\'); - appearBuf->append(c); -@@ -966,18 +1033,18 @@ - // compute string width - if (font && !font->isCIDFont()) { - w = 0; -- for (i = 0; i < text->getLength(); ++i) { -- w += ((Gfx8BitFont *)font)->getWidth(text->getChar(i)); -+ for (i = 0; i < text2->getLength(); ++i) { -+ w += ((Gfx8BitFont *)font)->getWidth(text2->getChar(i)); - } - } else { - // otherwise, make a crude estimate -- w = text->getLength() * 0.5; -+ w = text2->getLength() * 0.5; - } - -@@ -1029,8 +1096,8 @@ - - // write the text string - appearBuf->append('('); -- for (i = 0; i < text->getLength(); ++i) { -- c = text->getChar(i) & 0xff; -+ for (i = 0; i < text2->getLength(); ++i) { -+ c = text2->getChar(i) & 0xff; - if (c == '(' || c == ')' || c == '\\') { - appearBuf->append('\\'); - appearBuf->append(c); -@@ -1054,6 +1121,9 @@ - if (daToks) { - deleteGList(daToks, GString); - } -+ if (text2 != text) { -+ delete text2; -+ } - } - - // Draw the variable text or caption for a field. -diff -ru xpdf-3.02/xpdf/Annot.h xpdf-3.03/xpdf/Annot.h ---- xpdf-3.02/xpdf/Annot.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/Annot.h 2011-08-15 23:08:53.000000000 +0200 -@@ -106,6 +117,7 @@ - xMax, yMax; - Guint flags; - AnnotBorderStyle *borderStyle; -+ Object ocObj; // optional content entry - GBool ok; - }; - -diff -ru xpdf-3.02/xpdf/Catalog.cc xpdf-3.03/xpdf/Catalog.cc ---- xpdf-3.02/xpdf/Catalog.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/Catalog.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -12,80 +12,111 @@ - #pragma implementation - #endif - -+#include - #include -+#include - #include "gmem.h" -+#include "gfile.h" -+#include "GList.h" - #include "Object.h" -+#include "CharTypes.h" - #include "PDFDoc.h" - #include "XRef.h" - #include "Array.h" - #include "Dict.h" - #include "Page.h" - #include "Error.h" - #include "Link.h" -+#include "PDFDocEncoding.h" - #include "Catalog.h" - - //------------------------------------------------------------------------ -+// PageTreeNode -+//------------------------------------------------------------------------ -+ -+class PageTreeNode { -+public: -+ -+ PageTreeNode(Ref refA, int countA, PageTreeNode *parentA); -+ ~PageTreeNode(); -+ -+ Ref ref; -+ int count; -+ PageTreeNode *parent; -+ GList *kids; // [PageTreeNode] -+ PageAttrs *attrs; -+}; -+ -+PageTreeNode::PageTreeNode(Ref refA, int countA, PageTreeNode *parentA) { -+ ref = refA; -+ count = countA; -+ parent = parentA; -+ kids = NULL; -+ attrs = NULL; -+} -+ -+PageTreeNode::~PageTreeNode() { -+ delete attrs; -+ if (kids) { -+ deleteGList(kids, PageTreeNode); -+ } -+} -+ -+ -+//------------------------------------------------------------------------ - // Catalog - //------------------------------------------------------------------------ - - Catalog::Catalog(PDFDoc *docA) { -- Object catDict, pagesDict, pagesDictRef; -+ Object catDict; - Object obj, obj2; -- char *alreadyRead; -- int numPages0; -- int i; - - ok = gTrue; - doc = docA; - xref = doc->getXRef(); -+ pageTree = NULL; - pages = NULL; - pageRefs = NULL; -- numPages = pagesSize = 0; -+ numPages = 0; - baseURI = NULL; - - xref->getCatalog(&catDict); - if (!catDict.isDict()) { -- error(errSyntaxError, -1, "Catalog object is wrong type (%s)", catDict.getTypeName()); -+ error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); - } - - // read page tree -- catDict.dictLookup("Pages", &pagesDict); -- // This should really be isDict("Pages"), but I've seen at least one -- // PDF file where the /Type entry is missing. -- if (!pagesDict.isDict()) { -- error(-1, "Top-level pages object is wrong type (%s)", -- pagesDict.getTypeName()); -- goto err2; -- } -- pagesDict.dictLookup("Count", &obj); -- // some PDF files actually use real numbers here ("/Count 9.0") -- if (!obj.isNum()) { -- error(-1, "Page count in top-level pages object is wrong type (%s)", -- obj.getTypeName()); -- goto err3; -- } -- pagesSize = numPages0 = (int)obj.getNum(); -- obj.free(); -- pages = (Page **)gmallocn(pagesSize, sizeof(Page *)); -- pageRefs = (Ref *)gmallocn(pagesSize, sizeof(Ref)); -- for (i = 0; i < pagesSize; ++i) { -- pages[i] = NULL; -- pageRefs[i].num = -1; -- pageRefs[i].gen = -1; -- } -- alreadyRead = (char *)gmalloc(xref->getNumObjects()); -- memset(alreadyRead, 0, xref->getNumObjects()); -- if (catDict.dictLookupNF("Pages", &pagesDictRef)->isRef() && -- pagesDictRef.getRefNum() >= 0 && -- pagesDictRef.getRefNum() < xref->getNumObjects()) { -- alreadyRead[pagesDictRef.getRefNum()] = 1; -- } -- pagesDictRef.free(); -- numPages = readPageTree(pagesDict.getDict(), NULL, 0, alreadyRead); -- gfree(alreadyRead); -- if (numPages != numPages0) { -- error(-1, "Page count in top-level pages object is incorrect"); -+ if (!readPageTree(&catDict)) { -+ goto err1; - } -- pagesDict.free(); - - // read named destination dictionary - catDict.dictLookup("Dests", &dests); -@@ -105,6 +137,21 @@ - obj2.free(); - } - obj.free(); -+ if (!baseURI || baseURI->getLength() == 0) { -+ if (baseURI) { -+ delete baseURI; -+ } -+ if (doc->getFileName()) { -+ baseURI = makePathAbsolute(grabPath(doc->getFileName()->getCString())); -+ if (baseURI->getChar(0) == '/') { -+ baseURI->insert(0, "file://localhost"); -+ } else { -+ baseURI->insert(0, "file://localhost/"); -+ } -+ } else { -+ baseURI = new GString("file://localhost/"); -+ } -+ } - - // get the metadata stream - catDict.dictLookup("Metadata", &metadata); -@@ -118,13 +165,15 @@ - // get the AcroForm dictionary - catDict.dictLookup("AcroForm", &acroForm); - -+ // get the OCProperties dictionary -+ catDict.dictLookup("OCProperties", &ocProperties); -+ - catDict.free(); - return; - -- err3: -- obj.free(); -- err2: -- pagesDict.free(); - err1: - catDict.free(); - dests.initNull(); -@@ -135,8 +184,11 @@ - Catalog::~Catalog() { - int i; - -+ if (pageTree) { -+ delete pageTree; -+ } - if (pages) { -- for (i = 0; i < pagesSize; ++i) { -+ for (i = 0; i < numPages; ++i) { - if (pages[i]) { - delete pages[i]; - } -@@ -153,6 +205,31 @@ - structTreeRoot.free(); - outline.free(); - acroForm.free(); -+ ocProperties.free(); -+} -+ -+Page *Catalog::getPage(int i) { -+ if (!pages[i-1]) { -+ loadPage(i); -+ } -+ return pages[i-1]; -+} -+ -+Ref *Catalog::getPageRef(int i) { -+ if (!pages[i-1]) { -+ loadPage(i); -+ } -+ return &pageRefs[i-1]; -+} -+ -+void Catalog::doneWithPage(int i) { -+ if (pages[i-1]) { -+ delete pages[i-1]; -+ pages[i-1] = NULL; -+ } - } - - GString *Catalog::readMetadata() { -@@ -179,90 +256,13 @@ - return s; - } - --int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start, -- char *alreadyRead) { -- Object kids; -- Object kid; -- Object kidRef; -- PageAttrs *attrs1, *attrs2; -- Page *page; -- int i, j; -- -- attrs1 = new PageAttrs(attrs, pagesDict); -- pagesDict->lookup("Kids", &kids); -- if (!kids.isArray()) { -- error(-1, "Kids object (page %d) is wrong type (%s)", -- start+1, kids.getTypeName()); -- goto err1; -- } -- for (i = 0; i < kids.arrayGetLength(); ++i) { -- kids.arrayGetNF(i, &kidRef); -- if (kidRef.isRef() && -- kidRef.getRefNum() >= 0 && -- kidRef.getRefNum() < xref->getNumObjects()) { -- if (alreadyRead[kidRef.getRefNum()]) { -- error(-1, "Loop in Pages tree"); -- kidRef.free(); -- continue; -- } -- alreadyRead[kidRef.getRefNum()] = 1; -- } -- kids.arrayGet(i, &kid); -- if (kid.isDict("Page")) { -- attrs2 = new PageAttrs(attrs1, kid.getDict()); -- page = new Page(xref, start+1, kid.getDict(), attrs2); -- if (!page->isOk()) { -- ++start; -- goto err3; -- } -- if (start >= pagesSize) { -- pagesSize += 32; -- pages = (Page **)greallocn(pages, pagesSize, sizeof(Page *)); -- pageRefs = (Ref *)greallocn(pageRefs, pagesSize, sizeof(Ref)); -- for (j = pagesSize - 32; j < pagesSize; ++j) { -- pages[j] = NULL; -- pageRefs[j].num = -1; -- pageRefs[j].gen = -1; -- } -- } -- pages[start] = page; -- if (kidRef.isRef()) { -- pageRefs[start].num = kidRef.getRefNum(); -- pageRefs[start].gen = kidRef.getRefGen(); -- } -- ++start; -- // This should really be isDict("Pages"), but I've seen at least one -- // PDF file where the /Type entry is missing. -- } else if (kid.isDict()) { -- if ((start = readPageTree(kid.getDict(), attrs1, start, alreadyRead)) -- < 0) -- goto err2; -- } else { -- error(-1, "Kid object (page %d) is wrong type (%s)", -- start+1, kid.getTypeName()); -- } -- kid.free(); -- kidRef.free(); -- } -- delete attrs1; -- kids.free(); -- return start; -- -- err3: -- delete page; -- err2: -- kid.free(); -- err1: -- kids.free(); -- delete attrs1; -- ok = gFalse; -- return -1; --} -- - int Catalog::findPage(int num, int gen) { - int i; - - for (i = 0; i < numPages; ++i) { -+ if (!pages[i]) { -+ loadPage(i+1); -+ } - if (pageRefs[i].num == num && pageRefs[i].gen == gen) - return i + 1; - } -@@ -372,3 +372,367 @@ - - return obj; - } -+ -+GBool Catalog::readPageTree(Object *catDict) { -+ Object topPagesRef, topPagesObj, countObj; -+ int i; -+ -+ if (!catDict->dictLookupNF("Pages", &topPagesRef)->isRef()) { -+ error(errSyntaxError, -1, "Top-level pages reference is wrong type ({0:s})", -+ topPagesRef.getTypeName()); -+ topPagesRef.free(); -+ return gFalse; -+ } -+ if (!topPagesRef.fetch(xref, &topPagesObj)->isDict()) { -+ error(errSyntaxError, -1, "Top-level pages object is wrong type ({0:s})", -+ topPagesObj.getTypeName()); -+ topPagesObj.free(); -+ topPagesRef.free(); -+ return gFalse; -+ } -+ if (topPagesObj.dictLookup("Count", &countObj)->isInt()) { -+ numPages = countObj.getInt(); -+ if (numPages == 0) { -+ // Acrobat apparently scans the page tree if it sees a zero count -+ numPages = countPageTree(&topPagesObj); -+ } -+ } else { -+ // assume we got a Page node instead of a Pages node -+ numPages = 1; -+ } -+ countObj.free(); -+ if (numPages < 0) { -+ error(errSyntaxError, -1, "Invalid page count"); -+ topPagesObj.free(); -+ topPagesRef.free(); -+ numPages = 0; -+ return gFalse; -+ } -+ pageTree = new PageTreeNode(topPagesRef.getRef(), numPages, NULL); -+ topPagesObj.free(); -+ topPagesRef.free(); -+ pages = (Page **)greallocn(pages, numPages, sizeof(Page *)); -+ pageRefs = (Ref *)greallocn(pageRefs, numPages, sizeof(Ref)); -+ for (i = 0; i < numPages; ++i) { -+ pages[i] = NULL; -+ pageRefs[i].num = -1; -+ pageRefs[i].gen = -1; -+ } -+ return gTrue; -+} -+ -+int Catalog::countPageTree(Object *pagesObj) { -+ Object kids, kid; -+ int n, n2, i; -+ -+ if (!pagesObj->isDict()) { -+ return 0; -+ } -+ if (pagesObj->dictLookup("Kids", &kids)->isArray()) { -+ n = 0; -+ for (i = 0; i < kids.arrayGetLength(); ++i) { -+ kids.arrayGet(i, &kid); -+ n2 = countPageTree(&kid); -+ if (n2 < INT_MAX - n) { -+ n += n2; -+ } else { -+ error(errSyntaxError, -1, "Page tree contains too many pages"); -+ n = INT_MAX; -+ } -+ kid.free(); -+ } -+ } else { -+ n = 1; -+ } -+ kids.free(); -+ return n; -+} -+ -+void Catalog::loadPage(int pg) { -+ loadPage2(pg, pg - 1, pageTree); -+} -+ -+void Catalog::loadPage2(int pg, int relPg, PageTreeNode *node) { -+ Object pageRefObj, pageObj, kidsObj, kidRefObj, kidObj, countObj; -+ PageTreeNode *kidNode, *p; -+ PageAttrs *attrs; -+ int count, i; -+ -+ if (relPg >= node->count) { -+ error(errSyntaxError, -1, "Internal error in page tree"); -+ pages[pg-1] = new Page(doc, pg); -+ return; -+ } -+ -+ // if this node has not been filled in yet, it's either a leaf node -+ // or an unread internal node -+ if (!node->kids) { -+ -+ // check for a loop in the page tree -+ for (p = node->parent; p; p = p->parent) { -+ if (node->ref.num == p->ref.num && node->ref.gen == p->ref.gen) { -+ error(errSyntaxError, -1, "Loop in Pages tree"); -+ pages[pg-1] = new Page(doc, pg); -+ return; -+ } -+ } -+ -+ // fetch the Page/Pages object -+ pageRefObj.initRef(node->ref.num, node->ref.gen); -+ if (!pageRefObj.fetch(xref, &pageObj)->isDict()) { -+ error(errSyntaxError, -1, "Page tree object is wrong type ({0:s})", -+ pageObj.getTypeName()); -+ pageObj.free(); -+ pageRefObj.free(); -+ pages[pg-1] = new Page(doc, pg); -+ return; -+ } -+ -+ // merge the PageAttrs -+ attrs = new PageAttrs(node->parent ? node->parent->attrs -+ : (PageAttrs *)NULL, -+ pageObj.getDict()); -+ -+ // if "Kids" exists, it's an internal node -+ if (pageObj.dictLookup("Kids", &kidsObj)->isArray()) { -+ -+ // save the PageAttrs -+ node->attrs = attrs; -+ -+ // read the kids -+ node->kids = new GList(); -+ for (i = 0; i < kidsObj.arrayGetLength(); ++i) { -+ if (kidsObj.arrayGetNF(i, &kidRefObj)->isRef()) { -+ if (kidRefObj.fetch(xref, &kidObj)->isDict()) { -+ if (kidObj.dictLookup("Count", &countObj)->isInt()) { -+ count = countObj.getInt(); -+ } else { -+ count = 1; -+ } -+ countObj.free(); -+ node->kids->append(new PageTreeNode(kidRefObj.getRef(), count, -+ node)); -+ } else { -+ error(errSyntaxError, -1, "Page tree object is wrong type ({0:s})", -+ kidObj.getTypeName()); -+ } -+ kidObj.free(); -+ } else { -+ error(errSyntaxError, -1, -+ "Page tree reference is wrong type ({0:s})", -+ kidRefObj.getTypeName()); -+ } -+ kidRefObj.free(); -+ } -+ -+ } else { -+ -+ // create the Page object -+ pageRefs[pg-1] = node->ref; -+ pages[pg-1] = new Page(doc, pg, pageObj.getDict(), attrs); -+ if (!pages[pg-1]->isOk()) { -+ delete pages[pg-1]; -+ pages[pg-1] = new Page(doc, pg); -+ } -+ -+ } -+ -+ kidsObj.free(); -+ pageObj.free(); -+ pageRefObj.free(); -+ } -+ -+ // recursively descend the tree -+ if (node->kids) { -+ for (i = 0; i < node->kids->getLength(); ++i) { -+ kidNode = (PageTreeNode *)node->kids->get(i); -+ if (relPg < kidNode->count) { -+ loadPage2(pg, relPg, kidNode); -+ break; -+ } -+ relPg -= kidNode->count; -+ } -+ -+ // this will only happen if the page tree is invalid -+ // (i.e., parent count > sum of children counts) -+ if (i == node->kids->getLength()) { -+ error(errSyntaxError, -1, "Invalid page count in page tree"); -+ pages[pg-1] = new Page(doc, pg); -+ } -+ } -+} -+ -diff -ru xpdf-3.02/xpdf/Catalog.h xpdf-3.03/xpdf/Catalog.h ---- xpdf-3.02/xpdf/Catalog.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/Catalog.h 2011-08-15 23:08:53.000000000 +0200 -@@ -15,12 +15,17 @@ - #pragma interface - #endif - -+#include "CharTypes.h" -+ -+class GList; - class PDFDoc; - class XRef; - class Object; - class Page; - class PageAttrs; - struct Ref; - class LinkDest; -+class PageTreeNode; - - //------------------------------------------------------------------------ - // Catalog -@@ -42,10 +47,14 @@ - int getNumPages() { return numPages; } - - // Get a page. -- Page *getPage(int i) { return pages[i-1]; } -+ Page *getPage(int i); - - // Get the reference for a page object. -- Ref *getPageRef(int i) { return &pageRefs[i-1]; } -+ Ref *getPageRef(int i); -+ -+ // Remove a page from the catalog. (It can be reloaded later by -+ // calling getPage). -+ void doneWithPage(int i); - - // Return base URI, or NULL if none. - GString *getBaseURI() { return baseURI; } -@@ -73,9 +82,19 @@ - - Object *getAcroForm() { return &acroForm; } - -+ Object *getOCProperties() { return &ocProperties; } -+ - private: - - PDFDoc *doc; - XRef *xref; // the xref table for this PDF file -+ PageTreeNode *pageTree; // the page tree - Page **pages; // array of pages - Ref *pageRefs; // object ID for each page - int numPages; // number of pages -@@ -87,11 +106,20 @@ - Object structTreeRoot; // structure tree root dictionary - Object outline; // outline dictionary - Object acroForm; // AcroForm dictionary -+ Object ocProperties; // OCProperties dictionary - GBool ok; // true if catalog is valid - -- int readPageTree(Dict *pages, PageAttrs *attrs, int start, -- char *alreadyRead); - Object *findDestInTree(Object *tree, GString *name, Object *obj); -+ GBool readPageTree(Object *catDict); -+ int countPageTree(Object *pagesObj); -+ void loadPage(int pg); -+ void loadPage2(int pg, int relPg, PageTreeNode *node); - }; - - #endif -diff -ru xpdf-3.02/xpdf/Gfx.cc xpdf-3.03/xpdf/Gfx.cc ---- xpdf-3.02/xpdf/Gfx.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/Gfx.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -18,9 +18,12 @@ - #include - #include - #include "gmem.h" -+#include "GString.h" -+#include "GList.h" - #include "GlobalParams.h" - #include "CharTypes.h" - #include "Object.h" - #include "PDFDoc.h" - #include "Array.h" - #include "Dict.h" - #include "Stream.h" -@@ -31,7 +34,9 @@ - #include "OutputDev.h" - #include "Page.h" - #include "Annot.h" -+#include "OptionalContent.h" - #include "Error.h" -+#include "PDFDocEncoding.h" - #include "Gfx.h" - - // the MSVC math.h doesn't define this -@@ -461,6 +487,10 @@ - baseMatrix[i] = state->getCTM()[i]; - } - formDepth = 0; -+ textClipBBoxEmpty = gTrue; -+ markedContentStack = new GList(); -+ ocState = gTrue; -+ parser = NULL; - abortCheckCbk = abortCheckCbkA; - abortCheckCbkData = abortCheckCbkDataA; - -@@ -500,6 +531,10 @@ - baseMatrix[i] = state->getCTM()[i]; - } - formDepth = 0; -+ textClipBBoxEmpty = gTrue; -+ markedContentStack = new GList(); -+ ocState = gTrue; -+ parser = NULL; - abortCheckCbk = abortCheckCbkA; - abortCheckCbkData = abortCheckCbkDataA; - -@@ -517,18 +552,17 @@ - } - - Gfx::~Gfx() { -- while (state->hasSaves()) { -- restoreState(); -- } - if (!subPage) { - out->endPage(); - } -+ while (state->hasSaves()) { -+ restoreState(); -+ } -+ delete state; - while (res) { - popResources(); - } -- if (state) { -- delete state; -- } -+ deleteGList(markedContentStack, GfxMarkedContent); - } - - void Gfx::display(Object *obj, GBool topLevel) { -@@ -562,7 +596,8 @@ - int lastAbortCheck; - - // scan a sequence of objects -- updateLevel = lastAbortCheck = 0; -+ updateLevel = 1; // make sure even empty pages trigger a call to dump() -+ lastAbortCheck = 0; - numArgs = 0; - parser->getObj(&obj); - while (!obj.isEOF()) { -@@ -695,6 +734,7 @@ - - a = -1; - b = numOps; -+ cmp = 0; // make gcc happy - // invariant: opTab[a] < name < opTab[b] - while (b - a > 1) { - m = (a + b) / 2; -@@ -801,6 +841,7 @@ - - void Gfx::opSetExtGState(Object args[], int numArgs) { - Object obj1, obj2, obj3, obj4, obj5; -+ Object args2[2]; - GfxBlendMode mode; - GBool haveFillOP; - Function *funcs[4]; -@@ -808,9 +849,10 @@ - GBool haveBackdropColor; - GfxColorSpace *blendingColorSpace; - GBool alpha, isolated, knockout; -+ double opac; - int i; - - if (!res->lookupGState(args[0].getName(), &obj1)) { - return; - } - if (!obj1.isDict()) { -@@ -824,28 +867,77 @@ - printf("\n"); - } - -+ // parameters that are also set by individual PDF operators -+ if (obj1.dictLookup("LW", &obj2)->isNum()) { -+ opSetLineWidth(&obj2, 1); -+ } -+ obj2.free(); -+ if (obj1.dictLookup("LC", &obj2)->isInt()) { -+ opSetLineCap(&obj2, 1); -+ } -+ obj2.free(); -+ if (obj1.dictLookup("LJ", &obj2)->isInt()) { -+ opSetLineJoin(&obj2, 1); -+ } -+ obj2.free(); -+ if (obj1.dictLookup("ML", &obj2)->isNum()) { -+ opSetMiterLimit(&obj2, 1); -+ } -+ obj2.free(); -+ if (obj1.dictLookup("D", &obj2)->isArray() && -+ obj2.arrayGetLength() == 2) { -+ obj2.arrayGet(0, &args2[0]); -+ obj2.arrayGet(1, &args2[1]); -+ if (args2[0].isArray() && args2[1].isNum()) { -+ opSetDash(args2, 2); -+ } -+ args2[0].free(); -+ args2[1].free(); -+ } -+ obj2.free(); -+#if 0 //~ need to add a new version of GfxResources::lookupFont() that -+ //~ takes an indirect ref instead of a name -+ if (obj1.dictLookup("Font", &obj2)->isArray() && -+ obj2.arrayGetLength() == 2) { -+ obj2.arrayGet(0, &args2[0]); -+ obj2.arrayGet(1, &args2[1]); -+ if (args2[0].isDict() && args2[1].isNum()) { -+ opSetFont(args2, 2); -+ } -+ args2[0].free(); -+ args2[1].free(); -+ } -+ obj2.free(); -+#endif -+ if (obj1.dictLookup("FL", &obj2)->isNum()) { -+ opSetFlat(&obj2, 1); -+ } -+ obj2.free(); -+ - // 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(errSyntaxError, getPos(), "Invalid blend mode in ExtGState"); - } - } - obj2.free(); - if (obj1.dictLookup("ca", &obj2)->isNum()) { -- state->setFillOpacity(obj2.getNum()); -+ opac = obj2.getNum(); -+ state->setFillOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac); - out->updateFillOpacity(state); - } - obj2.free(); - if (obj1.dictLookup("CA", &obj2)->isNum()) { -- state->setStrokeOpacity(obj2.getNum()); -+ opac = obj2.getNum(); -+ state->setStrokeOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac); - out->updateStrokeOpacity(state); - } - obj2.free(); - -- // fill/stroke overprint -+ // fill/stroke overprint, overprint mode - if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) { - state->setFillOverprint(obj2.getBool()); - out->updateFillOverprint(state); -@@ -860,6 +952,11 @@ - } - } - obj2.free(); -+ if (obj1.dictLookup("OPM", &obj2)->isInt()) { -+ state->setOverprintMode(obj2.getInt()); -+ out->updateOverprintMode(state); -+ } -+ obj2.free(); - - // stroke adjust - if (obj1.dictLookup("SA", &obj2)->isBool()) { -@@ -915,13 +1012,18 @@ - obj3.free(); - funcs[0] = NULL; - if (!obj2.dictLookup("TR", &obj3)->isNull()) { -- funcs[0] = Function::parse(&obj3); -- if (funcs[0]->getInputSize() != 1 || -- funcs[0]->getOutputSize() != 1) { -- error(getPos(), -- "Invalid transfer function in soft mask in ExtGState"); -- delete funcs[0]; -+ if (obj3.isName("Default") || -+ obj3.isName("Identity")) { - funcs[0] = NULL; -+ } else { -+ funcs[0] = Function::parse(&obj3); -+ if (funcs[0]->getInputSize() != 1 || -+ funcs[0]->getOutputSize() != 1) { -+ error(errSyntaxError, getPos(), -+ "Invalid transfer function in soft mask in ExtGState"); -+ delete funcs[0]; -+ funcs[0] = NULL; -+ } - } - } - obj3.free(); -@@ -1045,9 +1149,9 @@ - - // draw it - ++formDepth; -- doForm1(str, resDict, m, bbox, gTrue, gTrue, -- blendingColorSpace, isolated, knockout, -- alpha, transferFunc, backdropColor); -+ drawForm(str, resDict, m, bbox, gTrue, gTrue, -+ blendingColorSpace, isolated, knockout, -+ alpha, transferFunc, backdropColor); - --formDepth; - - if (blendingColorSpace) { -@@ -1402,14 +1512,16 @@ - - void Gfx::opStroke(Object args[], int numArgs) { - if (!state->isCurPt()) { -- //error(getPos(), "No path in stroke"); -+ //error(errSyntaxError, getPos(), "No path in stroke"); - return; - } - if (state->isPath()) { -- if (state->getStrokeColorSpace()->getMode() == csPattern) { -- doPatternStroke(); -- } else { -- out->stroke(state); -+ if (ocState) { -+ if (state->getStrokeColorSpace()->getMode() == csPattern) { -+ doPatternStroke(); -+ } else { -+ out->stroke(state); -+ } - } - } - doEndPath(); -@@ -1417,15 +1529,17 @@ - - void Gfx::opCloseStroke(Object args[], int numArgs) { - if (!state->isCurPt()) { -- //error(getPos(), "No path in closepath/stroke"); -+ //error(errSyntaxError, getPos(), "No path in closepath/stroke"); - return; - } - if (state->isPath()) { - state->closePath(); -- if (state->getStrokeColorSpace()->getMode() == csPattern) { -- doPatternStroke(); -- } else { -- out->stroke(state); -+ if (ocState) { -+ if (state->getStrokeColorSpace()->getMode() == csPattern) { -+ doPatternStroke(); -+ } else { -+ out->stroke(state); -+ } - } - } - doEndPath(); -@@ -1433,14 +1547,16 @@ - - void Gfx::opFill(Object args[], int numArgs) { - if (!state->isCurPt()) { -- //error(getPos(), "No path in fill"); -+ //error(errSyntaxError, getPos(), "No path in fill"); - return; - } - if (state->isPath()) { -- if (state->getFillColorSpace()->getMode() == csPattern) { -- doPatternFill(gFalse); -- } else { -- out->fill(state); -+ if (ocState) { -+ if (state->getFillColorSpace()->getMode() == csPattern) { -+ doPatternFill(gFalse); -+ } else { -+ out->fill(state); -+ } - } - } - doEndPath(); -@@ -1448,14 +1564,16 @@ - - void Gfx::opEOFill(Object args[], int numArgs) { - if (!state->isCurPt()) { -- //error(getPos(), "No path in eofill"); -+ //error(errSyntaxError, getPos(), "No path in eofill"); - return; - } - if (state->isPath()) { -- if (state->getFillColorSpace()->getMode() == csPattern) { -- doPatternFill(gTrue); -- } else { -- out->eoFill(state); -+ if (ocState) { -+ if (state->getFillColorSpace()->getMode() == csPattern) { -+ doPatternFill(gTrue); -+ } else { -+ out->eoFill(state); -+ } - } - } - doEndPath(); -@@ -1463,19 +1581,21 @@ - - void Gfx::opFillStroke(Object args[], int numArgs) { - if (!state->isCurPt()) { -- //error(getPos(), "No path in fill/stroke"); -+ //error(errSyntaxError, getPos(), "No path in fill/stroke"); - return; - } - if (state->isPath()) { -- if (state->getFillColorSpace()->getMode() == csPattern) { -- doPatternFill(gFalse); -- } else { -- out->fill(state); -- } -- if (state->getStrokeColorSpace()->getMode() == csPattern) { -- doPatternStroke(); -- } else { -- out->stroke(state); -+ if (ocState) { -+ if (state->getFillColorSpace()->getMode() == csPattern) { -+ doPatternFill(gFalse); -+ } else { -+ out->fill(state); -+ } -+ if (state->getStrokeColorSpace()->getMode() == csPattern) { -+ doPatternStroke(); -+ } else { -+ out->stroke(state); -+ } - } - } - doEndPath(); -@@ -1483,20 +1603,22 @@ - - void Gfx::opCloseFillStroke(Object args[], int numArgs) { - if (!state->isCurPt()) { -- //error(getPos(), "No path in closepath/fill/stroke"); -+ //error(errSyntaxError, getPos(), "No path in closepath/fill/stroke"); - return; - } - if (state->isPath()) { - state->closePath(); -- if (state->getFillColorSpace()->getMode() == csPattern) { -- doPatternFill(gFalse); -- } else { -- out->fill(state); -- } -- if (state->getStrokeColorSpace()->getMode() == csPattern) { -- doPatternStroke(); -- } else { -- out->stroke(state); -+ if (ocState) { -+ if (state->getFillColorSpace()->getMode() == csPattern) { -+ doPatternFill(gFalse); -+ } else { -+ out->fill(state); -+ } -+ if (state->getStrokeColorSpace()->getMode() == csPattern) { -+ doPatternStroke(); -+ } else { -+ out->stroke(state); -+ } - } - } - doEndPath(); -@@ -1504,19 +1626,21 @@ - - void Gfx::opEOFillStroke(Object args[], int numArgs) { - if (!state->isCurPt()) { -- //error(getPos(), "No path in eofill/stroke"); -+ //error(errSyntaxError, getPos(), "No path in eofill/stroke"); - return; - } - if (state->isPath()) { -- if (state->getFillColorSpace()->getMode() == csPattern) { -- doPatternFill(gTrue); -- } else { -- out->eoFill(state); -- } -- if (state->getStrokeColorSpace()->getMode() == csPattern) { -- doPatternStroke(); -- } else { -- out->stroke(state); -+ if (ocState) { -+ if (state->getFillColorSpace()->getMode() == csPattern) { -+ doPatternFill(gTrue); -+ } else { -+ out->eoFill(state); -+ } -+ if (state->getStrokeColorSpace()->getMode() == csPattern) { -+ doPatternStroke(); -+ } else { -+ out->stroke(state); -+ } - } - } - doEndPath(); -@@ -1524,20 +1648,22 @@ - - void Gfx::opCloseEOFillStroke(Object args[], int numArgs) { - if (!state->isCurPt()) { -- //error(getPos(), "No path in closepath/eofill/stroke"); -+ //error(errSyntaxError, getPos(), "No path in closepath/eofill/stroke"); - return; - } - if (state->isPath()) { - state->closePath(); -- if (state->getFillColorSpace()->getMode() == csPattern) { -- doPatternFill(gTrue); -- } else { -- out->eoFill(state); -- } -- if (state->getStrokeColorSpace()->getMode() == csPattern) { -- doPatternStroke(); -- } else { -- out->stroke(state); -+ if (ocState) { -+ if (state->getFillColorSpace()->getMode() == csPattern) { -+ doPatternFill(gTrue); -+ } else { -+ out->eoFill(state); -+ } -+ if (state->getStrokeColorSpace()->getMode() == csPattern) { -+ doPatternStroke(); -+ } else { -+ out->stroke(state); -+ } - } - } - doEndPath(); -@@ -1558,13 +1684,13 @@ - } - switch (pattern->getType()) { - case 1: -- doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill); -+ doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill, gFalse); - break; - case 2: -- doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill); -+ doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill, gFalse); - break; - default: -- error(getPos(), "Unimplemented pattern type (%d) in fill", -+ error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill", - pattern->getType()); - break; - } -@@ -1585,23 +1711,68 @@ - } - switch (pattern->getType()) { - case 1: -- doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse); -+ doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse, gFalse); -+ break; -+ case 2: -+ doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse, gFalse); -+ break; -+ default: -+ error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in stroke", -+ pattern->getType()); -+ break; -+ } -+} -+ -+void Gfx::doPatternText() { -+ GfxPattern *pattern; -+ -+ // this is a bit of a kludge -- patterns can be really slow, so we -+ // skip them if we're only doing text extraction, since they almost -+ // certainly don't contain any text -+ if (!out->needNonText()) { -+ return; -+ } -+ -+ if (!(pattern = state->getFillPattern())) { -+ return; -+ } -+ switch (pattern->getType()) { -+ case 1: -+ doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, gFalse, gTrue); - break; - case 2: -- doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse); -+ doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, gFalse, gTrue); - break; - default: -- error(getPos(), "Unimplemented pattern type (%d) in stroke", -+ error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill", - pattern->getType()); - break; - } - } - -+void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height, -+ GBool invert, GBool inlineImg) { -+ saveState(); -+ -+ out->setSoftMaskFromImageMask(state, ref, str, -+ width, height, invert, inlineImg); -+ -+ state->clearPath(); -+ state->moveTo(0, 0); -+ state->lineTo(1, 0); -+ state->lineTo(1, 1); -+ state->lineTo(0, 1); -+ state->closePath(); -+ doPatternFill(gTrue); -+ -+ restoreState(); -+} -+ - void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, -- GBool stroke, GBool eoFill) { -+ GBool stroke, GBool eoFill, GBool text) { - GfxPatternColorSpace *patCS; - GfxColorSpace *cs; -- GfxPath *savedPath; -+ GfxState *savedState; - double xMin, yMin, xMax, yMax, x, y, x1, y1; - double cxMin, cyMin, cxMax, cyMax; - int xi0, yi0, xi1, yi1, xi, yi; -@@ -1652,28 +1823,27 @@ - imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det; - - // save current graphics state -- savedPath = state->getPath()->copy(); -- saveState(); -+ savedState = saveStateStack(); - - // set underlying color space (for uncolored tiling patterns); set - // various other parameters (stroke color, line width) to match - // Adobe's behavior -+ state->setFillPattern(NULL); -+ state->setStrokePattern(NULL); - 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()); -+ out->updateFillColor(state); -+ out->updateStrokeColor(state); - } else { - state->setFillColorSpace(new GfxDeviceGrayColorSpace()); - out->updateFillColorSpace(state); - state->setStrokeColorSpace(new GfxDeviceGrayColorSpace()); - out->updateStrokeColorSpace(state); - } -- state->setFillPattern(NULL); -- out->updateFillColor(state); -- state->setStrokePattern(NULL); -- out->updateStrokeColor(state); - if (!stroke) { - state->setLineWidth(0); - out->updateLineWidth(state); -@@ -1683,7 +1853,7 @@ - if (stroke) { - state->clipToStrokePath(); - out->clipToStrokePath(state); -- } else { -+ } else if (!text) { - state->clip(); - if (eoFill) { - out->eoClip(state); -@@ -1754,7 +1924,7 @@ - if (out->useTilingPatternFill()) { - m1[4] = m[4]; - m1[5] = m[5]; -- out->tilingPatternFill(state, tPat->getContentStream(), -+ out->tilingPatternFill(state, this, tPat->getContentStream(), - tPat->getPaintType(), tPat->getResDict(), - m1, tPat->getBBox(), - xi0, yi0, xi1, yi1, xstep, ystep); -@@ -1765,22 +1935,21 @@ - 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()); -+ drawForm(tPat->getContentStream(), tPat->getResDict(), -+ m1, tPat->getBBox()); - } - } - } - - // restore graphics state - err: -- restoreState(); -- state->setPath(savedPath); -+ restoreStateStack(savedState); - } - - void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, -- GBool stroke, GBool eoFill) { -+ GBool stroke, GBool eoFill, GBool text) { - GfxShading *shading; -- GfxPath *savedPath; -+ GfxState *savedState; - double *ctm, *btm, *ptm; - double m[6], ictm[6], m1[6]; - double xMin, yMin, xMax, yMax; -@@ -1789,27 +1958,13 @@ - shading = sPat->getShading(); - - // save current graphics state -- savedPath = state->getPath()->copy(); -- saveState(); -- -- // clip to bbox -- if (shading->getHasBBox()) { -- shading->getBBox(&xMin, &yMin, &xMax, &yMax); -- state->moveTo(xMin, yMin); -- state->lineTo(xMax, yMin); -- state->lineTo(xMax, yMax); -- state->lineTo(xMin, yMax); -- state->closePath(); -- state->clip(); -- out->clip(state); -- state->setPath(savedPath->copy()); -- } -+ savedState = saveStateStack(); - - // clip to current path - if (stroke) { - state->clipToStrokePath(); - out->clipToStrokePath(state); -- } else { -+ } else if (!text) { - state->clip(); - if (eoFill) { - out->eoClip(state); -@@ -1817,17 +1972,6 @@ - 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 -@@ -1861,11 +2005,39 @@ - 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]); - --#if 1 //~tmp: turn off anti-aliasing temporarily -- GBool vaa = out->getVectorAntialias(); -- if (vaa) { -- out->setVectorAntialias(gFalse); -+ // clip to bbox -+ if (shading->getHasBBox()) { -+ shading->getBBox(&xMin, &yMin, &xMax, &yMax); -+ state->moveTo(xMin, yMin); -+ state->lineTo(xMax, yMin); -+ state->lineTo(xMax, yMax); -+ state->lineTo(xMin, yMax); -+ state->closePath(); -+ state->clip(); -+ out->clip(state); -+ state->clearPath(); -+ } -+ -+ // 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); -+ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); -+ state->moveTo(xMin, yMin); -+ state->lineTo(xMax, yMin); -+ state->lineTo(xMax, yMax); -+ state->lineTo(xMin, yMax); -+ state->closePath(); -+ out->fill(state); -+ state->clearPath(); - } -+ -+#if 1 //~tmp: turn off anti-aliasing temporarily -+ out->setInShading(gTrue); - #endif - - // do shading type-specific operations -@@ -1890,28 +2062,28 @@ - } - - #if 1 //~tmp: turn off anti-aliasing temporarily -- if (vaa) { -- out->setVectorAntialias(gTrue); -- } -+ out->setInShading(gFalse); - #endif - - // restore graphics state -- restoreState(); -- state->setPath(savedPath); -+ restoreStateStack(savedState); - } - - void Gfx::opShFill(Object args[], int numArgs) { - GfxShading *shading; -- GfxPath *savedPath; -+ GfxState *savedState; - double xMin, yMin, xMax, yMax; - -+ if (!ocState) { -+ return; -+ } -+ - if (!(shading = res->lookupShading(args[0].getName()))) { - return; - } - - // save current graphics state -- savedPath = state->getPath()->copy(); -- saveState(); -+ savedState = saveStateStack(); - - // clip to bbox - if (shading->getHasBBox()) { -@@ -1931,10 +2103,7 @@ - out->updateFillColorSpace(state); - - #if 1 //~tmp: turn off anti-aliasing temporarily -- GBool vaa = out->getVectorAntialias(); -- if (vaa) { -- out->setVectorAntialias(gFalse); -- } -+ out->setInShading(gTrue); - #endif - - // do shading type-specific operations -@@ -1959,14 +2128,11 @@ - } - - #if 1 //~tmp: turn off anti-aliasing temporarily -- if (vaa) { -- out->setVectorAntialias(gTrue); -- } -+ out->setInShading(gFalse); - #endif - - // restore graphics state -- restoreState(); -- state->setPath(savedPath); -+ restoreStateStack(savedState); - - delete shading; - } -@@ -2100,16 +2266,16 @@ - double xMin, yMin, xMax, yMax; - double x0, y0, x1, y1; - double dx, dy, mul; -- GBool dxZero, dyZero; -+ GBool dxdyZero, horiz; - double tMin, tMax, t, tx, ty; -- double s[4], sMin, sMax, tmp; -+ double sMin, sMax, tmp; - double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1; - double t0, t1, tt; - double ta[axialMaxSplits + 1]; - int next[axialMaxSplits + 1]; - GfxColor color0, color1; - int nComps; -- int i, j, k, kk; -+ int i, j, k; - - if (out->useShadedFills() && - out->axialShadedFill(state, shading)) { -@@ -2124,9 +2290,9 @@ - shading->getCoords(&x0, &y0, &x1, &y1); - dx = x1 - x0; - dy = y1 - y0; -- dxZero = fabs(dx) < 0.01; -- dyZero = fabs(dy) < 0.01; -- if (dxZero && dyZero) { -+ dxdyZero = fabs(dx) < 0.01 && fabs(dy) < 0.01; -+ horiz = fabs(dy) < fabs(dx); -+ if (dxdyZero) { - tMin = tMax = 0; - } else { - mul = 1 / (dx * dx + dy * dy); -@@ -2170,18 +2336,16 @@ - // 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: -+ // (xMin, yMin, xMax, yMax). For -1 < |dy/dx| < 1, look at the -+ // intersection with yMin, yMax: - // -- // s0 = (xMin - tx) / -dy -- // s1 = (xMax - tx) / -dy -- // s2 = (yMin - ty) / dx -- // s3 = (yMax - ty) / dx -+ // s0 = (yMin - ty) / dx -+ // s1 = (yMax - ty) / dx - // -- // and we want the middle two s values. -+ // else look at the intersection with xMin, xMax: - // -- // In the case where dx = 0, take s0 and s1; in the case where dy = -- // 0, take s2 and s3. -+ // s0 = (xMin - tx) / -dy -+ // s1 = (xMax - tx) / -dy - // - // Each filled polygon is bounded by two of these line segments - // perpdendicular to the t axis. -@@ -2190,13 +2354,10 @@ - // 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 -+ // set up - nComps = shading->getColorSpace()->getNComps(); - ta[0] = tMin; -- next[0] = axialMaxSplits / 2; -- ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax); -- next[axialMaxSplits / 2] = axialMaxSplits; -+ next[0] = axialMaxSplits; - ta[axialMaxSplits] = tMax; - - // compute the color at t = tMin -@@ -2214,32 +2375,19 @@ - // bounding box - tx = x0 + tMin * dx; - ty = y0 + tMin * dy; -- if (dxZero && dyZero) { -+ if (dxdyZero) { - sMin = sMax = 0; -- } else 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; -+ if (horiz) { -+ sMin = (yMin - ty) / dx; -+ sMax = (yMax - ty) / dx; -+ } else { -+ sMin = (xMin - tx) / -dy; -+ sMax = (xMax - tx) / -dy; -+ } -+ if (sMin > sMax) { -+ tmp = sMin; sMin = sMax; sMax = tmp; - } -- sMin = s[1]; -- sMax = s[2]; - } - ux0 = tx - sMin * dy; - uy0 = ty + sMin * dx; -@@ -2260,15 +2408,19 @@ - } 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) { -+ // require at least two splits (to avoid problems where the -+ // color doesn't change smoothly along the t axis) -+ if (j - i <= axialMaxSplits / 4) { -+ shading->getColor(tt, &color1); -+ for (k = 0; k < nComps; ++k) { -+ if (abs(color1.c[k] - color0.c[k]) > axialColorDelta) { -+ break; -+ } -+ } -+ if (k == nComps) { - break; - } - } -- if (k == nComps) { -- break; -- } - k = (i + j) / 2; - ta[k] = 0.5 * (ta[i] + ta[j]); - next[i] = k; -@@ -2286,32 +2438,19 @@ - // bounding box - tx = x0 + ta[j] * dx; - ty = y0 + ta[j] * dy; -- if (dxZero && dyZero) { -+ if (dxdyZero) { - sMin = sMax = 0; -- } else 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; -+ if (horiz) { -+ sMin = (yMin - ty) / dx; -+ sMax = (yMax - ty) / dx; -+ } else { -+ sMin = (xMin - tx) / -dy; -+ sMax = (xMax - tx) / -dy; -+ } -+ if (sMin > sMax) { -+ tmp = sMin; sMin = sMax; sMax = tmp; - } -- sMin = s[1]; -- sMax = s[2]; - } - ux1 = tx - sMin * dy; - uy1 = ty + sMin * dx; -@@ -2348,7 +2487,10 @@ - GfxColor colorA, colorB; - double xa, ya, xb, yb, ra, rb; - double ta, tb, sa, sb; -- double sz, xz, yz, sMin, sMax; -+ double sz, sMin, sMax, h; -+ double sLeft, sRight, sTop, sBottom, sZero, sDiag; -+ GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero; -+ GBool haveSMin, haveSMax; - GBool enclosed; - int ia, ib, k, n; - double *ctm; -@@ -2367,23 +2509,23 @@ - - // Compute the point at which r(s) = 0; check for the enclosed - // circles case; and compute the angles for the tangent lines. -- if (x0 == x1 && y0 == y1) { -+ h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); -+ if (h == 0) { - enclosed = gTrue; - theta = 0; // make gcc happy - sz = 0; // make gcc happy -- } else if (r0 == r1) { -+ } else if (r1 - r0 == 0) { - enclosed = gFalse; - theta = 0; - sz = 0; // make gcc happy -+ } else if (fabs(r1 - r0) >= h) { -+ enclosed = gTrue; -+ theta = 0; // make gcc happy -+ sz = 0; // make gcc happy - } else { -+ enclosed = gFalse; - sz = -r0 / (r1 - r0); -- xz = x0 + sz * (x1 - x0); -- yz = y0 + sz * (y1 - y0); -- enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0; -- theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz))); -- if (r0 > r1) { -- theta = -theta; -- } -+ theta = asin((r1 - r0) / h); - } - if (enclosed) { - alpha = 0; -@@ -2397,59 +2539,101 @@ - sMin = 0; - sMax = 1; - } else { -- sMin = 1; -- sMax = 0; -- // solve for x(s) + r(s) = xMin -- if ((x1 + r1) - (x0 + r0) != 0) { -- sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); -- if (sa < sMin) { -- sMin = sa; -- } else if (sa > sMax) { -- sMax = sa; -- } -- } -- // solve for x(s) - r(s) = xMax -- if ((x1 - r1) - (x0 - r0) != 0) { -- sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); -- if (sa < sMin) { -- sMin = sa; -- } else if (sa > sMax) { -- sMax = sa; -- } -- } -- // solve for y(s) + r(s) = yMin -- if ((y1 + r1) - (y0 + r0) != 0) { -- sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); -- if (sa < sMin) { -- sMin = sa; -- } else if (sa > sMax) { -- sMax = sa; -- } -- } -- // solve for y(s) - r(s) = yMax -- if ((y1 - r1) - (y0 - r0) != 0) { -- sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); -- if (sa < sMin) { -- sMin = sa; -- } else if (sa > sMax) { -- sMax = sa; -- } -- } -- // check against sz -- if (r0 < r1) { -- if (sMin < sz) { -- sMin = sz; -- } -- } else if (r0 > r1) { -- if (sMax > sz) { -- sMax = sz; -- } -+ // solve x(sLeft) + r(sLeft) = xMin -+ if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) { -+ sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); -+ } else { -+ sLeft = 0; // make gcc happy - } -- // check the 'extend' flags -- if (!shading->getExtend0() && sMin < 0) { -+ // solve x(sRight) - r(sRight) = xMax -+ if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) { -+ sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); -+ } else { -+ sRight = 0; // make gcc happy -+ } -+ // solve y(sBottom) + r(sBottom) = yMin -+ if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) { -+ sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); -+ } else { -+ sBottom = 0; // make gcc happy -+ } -+ // solve y(sTop) - r(sTop) = yMax -+ if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) { -+ sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); -+ } else { -+ sTop = 0; // make gcc happy -+ } -+ // solve r(sZero) = 0 -+ if ((haveSZero = fabs(r1 - r0) > 0.000001)) { -+ sZero = -r0 / (r1 - r0); -+ } else { -+ sZero = 0; // make gcc happy -+ } -+ // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2) -+ if (haveSZero) { -+ sDiag = (sqrt((xMax - xMin) * (xMax - xMin) + -+ (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); -+ } else { -+ sDiag = 0; // make gcc happy -+ } -+ // compute sMin -+ if (shading->getExtend0()) { -+ sMin = 0; -+ haveSMin = gFalse; -+ if (x0 < x1 && haveSLeft && sLeft < 0) { -+ sMin = sLeft; -+ haveSMin = gTrue; -+ } else if (x0 > x1 && haveSRight && sRight < 0) { -+ sMin = sRight; -+ haveSMin = gTrue; -+ } -+ if (y0 < y1 && haveSBottom && sBottom < 0) { -+ if (!haveSMin || sBottom > sMin) { -+ sMin = sBottom; -+ haveSMin = gTrue; -+ } -+ } else if (y0 > y1 && haveSTop && sTop < 0) { -+ if (!haveSMin || sTop > sMin) { -+ sMin = sTop; -+ haveSMin = gTrue; -+ } -+ } -+ if (haveSZero && sZero < 0) { -+ if (!haveSMin || sZero > sMin) { -+ sMin = sZero; -+ } -+ } -+ } else { - sMin = 0; - } -- if (!shading->getExtend1() && sMax > 1) { -+ // compute sMax -+ if (shading->getExtend1()) { -+ sMax = 1; -+ haveSMax = gFalse; -+ if (x1 < x0 && haveSLeft && sLeft > 1) { -+ sMax = sLeft; -+ haveSMax = gTrue; -+ } else if (x1 > x0 && haveSRight && sRight > 1) { -+ sMax = sRight; -+ haveSMax = gTrue; -+ } -+ if (y1 < y0 && haveSBottom && sBottom > 1) { -+ if (!haveSMax || sBottom < sMax) { -+ sMax = sBottom; -+ haveSMax = gTrue; -+ } -+ } else if (y1 > y0 && haveSTop && sTop > 1) { -+ if (!haveSMax || sTop < sMax) { -+ sMax = sTop; -+ haveSMax = gTrue; -+ } -+ } -+ if (haveSZero && sDiag > 1) { -+ if (!haveSMax || sDiag < sMax) { -+ sMax = sDiag; -+ } -+ } -+ } else { - sMax = 1; - } - } -@@ -2922,6 +3106,7 @@ - out->updateTextMat(state); - out->updateTextPos(state); - fontChanged = gTrue; -+ textClipBBoxEmpty = gTrue; - } - - void Gfx::opEndText(Object args[], int numArgs) { -@@ -3028,19 +3213,23 @@ - - void Gfx::opShowText(Object args[], int numArgs) { - if (!state->getFont()) { -- error(getPos(), "No font in show"); -+ error(errSyntaxError, getPos(), "No font in show"); - return; - } - if (fontChanged) { - out->updateFont(state); - fontChanged = gFalse; - } -- out->beginStringOp(state); -- doShowText(args[0].getString()); -- out->endStringOp(state); -+ if (ocState) { -+ out->beginStringOp(state); -+ doShowText(args[0].getString()); -+ out->endStringOp(state); -+ } else { -+ doIncCharCount(args[0].getString()); -+ } - } - - void Gfx::opMoveShowText(Object args[], int numArgs) { - double tx, ty; - - if (!state->getFont()) { -@@ -3055,12 +3244,16 @@ - ty = state->getLineY() - state->getLeading(); - state->textMoveTo(tx, ty); - out->updateTextPos(state); -- out->beginStringOp(state); -- doShowText(args[0].getString()); -- out->endStringOp(state); -+ if (ocState) { -+ out->beginStringOp(state); -+ doShowText(args[0].getString()); -+ out->endStringOp(state); -+ } else { -+ doIncCharCount(args[0].getString()); -+ } - } - - void Gfx::opMoveSetShowText(Object args[], int numArgs) { - double tx, ty; - - if (!state->getFont()) { -@@ -3079,9 +3272,13 @@ - out->updateWordSpace(state); - out->updateCharSpace(state); - out->updateTextPos(state); -- out->beginStringOp(state); -- doShowText(args[2].getString()); -- out->endStringOp(state); -+ if (ocState) { -+ out->beginStringOp(state); -+ doShowText(args[2].getString()); -+ out->endStringOp(state); -+ } else { -+ doIncCharCount(args[2].getString()); -+ } - } - - void Gfx::opShowSpaceText(Object args[], int numArgs) { -@@ -3091,37 +3288,48 @@ - int i; - - if (!state->getFont()) { -- error(getPos(), "No font in show/space"); -+ error(errSyntaxError, 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 * -- fabs(state->getFontSize())); -+ if (ocState) { -+ 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()) { -+ if (wMode) { -+ state->textShift(0, -obj.getNum() * 0.001 * -+ state->getFontSize()); -+ } else { -+ state->textShift(-obj.getNum() * 0.001 * -+ state->getFontSize() * -+ state->getHorizScaling(), 0); -+ } -+ out->updateTextShift(state, obj.getNum()); -+ } else if (obj.isString()) { -+ doShowText(obj.getString()); - } else { -- state->textShift(-obj.getNum() * 0.001 * -- fabs(state->getFontSize()), 0); -+ error(errSyntaxError, getPos(), -+ "Element of show/space array must be number or string"); - } -- out->updateTextShift(state, obj.getNum()); -- } else if (obj.isString()) { -- doShowText(obj.getString()); -- } else { -- error(getPos(), "Element of show/space array must be number or string"); -+ obj.free(); -+ } -+ out->endStringOp(state); -+ } else { -+ a = args[0].getArray(); -+ for (i = 0; i < a->getLength(); ++i) { -+ a->get(i, &obj); -+ if (obj.isString()) { -+ doIncCharCount(obj.getString()); -+ } -+ obj.free(); - } -- obj.free(); - } -- out->endStringOp(state); - } - - void Gfx::doShowText(GString *s) { -@@ -3130,14 +3338,18 @@ - double riseX, riseY; - CharCode code; - Unicode u[8]; -- double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY; -+ double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, ddx, ddy; - double originX, originY, tOriginX, tOriginY; -+ double x0, y0, x1, y1; - double oldCTM[6], newCTM[6]; - double *mat; - Object charProc; - Dict *resDict; - Parser *oldParser; -+ GfxState *savedState; - char *p; -+ int render; -+ GBool patternFill; - int len, n, uLen, nChars, nSpaces, i; - - font = state->getFont(); -@@ -3147,6 +3359,28 @@ - out->beginString(state, s); - } - -+ // if we're doing a pattern fill, set up clipping -+ render = state->getRender(); -+ if (!(render & 1) && -+ state->getFillColorSpace()->getMode() == csPattern) { -+ patternFill = gTrue; -+ saveState(); -+ // disable fill, enable clipping, leave stroke unchanged -+ if ((render ^ (render >> 1)) & 1) { -+ render = 5; -+ } else { -+ render = 7; -+ } -+ state->setRender(render); -+ out->updateRender(state); -+ } else { -+ patternFill = gFalse; -+ } -+ -+ state->textTransformDelta(0, state->getRise(), &riseX, &riseY); -+ x0 = state->getCurX() + riseX; -+ y0 = state->getCurY() + riseY; -+ - // handle a Type 3 char - if (font->getType() == fontType3 && out->interpretType3Chars()) { - mat = state->getCTM(); -@@ -3169,11 +3403,8 @@ - newCTM[3] *= state->getFontSize(); - newCTM[0] *= state->getHorizScaling(); - newCTM[2] *= state->getHorizScaling(); -- state->textTransformDelta(0, state->getRise(), &riseX, &riseY); - curX = state->getCurX(); - curY = state->getCurY(); -- lineX = state->getLineX(); -- lineY = state->getLineY(); - oldParser = parser; - p = s->getCString(); - len = s->getLength(); -@@ -3189,11 +3420,12 @@ - dy *= state->getFontSize(); - state->textTransformDelta(dx, dy, &tdx, &tdy); - state->transform(curX + riseX, curY + riseY, &x, &y); -- saveState(); -+ savedState = saveStateStack(); - state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y); - //~ the CTM concat values here are wrong (but never used) - out->updateCTM(state, 1, 0, 0, 1, 0, 0); -- if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy, -+ state->transformDelta(dx, dy, &ddx, &ddy); -+ if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy, - code, u, uLen)) { - ((Gfx8BitFont *)font)->getCharProc(code, &charProc); - if ((resDict = ((Gfx8BitFont *)font)->getResources())) { -@@ -3210,20 +3443,16 @@ - } - charProc.free(); - } -- restoreState(); -- // GfxState::restore() does *not* restore the current position, -- // so we deal with it here using (curX, curY) and (lineX, lineY) -+ restoreStateStack(savedState); - curX += tdx; - curY += tdy; - state->moveTo(curX, curY); -- state->textSetPos(lineX, lineY); - p += n; - len -= n; - } - parser = oldParser; - - } else if (out->useDrawChar()) { -- state->textTransformDelta(0, state->getRise(), &riseX, &riseY); - p = s->getCString(); - len = s->getLength(); - while (len > 0) { -@@ -3294,9 +3523,52 @@ - out->endString(state); - } - -+ if (patternFill) { -+ out->saveTextPos(state); -+ // tell the OutputDev to do the clipping -+ out->endTextObject(state); -+ // set up a clipping bbox so doPatternText will work -- assume -+ // that the text bounding box does not extend past the baseline in -+ // any direction by more than twice the font size -+ x1 = state->getCurX() + riseX; -+ y1 = state->getCurY() + riseY; -+ if (x0 > x1) { -+ x = x0; x0 = x1; x1 = x; -+ } -+ if (y0 > y1) { -+ y = y0; y0 = y1; y1 = y; -+ } -+ state->textTransformDelta(0, state->getFontSize(), &dx, &dy); -+ state->textTransformDelta(state->getFontSize(), 0, &dx2, &dy2); -+ dx = fabs(dx); -+ dx2 = fabs(dx2); -+ if (dx2 > dx) { -+ dx = dx2; -+ } -+ dy = fabs(dy); -+ dy2 = fabs(dy2); -+ if (dy2 > dy) { -+ dy = dy2; -+ } -+ state->clipToRect(x0 - 2 * dx, y0 - 2 * dy, x1 + 2 * dx, y1 + 2 * dy); -+ // set render mode to fill-only -+ state->setRender(0); -+ out->updateRender(state); -+ doPatternText(); -+ restoreState(); -+ out->restoreTextPos(state); -+ } -+ - updateLevel += 10 * s->getLength(); - } - -+// NB: this is only called when ocState is false. -+void Gfx::doIncCharCount(GString *s) { -+ if (out->needCharCount()) { -+ out->incCharCount(s->getLength()); -+ } -+} -+ - //------------------------------------------------------------------------ - // XObject operators - //------------------------------------------------------------------------ -@@ -3308,8 +3580,11 @@ - Object opiDict; - #endif - -+ if (!ocState && !out->needCharCount()) { -+ return; -+ } - name = args[0].getName(); - if (!res->lookupXObject(name, &obj1)) { - return; - } - if (!obj1.isStream()) { -@@ -3373,7 +3650,7 @@ - GBool maskInvert; - Stream *maskStr; - Object obj1, obj2; -- int i; -+ int i, n; - - // get info from the stream - bits = 0; -@@ -3389,19 +3666,27 @@ - obj1.free(); - dict->lookup("W", &obj1); - } -- if (!obj1.isInt()) -+ if (!obj1.isInt()) { - goto err2; -+ } - width = obj1.getInt(); - obj1.free(); -+ if (width <= 0) { -+ goto err1; -+ } - dict->lookup("Height", &obj1); - if (obj1.isNull()) { - obj1.free(); - dict->lookup("H", &obj1); - } -- if (!obj1.isInt()) -+ if (!obj1.isInt()) { - goto err2; -+ } - height = obj1.getInt(); - obj1.free(); -+ if (height <= 0) { -+ goto err1; -+ } - - // image or mask? - dict->lookup("ImageMask", &obj1); -@@ -3447,16 +3732,30 @@ - } - if (obj1.isArray()) { - obj1.arrayGet(0, &obj2); -- if (obj2.isInt() && obj2.getInt() == 1) -- invert = gTrue; -+ invert = obj2.isNum() && obj2.getNum() == 1; - obj2.free(); - } else if (!obj1.isNull()) { - goto err2; - } - obj1.free(); - -+ // if drawing is disabled, skip over inline image data -+ if (!ocState) { -+ str->reset(); -+ n = height * ((width + 7) / 8); -+ for (i = 0; i < n; ++i) { -+ str->getChar(); -+ } -+ str->close(); -+ - // draw it -- out->drawImageMask(state, ref, str, width, height, invert, inlineImg); -+ } else { -+ if (state->getFillColorSpace()->getMode() == csPattern) { -+ doPatternImageMask(ref, str, width, height, invert, inlineImg); -+ } else { -+ out->drawImageMask(state, ref, str, width, height, invert, inlineImg); -+ } -+ } - - } else { - -@@ -3581,14 +3880,36 @@ - haveSoftMask = gTrue; - } else if (maskObj.isArray()) { - // color key mask -+ haveColorKeyMask = gTrue; - for (i = 0; -- i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps; -- ++i) { -+ i+1 < maskObj.arrayGetLength() && i+1 < 2*gfxColorMaxComps; -+ i += 2) { - maskObj.arrayGet(i, &obj1); -+ if (!obj1.isInt()) { -+ obj1.free(); -+ haveColorKeyMask = gFalse; -+ break; -+ } - maskColors[i] = obj1.getInt(); - obj1.free(); -+ if (maskColors[i] < 0 || maskColors[i] >= (1 << bits)) { -+ haveColorKeyMask = gFalse; -+ break; -+ } -+ maskObj.arrayGet(i+1, &obj1); -+ if (!obj1.isInt()) { -+ obj1.free(); -+ haveColorKeyMask = gFalse; -+ break; -+ } -+ maskColors[i+1] = obj1.getInt(); -+ obj1.free(); -+ if (maskColors[i+1] < 0 || maskColors[i+1] >= (1 << bits) || -+ maskColors[i] > maskColors[i+1]) { -+ haveColorKeyMask = gFalse; -+ break; -+ } - } -- haveColorKeyMask = gTrue; - } else if (maskObj.isStream()) { - // explicit mask - if (inlineImg) { -@@ -3633,9 +3954,7 @@ - } - if (obj1.isArray()) { - obj1.arrayGet(0, &obj2); -- if (obj2.isInt() && obj2.getInt() == 1) { -- maskInvert = gTrue; -- } -+ maskInvert = obj2.isNum() && obj2.getNum() == 1; - obj2.free(); - } else if (!obj1.isNull()) { - goto err2; -@@ -3644,20 +3963,32 @@ - haveExplicitMask = gTrue; - } - -+ // if drawing is disabled, skip over inline image data -+ if (!ocState) { -+ str->reset(); -+ n = height * ((width * colorMap->getNumPixelComps() * -+ colorMap->getBits() + 7) / 8); -+ for (i = 0; i < n; ++i) { -+ str->getChar(); -+ } -+ str->close(); -+ - // draw it -- 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); -+ 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; - -+ delete colorMap; - maskObj.free(); - smaskObj.free(); - } -@@ -3683,11 +4014,12 @@ - double m[6], bbox[4]; - Object resObj; - Dict *resDict; -+ GBool oc, ocSaved; - Object obj1, obj2, obj3; - int i; - - // check for excessive recursion - if (formDepth > 100) { - return; - } - -@@ -3697,7 +4029,20 @@ - // check form type - dict->lookup("FormType", &obj1); - if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) { - error(errSyntaxError, getPos(), "Unknown form type"); -+ } -+ obj1.free(); -+ -+ // check for optional content key -+ ocSaved = ocState; -+ dict->lookupNF("OC", &obj1); -+ if (doc->getOptionalContent()->evalOCObject(&obj1, &oc) && !oc) { -+ obj1.free(); -+ if (out->needCharCount()) { -+ ocState = gFalse; -+ } else { -+ return; -+ } - } - obj1.free(); - -@@ -3705,7 +4050,8 @@ - dict->lookup("BBox", &bboxObj); - if (!bboxObj.isArray()) { - bboxObj.free(); - error(errSyntaxError, getPos(), "Bad form bounding box"); -+ ocState = ocSaved; - return; - } - for (i = 0; i < 4; ++i) { -@@ -3759,23 +4105,26 @@ - - // draw it - ++formDepth; -- doForm1(str, resDict, m, bbox, -- transpGroup, gFalse, blendingColorSpace, isolated, knockout); -+ drawForm(str, resDict, m, bbox, -+ transpGroup, gFalse, blendingColorSpace, isolated, knockout); - --formDepth; - - if (blendingColorSpace) { - delete blendingColorSpace; - } - resObj.free(); -+ -+ ocState = ocSaved; - } - --void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox, -- GBool transpGroup, GBool softMask, -- GfxColorSpace *blendingColorSpace, -- GBool isolated, GBool knockout, -- GBool alpha, Function *transferFunc, -- GfxColor *backdropColor) { -+void Gfx::drawForm(Object *str, Dict *resDict, double *matrix, double *bbox, -+ GBool transpGroup, GBool softMask, -+ GfxColorSpace *blendingColorSpace, -+ GBool isolated, GBool knockout, -+ GBool alpha, Function *transferFunc, -+ GfxColor *backdropColor) { - Parser *oldParser; -+ GfxState *savedState; - double oldBaseMatrix[6]; - int i; - -@@ -3783,7 +4132,7 @@ - pushResources(resDict); - - // save current graphics state -- saveState(); -+ savedState = saveStateStack(); - - // kill any pre-existing path - state->clearPath(); -@@ -3847,7 +4196,7 @@ - parser = oldParser; - - // restore graphics state -- restoreState(); -+ restoreStateStack(savedState); - - // pop resource stack - popResources(); -@@ -3869,6 +4218,9 @@ - Stream *str; - int c1, c2; - -+ // NB: this function is run even if ocState is false -- doImage() is -+ // responsible for skipping over the inline image data -+ - // build dict/stream - str = buildImageStream(); - -@@ -3921,18 +4274,23 @@ - obj.free(); - - // make stream -- str = new EmbedStream(parser->getStream(), &dict, gFalse, 0); -+ if (!(str = parser->getStream())) { -+ error(errSyntaxError, getPos(), "Invalid inline image data"); -+ dict.free(); -+ return NULL; -+ } -+ str = new EmbedStream(str, &dict, gFalse, 0); - str = str->addFilters(&dict); - - return str; - } - - void Gfx::opImageData(Object args[], int numArgs) { - error(errInternal, getPos(), "Got 'ID' operator"); - } - - void Gfx::opEndImage(Object args[], int numArgs) { - error(errInternal, getPos(), "Got 'EI' operator"); - } - - //------------------------------------------------------------------------ -@@ -3967,23 +4325,88 @@ - //------------------------------------------------------------------------ - - void Gfx::opBeginMarkedContent(Object args[], int numArgs) { -+ GfxMarkedContent *mc; -+ Object obj; -+ GBool ocStateNew; -+ GString *s; -+ Unicode *u; -+ int uLen, i; -+ GfxMarkedContentKind mcKind; -+ - if (printCommands) { - printf(" marked content: %s ", args[0].getName()); -- if (numArgs == 2) -- args[2].print(stdout); -+ if (numArgs == 2) { -+ args[1].print(stdout); -+ } - printf("\n"); - fflush(stdout); - } -+ mcKind = gfxMCOther; -+ if (args[0].isName("OC") && numArgs == 2 && args[1].isName() && -+ res->lookupPropertiesNF(args[1].getName(), &obj)) { -+ if (doc->getOptionalContent()->evalOCObject(&obj, &ocStateNew)) { -+ ocState = ocStateNew; -+ } -+ obj.free(); -+ mcKind = gfxMCOptionalContent; - } - - void Gfx::opEndMarkedContent(Object args[], int numArgs) { -+ GfxMarkedContent *mc; -+ GfxMarkedContentKind mcKind; -+ -+ if (markedContentStack->getLength() > 0) { -+ mc = (GfxMarkedContent *) -+ markedContentStack->del(markedContentStack->getLength() - 1); -+ mcKind = mc->kind; -+ delete mc; -+ if (mcKind == gfxMCOptionalContent) { -+ if (markedContentStack->getLength() > 0) { -+ mc = (GfxMarkedContent *) -+ markedContentStack->get(markedContentStack->getLength() - 1); -+ ocState = mc->ocState; -+ } else { -+ ocState = gTrue; -+ } - - - void Gfx::opMarkPoint(Object args[], int numArgs) { - if (printCommands) { - printf(" mark point: %s ", args[0].getName()); - if (numArgs == 2) -- args[2].print(stdout); -+ args[1].print(stdout); - printf("\n"); - fflush(stdout); - } -@@ -3996,45 +4419,23 @@ - dict->lookup("Resources", &resObj); - resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL; - - // draw it -- doForm1(str, resDict, m, bbox); -+ drawForm(str, resDict, m, bbox); - - resObj.free(); - } -@@ -4168,6 +4598,27 @@ - out->restoreState(state); - } - -+// Create a new state stack, and initialize it with a copy of the -+// current state. -+GfxState *Gfx::saveStateStack() { -+ GfxState *oldState; -+ -+ out->saveState(state); -+ oldState = state; -+ state = state->copy(gTrue); -+ return oldState; -+} -+ -+// Switch back to the previous state stack. -+void Gfx::restoreStateStack(GfxState *oldState) { -+ while (state->hasSaves()) { -+ restoreState(); -+ } -+ delete state; -+ state = oldState; -+ out->restoreState(state); -+} -+ - void Gfx::pushResources(Dict *resDict) { - res = new GfxResources(xref, resDict, res); - } -diff -ru xpdf-3.02/xpdf/GfxFont.cc xpdf-3.03/xpdf/GfxFont.cc ---- xpdf-3.02/xpdf/GfxFont.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/GfxFont.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -16,6 +16,11 @@ - #include - #include - #include -+#include -+#include -+#if HAVE_STD_SORT -+#include -+#endif - #include "gmem.h" - #include "Error.h" - #include "Object.h" -@@ -25,6 +30,7 @@ - #include "CharCodeToUnicode.h" - #include "FontEncodingTables.h" - #include "BuiltinFontTables.h" -+#include "FoFiIdentifier.h" - #include "FoFiType1.h" - #include "FoFiType1C.h" - #include "FoFiTrueType.h" -@@ -93,15 +107,66 @@ - GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) { - GString *nameA; -+ Ref embFontIDA; -+ GfxFontType typeA; - GfxFont *font; - Object obj1; - -@@ -113,53 +178,235 @@ - } - obj1.free(); - -- // get font type -+ // get embedded font ID and font type -+ typeA = getFontType(xref, fontDict, &embFontIDA); -+ -+ // create the font object - font = NULL; -- fontDict->lookup("Subtype", &obj1); -- if (obj1.isName("Type1") || obj1.isName("MMType1")) { -- font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1, fontDict); -- } else if (obj1.isName("Type1C")) { -- font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1C, fontDict); -- } else if (obj1.isName("Type3")) { -- font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType3, fontDict); -- } else if (obj1.isName("TrueType")) { -- font = new Gfx8BitFont(xref, tagA, idA, nameA, fontTrueType, fontDict); -- } else if (obj1.isName("Type0")) { -- font = new GfxCIDFont(xref, tagA, idA, nameA, fontDict); -+ if (typeA < fontCIDType0) { -+ font = new Gfx8BitFont(xref, tagA, idA, nameA, typeA, embFontIDA, -+ fontDict); - } else { -- error(-1, "Unknown font type: '%s'", -- obj1.isName() ? obj1.getName() : "???"); -- font = new Gfx8BitFont(xref, tagA, idA, nameA, fontUnknownType, fontDict); -+ font = new GfxCIDFont(xref, tagA, idA, nameA, typeA, embFontIDA, -+ fontDict); - } -- obj1.free(); - - return font; - } - --GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA) { -+GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA, -+ GfxFontType typeA, Ref embFontIDA) { - ok = gFalse; - tag = new GString(tagA); - id = idA; - name = nameA; -+ type = typeA; -+ embFontID = embFontIDA; - embFontName = NULL; -- extFontFile = NULL; - } - - GfxFont::~GfxFont() { - delete tag; - if (name) { - delete name; - } - if (embFontName) { - delete embFontName; - } -- if (extFontFile) { -- delete extFontFile; -+} -+ -+// This function extracts three pieces of information: -+// 1. the "expected" font type, i.e., the font type implied by -+// Font.Subtype, DescendantFont.Subtype, and -+// FontDescriptor.FontFile3.Subtype -+// 2. the embedded font object ID -+// 3. the actual font type - determined by examining the embedded font -+// if there is one, otherwise equal to the expected font type -+// If the expected and actual font types don't match, a warning -+// message is printed. The expected font type is not used for -+// anything else. -+GfxFontType GfxFont::getFontType(XRef *xref, Dict *fontDict, Ref *embID) { -+ GfxFontType t, expectedType; -+ FoFiIdentifierType fft; -+ Dict *fontDict2; -+ Object subtype, fontDesc, obj1, obj2, obj3, obj4; -+ GBool isType0, err; -+ -+ t = fontUnknownType; -+ embID->num = embID->gen = -1; -+ err = gFalse; -+ -+ fontDict->lookup("Subtype", &subtype); -+ expectedType = fontUnknownType; -+ isType0 = gFalse; -+ if (subtype.isName("Type1") || subtype.isName("MMType1")) { -+ expectedType = fontType1; -+ } else if (subtype.isName("Type1C")) { -+ expectedType = fontType1C; -+ } else if (subtype.isName("Type3")) { -+ expectedType = fontType3; -+ } else if (subtype.isName("TrueType")) { -+ expectedType = fontTrueType; -+ } else if (subtype.isName("Type0")) { -+ isType0 = gTrue; -+ } else { -+ error(errSyntaxWarning, -1, "Unknown font type: '{0:s}'", -+ subtype.isName() ? subtype.getName() : "???"); -+ } -+ subtype.free(); -+ -+ fontDict2 = fontDict; -+ if (fontDict->lookup("DescendantFonts", &obj1)->isArray()) { -+ if (obj1.arrayGetLength() == 0) { -+ error(errSyntaxWarning, -1, "Empty DescendantFonts array in font"); -+ obj2.initNull(); -+ } else if (obj1.arrayGet(0, &obj2)->isDict()) { -+ if (!isType0) { -+ error(errSyntaxWarning, -1, "Non-CID font with DescendantFonts array"); -+ } -+ fontDict2 = obj2.getDict(); -+ fontDict2->lookup("Subtype", &subtype); -+ if (subtype.isName("CIDFontType0")) { -+ if (isType0) { -+ expectedType = fontCIDType0; -+ } -+ } else if (subtype.isName("CIDFontType2")) { -+ if (isType0) { -+ expectedType = fontCIDType2; -+ } -+ } -+ subtype.free(); -+ } -+ } else { -+ obj2.initNull(); -+ } -+ -+ if (fontDict2->lookup("FontDescriptor", &fontDesc)->isDict()) { -+ if (fontDesc.dictLookupNF("FontFile", &obj3)->isRef()) { -+ *embID = obj3.getRef(); -+ if (expectedType != fontType1) { -+ err = gTrue; -+ } -+ } -+ obj3.free(); -+ if (embID->num == -1 && -+ fontDesc.dictLookupNF("FontFile2", &obj3)->isRef()) { -+ *embID = obj3.getRef(); -+ if (isType0) { -+ expectedType = fontCIDType2; -+ } else if (expectedType != fontTrueType) { -+ err = gTrue; -+ } -+ } -+ obj3.free(); -+ if (embID->num == -1 && -+ fontDesc.dictLookupNF("FontFile3", &obj3)->isRef()) { -+ *embID = obj3.getRef(); -+ if (obj3.fetch(xref, &obj4)->isStream()) { -+ obj4.streamGetDict()->lookup("Subtype", &subtype); -+ if (subtype.isName("Type1")) { -+ if (expectedType != fontType1) { -+ err = gTrue; -+ expectedType = isType0 ? fontCIDType0 : fontType1; -+ } -+ } else if (subtype.isName("Type1C")) { -+ if (expectedType == fontType1) { -+ expectedType = fontType1C; -+ } else if (expectedType != fontType1C) { -+ err = gTrue; -+ expectedType = isType0 ? fontCIDType0C : fontType1C; -+ } -+ } else if (subtype.isName("TrueType")) { -+ if (expectedType != fontTrueType) { -+ err = gTrue; -+ expectedType = isType0 ? fontCIDType2 : fontTrueType; -+ } -+ } else if (subtype.isName("CIDFontType0C")) { -+ if (expectedType == fontCIDType0) { -+ expectedType = fontCIDType0C; -+ } else { -+ err = gTrue; -+ expectedType = isType0 ? fontCIDType0C : fontType1C; -+ } -+ } else if (subtype.isName("OpenType")) { -+ if (expectedType == fontTrueType) { -+ expectedType = fontTrueTypeOT; -+ } else if (expectedType == fontType1) { -+ expectedType = fontType1COT; -+ } else if (expectedType == fontCIDType0) { -+ expectedType = fontCIDType0COT; -+ } else if (expectedType == fontCIDType2) { -+ expectedType = fontCIDType2OT; -+ } else { -+ err = gTrue; -+ } -+ } else { -+ error(errSyntaxError, -1, "Unknown font type '{0:s}'", -+ subtype.isName() ? subtype.getName() : "???"); -+ } -+ subtype.free(); -+ } -+ obj4.free(); -+ } -+ obj3.free(); -+ } -+ fontDesc.free(); -+ -+ t = fontUnknownType; -+ if (embID->num >= 0) { -+ obj3.initRef(embID->num, embID->gen); -+ obj3.fetch(xref, &obj4); -+ if (obj4.isStream()) { -+ obj4.streamReset(); -+ fft = FoFiIdentifier::identifyStream(&readFromStream, obj4.getStream()); -+ obj4.streamClose(); -+ switch (fft) { -+ case fofiIdType1PFA: -+ case fofiIdType1PFB: -+ t = fontType1; -+ break; -+ case fofiIdCFF8Bit: -+ t = isType0 ? fontCIDType0C : fontType1C; -+ break; -+ case fofiIdCFFCID: -+ t = fontCIDType0C; -+ break; -+ case fofiIdTrueType: -+ case fofiIdTrueTypeCollection: -+ t = isType0 ? fontCIDType2 : fontTrueType; -+ break; -+ case fofiIdOpenTypeCFF8Bit: -+ t = isType0 ? fontCIDType0COT : fontType1COT; -+ break; -+ case fofiIdOpenTypeCFFCID: -+ t = fontCIDType0COT; -+ break; -+ default: -+ error(errSyntaxError, -1, "Embedded font file may be invalid"); -+ break; -+ } -+ } -+ obj4.free(); -+ obj3.free(); -+ } -+ -+ if (t == fontUnknownType) { -+ t = expectedType; - } -+ -+ if (t != expectedType) { -+ err = gTrue; -+ } -+ -+ if (err) { -+ error(errSyntaxWarning, -1, -+ "Mismatch between font type and embedded font file"); -+ } -+ -+ obj2.free(); -+ obj1.free(); -+ -+ return t; - } - - void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) { -@@ -170,8 +417,6 @@ - // assume Times-Roman by default (for substitution purposes) - flags = fontSerif; - -- embFontID.num = -1; -- embFontID.gen = -1; - missingWidth = 0; - - if (fontDict->lookup("FontDescriptor", &obj1)->isDict()) { -@@ -189,75 +434,6 @@ - } - obj2.free(); - -- // look for embedded font file -- if (obj1.dictLookupNF("FontFile", &obj2)->isRef()) { -- embFontID = obj2.getRef(); -- if (type != fontType1) { -- error(-1, "Mismatch between font type and embedded font file"); -- type = fontType1; -- } -- } -- obj2.free(); -- if (embFontID.num == -1 && -- obj1.dictLookupNF("FontFile2", &obj2)->isRef()) { -- embFontID = obj2.getRef(); -- if (type != fontTrueType && type != fontCIDType2) { -- error(-1, "Mismatch between font type and embedded font file"); -- type = type == fontCIDType0 ? fontCIDType2 : fontTrueType; -- } -- } -- obj2.free(); -- if (embFontID.num == -1 && -- obj1.dictLookupNF("FontFile3", &obj2)->isRef()) { -- if (obj2.fetch(xref, &obj3)->isStream()) { -- obj3.streamGetDict()->lookup("Subtype", &obj4); -- if (obj4.isName("Type1")) { -- embFontID = obj2.getRef(); -- if (type != fontType1) { -- error(-1, "Mismatch between font type and embedded font file"); -- type = fontType1; -- } -- } else if (obj4.isName("Type1C")) { -- embFontID = obj2.getRef(); -- if (type != fontType1 && type != fontType1C) { -- error(-1, "Mismatch between font type and embedded font file"); -- } -- type = fontType1C; -- } else if (obj4.isName("TrueType")) { -- embFontID = obj2.getRef(); -- if (type != fontTrueType) { -- error(-1, "Mismatch between font type and embedded font file"); -- type = fontTrueType; -- } -- } else if (obj4.isName("CIDFontType0C")) { -- embFontID = obj2.getRef(); -- if (type != fontCIDType0) { -- error(-1, "Mismatch between font type and embedded font file"); -- } -- type = fontCIDType0C; -- } else if (obj4.isName("OpenType")) { -- embFontID = obj2.getRef(); -- if (type == fontTrueType) { -- type = fontTrueTypeOT; -- } else if (type == fontType1) { -- type = fontType1COT; -- } else if (type == fontCIDType0) { -- type = fontCIDType0COT; -- } else if (type == fontCIDType2) { -- type = fontCIDType2OT; -- } else { -- error(-1, "Mismatch between font type and embedded font file"); -- } -- } else { -- error(-1, "Unknown embedded font type '%s'", -- obj4.isName() ? obj4.getName() : "???"); -- } -- obj4.free(); -- } -- obj3.free(); -- } -- obj2.free(); -- - // look for MissingWidth - obj1.dictLookup("MissingWidth", &obj2); - if (obj2.isNum()) { -@@ -269,8 +445,13 @@ - obj1.dictLookup("Ascent", &obj2); - if (obj2.isNum()) { - t = 0.001 * obj2.getNum(); -- // some broken font descriptors set ascent and descent to 0 -- if (t != 0) { -+ // some broken font descriptors specify a negative ascent -+ if (t < 0) { -+ t = -t; -+ } -+ // some broken font descriptors set ascent and descent to 0; -+ // others set it to ridiculous values (e.g., 32768) -+ if (t != 0 && t < 3) { - ascent = t; - } - } -@@ -278,14 +459,14 @@ - obj1.dictLookup("Descent", &obj2); - if (obj2.isNum()) { - t = 0.001 * obj2.getNum(); -+ // some broken font descriptors specify a positive descent -+ if (t > 0) { -+ t = -t; -+ } - // some broken font descriptors set ascent and descent to 0 -- if (t != 0) { -+ if (t != 0 && t > -3) { - descent = t; - } -- // some broken font descriptors specify a positive descent -- if (descent > 0) { -- descent = -descent; -- } - } - obj2.free(); - -@@ -330,37 +511,280 @@ - return ctu; - } - --void GfxFont::findExtFontFile() { -- static char *type1Exts[] = { ".pfa", ".pfb", ".ps", "", NULL }; -- static char *ttExts[] = { ".ttf", NULL }; -+GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) { -+ GfxFontLoc *fontLoc; -+ SysFontType sysFontType; -+ GString *path, *base14Name, *substName; -+ PSFontParam16 *psFont16; -+ Object refObj, embFontObj; -+ int substIdx, fontNum; -+ GBool embed; - -- if (name) { -- if (type == fontType1) { -- extFontFile = globalParams->findFontFile(name, type1Exts); -- } else if (type == fontTrueType) { -- extFontFile = globalParams->findFontFile(name, ttExts); -+ if (type == fontType3) { -+ return NULL; -+ } -+ -+ //----- embedded font -+ if (embFontID.num >= 0) { -+ embed = gTrue; -+ refObj.initRef(embFontID.num, embFontID.gen); -+ refObj.fetch(xref, &embFontObj); -+ if (!embFontObj.isStream()) { -+ error(errSyntaxError, -1, "Embedded font object is wrong type"); -+ embed = gFalse; -+ } -+ embFontObj.free(); -+ refObj.free(); -+ if (embed) { -+ if (ps) { -+ switch (type) { -+ case fontType1: -+ case fontType1C: -+ case fontType1COT: -+ embed = globalParams->getPSEmbedType1(); -+ break; -+ case fontTrueType: -+ case fontTrueTypeOT: -+ embed = globalParams->getPSEmbedTrueType(); -+ break; -+ case fontCIDType0C: -+ case fontCIDType0COT: -+ embed = globalParams->getPSEmbedCIDPostScript(); -+ break; -+ case fontCIDType2: -+ case fontCIDType2OT: -+ embed = globalParams->getPSEmbedCIDTrueType(); -+ break; -+ default: -+ break; -+ } -+ } -+ if (embed) { -+ fontLoc = new GfxFontLoc(); -+ fontLoc->locType = gfxFontLocEmbedded; -+ fontLoc->fontType = type; -+ fontLoc->embFontID = embFontID; -+ return fontLoc; -+ } -+ } -+ } -+ -+ //----- PS passthrough -+ if (ps && !isCIDFont() && globalParams->getPSFontPassthrough()) { -+ fontLoc = new GfxFontLoc(); -+ fontLoc->locType = gfxFontLocResident; -+ fontLoc->fontType = fontType1; -+ fontLoc->path = name->copy(); -+ return fontLoc; -+ } -+ -+ //----- PS resident Base-14 font -+ if (ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) { -+ fontLoc = new GfxFontLoc(); -+ fontLoc->locType = gfxFontLocResident; -+ fontLoc->fontType = fontType1; -+ fontLoc->path = new GString(((Gfx8BitFont *)this)->base14->base14Name); -+ return fontLoc; -+ } -+ -+ //----- external font file (fontFile, fontDir) -+ if ((path = globalParams->findFontFile(name))) { -+ if ((fontLoc = getExternalFont(path, isCIDFont()))) { -+ return fontLoc; -+ } -+ } -+ -+ //----- external font file for Base-14 font -+ if (!ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) { -+ base14Name = new GString(((Gfx8BitFont *)this)->base14->base14Name); -+ if ((path = globalParams->findFontFile(base14Name))) { -+ if ((fontLoc = getExternalFont(path, gFalse))) { -+ delete base14Name; -+ return fontLoc; -+ } -+ } -+ delete base14Name; -+ } -+ -+ //----- system font -+ if ((path = globalParams->findSystemFontFile(name, &sysFontType, -+ &fontNum))) { -+ if (isCIDFont()) { -+ if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) { -+ fontLoc = new GfxFontLoc(); -+ fontLoc->locType = gfxFontLocExternal; -+ fontLoc->fontType = fontCIDType2; -+ fontLoc->path = path; -+ fontLoc->fontNum = fontNum; -+ return fontLoc; -+ } -+ } else { -+ if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) { -+ fontLoc = new GfxFontLoc(); -+ fontLoc->locType = gfxFontLocExternal; -+ fontLoc->fontType = fontTrueType; -+ fontLoc->path = path; -+ return fontLoc; -+ } else if (sysFontType == sysFontPFA || sysFontType == sysFontPFB) { -+ fontLoc = new GfxFontLoc(); -+ fontLoc->locType = gfxFontLocExternal; -+ fontLoc->fontType = fontType1; -+ fontLoc->path = path; -+ fontLoc->fontNum = fontNum; -+ return fontLoc; -+ } -+ } -+ delete path; -+ } -+ -+ if (!isCIDFont()) { -+ -+ //----- 8-bit PS resident font -+ if (ps) { -+ if ((path = globalParams->getPSResidentFont(name))) { -+ fontLoc = new GfxFontLoc(); -+ fontLoc->locType = gfxFontLocResident; -+ fontLoc->fontType = fontType1; -+ fontLoc->path = path; -+ return fontLoc; -+ } -+ } -+ -+ //----- 8-bit font substitution -+ if (flags & fontFixedWidth) { -+ substIdx = 0; -+ } else if (flags & fontSerif) { -+ substIdx = 8; -+ } else { -+ substIdx = 4; -+ } -+ if (isBold()) { -+ substIdx += 2; -+ } -+ if (isItalic()) { -+ substIdx += 1; -+ } -+ substName = new GString(base14SubstFonts[substIdx]); -+ if (ps) { -+ error(errSyntaxWarning, -1, "Substituting font '{0:s}' for '{1:t}'", -+ base14SubstFonts[substIdx], name); -+ fontLoc = new GfxFontLoc(); -+ fontLoc->locType = gfxFontLocResident; -+ fontLoc->fontType = fontType1; -+ fontLoc->path = substName; -+ fontLoc->substIdx = substIdx; -+ return fontLoc; -+ } else { -+ path = globalParams->findFontFile(substName); -+ delete substName; -+ if (path) { -+ if ((fontLoc = getExternalFont(path, gFalse))) { -+ error(errSyntaxWarning, -1, "Substituting font '{0:s}' for '{1:t}'", -+ base14SubstFonts[substIdx], name); -+ fontLoc->substIdx = substIdx; -+ return fontLoc; -+ } -+ } -+ } -+ -+ // failed to find a substitute font -+ return NULL; -+ } -+ -+ //----- 16-bit PS resident font -+ if (ps && ((psFont16 = globalParams->getPSResidentFont16( -+ name, -+ ((GfxCIDFont *)this)->getWMode())))) { -+ fontLoc = new GfxFontLoc(); -+ fontLoc->locType = gfxFontLocResident; -+ fontLoc->fontType = fontCIDType0; // this is not used -+ fontLoc->path = psFont16->psFontName->copy(); -+ fontLoc->encoding = psFont16->encoding->copy(); -+ fontLoc->wMode = psFont16->wMode; -+ return fontLoc; -+ } -+ if (ps && ((psFont16 = globalParams->getPSResidentFontCC( -+ ((GfxCIDFont *)this)->getCollection(), -+ ((GfxCIDFont *)this)->getWMode())))) { -+ error(errSyntaxWarning, -1, "Substituting font '{0:t}' for '{1:t}'", -+ psFont16->psFontName, name); -+ fontLoc = new GfxFontLoc(); -+ fontLoc->locType = gfxFontLocResident; -+ fontLoc->fontType = fontCIDType0; // this is not used -+ fontLoc->path = psFont16->psFontName->copy(); -+ fontLoc->encoding = psFont16->encoding->copy(); -+ fontLoc->wMode = psFont16->wMode; -+ return fontLoc; -+ } -+ -+ //----- CID font substitution -+ if ((path = globalParams->findCCFontFile( -+ ((GfxCIDFont *)this)->getCollection()))) { -+ if ((fontLoc = getExternalFont(path, gTrue))) { -+ error(errSyntaxWarning, -1, "Substituting font '{0:t}' for '{1:t}'", -+ fontLoc->path, name); -+ return fontLoc; - } - } -+ -+ // failed to find a substitute font -+ return NULL; - } - --char *GfxFont::readExtFontFile(int *len) { -- FILE *f; -- char *buf; -+GfxFontLoc *GfxFont::locateBase14Font(GString *base14Name) { -+ GString *path; - -- if (!(f = fopen(extFontFile->getCString(), "rb"))) { -- error(-1, "External font file '%s' vanished", extFontFile->getCString()); -+ path = globalParams->findFontFile(base14Name); -+ if (!path) { - return NULL; - } -- fseek(f, 0, SEEK_END); -- *len = (int)ftell(f); -- fseek(f, 0, SEEK_SET); -- buf = (char *)gmalloc(*len); -- if ((int)fread(buf, 1, *len, f) != *len) { -- error(-1, "Error reading external font file '%s'", -- extFontFile->getCString()); -+ return getExternalFont(path, gFalse); -+} -+ -+GfxFontLoc *GfxFont::getExternalFont(GString *path, GBool cid) { -+ FoFiIdentifierType fft; -+ GfxFontType fontType; -+ GfxFontLoc *fontLoc; -+ -+ fft = FoFiIdentifier::identifyFile(path->getCString()); -+ switch (fft) { -+ case fofiIdType1PFA: -+ case fofiIdType1PFB: -+ fontType = fontType1; -+ break; -+ case fofiIdCFF8Bit: -+ fontType = fontType1C; -+ break; -+ case fofiIdCFFCID: -+ fontType = fontCIDType0C; -+ break; -+ case fofiIdTrueType: -+ case fofiIdTrueTypeCollection: -+ fontType = cid ? fontCIDType2 : fontTrueType; -+ break; -+ case fofiIdOpenTypeCFF8Bit: -+ fontType = fontType1COT; -+ break; -+ case fofiIdOpenTypeCFFCID: -+ fontType = fontCIDType0COT; -+ break; -+ case fofiIdUnknown: -+ case fofiIdError: -+ default: -+ fontType = fontUnknownType; -+ break; -+ } -+ if (fontType == fontUnknownType || -+ (cid ? (fontType < fontCIDType0) -+ : (fontType >= fontCIDType0))) { -+ delete path; -+ return NULL; - } -- fclose(f); -- return buf; -+ fontLoc = new GfxFontLoc(); -+ fontLoc->locType = gfxFontLocExternal; -+ fontLoc->fontType = fontType; -+ fontLoc->path = path; -+ return fontLoc; - } - - char *GfxFont::readEmbFontFile(XRef *xref, int *len) { -@@ -386,6 +810,10 @@ - str->reset(); - while ((c = str->getChar()) != EOF) { - if (i == size) { -+ if (size > INT_MAX - 4096) { -+ error(errSyntaxError, -1, "Embedded font file is too large"); -+ break; -+ } - size += 4096; - buf = (char *)grealloc(buf, size); - } -@@ -405,8 +833,8 @@ - //------------------------------------------------------------------------ - - Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA, -- GfxFontType typeA, Dict *fontDict): -- GfxFont(tagA, idA, nameA) -+ GfxFontType typeA, Ref embFontIDA, Dict *fontDict): -+ GfxFont(tagA, idA, nameA, typeA, embFontIDA) - { - GString *name2; - BuiltinFont *builtinFont; -@@ -428,11 +856,11 @@ - Object obj1, obj2, obj3; - int n, i, a, b, m; - -- type = typeA; - ctu = NULL; - - // do font name substitution for various aliases of the Base 14 font - // names - base14 = NULL; - if (name) { - name2 = name->copy(); - i = 0; -@@ -499,9 +927,6 @@ - fontBBox[3] = 0.001 * builtinFont->bbox[3]; - } - -- // look for an external font file -- findExtFontFile(); -- - // get font matrix - fontMat[0] = fontMat[3] = 1; - fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0; -@@ -581,54 +1007,45 @@ - baseEnc = winAnsiEncoding; - } - -- // check embedded or external font file for base encoding -+ // check embedded font file for base encoding - // (only for Type 1 fonts - trying to get an encoding out of a - // TrueType font is a losing proposition) - ffT1 = NULL; - ffT1C = NULL; - buf = NULL; -- if (type == fontType1 && (extFontFile || embFontID.num >= 0)) { -- if (extFontFile) { -- ffT1 = FoFiType1::load(extFontFile->getCString()); -- } else { -- buf = readEmbFontFile(xref, &len); -- ffT1 = FoFiType1::make(buf, len); -- } -- if (ffT1) { -- if (ffT1->getName()) { -- if (embFontName) { -- delete embFontName; -+ if (type == fontType1 && embFontID.num >= 0) { -+ if ((buf = readEmbFontFile(xref, &len))) { -+ if ((ffT1 = FoFiType1::make(buf, len))) { -+ if (ffT1->getName()) { -+ if (embFontName) { -+ delete embFontName; -+ } -+ embFontName = new GString(ffT1->getName()); -+ } -+ if (!baseEnc) { -+ baseEnc = (const char **)ffT1->getEncoding(); -+ baseEncFromFontFile = gTrue; - } -- embFontName = new GString(ffT1->getName()); -- } -- if (!baseEnc) { -- baseEnc = (const char **)ffT1->getEncoding(); -- baseEncFromFontFile = gTrue; - } -+ gfree(buf); - } -- } else if (type == fontType1C && (extFontFile || embFontID.num >= 0)) { -- if (extFontFile) { -- ffT1C = FoFiType1C::load(extFontFile->getCString()); -- } else { -- buf = readEmbFontFile(xref, &len); -- ffT1C = FoFiType1C::make(buf, len); -- } -- if (ffT1C) { -- if (ffT1C->getName()) { -- if (embFontName) { -- delete embFontName; -+ } else if (type == fontType1C && embFontID.num >= 0) { -+ if ((buf = readEmbFontFile(xref, &len))) { -+ if ((ffT1C = FoFiType1C::make(buf, len))) { -+ if (ffT1C->getName()) { -+ if (embFontName) { -+ delete embFontName; -+ } -+ embFontName = new GString(ffT1C->getName()); -+ } -+ if (!baseEnc) { -+ baseEnc = (const char **)ffT1C->getEncoding(); -+ baseEncFromFontFile = gTrue; - } -- embFontName = new GString(ffT1C->getName()); -- } -- if (!baseEnc) { -- baseEnc = (const char **)ffT1C->getEncoding(); -- baseEncFromFontFile = gTrue; - } -+ gfree(buf); - } - } -- if (buf) { -- gfree(buf); -- } - - // get default base encoding - if (!baseEnc) { -@@ -644,7 +1061,7 @@ - - // copy the base encoding - for (i = 0; i < 256; ++i) { -- enc[i] = baseEnc[i]; -+ enc[i] = (char *)baseEnc[i]; - if ((encFree[i] = baseEncFromFontFile) && enc[i]) { - enc[i] = copyString(baseEnc[i]); - } -@@ -654,11 +1071,10 @@ - // T1C->T1 conversion (since the 'seac' operator depends on having - // the accents in the encoding), so we fill in any gaps from - // StandardEncoding -- if (type == fontType1C && (extFontFile || embFontID.num >= 0) && -- baseEncFromFontFile) { -+ if (type == fontType1C && embFontID.num >= 0 && baseEncFromFontFile) { - for (i = 0; i < 256; ++i) { - if (!enc[i] && standardEncoding[i]) { - enc[i] = (char *)standardEncoding[i]; - encFree[i] = gFalse; - } - } -@@ -734,14 +1151,21 @@ - } - - // pass 2: try to fill in the missing chars, looking for names of -- // the form 'Axx', 'xx', 'Ann', 'ABnn', or 'nn', where 'A' and 'B' -- // are any letters, 'xx' is two hex digits, and 'nn' is 2-4 -- // decimal digits -+ // any of the following forms: -+ // - 'xx' -+ // - 'Axx' -+ // - 'nn' -+ // - 'Ann' -+ // - 'ABnn' -+ // - 'unixxxx' (possibly followed by garbage - some Arabic files -+ // use 'uni0628.medi', etc.) -+ // where 'A' and 'B' are any letters, 'xx' is two hex digits, 'xxxx' -+ // is four hex digits, and 'nn' is 2-4 decimal digits - if (missing && globalParams->getMapNumericCharNames()) { - for (code = 0; code < 256; ++code) { - if ((charName = enc[code]) && !toUnicode[code] && - strcmp(charName, ".notdef")) { - n = strlen(charName); - code2 = -1; - if (hex && n == 3 && isalpha(charName[0]) && - isxdigit(charName[1]) && isxdigit(charName[2])) { -@@ -758,8 +1182,13 @@ - } else if (n >= 4 && n <= 6 && - isdigit(charName[2]) && isdigit(charName[3])) { - code2 = atoi(charName+2); -+ } else if (n >= 7 && charName[0] == 'u' && charName[1] == 'n' && -+ charName[2] == 'i' && -+ isxdigit(charName[3]) && isxdigit(charName[4]) && -+ isxdigit(charName[5]) && isxdigit(charName[6])) { -+ sscanf(charName + 3, "%x", &code2); - } -- if (code2 >= 0 && code2 <= 0xff) { -+ if (code2 >= 0 && code2 <= 0xffff) { - toUnicode[code] = (Unicode)code2; - } - } -@@ -835,7 +1264,7 @@ - obj1.arrayGet(code - firstChar, &obj2); - if (obj2.isNum()) { - widths[code] = obj2.getNum() * mul; -- if (widths[code] != widths[firstChar]) { -+ if (fabs(widths[code] - widths[firstChar]) > 0.00001) { - flags &= ~fontFixedWidth; - } - } -@@ -945,9 +1374,10 @@ - // TrueType font has a Macintosh Roman cmap, use it, and - // reverse map the char names through MacRomanEncoding to - // get char codes. -- // 1b. If the TrueType font has a Microsoft Unicode cmap or a -- // non-Microsoft Unicode cmap, use it, and use the Unicode -- // indexes, not the char codes. -+ // 1b. If the PDF font is not symbolic or the PDF font is not -+ // embedded, and the TrueType font has a Microsoft Unicode -+ // cmap or a non-Microsoft Unicode cmap, use it, and use the -+ // Unicode indexes, not the char codes. - // 1c. If the PDF font is symbolic and the TrueType font has a - // Microsoft Symbol cmap, use it, and use char codes - // directly (possibly with an offset of 0xf000). -@@ -983,7 +1413,8 @@ - if (usesMacRomanEnc && macRomanCmap >= 0) { - cmap = macRomanCmap; - useMacRoman = gTrue; -- } else if (unicodeCmap >= 0) { -+ } else if ((!(flags & fontSymbolic) || embFontID.num < 0) && -+ unicodeCmap >= 0) { - cmap = unicodeCmap; - useUnicode = gTrue; - } else if ((flags & fontSymbolic) && msSymbolCmap >= 0) { -@@ -1010,6 +1441,8 @@ - if ((code = globalParams->getMacRomanCharCode(charName))) { - map[i] = ff->mapCodeToGID(cmap, code); - } -+ } else { -+ map[i] = -1; - } - } - -@@ -1020,6 +1453,8 @@ - (u = globalParams->mapNameToUnicode(charName))) || - (n = ctu->mapToUnicode((CharCode)i, &u, 1))) { - map[i] = ff->mapCodeToGID(cmap, u); -+ } else { -+ map[i] = -1; - } - } - -@@ -1035,8 +1470,8 @@ - - // try the TrueType 'post' table to handle any unmapped characters - for (i = 0; i < 256; ++i) { -- if (!map[i] && (charName = enc[i])) { -+ if (map[i] <= 0 && (charName = enc[i])) { - map[i] = ff->mapNameToGID(charName); - } - } - -@@ -1077,12 +1530,11 @@ - GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA, -- Dict *fontDict): -- GfxFont(tagA, idA, nameA) -+ GfxFontType typeA, Ref embFontIDA, Dict *fontDict): -+ GfxFont(tagA, idA, nameA, typeA, embFontIDA) - { - Dict *desFontDict; -- GString *collection, *cMapName; - Object desFontDictObj; - Object obj1, obj2, obj3, obj4, obj5, obj6; - CharCodeToUnicode *utu; -@@ -1091,8 +1545,10 @@ - ascent = 0.95; - descent = -0.35; - fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0; -+ collection = NULL; - cMap = NULL; - ctu = NULL; -+ ctuUsesCharCode = gTrue; - widths.defWidth = 1.0; - widths.defHeight = -1.0; - widths.defVY = 0.880; -@@ -1104,52 +1560,38 @@ - cidToGIDLen = 0; - - // get the descendant font -- if (!fontDict->lookup("DescendantFonts", &obj1)->isArray()) { -- error(-1, "Missing DescendantFonts entry in Type 0 font"); -+ if (!fontDict->lookup("DescendantFonts", &obj1)->isArray() || -+ obj1.arrayGetLength() == 0) { -+ error(errSyntaxError, -1, -+ "Missing or empty DescendantFonts entry in Type 0 font"); - obj1.free(); -+ - goto err1; - } - if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) { -- error(-1, "Bad descendant font in Type 0 font"); -- goto err3; -+ error(errSyntaxError, -1, "Bad descendant font in Type 0 font"); -+ goto err2; - } - obj1.free(); - desFontDict = desFontDictObj.getDict(); - -- // font type -- if (!desFontDict->lookup("Subtype", &obj1)) { -- error(-1, "Missing Subtype entry in Type 0 descendant font"); -- goto err3; -- } -- if (obj1.isName("CIDFontType0")) { -- type = fontCIDType0; -- } else if (obj1.isName("CIDFontType2")) { -- type = fontCIDType2; -- } else { -- error(-1, "Unknown Type 0 descendant font type '%s'", -- obj1.isName() ? obj1.getName() : "???"); -- goto err3; -- } -- obj1.free(); -- - // get info from font descriptor - readFontDescriptor(xref, desFontDict); - -- // look for an external font file -- findExtFontFile(); -- - //----- encoding info ----- - - // char collection - if (!desFontDict->lookup("CIDSystemInfo", &obj1)->isDict()) { -- error(-1, "Missing CIDSystemInfo dictionary in Type 0 descendant font"); -- goto err3; -+ error(errSyntaxError, -1, -+ "Missing CIDSystemInfo dictionary in Type 0 descendant font"); -+ goto err2; - } - obj1.dictLookup("Registry", &obj2); - obj1.dictLookup("Ordering", &obj3); - if (!obj2.isString() || !obj3.isString()) { -- error(-1, "Invalid CIDSystemInfo dictionary in Type 0 descendant font"); -- goto err4; -+ error(errSyntaxError, -1, -+ "Invalid CIDSystemInfo dictionary in Type 0 descendant font"); -+ goto err3; - } - collection = obj2.getString()->copy()->append('-')->append(obj3.getString()); - obj3.free(); -@@ -1158,19 +1600,18 @@ - - // look for a ToUnicode CMap - if (!(ctu = readToUnicodeCMap(fontDict, 16, NULL))) { -+ ctuUsesCharCode = gFalse; - -- // the "Adobe-Identity" and "Adobe-UCS" collections don't have -- // cidToUnicode files -- if (collection->cmp("Adobe-Identity") && -- collection->cmp("Adobe-UCS")) { -- -- // look for a user-supplied .cidToUnicode file -- if (!(ctu = globalParams->getCIDToUnicode(collection))) { -- error(-1, "Unknown character collection '%s'", -- collection->getCString()); -- // fall-through, assuming the Identity mapping -- this appears -- // to match Adobe's behavior -- } -+ // use an identity mapping for the "Adobe-Identity" and -+ // "Adobe-UCS" collections -+ if (!collection->cmp("Adobe-Identity") || -+ !collection->cmp("Adobe-UCS")) { -+ ctu = CharCodeToUnicode::makeIdentityMapping(); -+ -+ // look for a user-supplied .cidToUnicode file -+ } else if (!(ctu = globalParams->getCIDToUnicode(collection))) { -+ error(errSyntaxError, -1, -+ "Unknown character collection '{0:t}'", collection); - } - } - -@@ -1193,43 +1634,35 @@ - } - - // encoding (i.e., CMap) -- //~ need to handle a CMap stream here -- //~ also need to deal with the UseCMap entry in the stream dict -- if (!fontDict->lookup("Encoding", &obj1)->isName()) { -- error(-1, "Missing or invalid Encoding entry in Type 0 font"); -- delete collection; -- goto err3; -+ if (fontDict->lookup("Encoding", &obj1)->isNull()) { -+ error(errSyntaxError, -1, "Missing Encoding entry in Type 0 font"); -+ goto err2; - } -- cMapName = new GString(obj1.getName()); -- obj1.free(); -- if (!(cMap = globalParams->getCMap(collection, cMapName))) { -- error(-1, "Unknown CMap '%s' for character collection '%s'", -- cMapName->getCString(), collection->getCString()); -- delete collection; -- delete cMapName; -+ if (!(cMap = CMap::parse(NULL, collection, &obj1))) { - goto err2; - } -- delete collection; -- delete cMapName; -+ obj1.free(); - -- // CIDToGIDMap (for embedded TrueType fonts) -- if (type == fontCIDType2) { -+ // CIDToGIDMap -+ // (the PDF spec only allows these for TrueType fonts, but Acrobat -+ // apparently also allows them for OpenType CFF fonts) -+ if (type == fontCIDType2 || type == fontCIDType0COT) { - desFontDict->lookup("CIDToGIDMap", &obj1); - if (obj1.isStream()) { - cidToGIDLen = 0; - i = 64; -@@ -1387,17 +1830,19 @@ - ok = gTrue; - return; - -- err4: -+ err3: - obj3.free(); - obj2.free(); -- err3: -- obj1.free(); - err2: -+ obj1.free(); - desFontDictObj.free(); - err1:; - } - - GfxCIDFont::~GfxCIDFont() { -+ if (collection) { -+ delete collection; -+ } - if (cMap) { - cMap->decRefCnt(); - } -@@ -1425,12 +1871,16 @@ - return 1; - } - - *code = (CharCode)(cid = cMap->getCID(s, len, &c, &n)); - if (ctu) { -- *uLen = ctu->mapToUnicode(cid, u, uSize); -+ *uLen = ctu->mapToUnicode(ctuUsesCharCode ? c : cid, u, uSize); - } else { - *uLen = 0; - } -+ if (!*uLen && uSize >= 1 && globalParams->getMapUnknownCharNames()) { -+ u[0] = *code; -+ *uLen = 1; -+ } - - // horizontal - if (cMap->getWMode() == 0) { -diff -ru xpdf-3.02/xpdf/GfxFont.h xpdf-3.03/xpdf/GfxFont.h ---- xpdf-3.02/xpdf/GfxFont.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/GfxFont.h 2011-08-15 23:08:53.000000000 +0200 -@@ -91,7 +127,8 @@ - // Build a GfxFont object. - static GfxFont *makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict); - -- GfxFont(char *tagA, Ref idA, GString *nameA); -+ GfxFont(char *tagA, Ref idA, GString *nameA, -+ GfxFontType typeA, Ref embFontIDA); - - virtual ~GfxFont(); - -@@ -126,10 +160,6 @@ - // NULL if there is no embedded font. - GString *getEmbeddedFontName() { return embFontName; } - -- // Get the name of the external font file. Returns NULL if there -- // is no external font file. -- GString *getExtFontFile() { return extFontFile; } -- - // Get font descriptor flags. - int getFlags() { return flags; } - GBool isFixedWidth() { return flags & fontFixedWidth; } -@@ -151,8 +181,14 @@ - // Return the writing mode (0=horizontal, 1=vertical). - virtual int getWMode() { return 0; } - -- // Read an external or embedded font file into a buffer. -- char *readExtFontFile(int *len); -+ // Locate the font file for this font. If is true, includes PS -+ // printer-resident fonts. Returns NULL on failure. -+ GfxFontLoc *locateFont(XRef *xref, GBool ps); -+ -+ // Locate a Base-14 font file for a specified font name. -+ static GfxFontLoc *locateBase14Font(GString *base14Name); -+ -+ // Read an embedded font file into a buffer. - char *readEmbFontFile(XRef *xref, int *len); - - // Get the next char from a string of bytes, returning the -@@ -167,22 +203,21 @@ - - protected: - -+ static GfxFontType getFontType(XRef *xref, Dict *fontDict, Ref *embID); - void readFontDescriptor(XRef *xref, Dict *fontDict); - CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits, - CharCodeToUnicode *ctu); -- void findExtFontFile(); -+ static GfxFontLoc *getExternalFont(GString *path, GBool cid); - - GString *tag; // PDF font tag - Ref id; // reference (used as unique ID) - GString *name; // font name - GfxFontType type; // type of font - int flags; // font descriptor flags - GString *embFontName; // name of embedded font - Ref embFontID; // ref to embedded font file stream -- GString *extFontFile; // external font file name -- double fontMat[6]; // font matrix (Type 3 only) -- double fontBBox[4]; // font bounding box (Type 3 only) -+ double fontMat[6]; // font matrix -+ double fontBBox[4]; // font bounding box - double missingWidth; // "default" width - double ascent; // max height above baseline - double descent; // max depth below baseline -@@ -197,7 +232,7 @@ - public: - - Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA, -- GfxFontType typeA, Dict *fontDict); -+ GfxFontType typeA, Ref embFontIDA, Dict *fontDict); - - virtual ~Gfx8BitFont(); - -@@ -247,6 +283,8 @@ - double widths[256]; // character widths - Object charProcs; // Type 3 CharProcs dictionary - Object resources; // Type 3 Resources dictionary -+ -+ friend class GfxFont; - }; - - //------------------------------------------------------------------------ -@@ -257,7 +295,7 @@ - public: - - GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA, -- Dict *fontDict); -+ GfxFontType typeA, Ref embFontIDA, Dict *fontDict); - - virtual ~GfxCIDFont(); - -@@ -278,15 +316,18 @@ - - private: - -+ GString *collection; // collection name - CMap *cMap; // char code --> CID -- CharCodeToUnicode *ctu; // CID --> Unicode -+ CharCodeToUnicode *ctu; // CID/char code --> Unicode -+ GBool ctuUsesCharCode; // true: ctu maps char code to Unicode; -+ // false: ctu maps CID to Unicode - GfxFontCIDWidths widths; // character widths -diff -ru xpdf-3.02/xpdf/Gfx.h xpdf-3.03/xpdf/Gfx.h ---- xpdf-3.02/xpdf/Gfx.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/Gfx.h 2011-08-15 23:08:53.000000000 +0200 -@@ -18,6 +18,8 @@ - #include "gtypes.h" - - class GString; -+class GList; - class PDFDoc; - class XRef; - class Array; - class Stream; -@@ -141,8 +169,16 @@ - // Get the current graphics state object. - GfxState *getState() { return state; } - -+ void drawForm(Object *str, Dict *resDict, double *matrix, double *bbox, -+ GBool transpGroup = gFalse, GBool softMask = gFalse, -+ GfxColorSpace *blendingColorSpace = NULL, -+ GBool isolated = gFalse, GBool knockout = gFalse, -+ GBool alpha = gFalse, Function *transferFunc = NULL, -+ GfxColor *backdropColor = NULL); -+ - private: - - PDFDoc *doc; - XRef *xref; // the xref table for this PDF file - OutputDev *out; // output device - GBool subPage; // is this a sub-page object? -@@ -157,6 +193,12 @@ - double baseMatrix[6]; // default matrix for most recent - // page/form/pattern - int formDepth; -+ double textClipBBox[4]; // text clipping bounding box -+ GBool textClipBBoxEmpty; // true if textClipBBox has not been -+ // initialized yet -+ GBool ocState; // true if drawing is enabled, false if -+ // disabled -+ GList *markedContentStack; // BMC/BDC/EMC stack [GfxMarkedContent] - - Parser *parser; // parser for page content stream(s) - -@@ -224,10 +266,13 @@ - void opCloseEOFillStroke(Object args[], int numArgs); - void doPatternFill(GBool eoFill); - void doPatternStroke(); -+ void doPatternText(); -+ void doPatternImageMask(Object *ref, Stream *str, int width, int height, -+ GBool invert, GBool inlineImg); - void doTilingPatternFill(GfxTilingPattern *tPat, -- GBool stroke, GBool eoFill); -+ GBool stroke, GBool eoFill, GBool text); - void doShadingPatternFill(GfxShadingPattern *sPat, -- GBool stroke, GBool eoFill); -+ GBool stroke, GBool eoFill, GBool text); - void opShFill(Object args[], int numArgs); - void doFunctionShFill(GfxFunctionShading *shading); - void doFunctionShFill1(GfxFunctionShading *shading, -@@ -274,17 +319,12 @@ - void opMoveSetShowText(Object args[], int numArgs); - void opShowSpaceText(Object args[], int numArgs); - void doShowText(GString *s); -+ void doIncCharCount(GString *s); - - // XObject operators - void opXObject(Object args[], int numArgs); - void doImage(Object *ref, Stream *str, GBool inlineImg); - void doForm(Object *str); -- void doForm1(Object *str, Dict *resDict, double *matrix, double *bbox, -- GBool transpGroup = gFalse, GBool softMask = gFalse, -- GfxColorSpace *blendingColorSpace = NULL, -- GBool isolated = gFalse, GBool knockout = gFalse, -- GBool alpha = gFalse, Function *transferFunc = NULL, -- GfxColor *backdropColor = NULL); - - // in-line image operators - void opBeginImage(Object args[], int numArgs); -@@ -305,6 +345,8 @@ - void opEndMarkedContent(Object args[], int numArgs); - void opMarkPoint(Object args[], int numArgs); - -+ GfxState *saveStateStack(); -+ void restoreStateStack(GfxState *oldState); - void pushResources(Dict *resDict); - void popResources(); - }; -diff -ru xpdf-3.02/xpdf/GfxState.cc xpdf-3.03/xpdf/GfxState.cc ---- xpdf-3.02/xpdf/GfxState.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/GfxState.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -819,26 +854,27 @@ - obj1.free(); - arr->get(1, &obj1); - if (!obj1.isStream()) { - error(errSyntaxError, -1, "Bad ICCBased color space (stream)"); - obj1.free(); - return NULL; - } - dict = obj1.streamGetDict(); - if (!dict->lookup("N", &obj2)->isInt()) { - error(errSyntaxError, -1, "Bad ICCBased color space (N)"); - obj2.free(); - obj1.free(); - return NULL; - } - nCompsA = obj2.getInt(); - obj2.free(); -- if (nCompsA > gfxColorMaxComps) { -- error(-1, "ICCBased color space with too many (%d > %d) components", -- nCompsA, gfxColorMaxComps); -- nCompsA = gfxColorMaxComps; -+ if (nCompsA > 4) { -+ error(errSyntaxError, -1, -+ "ICCBased color space with too many ({0:d} > 4) components", -+ nCompsA); -+ nCompsA = 4; - } - if (dict->lookup("Alternate", &obj2)->isNull() || -@@ -986,8 +1025,9 @@ - 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; -+ error(errSyntaxError, -1, -+ "Bad Indexed color space (lookup table stream too short)"); -+ cs->indexHigh = indexHighA = i - 1; - } - cs->lookup[i*n + j] = (Guchar)x; - } -@@ -995,8 +1035,9 @@ - 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; -+ error(errSyntaxError, -1, -+ "Bad Indexed color space (lookup table string too short)"); -+ cs->indexHigh = indexHighA = obj1.getString()->getLength() / n - 1; - } - s = obj1.getString()->getCString(); - for (i = 0; i <= indexHighA; ++i) { -@@ -1254,14 +1354,7 @@ - goto err4; - } - obj1.free(); - cs = new GfxDeviceNColorSpace(nCompsA, altA, funcA); -- cs->nonMarking = gTrue; -- for (i = 0; i < nCompsA; ++i) { -- cs->names[i] = namesA[i]; -- if (namesA[i]->cmp("None")) { -- cs->nonMarking = gFalse; -- } -- } - return cs; - - err4: -@@ -3187,7 +3299,7 @@ - GfxIndexedColorSpace *indexedCS; - GfxSeparationColorSpace *sepCS; - int maxPixel, indexHigh; -- Guchar *lookup2; -+ Guchar *indexedLookup; - Function *sepFunc; - Object obj; - double x[gfxColorMaxComps]; -@@ -3204,6 +3316,7 @@ - // initialize - for (k = 0; k < gfxColorMaxComps; ++k) { - lookup[k] = NULL; -+ lookup2[k] = NULL; - } - - // get decode map -@@ -3236,10 +3353,18 @@ - // 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. -- // -+ for (k = 0; k < nComps; ++k) { -+ lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1, -+ sizeof(GfxColorComp)); -+ for (i = 0; i <= maxPixel; ++i) { -+ lookup[k][i] = dblToCol(decodeLow[k] + -+ (i * decodeRange[k]) / maxPixel); -+ } -+ } -+ - // Optimization: for Indexed and Separation color spaces (which have -- // only one component), we store color values in the lookup table -- // rather than component values. -+ // only one component), we pre-compute a second lookup table with -+ // color values - colorSpace2 = NULL; - nComps2 = 0; - if (colorSpace->getMode() == csIndexed) { -@@ -3250,20 +3375,22 @@ - colorSpace2 = indexedCS->getBase(); - indexHigh = indexedCS->getIndexHigh(); - nComps2 = colorSpace2->getNComps(); -- lookup2 = indexedCS->getLookup(); -+ indexedLookup = indexedCS->getLookup(); - colorSpace2->getDefaultRanges(x, y, indexHigh); - for (k = 0; k < nComps2; ++k) { -- lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1, -- sizeof(GfxColorComp)); -- 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; -- } -- lookup[k][i] = -- dblToCol(x[k] + (lookup2[j*nComps2 + k] / 255.0) * y[k]); -+ lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1, -+ sizeof(GfxColorComp)); -+ } -+ 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) { -+ lookup2[k][i] = -+ dblToCol(x[k] + (indexedLookup[j*nComps2 + k] / 255.0) * y[k]); - } - } - } else if (colorSpace->getMode() == csSeparation) { -@@ -3272,21 +3399,14 @@ - nComps2 = colorSpace2->getNComps(); - sepFunc = sepCS->getFunc(); - for (k = 0; k < nComps2; ++k) { -- lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1, -- sizeof(GfxColorComp)); -- for (i = 0; i <= maxPixel; ++i) { -- x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel; -- sepFunc->transform(x, y); -- lookup[k][i] = dblToCol(y[k]); -- } -+ lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1, -+ sizeof(GfxColorComp)); - } -- } else { -- for (k = 0; k < nComps; ++k) { -- lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1, -- sizeof(GfxColorComp)); -- for (i = 0; i <= maxPixel; ++i) { -- lookup[k][i] = dblToCol(decodeLow[k] + -- (i * decodeRange[k]) / maxPixel); -+ for (i = 0; i <= maxPixel; ++i) { -+ x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel; -+ sepFunc->transform(x, y); -+ for (k = 0; k < nComps2; ++k) { -+ lookup2[k][i] = dblToCol(y[k]); - } - } - } -@@ -3309,24 +3429,24 @@ - colorSpace2 = NULL; - for (k = 0; k < gfxColorMaxComps; ++k) { - lookup[k] = NULL; -+ lookup2[k] = NULL; - } - n = 1 << bits; -+ for (k = 0; k < nComps; ++k) { -+ lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); -+ memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp)); -+ } - if (colorSpace->getMode() == csIndexed) { - colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase(); - for (k = 0; k < nComps2; ++k) { -- lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); -- memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp)); -+ lookup2[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); -+ memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp)); - } - } else if (colorSpace->getMode() == csSeparation) { - colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt(); - for (k = 0; k < nComps2; ++k) { -- lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); -- memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp)); -- } -- } else { -- for (k = 0; k < nComps; ++k) { -- lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); -- memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp)); -+ lookup2[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); -+ memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp)); - } - } - for (i = 0; i < nComps; ++i) { -@@ -3342,6 +3462,7 @@ - delete colorSpace; - for (i = 0; i < gfxColorMaxComps; ++i) { - gfree(lookup[i]); -+ gfree(lookup2[i]); - } - } - -@@ -3351,7 +3472,7 @@ - - if (colorSpace2) { - for (i = 0; i < nComps2; ++i) { -- color.c[i] = lookup[i][x[0]]; -+ color.c[i] = lookup2[i][x[0]]; - } - colorSpace2->getGray(&color, gray); - } else { -@@ -3368,7 +3489,7 @@ - - if (colorSpace2) { - for (i = 0; i < nComps2; ++i) { -- color.c[i] = lookup[i][x[0]]; -+ color.c[i] = lookup2[i][x[0]]; - } - colorSpace2->getRGB(&color, rgb); - } else { -@@ -3385,7 +3506,7 @@ - - if (colorSpace2) { - for (i = 0; i < nComps2; ++i) { -- color.c[i] = lookup[i][x[0]]; -+ color.c[i] = lookup2[i][x[0]]; - } - colorSpace2->getCMYK(&color, cmyk); - } else { -@@ -3405,6 +3527,88 @@ - } - } - -+void GfxImageColorMap::getGrayByteLine(Guchar *in, Guchar *out, int n) { -+ GfxColor color; -+ GfxGray gray; -+ int i, j; -+ -+ if (colorSpace2) { -+ for (j = 0; j < n; ++j) { -+ for (i = 0; i < nComps2; ++i) { -+ color.c[i] = lookup2[i][in[j]]; -+ } -+ colorSpace2->getGray(&color, &gray); -+ out[j] = colToByte(gray); -+ } -+ } else { -+ for (j = 0; j < n; ++j) { -+ for (i = 0; i < nComps; ++i) { -+ color.c[i] = lookup[i][in[j * nComps + i]]; -+ } -+ colorSpace->getGray(&color, &gray); -+ out[j] = colToByte(gray); -+ } -+ } -+} -+ -+void GfxImageColorMap::getRGBByteLine(Guchar *in, Guchar *out, int n) { -+ GfxColor color; -+ GfxRGB rgb; -+ int i, j; -+ -+ if (colorSpace2) { -+ for (j = 0; j < n; ++j) { -+ for (i = 0; i < nComps2; ++i) { -+ color.c[i] = lookup2[i][in[j]]; -+ } -+ colorSpace2->getRGB(&color, &rgb); -+ out[j*3] = colToByte(rgb.r); -+ out[j*3 + 1] = colToByte(rgb.g); -+ out[j*3 + 2] = colToByte(rgb.b); -+ } -+ } else { -+ for (j = 0; j < n; ++j) { -+ for (i = 0; i < nComps; ++i) { -+ color.c[i] = lookup[i][in[j * nComps + i]]; -+ } -+ colorSpace->getRGB(&color, &rgb); -+ out[j*3] = colToByte(rgb.r); -+ out[j*3 + 1] = colToByte(rgb.g); -+ out[j*3 + 2] = colToByte(rgb.b); -+ } -+ } -+} -+ -+void GfxImageColorMap::getCMYKByteLine(Guchar *in, Guchar *out, int n) { -+ GfxColor color; -+ GfxCMYK cmyk; -+ int i, j; -+ -+ if (colorSpace2) { -+ for (j = 0; j < n; ++j) { -+ for (i = 0; i < nComps2; ++i) { -+ color.c[i] = lookup2[i][in[j]]; -+ } -+ colorSpace2->getCMYK(&color, &cmyk); -+ out[j*4] = colToByte(cmyk.c); -+ out[j*4 + 1] = colToByte(cmyk.m); -+ out[j*4 + 2] = colToByte(cmyk.y); -+ out[j*4 + 3] = colToByte(cmyk.k); -+ } -+ } else { -+ for (j = 0; j < n; ++j) { -+ for (i = 0; i < nComps; ++i) { -+ color.c[i] = lookup[i][in[j * nComps + i]]; -+ } -+ colorSpace->getCMYK(&color, &cmyk); -+ out[j*4] = colToByte(cmyk.c); -+ out[j*4 + 1] = colToByte(cmyk.m); -+ out[j*4 + 2] = colToByte(cmyk.y); -+ out[j*4 + 3] = colToByte(cmyk.k); -+ } -+ } -+} -+ - //------------------------------------------------------------------------ - // GfxSubpath and GfxPath - //------------------------------------------------------------------------ -@@ -3526,13 +3730,18 @@ - } - - void GfxPath::lineTo(double x, double y) { -- if (justMoved) { -+ if (justMoved || (n > 0 && subpaths[n-1]->isClosed())) { - if (n >= size) { - size *= 2; - subpaths = (GfxSubpath **) - greallocn(subpaths, size, sizeof(GfxSubpath *)); - } -- subpaths[n] = new GfxSubpath(firstX, firstY); -+ if (justMoved) { -+ subpaths[n] = new GfxSubpath(firstX, firstY); -+ } else { -+ subpaths[n] = new GfxSubpath(subpaths[n-1]->getLastX(), -+ subpaths[n-1]->getLastY()); -+ } - ++n; - justMoved = gFalse; - } -@@ -3541,13 +3750,18 @@ - - void GfxPath::curveTo(double x1, double y1, double x2, double y2, - double x3, double y3) { -- if (justMoved) { -+ if (justMoved || (n > 0 && subpaths[n-1]->isClosed())) { - if (n >= size) { - size *= 2; - subpaths = (GfxSubpath **) - greallocn(subpaths, size, sizeof(GfxSubpath *)); - } -- subpaths[n] = new GfxSubpath(firstX, firstY); -+ if (justMoved) { -+ subpaths[n] = new GfxSubpath(firstX, firstY); -+ } else { -+ subpaths[n] = new GfxSubpath(subpaths[n-1]->getLastX(), -+ subpaths[n-1]->getLastY()); -+ } - ++n; - justMoved = gFalse; - } -@@ -3658,6 +3872,7 @@ - strokeOpacity = 1; - fillOverprint = gFalse; - strokeOverprint = gFalse; -+ overprintMode = 0; - transfer[0] = transfer[1] = transfer[2] = transfer[3] = NULL; - - lineWidth = 1; -@@ -3719,13 +3934,10 @@ - // this gets set to NULL by restore() - delete path; - } -- if (saved) { -- delete saved; -- } - } - - // Used for copy(); --GfxState::GfxState(GfxState *state) { -+GfxState::GfxState(GfxState *state, GBool copyPath) { - int i; - - memcpy(this, state, sizeof(GfxState)); -@@ -3750,6 +3962,9 @@ - lineDash = (double *)gmallocn(lineDashLength, sizeof(double)); - memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double)); - } -+ if (copyPath) { -+ path = state->path->copy(); -+ } - saved = NULL; - } - -@@ -4056,6 +4271,60 @@ - } - } - -+void GfxState::clipToRect(double xMin, double yMin, double xMax, double yMax) { -+ double x, y, xMin1, yMin1, xMax1, yMax1; -+ -+ transform(xMin, yMin, &x, &y); -+ xMin1 = xMax1 = x; -+ yMin1 = yMax1 = y; -+ transform(xMax, yMin, &x, &y); -+ if (x < xMin1) { -+ xMin1 = x; -+ } else if (x > xMax1) { -+ xMax1 = x; -+ } -+ if (y < yMin1) { -+ yMin1 = y; -+ } else if (y > yMax1) { -+ yMax1 = y; -+ } -+ transform(xMax, yMax, &x, &y); -+ if (x < xMin1) { -+ xMin1 = x; -+ } else if (x > xMax1) { -+ xMax1 = x; -+ } -+ if (y < yMin1) { -+ yMin1 = y; -+ } else if (y > yMax1) { -+ yMax1 = y; -+ } -+ transform(xMin, yMax, &x, &y); -+ if (x < xMin1) { -+ xMin1 = x; -+ } else if (x > xMax1) { -+ xMax1 = x; -+ } -+ if (y < yMin1) { -+ yMin1 = y; -+ } else if (y > yMax1) { -+ yMax1 = y; -+ } -+ -+ if (xMin1 > clipXMin) { -+ clipXMin = xMin1; -+ } -+ if (yMin1 > clipYMin) { -+ clipYMin = yMin1; -+ } -+ if (xMax1 < clipXMax) { -+ clipXMax = xMax1; -+ } -+ if (yMax1 < clipYMax) { -+ clipYMax = yMax1; -+ } -+} -+ - void GfxState::textShift(double tx, double ty) { - double dx, dy; - -diff -ru xpdf-3.02/xpdf/GfxState.h xpdf-3.03/xpdf/GfxState.h ---- xpdf-3.02/xpdf/GfxState.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/GfxState.h 2011-08-15 23:08:53.000000000 +0200 -@@ -878,6 +894,11 @@ - void getCMYK(Guchar *x, GfxCMYK *cmyk); - void getColor(Guchar *x, GfxColor *color); - -+ // Convert a line of pixels to 8-bit colors. -+ void getGrayByteLine(Guchar *in, Guchar *out, int n); -+ void getRGBByteLine(Guchar *in, Guchar *out, int n); -+ void getCMYKByteLine(Guchar *in, Guchar *out, int n); -+ - private: - - GfxImageColorMap(GfxImageColorMap *colorMap); -@@ -889,6 +910,8 @@ - int nComps2; // number of components in colorSpace2 - GfxColorComp * // lookup table - lookup[gfxColorMaxComps]; -+ GfxColorComp * // optimized case lookup table -+ lookup2[gfxColorMaxComps]; - double // minimum values for each component - decodeLow[gfxColorMaxComps]; - double // max - min value for each component -@@ -1023,7 +1046,8 @@ - ~GfxState(); - - // Copy. -- GfxState *copy() { return new GfxState(this); } -+ GfxState *copy(GBool copyPath = gFalse) -+ { return new GfxState(this, copyPath); } - - // Accessors. - double getHDPI() { return hDPI; } -@@ -1059,6 +1083,7 @@ - double getStrokeOpacity() { return strokeOpacity; } - GBool getFillOverprint() { return fillOverprint; } - GBool getStrokeOverprint() { return strokeOverprint; } -+ int getOverprintMode() { return overprintMode; } - Function **getTransfer() { return transfer; } - double getLineWidth() { return lineWidth; } - void getLineDash(double **dash, int *length, double *start) -@@ -1127,6 +1152,7 @@ - void setStrokeOpacity(double opac) { strokeOpacity = opac; } - void setFillOverprint(GBool op) { fillOverprint = op; } - void setStrokeOverprint(GBool op) { strokeOverprint = op; } -+ void setOverprintMode(int opm) { overprintMode = opm; } - void setTransfer(Function **funcs); - void setLineWidth(double width) { lineWidth = width; } - void setLineDash(double *dash, int length, double start); -@@ -1169,6 +1195,7 @@ - // Update clip region. - void clip(); - void clipToStrokePath(); -+ void clipToRect(double xMin, double yMin, double xMax, double yMax); - - // Text position. - void textSetPos(double tx, double ty) { lineX = tx; lineY = ty; } -@@ -1204,6 +1231,7 @@ - double strokeOpacity; // stroke opacity - GBool fillOverprint; // fill overprint - GBool strokeOverprint; // stroke overprint -+ int overprintMode; // overprint mode ("OPM") - Function *transfer[4]; // transfer function (entries may be: all - // NULL = identity; last three NULL = - // single function; all four non-NULL = -@@ -1238,7 +1266,7 @@ - - GfxState *saved; // next GfxState on stack - -- GfxState(GfxState *state); -+ GfxState(GfxState *state, GBool copyPath); - }; - - #endif -diff -ru xpdf-3.02/xpdf/GlobalParams.cc xpdf-3.03/xpdf/GlobalParams.cc ---- xpdf-3.02/xpdf/GlobalParams.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/GlobalParams.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -124,215 +124,138 @@ - GlobalParams *globalParams = NULL; - - //------------------------------------------------------------------------ --// DisplayFontParam -+// PSFontParam16 - //------------------------------------------------------------------------ - --DisplayFontParam::DisplayFontParam(GString *nameA, -- DisplayFontParamKind kindA) { -+PSFontParam16::PSFontParam16(GString *nameA, int wModeA, -+ GString *psFontNameA, GString *encodingA) { - name = nameA; -- kind = kindA; -- switch (kind) { -- case displayFontT1: -- t1.fileName = NULL; -- break; -- case displayFontTT: -- tt.fileName = NULL; -- break; -- } -+ wMode = wModeA; -+ psFontName = psFontNameA; -+ encoding = encodingA; - } - --DisplayFontParam::~DisplayFontParam() { -+PSFontParam16::~PSFontParam16() { - delete name; -- switch (kind) { -- case displayFontT1: -- if (t1.fileName) { -- delete t1.fileName; -- } -- break; -- case displayFontTT: -- if (tt.fileName) { -- delete tt.fileName; -- } -- break; -- } -+ delete psFontName; -+ delete encoding; - } - --#ifdef WIN32 -- - //------------------------------------------------------------------------ --// WinFontInfo -+// SysFontInfo - //------------------------------------------------------------------------ - --class WinFontInfo: public DisplayFontParam { -+class SysFontInfo { - public: - -- GBool bold, italic; -+ GString *name; -+ GBool bold; -+ GBool italic; -+ GString *path; -+ SysFontType type; -+ int fontNum; // for TrueType collections - -- static WinFontInfo *make(GString *nameA, GBool boldA, GBool italicA, -- HKEY regKey, char *winFontDir); -- WinFontInfo(GString *nameA, GBool boldA, GBool italicA, -- GString *fileNameA); -- virtual ~WinFontInfo(); -- GBool equals(WinFontInfo *fi); -+ SysFontInfo(GString *nameA, GBool boldA, GBool italicA, -+ GString *pathA, SysFontType typeA, int fontNumA); -+ ~SysFontInfo(); -+ GBool match(SysFontInfo *fi); -+ GBool match(GString *nameA, GBool boldA, GBool italicA); - }; - --WinFontInfo *WinFontInfo::make(GString *nameA, GBool boldA, GBool italicA, -- HKEY regKey, char *winFontDir) { -- GString *regName; -- GString *fileNameA; -- char buf[MAX_PATH]; -- DWORD n; -- char c; -- int i; -- -- //----- find the font file -- fileNameA = NULL; -- regName = nameA->copy(); -- if (boldA) { -- regName->append(" Bold"); -- } -- if (italicA) { -- regName->append(" Italic"); -- } -- regName->append(" (TrueType)"); -- n = sizeof(buf); -- if (RegQueryValueEx(regKey, regName->getCString(), NULL, NULL, -- (LPBYTE)buf, &n) == ERROR_SUCCESS) { -- fileNameA = new GString(winFontDir); -- fileNameA->append('\\')->append(buf); -- } -- delete regName; -- if (!fileNameA) { -- delete nameA; -- return NULL; -- } -- -- //----- normalize the font name -- i = 0; -- while (i < nameA->getLength()) { -- c = nameA->getChar(i); -- if (c == ' ' || c == ',' || c == '-') { -- nameA->del(i); -- } else { -- ++i; -- } -- } -- -- return new WinFontInfo(nameA, boldA, italicA, fileNameA); --} -- --WinFontInfo::WinFontInfo(GString *nameA, GBool boldA, GBool italicA, -- GString *fileNameA): -- DisplayFontParam(nameA, displayFontTT) --{ -+SysFontInfo::SysFontInfo(GString *nameA, GBool boldA, GBool italicA, -+ GString *pathA, SysFontType typeA, int fontNumA) { -+ name = nameA; - bold = boldA; - italic = italicA; -- tt.fileName = fileNameA; -+ path = pathA; -+ type = typeA; -+ fontNum = fontNumA; -+} -+ -+SysFontInfo::~SysFontInfo() { -+ delete name; -+ delete path; - } - --WinFontInfo::~WinFontInfo() { -+GBool SysFontInfo::match(SysFontInfo *fi) { -+ return !strcasecmp(name->getCString(), fi->name->getCString()) && -+ bold == fi->bold && italic == fi->italic; - } - --GBool WinFontInfo::equals(WinFontInfo *fi) { -- return !name->cmp(fi->name) && bold == fi->bold && italic == fi->italic; -+GBool SysFontInfo::match(GString *nameA, GBool boldA, GBool italicA) { -+ return !strcasecmp(name->getCString(), nameA->getCString()) && -+ bold == boldA && italic == italicA; - } - - //------------------------------------------------------------------------ --// WinFontList -+// SysFontList - //------------------------------------------------------------------------ - --class WinFontList { -+class SysFontList { - public: - -- WinFontList(char *winFontDirA); -- ~WinFontList(); -- WinFontInfo *find(GString *font); -+ SysFontList(); -+ ~SysFontList(); -+ SysFontInfo *find(GString *name); -+ -+#ifdef WIN32 -+ void scanWindowsFonts(char *winFontDir); -+#endif - - private: - -- void add(WinFontInfo *fi); -- static int CALLBACK enumFunc1(CONST LOGFONT *font, -- CONST TEXTMETRIC *metrics, -- DWORD type, LPARAM data); -- static int CALLBACK enumFunc2(CONST LOGFONT *font, -- CONST TEXTMETRIC *metrics, -- DWORD type, LPARAM data); -- -- GList *fonts; // [WinFontInfo] -- HDC dc; // (only used during enumeration) -- HKEY regKey; // (only used during enumeration) -- char *winFontDir; // (only used during enumeration) --}; -+#ifdef WIN32 -+ SysFontInfo *makeWindowsFont(char *name, int fontNum, -+ char *path); -+#endif - --WinFontList::WinFontList(char *winFontDirA) { -- OSVERSIONINFO version; -- char *path; -+ GList *fonts; // [SysFontInfo] -+}; - -+SysFontList::SysFontList() { - fonts = new GList(); -- dc = GetDC(NULL); -- winFontDir = winFontDirA; -- version.dwOSVersionInfoSize = sizeof(version); -- GetVersionEx(&version); -- if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { -- path = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\"; -- } else { -- path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\"; -- } -- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, -- KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, -- ®Key) == ERROR_SUCCESS) { -- EnumFonts(dc, NULL, &WinFontList::enumFunc1, (LPARAM)this); -- RegCloseKey(regKey); -- } -- ReleaseDC(NULL, dc); - } - --WinFontList::~WinFontList() { -- deleteGList(fonts, WinFontInfo); -+SysFontList::~SysFontList() { -+ deleteGList(fonts, SysFontInfo); - } - --void WinFontList::add(WinFontInfo *fi) { -- int i; -- -- for (i = 0; i < fonts->getLength(); ++i) { -- if (((WinFontInfo *)fonts->get(i))->equals(fi)) { -- delete fi; -- return; -- } -- } -- fonts->append(fi); --} -- --WinFontInfo *WinFontList::find(GString *font) { -- GString *name; -+SysFontInfo *SysFontList::find(GString *name) { -+ GString *name2; - GBool bold, italic; -- WinFontInfo *fi; -+ SysFontInfo *fi; - char c; - int n, i; - -- name = font->copy(); -+ name2 = name->copy(); - - // remove space, comma, dash chars - i = 0; -- while (i < name->getLength()) { -- c = name->getChar(i); -+ while (i < name2->getLength()) { -+ c = name2->getChar(i); - if (c == ' ' || c == ',' || c == '-') { -- name->del(i); -+ name2->del(i); - } else { - ++i; - } - } -- n = name->getLength(); -+ n = name2->getLength(); - - // remove trailing "MT" (Foo-MT, Foo-BoldMT, etc.) -- if (!strcmp(name->getCString() + n - 2, "MT")) { -- name->del(n - 2, 2); -+ if (n > 2 && !strcmp(name2->getCString() + n - 2, "MT")) { -+ name2->del(n - 2, 2); - n -= 2; - } - -+ // look for "Regular" -+ if (n > 7 && !strcmp(name2->getCString() + n - 7, "Regular")) { -+ name2->del(n - 7, 7); -+ n -= 7; -+ } -+ - // look for "Italic" -- if (!strcmp(name->getCString() + n - 6, "Italic")) { -- name->del(n - 6, 6); -+ if (n > 6 && !strcmp(name2->getCString() + n - 6, "Italic")) { -+ name2->del(n - 6, 6); - italic = gTrue; - n -= 6; - } else { -@@ -340,8 +263,8 @@ - } - - // look for "Bold" -- if (!strcmp(name->getCString() + n - 4, "Bold")) { -- name->del(n - 4, 4); -+ if (n > 4 && !strcmp(name2->getCString() + n - 4, "Bold")) { -+ name2->del(n - 4, 4); - bold = gTrue; - n -= 4; - } else { -@@ -349,84 +272,183 @@ - } - - // remove trailing "MT" (FooMT-Bold, etc.) -- if (!strcmp(name->getCString() + n - 2, "MT")) { -- name->del(n - 2, 2); -+ if (n > 2 && !strcmp(name2->getCString() + n - 2, "MT")) { -+ name2->del(n - 2, 2); - n -= 2; - } - - // remove trailing "PS" -- if (!strcmp(name->getCString() + n - 2, "PS")) { -- name->del(n - 2, 2); -+ if (n > 2 && !strcmp(name2->getCString() + n - 2, "PS")) { -+ name2->del(n - 2, 2); - n -= 2; - } - -+ // remove trailing "IdentityH" -+ if (n > 9 && !strcmp(name2->getCString() + n - 9, "IdentityH")) { -+ name2->del(n - 9, 9); -+ n -= 9; -+ } -+ - // search for the font - fi = NULL; - for (i = 0; i < fonts->getLength(); ++i) { -- fi = (WinFontInfo *)fonts->get(i); -- if (!fi->name->cmp(name) && fi->bold == bold && fi->italic == italic) { -+ fi = (SysFontInfo *)fonts->get(i); -+ if (fi->match(name2, bold, italic)) { - break; - } - fi = NULL; - } -+ if (!fi && bold) { -+ // try ignoring the bold flag -+ for (i = 0; i < fonts->getLength(); ++i) { -+ fi = (SysFontInfo *)fonts->get(i); -+ if (fi->match(name2, gFalse, italic)) { -+ break; -+ } -+ fi = NULL; -+ } -+ } -+ if (!fi && (bold || italic)) { -+ // try ignoring the bold and italic flags -+ for (i = 0; i < fonts->getLength(); ++i) { -+ fi = (SysFontInfo *)fonts->get(i); -+ if (fi->match(name2, gFalse, gFalse)) { -+ break; -+ } -+ fi = NULL; -+ } -+ } - -- delete name; -+ delete name2; - return fi; - } - --int CALLBACK WinFontList::enumFunc1(CONST LOGFONT *font, -- CONST TEXTMETRIC *metrics, -- DWORD type, LPARAM data) { -- WinFontList *fl = (WinFontList *)data; -- -- EnumFonts(fl->dc, font->lfFaceName, &WinFontList::enumFunc2, (LPARAM)fl); -- return 1; --} -- --int CALLBACK WinFontList::enumFunc2(CONST LOGFONT *font, -- CONST TEXTMETRIC *metrics, -- DWORD type, LPARAM data) { -- WinFontList *fl = (WinFontList *)data; -- WinFontInfo *fi; -- -- if (type & TRUETYPE_FONTTYPE) { -- if ((fi = WinFontInfo::make(new GString(font->lfFaceName), -- font->lfWeight >= 600, -- font->lfItalic ? gTrue : gFalse, -- fl->regKey, fl->winFontDir))) { -- fl->add(fi); -+#ifdef WIN32 -+void SysFontList::scanWindowsFonts(char *winFontDir) { -+ OSVERSIONINFO version; -+ char *path; -+ DWORD idx, valNameLen, dataLen, type; -+ HKEY regKey; -+ char valName[1024], data[1024]; -+ int n, fontNum; -+ char *p0, *p1; -+ GString *fontPath; -+ -+ version.dwOSVersionInfoSize = sizeof(version); -+ GetVersionEx(&version); -+ if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { -+ path = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\"; -+ } else { -+ path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\"; -+ } -+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, -+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, -+ ®Key) == ERROR_SUCCESS) { -+ idx = 0; -+ while (1) { -+ valNameLen = sizeof(valName) - 1; -+ dataLen = sizeof(data) - 1; -+ if (RegEnumValue(regKey, idx, valName, &valNameLen, NULL, -+ &type, (LPBYTE)data, &dataLen) != ERROR_SUCCESS) { -+ break; -+ } -+ if (type == REG_SZ && -+ valNameLen > 0 && valNameLen < sizeof(valName) && -+ dataLen > 0 && dataLen < sizeof(data)) { -+ valName[valNameLen] = '\0'; -+ data[dataLen] = '\0'; -+ n = strlen(data); -+ if (!strcasecmp(data + n - 4, ".ttf") || -+ !strcasecmp(data + n - 4, ".ttc")) { -+ fontPath = new GString(data); -+ if (!(dataLen >= 3 && data[1] == ':' && data[2] == '\\')) { -+ fontPath->insert(0, '\\'); -+ fontPath->insert(0, winFontDir); -+ } -+ p0 = valName; -+ fontNum = 0; -+ while (*p0) { -+ p1 = strstr(p0, " & "); -+ if (p1) { -+ *p1 = '\0'; -+ p1 = p1 + 3; -+ } else { -+ p1 = p0 + strlen(p0); -+ } -+ fonts->append(makeWindowsFont(p0, fontNum, -+ fontPath->getCString())); -+ p0 = p1; -+ ++fontNum; -+ } -+ delete fontPath; -+ } -+ } -+ ++idx; - } -+ RegCloseKey(regKey); - } -- return 1; - } - --#endif // WIN32 -+SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum, -+ char *path) { -+ int n; -+ GBool bold, italic; -+ GString *s; -+ char c; -+ int i; -+ SysFontType type; - --//------------------------------------------------------------------------ --// PSFontParam --//------------------------------------------------------------------------ -+ n = strlen(name); -+ bold = italic = gFalse; - --PSFontParam::PSFontParam(GString *pdfFontNameA, int wModeA, -- GString *psFontNameA, GString *encodingA) { -- pdfFontName = pdfFontNameA; -- wMode = wModeA; -- psFontName = psFontNameA; -- encoding = encodingA; --} -+ // remove trailing ' (TrueType)' -+ if (n > 11 && !strncmp(name + n - 11, " (TrueType)", 11)) { -+ n -= 11; -+ } - --PSFontParam::~PSFontParam() { -- delete pdfFontName; -- delete psFontName; -- if (encoding) { -- delete encoding; -+ // remove trailing ' Italic' -+ if (n > 7 && !strncmp(name + n - 7, " Italic", 7)) { -+ n -= 7; -+ italic = gTrue; -+ } -+ -+ // remove trailing ' Bold' -+ if (n > 5 && !strncmp(name + n - 5, " Bold", 5)) { -+ n -= 5; -+ bold = gTrue; - } -+ -+ // remove trailing ' Regular' -+ if (n > 5 && !strncmp(name + n - 8, " Regular", 8)) { -+ n -= 8; -+ } -+ -+ //----- normalize the font name -+ s = new GString(name, n); -+ i = 0; -+ while (i < s->getLength()) { -+ c = s->getChar(i); -+ if (c == ' ' || c == ',' || c == '-') { -+ s->del(i); -+ } else { -+ ++i; -+ } -+ } -+ -+ if (!strcasecmp(path + strlen(path) - 4, ".ttc")) { -+ type = sysFontTTC; -+ } else { -+ type = sysFontTTF; -+ } -+ return new SysFontInfo(s, bold, italic, new GString(path), type, fontNum); - } -+#endif - - //------------------------------------------------------------------------ - // KeyBinding - //------------------------------------------------------------------------ - --KeyBinding::KeyBinding(int codeA, int modsA, int contextA, char *cmd0) { -+KeyBinding::KeyBinding(int codeA, int modsA, int contextA, const char *cmd0) { - code = codeA; - mods = modsA; - context = contextA; -@@ -637,9 +657,10 @@ - unicodeMaps = new GHash(gTrue); - cMapDirs = new GHash(gTrue); - toUnicodeDirs = new GList(); -- displayFonts = new GHash(); -- displayCIDFonts = new GHash(); -- displayNamedCIDFonts = new GHash(); -+ fontFiles = new GHash(gTrue); -+ fontDirs = new GList(); -+ ccFontFiles = new GHash(gTrue); -+ sysFonts = new SysFontList(); - #if HAVE_PAPER_H - char *paperName; - const struct paper *paperType; -@@ -668,16 +689,21 @@ - psDuplex = gFalse; - psLevel = psLevel2; - psFile = NULL; -- psFonts = new GHash(); -- psNamedFonts16 = new GList(); -- psFonts16 = new GList(); -+ psResidentFonts = new GHash(gTrue); -+ psResidentFonts16 = new GList(); -+ psResidentFontsCC = new GList(); - psEmbedType1 = gTrue; - psEmbedTrueType = gTrue; - psEmbedCIDPostScript = gTrue; - psEmbedCIDTrueType = gTrue; -+ psFontPassthrough = gFalse; - psPreload = gFalse; - psOPI = gFalse; - psASCIIHex = gFalse; -+ psUncompressPreloadedImages = gFalse; -+ psRasterResolution = 300; -+ psRasterMono = gFalse; -+ psAlwaysRasterize = gFalse; - textEncoding = new GString("Latin1"); - #if defined(WIN32) - textEOL = eolDOS; -@@ -688,13 +714,14 @@ - #endif - textPageBreaks = gTrue; - textKeepTinyChars = gFalse; -- fontDirs = new GList(); - initialZoom = new GString("125"); - continuousView = gFalse; - enableT1lib = gTrue; - enableFreeType = gTrue; -+ disableFreeTypeHinting = gFalse; - antialias = gTrue; - vectorAntialias = gTrue; -+ antialiasPrinting = gFalse; - strokeAdjust = gTrue; - screenType = screenUnset; - screenSize = -1; -@@ -702,6 +729,10 @@ - screenGamma = 1.0; - screenBlackThreshold = 0.0; - screenWhiteThreshold = 1.0; -+ minLineWidth = 0.0; - overprintPreview = gFalse; - urlCommand = NULL; - movieCommand = NULL; - mapNumericCharNames = gTrue; -@@ -716,10 +747,6 @@ - unicodeMapCache = new UnicodeMapCache(); - cMapCache = new CMapCache(); - --#ifdef WIN32 -- winFontList = NULL; --#endif -- - #ifdef ENABLE_PLUGINS - plugins = new GList(); - securityHandlers = new GList(); -@@ -764,7 +791,7 @@ - } - } - if (!f) { --#if defined(WIN32) && !defined(__CYGWIN32__) -+#ifdef WIN32 - char buf[512]; - i = GetModuleFileName(NULL, buf, sizeof(buf)); - if (i <= 0 || i >= sizeof(buf)) { -@@ -1762,23 +1833,21 @@ - deleteGHash(residentUnicodeMaps, UnicodeMap); - deleteGHash(unicodeMaps, GString); - deleteGList(toUnicodeDirs, GString); -- deleteGHash(displayFonts, DisplayFontParam); -- deleteGHash(displayCIDFonts, DisplayFontParam); -- deleteGHash(displayNamedCIDFonts, DisplayFontParam); --#ifdef WIN32 -- if (winFontList) { -- delete winFontList; -- } --#endif -+ deleteGHash(fontFiles, GString); -+ deleteGList(fontDirs, GString); -+ deleteGHash(ccFontFiles, GString); -+ delete sysFonts; - if (psFile) { - delete psFile; - } -- deleteGHash(psFonts, PSFontParam); -- deleteGList(psNamedFonts16, PSFontParam); -- deleteGList(psFonts16, PSFontParam); -+ deleteGHash(psResidentFonts, GString); -+ deleteGList(psResidentFonts16, PSFontParam16); -+ deleteGList(psResidentFontsCC, PSFontParam16); - delete textEncoding; -- deleteGList(fontDirs, GString); - delete initialZoom; -@@ -1829,8 +1898,6 @@ - char winFontDir[MAX_PATH]; - #endif - FILE *f; -- DisplayFontParamKind kind; -- DisplayFontParam *dfp; - int i, j; - - #ifdef WIN32 -@@ -1850,16 +1917,13 @@ - } - #endif - for (i = 0; displayFontTab[i].name; ++i) { -- fontName = new GString(displayFontTab[i].name); -- if (getDisplayFont(fontName)) { -- delete fontName; -+ if (fontFiles->lookup(displayFontTab[i].name)) { - continue; - } -+ fontName = new GString(displayFontTab[i].name); - fileName = NULL; -- kind = displayFontT1; // make gcc happy - if (dir) { - fileName = appendToPath(new GString(dir), displayFontTab[i].t1FileName); -- kind = displayFontT1; - if ((f = fopen(fileName->getCString(), "rb"))) { - fclose(f); - } else { -@@ -1871,7 +1935,6 @@ - if (!fileName && winFontDir[0] && displayFontTab[i].ttFileName) { - fileName = appendToPath(new GString(winFontDir), - displayFontTab[i].ttFileName); -- kind = displayFontTT; - if ((f = fopen(fileName->getCString(), "rb"))) { - fclose(f); - } else { -@@ -1886,7 +1949,6 @@ - for (j = 0; !fileName && displayFontDirs[j]; ++j) { - fileName = appendToPath(new GString(displayFontDirs[j]), - displayFontTab[i].ttFileName); -- kind = displayFontTT; - if ((f = fopen(fileName->getCString(), "rb"))) { - fclose(f); - } else { -@@ -1895,11 +1957,10 @@ - } - } - } --#else -+#else // WIN32 - for (j = 0; !fileName && displayFontDirs[j]; ++j) { - fileName = appendToPath(new GString(displayFontDirs[j]), - displayFontTab[i].t1FileName); -- kind = displayFontT1; - if ((f = fopen(fileName->getCString(), "rb"))) { - fclose(f); - } else { -@@ -1907,20 +1968,19 @@ - fileName = NULL; - } - } --#endif -+#endif // WIN32 - if (!fileName) { -- error(-1, "No display font for '%s'", displayFontTab[i].name); -+ error(errConfig, -1, "No display font for '{0:s}'", -+ displayFontTab[i].name); - delete fontName; - continue; - } -- dfp = new DisplayFontParam(fontName, kind); -- dfp->t1.fileName = fileName; -- globalParams->addDisplayFont(dfp); -+ addFontFile(fontName, fileName); - } - - #ifdef WIN32 - if (winFontDir[0]) { -- winFontList = new WinFontList(winFontDir); -+ sysFonts->scanWindowsFonts(winFontDir); - } - #endif - } -@@ -2020,31 +2080,72 @@ - return NULL; - } - --DisplayFontParam *GlobalParams::getDisplayFont(GString *fontName) { -- DisplayFontParam *dfp; -+GString *GlobalParams::findFontFile(GString *fontName) { -+ static const char *exts[] = { ".pfa", ".pfb", ".ttf", ".ttc" }; -+ GString *path, *dir; -+#ifdef WIN32 -+ GString *fontNameU; -+#endif -+ const char *ext; -+ FILE *f; -+ int i, j; - - lockGlobalParams; -- dfp = (DisplayFontParam *)displayFonts->lookup(fontName); --#ifdef WIN32 -- if (!dfp && winFontList) { -- dfp = winFontList->find(fontName); -+ if ((path = (GString *)fontFiles->lookup(fontName))) { -+ path = path->copy(); -+ unlockGlobalParams; -+ return path; - } -+ for (i = 0; i < fontDirs->getLength(); ++i) { -+ dir = (GString *)fontDirs->get(i); -+ for (j = 0; j < (int)(sizeof(exts) / sizeof(exts[0])); ++j) { -+ ext = exts[j]; -+#ifdef WIN32 -+ fontNameU = fileNameToUTF8(fontName->getCString()); -+ path = appendToPath(dir->copy(), fontNameU->getCString()); -+ delete fontNameU; -+#else -+ path = appendToPath(dir->copy(), fontName->getCString()); - #endif -+ path->append(ext); -+ if ((f = openFile(path->getCString(), "rb"))) { -+ fclose(f); -+ unlockGlobalParams; -+ return path; -+ } -+ delete path; -+ } -+ } -+ unlockGlobalParams; -+ return NULL; -+} -+ -+GString *GlobalParams::findSystemFontFile(GString *fontName, -+ SysFontType *type, -+ int *fontNum) { -+ SysFontInfo *fi; -+ GString *path; -+ -+ path = NULL; -+ lockGlobalParams; -+ if ((fi = sysFonts->find(fontName))) { -+ path = fi->path->copy(); -+ *type = fi->type; -+ *fontNum = fi->fontNum; -+ } - unlockGlobalParams; -- return dfp; -+ return path; - } - --DisplayFontParam *GlobalParams::getDisplayCIDFont(GString *fontName, -- GString *collection) { -- DisplayFontParam *dfp; -+GString *GlobalParams::findCCFontFile(GString *collection) { -+ GString *path; - - lockGlobalParams; -- if (!fontName || -- !(dfp = (DisplayFontParam *)displayNamedCIDFonts->lookup(fontName))) { -- dfp = (DisplayFontParam *)displayCIDFonts->lookup(collection); -+ if ((path = (GString *)ccFontFiles->lookup(collection))) { -+ path = path->copy(); - } - unlockGlobalParams; -- return dfp; -+ return path; - } - - GString *GlobalParams::getPSFile() { -@@ -2137,41 +2238,62 @@ - return level; - } - --PSFontParam *GlobalParams::getPSFont(GString *fontName) { -- PSFontParam *p; -+GString *GlobalParams::getPSResidentFont(GString *fontName) { -+ GString *psName; - - lockGlobalParams; -- p = (PSFontParam *)psFonts->lookup(fontName); -+ psName = (GString *)psResidentFonts->lookup(fontName); - unlockGlobalParams; -- return p; -+ return psName; - } - --PSFontParam *GlobalParams::getPSFont16(GString *fontName, -- GString *collection, int wMode) { -- PSFontParam *p; -+GList *GlobalParams::getPSResidentFonts() { -+ GList *names; -+ GHashIter *iter; -+ GString *name; -+ GString *psName; -+ -+ names = new GList(); -+ lockGlobalParams; -+ psResidentFonts->startIter(&iter); -+ while (psResidentFonts->getNext(&iter, &name, (void **)&psName)) { -+ names->append(psName->copy()); -+ } -+ unlockGlobalParams; -+ return names; -+} -+ -+PSFontParam16 *GlobalParams::getPSResidentFont16(GString *fontName, -+ int wMode) { -+ PSFontParam16 *p; - int i; - - lockGlobalParams; - p = NULL; -- if (fontName) { -- for (i = 0; i < psNamedFonts16->getLength(); ++i) { -- p = (PSFontParam *)psNamedFonts16->get(i); -- if (!p->pdfFontName->cmp(fontName) && -- p->wMode == wMode) { -- break; -- } -- p = NULL; -+ for (i = 0; i < psResidentFonts16->getLength(); ++i) { -+ p = (PSFontParam16 *)psResidentFonts16->get(i); -+ if (!(p->name->cmp(fontName)) && p->wMode == wMode) { -+ break; - } -+ p = NULL; - } -- if (!p && collection) { -- for (i = 0; i < psFonts16->getLength(); ++i) { -- p = (PSFontParam *)psFonts16->get(i); -- if (!p->pdfFontName->cmp(collection) && -- p->wMode == wMode) { -- break; -- } -- p = NULL; -+ unlockGlobalParams; -+ return p; -+} -+ -+PSFontParam16 *GlobalParams::getPSResidentFontCC(GString *collection, -+ int wMode) { -+ PSFontParam16 *p; -+ int i; -+ -+ lockGlobalParams; -+ p = NULL; -+ for (i = 0; i < psResidentFontsCC->getLength(); ++i) { -+ p = (PSFontParam16 *)psResidentFontsCC->get(i); -+ if (!(p->name->cmp(collection)) && p->wMode == wMode) { -+ break; - } -+ p = NULL; - } - unlockGlobalParams; - return p; -@@ -2213,6 +2335,15 @@ - return e; - } - -+GBool GlobalParams::getPSFontPassthrough() { -+ GBool e; -+ -+ lockGlobalParams; -+ e = psFontPassthrough; -+ unlockGlobalParams; -+ return e; -+} -+ - GBool GlobalParams::getPSPreload() { - GBool preload; - -@@ -2240,6 +2371,42 @@ - return ah; - } - -+GBool GlobalParams::getPSUncompressPreloadedImages() { -+ GBool ah; -+ -+ lockGlobalParams; -+ ah = psUncompressPreloadedImages; -+ unlockGlobalParams; -+ return ah; -+} -+ -+double GlobalParams::getPSRasterResolution() { -+ double res; -+ -+ lockGlobalParams; -+ res = psRasterResolution; -+ unlockGlobalParams; -+ return res; -+} -+ -+GBool GlobalParams::getPSRasterMono() { -+ GBool mono; -+ -+ lockGlobalParams; -+ mono = psRasterMono; -+ unlockGlobalParams; -+ return mono; -+} -+ -+GBool GlobalParams::getPSAlwaysRasterize() { -+ GBool rast; -+ -+ lockGlobalParams; -+ rast = psAlwaysRasterize; -+ unlockGlobalParams; -+ return rast; -+} -+ - GString *GlobalParams::getTextEncodingName() { - GString *s; - -@@ -2276,30 +2443,6 @@ - return tiny; - } - --GString *GlobalParams::findFontFile(GString *fontName, char **exts) { -- GString *dir, *fileName; -- char **ext; -- FILE *f; -- int i; -- -- lockGlobalParams; -- for (i = 0; i < fontDirs->getLength(); ++i) { -- dir = (GString *)fontDirs->get(i); -- for (ext = exts; *ext; ++ext) { -- fileName = appendToPath(dir->copy(), fontName->getCString()); -- fileName->append(*ext); -- if ((f = fopen(fileName->getCString(), "rb"))) { -- fclose(f); -- unlockGlobalParams; -- return fileName; -- } -- delete fileName; -- } -- } -- unlockGlobalParams; -- return NULL; --} -- - GString *GlobalParams::getInitialZoom() { - GString *s; - -@@ -2355,6 +2507,15 @@ - return f; - } - -+GBool GlobalParams::getAntialiasPrinting() { -+ GBool f; -+ -+ lockGlobalParams; -+ f = antialiasPrinting; -+ unlockGlobalParams; -+ return f; -+} -+ - GBool GlobalParams::getStrokeAdjust() { - GBool f; - -@@ -2418,6 +2579,24 @@ - return thresh; - } - -+double GlobalParams::getMinLineWidth() { -+ double w; -+ -+ lockGlobalParams; -+ w = minLineWidth; -+ unlockGlobalParams; -+ return w; -+} -+ - GBool GlobalParams::getMapNumericCharNames() { - GBool map; - -@@ -2552,14 +2731,9 @@ - // functions to set parameters - //------------------------------------------------------------------------ - --void GlobalParams::addDisplayFont(DisplayFontParam *param) { -- DisplayFontParam *old; -- -+void GlobalParams::addFontFile(GString *fontName, GString *path) { - lockGlobalParams; -- if ((old = (DisplayFontParam *)displayFonts->remove(param->name))) { -- delete old; -- } -- displayFonts->add(param->name, param); -+ fontFiles->add(fontName, path); - unlockGlobalParams; - } - -@@ -2684,6 +2858,12 @@ - unlockGlobalParams; - } - -+void GlobalParams::setPSFontPassthrough(GBool passthrough) { -+ lockGlobalParams; -+ psFontPassthrough = passthrough; -+ unlockGlobalParams; -+} -+ - void GlobalParams::setPSPreload(GBool preload) { - lockGlobalParams; - psPreload = preload; -diff -ru xpdf-3.02/xpdf/GlobalParams.h xpdf-3.03/xpdf/GlobalParams.h ---- xpdf-3.02/xpdf/GlobalParams.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/GlobalParams.h 2011-08-15 23:08:53.000000000 +0200 -@@ -35,9 +35,7 @@ - class CMapCache; - struct XpdfSecurityHandler; - class GlobalParams; --#ifdef WIN32 --class WinFontList; --#endif -+class SysFontList; - - //------------------------------------------------------------------------ - -@@ -46,51 +44,27 @@ - - //------------------------------------------------------------------------ - --enum DisplayFontParamKind { -- displayFontT1, -- displayFontTT --}; -- --struct DisplayFontParamT1 { -- GString *fileName; --}; -- --struct DisplayFontParamTT { -- GString *fileName; --}; -- --class DisplayFontParam { --public: -- -- GString *name; // font name for 8-bit fonts and named -- // CID fonts; collection name for -- // generic CID fonts -- DisplayFontParamKind kind; -- union { -- DisplayFontParamT1 t1; -- DisplayFontParamTT tt; -- }; -- -- DisplayFontParam(GString *nameA, DisplayFontParamKind kindA); -- virtual ~DisplayFontParam(); -+enum SysFontType { -+ sysFontPFA, -+ sysFontPFB, -+ sysFontTTF, -+ sysFontTTC - }; - - //------------------------------------------------------------------------ - --class PSFontParam { -+class PSFontParam16 { - public: - -- GString *pdfFontName; // PDF font name for 8-bit fonts and -- // named 16-bit fonts; char collection -- // name for generic 16-bit fonts -- int wMode; // writing mode (0=horiz, 1=vert) for -- // 16-bit fonts -+ GString *name; // PDF font name for psResidentFont16; -+ // char collection name for psResidentFontCC -+ int wMode; // writing mode (0=horiz, 1=vert) - GString *psFontName; // PostScript font name -- GString *encoding; // encoding, for 16-bit fonts only -+ GString *encoding; // encoding - -- PSFontParam(GString *pdfFontNameA, int wModeA, -- GString *psFontNameA, GString *encodingA); -- ~PSFontParam(); -+ PSFontParam16(GString *nameA, int wModeA, -+ GString *psFontNameA, GString *encodingA); -+ ~PSFontParam16(); - }; - - //------------------------------------------------------------------------ -@@ -212,9 +191,11 @@ - UnicodeMap *getResidentUnicodeMap(GString *encodingName); - FILE *getUnicodeMapFile(GString *encodingName); - FILE *findCMapFile(GString *collection, GString *cMapName); - FILE *findToUnicodeFile(GString *name); -- DisplayFontParam *getDisplayFont(GString *fontName); -- DisplayFontParam *getDisplayCIDFont(GString *fontName, GString *collection); -+ GString *findFontFile(GString *fontName); -+ GString *findSystemFontFile(GString *fontName, SysFontType *type, -+ int *fontNum); -+ GString *findCCFontFile(GString *collection); - GString *getPSFile(); - int getPSPaperWidth(); - int getPSPaperHeight(); -@@ -225,26 +206,34 @@ - GBool getPSShrinkLarger(); - GBool getPSCenter(); - PSLevel getPSLevel(); -- PSFontParam *getPSFont(GString *fontName); -- PSFontParam *getPSFont16(GString *fontName, GString *collection, int wMode); -+ GString *getPSResidentFont(GString *fontName); -+ GList *getPSResidentFonts(); -+ PSFontParam16 *getPSResidentFont16(GString *fontName, int wMode); -+ PSFontParam16 *getPSResidentFontCC(GString *collection, int wMode); - GBool getPSEmbedType1(); - GBool getPSEmbedTrueType(); - GBool getPSEmbedCIDPostScript(); - GBool getPSEmbedCIDTrueType(); -+ GBool getPSFontPassthrough(); - GBool getPSPreload(); - GBool getPSOPI(); - GBool getPSASCIIHex(); -+ GBool getPSUncompressPreloadedImages(); -+ double getPSRasterResolution(); -+ GBool getPSRasterMono(); -+ GBool getPSAlwaysRasterize(); - GString *getTextEncodingName(); - EndOfLineKind getTextEOL(); - GBool getTextPageBreaks(); - GBool getTextKeepTinyChars(); -- GString *findFontFile(GString *fontName, char **exts); - GString *getInitialZoom(); - GBool getContinuousView(); - GBool getEnableT1lib(); - GBool getEnableFreeType(); - GBool getAntialias(); - GBool getVectorAntialias(); -+ GBool getAntialiasPrinting(); - GBool getStrokeAdjust(); - ScreenType getScreenType(); - int getScreenSize(); -@@ -252,6 +241,10 @@ - double getScreenGamma(); - double getScreenBlackThreshold(); - double getScreenWhiteThreshold(); -+ double getMinLineWidth(); -+ GBool getDrawAnnotations(); - GBool getOverprintPreview() { return overprintPreview; } - GString *getURLCommand() { return urlCommand; } - GString *getMovieCommand() { return movieCommand; } - GBool getMapNumericCharNames(); -@@ -268,7 +261,7 @@ - - //----- functions to set parameters - -- void addDisplayFont(DisplayFontParam *param); -+ void addFontFile(GString *fontName, GString *path); - void setPSFile(char *file); - GBool setPSPaperSize(char *size); - void setPSPaperWidth(int width); -@@ -284,6 +277,7 @@ - void setPSEmbedTrueType(GBool embed); - void setPSEmbedCIDPostScript(GBool embed); - void setPSEmbedCIDTrueType(GBool embed); -+ void setPSFontPassthrough(GBool passthrough); - void setPSPreload(GBool preload); - void setPSOPI(GBool opi); - void setPSASCIIHex(GBool hex); -@@ -380,15 +374,12 @@ - GHash *cMapDirs; // list of CMap dirs, indexed by collection - // name [GList[GString]] - GList *toUnicodeDirs; // list of ToUnicode CMap dirs [GString] -- GHash *displayFonts; // display font info, indexed by font name -- // [DisplayFontParam] --#ifdef WIN32 -- WinFontList *winFontList; // system TrueType fonts --#endif -- GHash *displayCIDFonts; // display CID font info, indexed by -- // collection [DisplayFontParam] -- GHash *displayNamedCIDFonts; // display CID font info, indexed by -- // font name [DisplayFontParam] -+ GHash *fontFiles; // font files: font name mapped to path -+ // [GString] -+ GList *fontDirs; // list of font dirs [GString] -+ GHash *ccFontFiles; // character collection font files: -+ // collection name mapped to path [GString] -+ SysFontList *sysFonts; // system fonts - GString *psFile; // PostScript file or command (for xpdf) - int psPaperWidth; // paper size, in PostScript points, for - int psPaperHeight; // PostScript output -@@ -402,31 +393,44 @@ - GBool psCenter; // center pages on the paper - GBool psDuplex; // enable duplexing in PostScript? - PSLevel psLevel; // PostScript level to generate -- GHash *psFonts; // PostScript font info, indexed by PDF -- // font name [PSFontParam] -- GList *psNamedFonts16; // named 16-bit fonts [PSFontParam] -- GList *psFonts16; // generic 16-bit fonts [PSFontParam] -+ GHash *psResidentFonts; // 8-bit fonts resident in printer: -+ // PDF font name mapped to PS font name -+ // [GString] -+ GList *psResidentFonts16; // 16-bit fonts resident in printer: -+ // PDF font name mapped to font info -+ // [PSFontParam16] -+ GList *psResidentFontsCC; // 16-bit character collection fonts -+ // resident in printer: collection name -+ // mapped to font info [PSFontParam16] - GBool psEmbedType1; // embed Type 1 fonts? - GBool psEmbedTrueType; // embed TrueType fonts? - GBool psEmbedCIDPostScript; // embed CID PostScript fonts? - GBool psEmbedCIDTrueType; // embed CID TrueType fonts? -+ GBool psFontPassthrough; // pass all fonts through as-is? - GBool psPreload; // preload PostScript images and forms into - // memory - GBool psOPI; // generate PostScript OPI comments? - GBool psASCIIHex; // use ASCIIHex instead of ASCII85? -+ GBool psUncompressPreloadedImages; // uncompress all preloaded images -+ double psRasterResolution; // PostScript rasterization resolution (dpi) -+ GBool psRasterMono; // true to do PostScript rasterization -+ // in monochrome (gray); false to do it -+ // in color (RGB/CMYK) -+ GBool psAlwaysRasterize; // force PostScript rasterization - GString *textEncoding; // encoding (unicodeMap) to use for text - // output - EndOfLineKind textEOL; // type of EOL marker to use for text - // output - GBool textPageBreaks; // insert end-of-page markers? - GBool textKeepTinyChars; // keep all characters in text output -- GList *fontDirs; // list of font dirs [GString] - GString *initialZoom; // initial zoom level - GBool continuousView; // continuous view mode - GBool enableT1lib; // t1lib enable flag - GBool enableFreeType; // FreeType enable flag - GBool antialias; // font anti-aliasing enable flag - GBool vectorAntialias; // vector anti-aliasing enable flag -+ GBool antialiasPrinting; // allow anti-aliasing when printing - GBool strokeAdjust; // stroke adjustment enable flag - ScreenType screenType; // halftone screen type - int screenSize; // screen matrix size -@@ -434,6 +438,10 @@ - double screenGamma; // screen gamma correction - double screenBlackThreshold; // screen black clamping threshold - double screenWhiteThreshold; // screen white clamping threshold -+ double minLineWidth; // minimum line width - GBool overprintPreview; // enable overprint preview - GString *urlCommand; // command executed for URL links - GString *movieCommand; // command executed for movie annotations - GBool mapNumericCharNames; // map numeric char names (from font subsets)? -Només a xpdf-3.03/xpdf: OptionalContent.cc -Només a xpdf-3.03/xpdf: OptionalContent.h -diff -ru xpdf-3.02/xpdf/OutputDev.cc xpdf-3.03/xpdf/OutputDev.cc ---- xpdf-3.02/xpdf/OutputDev.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/OutputDev.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -65,6 +65,7 @@ - updateStrokeOpacity(state); - updateFillOverprint(state); - updateStrokeOverprint(state); -+ updateOverprintMode(state); - updateTransfer(state); - updateFont(state); - } -@@ -89,6 +90,13 @@ - } - } - -+void OutputDev::setSoftMaskFromImageMask(GfxState *state, -+ Object *ref, Stream *str, -+ int width, int height, GBool invert, -+ GBool inlineImg) { -+ drawImageMask(state, ref, str, width, height, invert, inlineImg); -+} -+ - void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, - int *maskColors, GBool inlineImg) { -diff -ru xpdf-3.02/xpdf/OutputDev.h xpdf-3.03/xpdf/OutputDev.h ---- xpdf-3.02/xpdf/OutputDev.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/OutputDev.h 2011-08-15 23:08:53.000000000 +0200 -@@ -19,6 +19,7 @@ - #include "CharTypes.h" - - class GString; -+class Gfx; - class GfxState; - struct GfxColor; - class GfxColorSpace; -@@ -76,6 +77,11 @@ - // Does this device need non-text content? - virtual GBool needNonText() { return gTrue; } - -+ // Does this device require incCharCount to be called for text on -+ // non-shown layers? -+ virtual GBool needCharCount() { return gFalse; } -+ -+ - //----- initialization and control - - // Set default transform matrix. -@@ -135,6 +141,7 @@ - virtual void updateStrokeOpacity(GfxState *state) {} - virtual void updateFillOverprint(GfxState *state) {} - virtual void updateStrokeOverprint(GfxState *state) {} -+ virtual void updateOverprintMode(GfxState *state) {} - virtual void updateTransfer(GfxState *state) {} - - //----- update text state -@@ -147,12 +154,14 @@ - virtual void updateHorizScaling(GfxState *state) {} - virtual void updateTextPos(GfxState *state) {} - virtual void updateTextShift(GfxState *state, double shift) {} -+ virtual void saveTextPos(GfxState *state) {} -+ virtual void restoreTextPos(GfxState *state) {} - - //----- path painting - virtual void stroke(GfxState *state) {} - virtual void fill(GfxState *state) {} - virtual void eoFill(GfxState *state) {} -- virtual void tilingPatternFill(GfxState *state, Object *str, -+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str, - int paintType, Dict *resDict, - double *mat, double *bbox, - int x0, int y0, int x1, int y1, -@@ -185,11 +194,18 @@ - CharCode code, Unicode *u, int uLen); - virtual void endType3Char(GfxState *state) {} - virtual void endTextObject(GfxState *state) {} -+ virtual void incCharCount(int nChars) {} - - //----- image drawing - virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, - GBool inlineImg); -+ virtual void setSoftMaskFromImageMask(GfxState *state, -+ Object *ref, Stream *str, -+ int width, int height, GBool invert, -+ GBool inlineImg); - virtual void drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, - int *maskColors, GBool inlineImg); -@@ -234,11 +250,10 @@ - virtual void clearSoftMask(GfxState *state) {} - - //----- links - virtual void processLink(Link *link) {} - - #if 1 //~tmp: turn off anti-aliasing temporarily -- virtual GBool getVectorAntialias() { return gFalse; } -- virtual void setVectorAntialias(GBool vaa) {} -+ virtual void setInShading(GBool sh) {} - #endif - - private: -diff -ru xpdf-3.02/xpdf/PDFDoc.cc xpdf-3.03/xpdf/PDFDoc.cc ---- xpdf-3.02/xpdf/PDFDoc.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/PDFDoc.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -36,6 +36,7 @@ - #ifndef DISABLE_OUTLINE - #include "Outline.h" - #endif -+#include "OptionalContent.h" - #include "PDFDoc.h" - - //------------------------------------------------------------------------ -@@ -174,6 +199,7 @@ - #ifndef DISABLE_OUTLINE - outline = NULL; - #endif -+ optContent = NULL; - ok = setup(ownerPassword, userPassword); - } - -@@ -183,38 +209,71 @@ - // check header - checkHeader(); - -+ // read the xref and catalog -+ if (!PDFDoc::setup2(ownerPassword, userPassword, gFalse)) { -+ if (errCode == errDamaged || errCode == errBadCatalog) { -+ // try repairing the xref table -+ error(errSyntaxWarning, -1, -+ "PDF file is damaged - attempting to reconstruct xref table..."); -+ if (!PDFDoc::setup2(ownerPassword, userPassword, gTrue)) { -+ return gFalse; -+ } -+ } else { -+ return gFalse; -+ } -+ } -+ -+#ifndef DISABLE_OUTLINE -+ // read outline -+ outline = new Outline(catalog->getOutline(), xref); -+#endif -+ -+ // read the optional content info -+ optContent = new OptionalContent(this); -+ -+ // done -+ return gTrue; -+} -+ -+GBool PDFDoc::setup2(GString *ownerPassword, GString *userPassword, -+ GBool repairXRef) { - // read xref table -- xref = new XRef(str); -+ xref = new XRef(str, repairXRef); - if (!xref->isOk()) { -- error(-1, "Couldn't read xref table"); -+ error(errSyntaxError, -1, "Couldn't read xref table"); - errCode = xref->getErrorCode(); -+ delete xref; -+ xref = NULL; - return gFalse; - } - - // check for encryption - if (!checkEncryption(ownerPassword, userPassword)) { - errCode = errEncrypted; -+ delete xref; -+ xref = NULL; - return gFalse; - } - - // read catalog - catalog = new Catalog(this); - if (!catalog->isOk()) { -- error(-1, "Couldn't read page catalog"); -+ error(errSyntaxError, -1, "Couldn't read page catalog"); - errCode = errBadCatalog; -+ delete catalog; -+ catalog = NULL; -+ delete xref; -+ xref = NULL; - return gFalse; - } - --#ifndef DISABLE_OUTLINE -- // read outline -- outline = new Outline(catalog->getOutline(), xref); --#endif -- -- // done - return gTrue; - } - - PDFDoc::~PDFDoc() { -+ if (optContent) { -+ delete optContent; -+ } - #ifndef DISABLE_OUTLINE - if (outline) { - delete outline; -@@ -280,7 +345,10 @@ - xref->getTrailerDict()->dictLookup("Encrypt", &encrypt); - if ((encrypted = encrypt.isDict())) { - if ((secHdlr = SecurityHandler::make(this, &encrypt))) { -- if (secHdlr->checkEncryption(ownerPassword, userPassword)) { -+ if (secHdlr->isUnencrypted()) { -+ // no encryption -+ ret = gTrue; -+ } else if (secHdlr->checkEncryption(ownerPassword, userPassword)) { - // authorization succeeded - xref->setEncryption(secHdlr->getPermissionFlags(), - secHdlr->getOwnerPasswordOk(), -@@ -315,7 +383,7 @@ - printf("***** page %d *****\n", page); - } - catalog->getPage(page)->display(out, hDPI, vDPI, -- rotate, useMediaBox, crop, printing, catalog, -+ rotate, useMediaBox, crop, printing, - abortCheckCbk, abortCheckCbkData); - } - -@@ -329,6 +397,7 @@ - for (page = firstPage; page <= lastPage; ++page) { - displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, printing, - abortCheckCbk, abortCheckCbkData); -+ catalog->doneWithPage(page); - } - } - -diff -ru xpdf-3.02/xpdf/PDFDoc.h xpdf-3.03/xpdf/PDFDoc.h ---- xpdf-3.02/xpdf/PDFDoc.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/PDFDoc.h 2011-08-15 23:08:53.000000000 +0200 -@@ -27,6 +27,8 @@ - class LinkAction; - class LinkDest; - class Outline; -+class OptionalContent; - - //------------------------------------------------------------------------ - // PDFDoc -@@ -128,6 +133,9 @@ - Outline *getOutline() { return outline; } - #endif - -+ // Return the OptionalContent object. -+ OptionalContent *getOptionalContent() { return optContent; } -+ - // Is the file encrypted? - GBool isEncrypted() { return xref->isEncrypted(); } - -@@ -154,27 +162,44 @@ - private: - - GBool setup(GString *ownerPassword, GString *userPassword); -+ GBool setup2(GString *ownerPassword, GString *userPassword, -+ GBool repairXRef); - void checkHeader(); - GBool checkEncryption(GString *ownerPassword, GString *userPassword); - - GString *fileName; - #ifdef WIN32 - wchar_t *fileNameU; - #endif - FILE *file; - BaseStream *str; - void *guiData; - double pdfVersion; - XRef *xref; - Catalog *catalog; - #ifndef DISABLE_OUTLINE - Outline *outline; - #endif -- -+ OptionalContent *optContent; - - GBool ok; - int errCode; -diff -ru xpdf-3.02/xpdf/pdftotext.cc xpdf-3.03/xpdf/pdftotext.cc ---- xpdf-3.02/xpdf/pdftotext.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/pdftotext.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -35,7 +35,8 @@ - - static int firstPage = 1; - static int lastPage = 0; - static GBool physLayout = gFalse; -+static double fixedPitch = 0; - static GBool rawOrder = gFalse; - static GBool htmlMeta = gFalse; - static char textEncName[128] = ""; -@@ -55,6 +58,8 @@ - "last page to convert"}, - {"-layout", argFlag, &physLayout, 0, - "maintain original physical layout"}, -+ {"-fixed", argFP, &fixedPitch, 0, -+ "assume fixed-pitch (or tabular) text"}, - {"-raw", argFlag, &rawOrder, 0, - "keep strings in content stream order"}, - {"-htmlmeta", argFlag, &htmlMeta, 0, -@@ -112,6 +117,9 @@ - goto err0; - } - fileName = new GString(argv[1]); -+ if (fixedPitch) { -+ physLayout = gTrue; -+ } - - // read config file - globalParams = new GlobalParams(cfgFileName); -@@ -232,7 +241,7 @@ - - // write text file - textOut = new TextOutputDev(textFileName->getCString(), -- physLayout, rawOrder, htmlMeta); -+ physLayout, fixedPitch, rawOrder, htmlMeta); - if (textOut->isOk()) { - doc->displayPages(textOut, firstPage, lastPage, 72, 72, 0, - gFalse, gTrue, gFalse); -diff -ru xpdf-3.02/xpdf/PreScanOutputDev.cc xpdf-3.03/xpdf/PreScanOutputDev.cc ---- xpdf-3.02/xpdf/PreScanOutputDev.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/PreScanOutputDev.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -14,6 +14,8 @@ - - #include - #include "GlobalParams.h" -+#include "Page.h" -+#include "Gfx.h" - #include "GfxFont.h" - #include "Link.h" - #include "PreScanOutputDev.h" -@@ -58,6 +60,62 @@ - state->getFillOpacity(), state->getBlendMode()); - } - -+void PreScanOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, -+ Object *str, -+ int paintType, Dict *resDict, -+ double *mat, double *bbox, -+ int x0, int y0, int x1, int y1, -+ double xStep, double yStep) { -+ if (paintType == 1) { -+ gfx->drawForm(str, resDict, mat, bbox); -+ } else { -+ check(state->getFillColorSpace(), state->getFillColor(), -+ state->getFillOpacity(), state->getBlendMode()); -+ } -+} -+ -+GBool PreScanOutputDev::functionShadedFill(GfxState *state, -+ GfxFunctionShading *shading) { -+ if (shading->getColorSpace()->getMode() != csDeviceGray && -+ shading->getColorSpace()->getMode() != csCalGray) { -+ gray = gFalse; -+ } -+ mono = gFalse; -+ if (state->getFillOpacity() != 1 || -+ state->getBlendMode() != gfxBlendNormal) { -+ transparency = gTrue; -+ } -+ return gTrue; -+} -+ -+GBool PreScanOutputDev::axialShadedFill(GfxState *state, -+ GfxAxialShading *shading) { -+ if (shading->getColorSpace()->getMode() != csDeviceGray && -+ shading->getColorSpace()->getMode() != csCalGray) { -+ gray = gFalse; -+ } -+ mono = gFalse; -+ if (state->getFillOpacity() != 1 || -+ state->getBlendMode() != gfxBlendNormal) { -+ transparency = gTrue; -+ } -+ return gTrue; -+} -+ -+GBool PreScanOutputDev::radialShadedFill(GfxState *state, -+ GfxRadialShading *shading) { -+ if (shading->getColorSpace()->getMode() != csDeviceGray && -+ shading->getColorSpace()->getMode() != csCalGray) { -+ gray = gFalse; -+ } -+ mono = gFalse; -+ if (state->getFillOpacity() != 1 || -+ state->getBlendMode() != gfxBlendNormal) { -+ transparency = gTrue; -+ } -+ return gTrue; -+} -+ - void PreScanOutputDev::clip(GfxState *state) { - //~ check for a rectangle "near" the edge of the page; - //~ else set gdi to false -@@ -71,8 +129,6 @@ - int render; - GfxFont *font; - double m11, m12, m21, m22; -- Ref embRef; -- DisplayFontParam *dfp; - GBool simpleTTF; - - render = state->getRender(); -@@ -87,18 +143,14 @@ - - font = state->getFont(); - state->getFontTransMat(&m11, &m12, &m21, &m22); -+ //~ this should check for external fonts that are non-TrueType - simpleTTF = fabs(m11 + m22) < 0.01 && - m11 > 0 && - fabs(m12) < 0.01 && - fabs(m21) < 0.01 && - fabs(state->getHorizScaling() - 1) < 0.001 && - (font->getType() == fontTrueType || -- font->getType() == fontTrueTypeOT) && -- (font->getEmbeddedFontID(&embRef) || -- font->getExtFontFile() || -- (font->getName() && -- (dfp = globalParams->getDisplayFont(font->getName())) && -- dfp->kind == displayFontTT)); -+ font->getType() == fontTrueTypeOT); - if (simpleTTF) { - //~ need to create a FoFiTrueType object, and check for a Unicode cmap - } -@@ -127,6 +179,9 @@ - - check(state->getFillColorSpace(), state->getFillColor(), - state->getFillOpacity(), state->getBlendMode()); -+ if (state->getFillColorSpace()->getMode() == csPattern) { -+ patternImgMask = gTrue; -+ } - gdi = gFalse; - - if (inlineImg) { -@@ -149,12 +204,17 @@ - if (colorSpace->getMode() == csIndexed) { - colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); - } -- if (colorSpace->getMode() != csDeviceGray && -- colorSpace->getMode() != csCalGray) { -+ if (colorSpace->getMode() == csDeviceGray || -+ colorSpace->getMode() == csCalGray) { -+ if (colorMap->getBits() > 1) { -+ mono = gFalse; -+ } -+ } else { - gray = gFalse; -+ mono = gFalse; - } -- mono = gFalse; -- if (state->getBlendMode() != gfxBlendNormal) { -+ if (state->getFillOpacity() != 1 || -+ state->getBlendMode() != gfxBlendNormal) { - transparency = gTrue; - } - gdi = gFalse; -@@ -182,12 +242,17 @@ - if (colorSpace->getMode() == csIndexed) { - colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); - } -- if (colorSpace->getMode() != csDeviceGray && -- colorSpace->getMode() != csCalGray) { -+ if (colorSpace->getMode() == csDeviceGray || -+ colorSpace->getMode() == csCalGray) { -+ if (colorMap->getBits() > 1) { -+ mono = gFalse; -+ } -+ } else { - gray = gFalse; -+ mono = gFalse; - } -- mono = gFalse; -- if (state->getBlendMode() != gfxBlendNormal) { -+ if (state->getFillOpacity() != 1 || -+ state->getBlendMode() != gfxBlendNormal) { - transparency = gTrue; - } - gdi = gFalse; -@@ -253,5 +318,6 @@ - mono = gTrue; - gray = gTrue; - transparency = gFalse; -+ patternImgMask = gFalse; - gdi = gTrue; - } -diff -ru xpdf-3.02/xpdf/PreScanOutputDev.h xpdf-3.03/xpdf/PreScanOutputDev.h ---- xpdf-3.02/xpdf/PreScanOutputDev.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/PreScanOutputDev.h 2011-08-15 23:08:53.000000000 +0200 -@@ -41,6 +41,16 @@ - // Does this device use drawChar() or drawString()? - virtual GBool useDrawChar() { return gTrue; } - -+ // Does this device use tilingPatternFill()? If this returns false, -+ // tiling pattern fills will be reduced to a series of other drawing -+ // operations. -+ virtual GBool useTilingPatternFill() { return gTrue; } -+ -+ // Does this device use functionShadedFill(), axialShadedFill(), and -+ // radialShadedFill()? If this returns false, these shaded fills -+ // will be reduced to a series of other drawing operations. -+ virtual GBool useShadedFills() { return gTrue; } -+ - // Does this device use beginType3Char/endType3Char? Otherwise, - // text in Type 3 fonts will be drawn with drawChar/drawString. - virtual GBool interpretType3Chars() { return gTrue; } -@@ -57,6 +67,15 @@ - virtual void stroke(GfxState *state); - virtual void fill(GfxState *state); - virtual void eoFill(GfxState *state); -+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str, -+ int paintType, Dict *resDict, -+ double *mat, double *bbox, -+ int x0, int y0, int x1, int y1, -+ double xStep, double yStep); -+ virtual GBool functionShadedFill(GfxState *state, -+ GfxFunctionShading *shading); -+ virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading); -+ virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading); - - //----- path clipping - virtual void clip(GfxState *state); -@@ -110,6 +129,11 @@ - GBool usesTransparency() { return transparency; } - - // Returns true if the operations performed since the last call to -+ // clearStats() included any image mask fills with a pattern color -+ // space. -+ GBool usesPatternImageMask() { return patternImgMask; } -+ -+ // Returns true if the operations performed since the last call to - // clearStats() are all rasterizable by GDI calls in GDIOutputDev. - GBool isAllGDI() { return gdi; } - -@@ -124,6 +148,7 @@ - GBool mono; - GBool gray; - GBool transparency; -+ GBool patternImgMask; - GBool gdi; - }; - -diff -ru xpdf-3.02/xpdf/PSOutputDev.cc xpdf-3.03/xpdf/PSOutputDev.cc ---- xpdf-3.02/xpdf/PSOutputDev.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/PSOutputDev.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -19,6 +19,7 @@ - #include - #include "GString.h" - #include "GList.h" -+#include "GHash.h" - #include "config.h" - #include "GlobalParams.h" - #include "Object.h" -@@ -34,8 +35,10 @@ - #include "Page.h" - #include "Stream.h" - #include "Annot.h" -+#include "PDFDoc.h" - #include "XRef.h" - #include "PreScanOutputDev.h" -+#include "CharCodeToUnicode.h" - #if HAVE_SPLASH - # include "Splash.h" - # include "SplashBitmap.h" -@@ -55,8 +58,8 @@ - - //------------------------------------------------------------------------ - --// Resolution at which pages with transparency will be rasterized. --#define splashDPI 300 -+// Max size of a slice when rasterizing pages, in pixels. -+#define rasterizationSliceSize 20000000 - - //------------------------------------------------------------------------ - // PostScript prolog and setup -@@ -82,16 +85,24 @@ - " } for", - "~123sn", - "/pdfSetup {", -- " 3 1 roll 2 array astore", - " /setpagedevice where {", -- " pop 3 dict begin", -+ " pop 2 dict begin", -+ " /Policies 1 dict dup begin /PageSize 6 def end def", -+ " { /Duplex true def } if", -+ " currentdict end setpagedevice", -+ " } {", -+ " pop", -+ " } ifelse", -+ "} def", -+ "/pdfSetupPaper {", -+ " 2 array astore", -+ " /setpagedevice where {", -+ " pop 2 dict begin", - " /PageSize exch def", - " /ImagingBBox null def", -- " /Policies 1 dict dup begin /PageSize 3 def end def", -- " { /Duplex true def } if", - " currentdict end setpagedevice", - " } {", -- " pop pop", -+ " pop", - " } ifelse", - "} def", - "~1sn", -@@ -377,82 +388,82 @@ - "/Td { pdfTextMat transform moveto } def", - "/Tm { /pdfTextMat exch def } def", - "% text string operators", -+ "/xyshow where {", -+ " pop", -+ " /xyshow2 {", -+ " dup length array", -+ " 0 2 2 index length 1 sub {", -+ " 2 index 1 index 2 copy get 3 1 roll 1 add get", -+ " pdfTextMat dtransform", -+ " 4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put", -+ " } for", -+ " exch pop", -+ " xyshow", -+ " } def", -+ "}{", -+ " /xyshow2 {", -+ " currentfont /FontType get 0 eq {", -+ " 0 2 3 index length 1 sub {", -+ " currentpoint 4 index 3 index 2 getinterval show moveto", -+ " 2 copy get 2 index 3 2 roll 1 add get", -+ " pdfTextMat dtransform rmoveto", -+ " } for", -+ " } {", -+ " 0 1 3 index length 1 sub {", -+ " currentpoint 4 index 3 index 1 getinterval show moveto", -+ " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", -+ " pdfTextMat dtransform rmoveto", -+ " } for", -+ " } ifelse", -+ " pop pop", -+ " } def", -+ "} ifelse", - "/cshow where {", - " pop", -- " /cshow2 {", -- " dup {", -- " pop pop", -- " 1 string dup 0 3 index put 3 index exec", -+ " /xycp {", // xycharpath -+ " 0 3 2 roll", -+ " {", -+ " pop pop currentpoint 3 2 roll", -+ " 1 string dup 0 4 3 roll put false charpath moveto", -+ " 2 copy get 2 index 2 index 1 add get", -+ " pdfTextMat dtransform rmoveto", -+ " 2 add", - " } exch cshow", - " pop pop", - " } def", - "}{", -- " /cshow2 {", -+ " /xycp {", // xycharpath - " currentfont /FontType get 0 eq {", -- " 0 2 2 index length 1 sub {", -- " 2 copy get exch 1 add 2 index exch get", -- " 2 copy exch 256 mul add", -- " 2 string dup 0 6 5 roll put dup 1 5 4 roll put", -- " 3 index exec", -+ " 0 2 3 index length 1 sub {", -+ " currentpoint 4 index 3 index 2 getinterval false charpath moveto", -+ " 2 copy get 2 index 3 2 roll 1 add get", -+ " pdfTextMat dtransform rmoveto", - " } for", - " } {", -- " dup {", -- " 1 string dup 0 3 index put 3 index exec", -- " } forall", -+ " 0 1 3 index length 1 sub {", -+ " currentpoint 4 index 3 index 1 getinterval false charpath moveto", -+ " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", -+ " pdfTextMat dtransform rmoveto", -+ " } for", - " } ifelse", - " pop pop", - " } def", - "} ifelse", -- "/awcp {", // awidthcharpath -- " exch {", -- " false charpath", -- " 5 index 5 index rmoveto", -- " 6 index eq { 7 index 7 index rmoveto } if", -- " } exch cshow2", -- " 6 {pop} repeat", -- "} def", - "/Tj {", - " fCol", // because stringwidth has to draw Type 3 chars -- " 1 index stringwidth pdfTextMat idtransform pop", -- " sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse", -- " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32", -- " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0", -- " pdfTextMat dtransform", -- " 6 5 roll Tj1", -- "} def", -- "/Tj16 {", -- " fCol", // because stringwidth has to draw Type 3 chars -- " 2 index stringwidth pdfTextMat idtransform pop", -- " sub exch div", -- " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32", -- " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0", -- " pdfTextMat dtransform", -- " 6 5 roll Tj1", -- "} def", -- "/Tj16V {", -- " fCol", // because stringwidth has to draw Type 3 chars -- " 2 index stringwidth pdfTextMat idtransform exch pop", -- " sub exch div", -- " 0 pdfWordSpacing pdfTextMat dtransform 32", -- " 4 3 roll pdfCharSpacing add 0 exch", -- " pdfTextMat dtransform", -- " 6 5 roll Tj1", -- "} def", -- "/Tj1 {", - " 0 pdfTextRise pdfTextMat dtransform rmoveto", -- " currentpoint 8 2 roll", -+ " currentpoint 4 2 roll", - " pdfTextRender 1 and 0 eq {", -- " 6 copy awidthshow", -+ " 2 copy xyshow2", - " } if", - " pdfTextRender 3 and dup 1 eq exch 2 eq or {", -- " 7 index 7 index moveto", -- " 6 copy", -+ " 3 index 3 index moveto", -+ " 2 copy", - " currentfont /FontType get 3 eq { fCol } { sCol } ifelse", -- " false awcp currentpoint stroke moveto", -+ " xycp currentpoint stroke moveto", - " } if", - " pdfTextRender 4 and 0 ne {", -- " 8 6 roll moveto", -- " false awcp", -+ " 4 2 roll moveto xycp", - " /pdfTextClipPath [ pdfTextClipPath aload pop", - " {/moveto cvx}", - " {/lineto cvx}", -@@ -461,13 +472,13 @@ - " pathforall ] def", - " currentpoint newpath moveto", - " } {", -- " 8 {pop} repeat", -+ " pop pop pop pop", - " } ifelse", - " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", - "} def", -- "/TJm { pdfFontSize 0.001 mul mul neg 0", -+ "/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0", - " pdfTextMat dtransform rmoveto } def", -- "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch", -+ "/TJmV { 0.001 mul pdfFontSize mul neg 0 exch", - " pdfTextMat dtransform rmoveto } def", - "/Tclip { pdfTextClipPath cvx exec clip newpath", - " /pdfTextClipPath [] def } def", -@@ -495,19 +506,52 @@ - " fCol /pdfImBuf1 4 index 7 add 8 idiv string def", - " { currentfile pdfImBuf1 readhexstring pop } imagemask", - "} def", -+ "/pdfImStr {", -+ " 2 copy exch length lt {", -+ " 2 copy get exch 1 add exch", -+ " } {", -+ " ()", -+ " } ifelse", -+ "} def", - "/pdfImM1a {", -- " { 2 copy get exch 1 add exch } imagemask", -+ " { pdfImStr } imagemask", - " pop pop", - "} def", - "~23sn", -- "% Level 2 image operators", -+ "% Level 2/3 image operators", - "/pdfImBuf 100 string def", -- "/pdfIm {", -- " image", -+ "/pdfImStr {", -+ " 2 copy exch length lt {", -+ " 2 copy get exch 1 add exch", -+ " } {", -+ " ()", -+ " } ifelse", -+ "} def", -+ "/skipEOD {", - " { currentfile pdfImBuf readline", - " not { pop exit } if", - " (%-EOD-) eq { exit } if } loop", - "} def", -+ "/pdfIm { image skipEOD } def", -+ "~3sn", -+ "/pdfMask {", -+ " /ReusableStreamDecode filter", -+ " skipEOD", -+ " /maskStream exch def", -+ "} def", -+ "/pdfMaskEnd { maskStream closefile } def", -+ "/pdfMaskInit {", -+ " /maskArray exch def", -+ " /maskIdx 0 def", -+ "} def", -+ "/pdfMaskSrc {", -+ " maskIdx maskArray length lt {", -+ " maskArray maskIdx get", -+ " /maskIdx maskIdx 1 add def", -+ " } {", -+ " ()", -+ " } ifelse", -+ "} def", - "~23s", - "/pdfImSep {", - " findcmykcustomcolor exch", -@@ -523,17 +567,10 @@ - " 255 exch sub put", - " } for }", - " 6 5 roll customcolorimage", -- " { currentfile pdfImBuf readline", -- " not { pop exit } if", -- " (%-EOD-) eq { exit } if } loop", -+ " skipEOD", - "} def", - "~23sn", -- "/pdfImM {", -- " fCol imagemask", -- " { currentfile pdfImBuf readline", -- " not { pop exit } if", -- " (%-EOD-) eq { exit } if } loop", -- "} def", -+ "/pdfImM { fCol imagemask skipEOD } def", - "/pr { 2 index 2 index 3 2 roll putinterval 4 add } def", - "/pdfImClip {", - " gsave", -@@ -607,7 +644,7 @@ - " func n array astore", - "} def", - "/axialSH {", -- " dup 0 eq {", -+ " dup 2 lt {", - " true", - " } {", - " dup 8 eq {", -@@ -753,25 +790,12 @@ - double mWidth; // width of 'm' character - }; - --static char *psFonts[] = { -- "Courier", -- "Courier-Bold", -- "Courier-Oblique", -- "Courier-BoldOblique", -- "Helvetica", -- "Helvetica-Bold", -- "Helvetica-Oblique", -- "Helvetica-BoldOblique", -- "Symbol", -- "Times-Roman", -- "Times-Bold", -- "Times-Italic", -- "Times-BoldItalic", -- "ZapfDingbats", -- NULL --}; -- --static PSSubstFont psSubstFonts[] = { -+// NB: must be in same order as base14SubstFonts in GfxFont.cc -+static PSSubstFont psBase14SubstFonts[14] = { -+ {"Courier", 0.600}, -+ {"Courier-Oblique", 0.600}, -+ {"Courier-Bold", 0.600}, -+ {"Courier-BoldOblique", 0.600}, - {"Helvetica", 0.833}, - {"Helvetica-Oblique", 0.833}, - {"Helvetica-Bold", 0.889}, -@@ -780,22 +804,28 @@ - {"Times-Italic", 0.722}, - {"Times-Bold", 0.833}, - {"Times-BoldItalic", 0.778}, -- {"Courier", 0.600}, -- {"Courier-Oblique", 0.600}, -- {"Courier-Bold", 0.600}, -- {"Courier-BoldOblique", 0.600} -+ // the last two are never used for substitution -+ {"Symbol", 0}, -+ {"ZapfDingbats", 0} -+}; -+ -+// Mapping from Type 1/1C font file to PS font name. -+struct PST1FontName { -+ Ref fontFileID; -+ GString *psName; // PostScript font name used for this -+ // embedded font file - }; - - // Encoding info for substitute 16-bit font - struct PSFont16Enc { - Ref fontID; -- GString *enc; -+ GString *enc; // NULL means font wasn't correctly substituted - }; - - //------------------------------------------------------------------------ -@@ -845,6 +875,13 @@ - }; - - //------------------------------------------------------------------------ -+ -+struct PSOutPaperSize { -+ PSOutPaperSize(int wA, int hA) { w = wA; h = hA; } -+ int w, h; -+}; -+ -+//------------------------------------------------------------------------ - // DeviceNRecoder - //------------------------------------------------------------------------ - -@@ -897,6 +934,9 @@ - if (imgStr) { - delete imgStr; - } -+ if (str->isEncoder()) { -+ delete str; -+ } - } - - void DeviceNRecoder::reset() { -@@ -942,10 +982,12 @@ - fwrite(data, 1, len, (FILE *)stream); - } - - PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA, - int firstPage, int lastPage, PSOutMode modeA, - int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, -- GBool manualCtrlA) { -+ GBool manualCtrlA, -+ PSOutCustomCodeCbk customCodeCbkA, -+ void *customCodeCbkDataA) { - FILE *f; - PSFileType fileTypeA; - -@@ -953,15 +995,18 @@ - underlayCbkData = NULL; - overlayCbk = NULL; - overlayCbkData = NULL; -+ customCodeCbk = customCodeCbkA; -+ customCodeCbkData = customCodeCbkDataA; - - fontIDs = NULL; -- fontFileIDs = NULL; -- fontFileNames = NULL; -+ fontNames = new GHash(gTrue); -+ t1FontNames = NULL; - font8Info = NULL; - font16Enc = NULL; - imgIDs = NULL; - formIDs = NULL; - xobjStack = NULL; -+ paperSizes = NULL; - embFontList = NULL; - customColors = NULL; - haveTextClip = gFalse; -@@ -978,71 +1023,82 @@ - signal(SIGPIPE, (SignalFunc)SIG_IGN); - #endif - if (!(f = popen(fileName + 1, "w"))) { - error(errIO, -1, "Couldn't run print command '{0:s}'", fileName); - ok = gFalse; - return; - } - #else - error(errIO, -1, "Print commands are not supported ('{0:s}')", fileName); - ok = gFalse; - return; - #endif - } else { - fileTypeA = psFile; - if (!(f = fopen(fileName, "w"))) { - error(errIO, -1, "Couldn't open PostScript file '{0:s}'", fileName); - ok = gFalse; - return; - } - } - - init(outputToFile, f, fileTypeA, -- xrefA, catalog, firstPage, lastPage, modeA, -+ docA, firstPage, lastPage, modeA, - imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA); - } - - PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, - PDFDoc *docA, - int firstPage, int lastPage, PSOutMode modeA, - int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, -- GBool manualCtrlA) { -+ GBool manualCtrlA, -+ PSOutCustomCodeCbk customCodeCbkA, -+ void *customCodeCbkDataA) { - underlayCbk = NULL; - underlayCbkData = NULL; - overlayCbk = NULL; - overlayCbkData = NULL; -+ customCodeCbk = customCodeCbkA; -+ customCodeCbkData = customCodeCbkDataA; - - fontIDs = NULL; -- fontFileIDs = NULL; -- fontFileNames = NULL; -+ fontNames = new GHash(gTrue); -+ t1FontNames = NULL; - font8Info = NULL; - font16Enc = NULL; - imgIDs = NULL; - formIDs = NULL; - xobjStack = NULL; -+ paperSizes = NULL; - embFontList = NULL; - customColors = NULL; - haveTextClip = gFalse; - t3String = NULL; - - init(outputFuncA, outputStreamA, psGeneric, - docA, firstPage, lastPage, modeA, - imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA); - } - - void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA, - PSFileType fileTypeA, PDFDoc *docA, - int firstPage, int lastPage, PSOutMode modeA, - int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, - GBool manualCtrlA) { - Catalog *catalog; - Page *page; - PDFRectangle *box; -+ PSOutPaperSize *size; -+ GList *names; -+ int pg, w, h, i; - - // initialize - ok = gTrue; - outputFunc = outputFuncA; - outputStream = outputStreamA; - fileType = fileTypeA; - doc = docA; - xref = doc->getXRef(); - catalog = doc->getCatalog(); - level = globalParams->getPSLevel(); - mode = modeA; - paperWidth = globalParams->getPSPaperWidth(); -@@ -1055,18 +1111,34 @@ - globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY); - } - if (paperWidth < 0 || paperHeight < 0) { -- // this check is needed in case the document has zero pages -- if (firstPage > 0 && firstPage <= catalog->getNumPages()) { -- page = catalog->getPage(firstPage); -- paperWidth = (int)ceil(page->getMediaWidth()); -- paperHeight = (int)ceil(page->getMediaHeight()); -- } else { -- paperWidth = 1; -- paperHeight = 1; -- } -- imgLLX = imgLLY = 0; -- imgURX = paperWidth; -- imgURY = paperHeight; -+ paperMatch = gTrue; -+ paperSizes = new GList(); -+ paperWidth = paperHeight = 1; // in case the document has zero pages -+ for (pg = (firstPage >= 1) ? firstPage : 1; -+ pg <= lastPage && pg <= catalog->getNumPages(); -+ ++pg) { -+ page = catalog->getPage(pg); -+ w = (int)ceil(page->getMediaWidth()); -+ h = (int)ceil(page->getMediaHeight()); -+ for (i = 0; i < paperSizes->getLength(); ++i) { -+ size = (PSOutPaperSize *)paperSizes->get(i); -+ if (size->w == w && size->h == h) { -+ break; -+ } -+ } -+ if (i == paperSizes->getLength()) { -+ paperSizes->append(new PSOutPaperSize(w, h)); -+ } -+ if (w > paperWidth) { -+ paperWidth = w; -+ } -+ if (h > paperHeight) { -+ paperHeight = h; -+ } -+ } -+ // NB: img{LLX,LLY,URX,URY} will be set by startPage() -+ } else { -+ paperMatch = gFalse; - } - preload = globalParams->getPSPreload(); - manualCtrl = manualCtrlA; -@@ -1088,17 +1160,21 @@ - clipLLX0 = clipLLY0 = 0; - clipURX0 = clipURY0 = -1; - -- // initialize fontIDs, fontFileIDs, and fontFileNames lists -+ // initialize fontIDs and fontNames lists - fontIDSize = 64; - fontIDLen = 0; - fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref)); -- fontFileIDSize = 64; -- fontFileIDLen = 0; -- fontFileIDs = (Ref *)gmallocn(fontFileIDSize, sizeof(Ref)); -- fontFileNameSize = 64; -- fontFileNameLen = 0; -- fontFileNames = (GString **)gmallocn(fontFileNameSize, sizeof(GString *)); -- nextTrueTypeNum = 0; -+ for (i = 0; i < 14; ++i) { -+ fontNames->add(new GString(psBase14SubstFonts[i].psName), 1); -+ } -+ names = globalParams->getPSResidentFonts(); -+ for (i = 0; i < names->getLength(); ++i) { -+ fontNames->add((GString *)names->get(i), 1); -+ } -+ delete names; -+ t1FontNameSize = 64; -+ t1FontNameLen = 0; -+ t1FontNames = (PST1FontName *)gmallocn(t1FontNameSize, sizeof(PST1FontName)); - font8InfoLen = 0; - font8InfoSize = 0; - font16EncLen = 0; -@@ -1173,20 +1249,21 @@ - } - #endif - } -+ if (paperSizes) { -+ deleteGList(paperSizes, PSOutPaperSize); -+ } - if (embFontList) { - delete embFontList; - } - if (fontIDs) { - gfree(fontIDs); - } -- if (fontFileIDs) { -- gfree(fontFileIDs); -- } -- if (fontFileNames) { -- for (i = 0; i < fontFileNameLen; ++i) { -- delete fontFileNames[i]; -+ delete fontNames; -+ if (t1FontNames) { -+ for (i = 0; i < t1FontNameLen; ++i) { -+ delete t1FontNames[i].psName; - } -- gfree(fontFileNames); -+ gfree(t1FontNames); - } - if (font8Info) { - for (i = 0; i < font8InfoLen; ++i) { -@@ -1196,7 +1273,9 @@ - } - if (font16Enc) { - for (i = 0; i < font16EncLen; ++i) { -- delete font16Enc[i].enc; -+ if (font16Enc[i].enc) { -+ delete font16Enc[i].enc; -+ } - } - gfree(font16Enc); - } -@@ -1216,7 +1295,9 @@ - PDFRectangle *mediaBox, PDFRectangle *cropBox, - int pageRotate) { - Object info, obj1; -+ PSOutPaperSize *size; - double x1, y1, x2, y2; -+ int i; - - switch (mode) { - case psModePS: -@@ -1230,7 +1311,7 @@ - break; - } - -- writePSFmt("% Produced by xpdf/pdftops {0:s}\n", xpdfVersion); -+ writePSFmt("%XpdfVersion: {0:s}\n", xpdfVersion); - xref->getDocInfo(&info); - if (info.isDict() && info.dictLookup("Creator", &obj1)->isString()) { - writePS("%%Creator: "); -@@ -1254,14 +1335,24 @@ - - switch (mode) { - case psModePS: -- writePSFmt("%%DocumentMedia: plain {0:d} {1:d} 0 () ()\n", -- paperWidth, paperHeight); -+ if (paperMatch) { -+ for (i = 0; i < paperSizes->getLength(); ++i) { -+ size = (PSOutPaperSize *)paperSizes->get(i); -+ writePSFmt("%%{0:s} {1:d}x{2:d} {1:d} {2:d} 0 () ()\n", -+ i==0 ? "DocumentMedia:" : "+", size->w, size->h); -+ } -+ } else { -+ writePSFmt("%%DocumentMedia: plain {0:d} {1:d} 0 () ()\n", -+ paperWidth, paperHeight); -+ } - writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight); - writePSFmt("%%Pages: {0:d}\n", lastPage - firstPage + 1); - writePS("%%EndComments\n"); -- writePS("%%BeginDefaults\n"); -- writePS("%%PageMedia: plain\n"); -- writePS("%%EndDefaults\n"); -+ if (!paperMatch) { -+ writePS("%%BeginDefaults\n"); -+ writePS("%%PageMedia: plain\n"); -+ writePS("%%EndDefaults\n"); -+ } - break; - case psModeEPS: - epsX1 = cropBox->x1; -@@ -1343,7 +1434,9 @@ - Page *page; - Dict *resDict; - Annots *annots; -- Object obj1, obj2; -+ Object *acroForm; -+ Object obj1, obj2, obj3; -+ GString *s; - int pg, i; - - if (mode == psModeForm) { -@@ -1371,11 +1464,31 @@ - } - delete annots; - } -+ if ((acroForm = catalog->getAcroForm()) && acroForm->isDict()) { -+ if (acroForm->dictLookup("DR", &obj1)->isDict()) { -+ setupResources(obj1.getDict()); -+ } -+ obj1.free(); -+ if (acroForm->dictLookup("Fields", &obj1)->isArray()) { -+ for (i = 0; i < obj1.arrayGetLength(); ++i) { -+ if (obj1.arrayGet(i, &obj2)->isDict()) { -+ if (obj2.dictLookup("DR", &obj3)->isDict()) { -+ setupResources(obj3.getDict()); -+ } -+ obj3.free(); -+ } -+ obj2.free(); -+ } -+ } -+ obj1.free(); -+ } - if (mode != psModeForm) { - if (mode != psModeEPS && !manualCtrl) { -- writePSFmt("{0:d} {1:d} {2:s} pdfSetup\n", -- paperWidth, paperHeight, -+ writePSFmt("{0:s} pdfSetup\n", - globalParams->getPSDuplex() ? "true" : "false"); -+ if (!paperMatch) { -+ writePSFmt("{0:d} {1:d} pdfSetupPaper\n", paperWidth, paperHeight); -+ } - } - #if OPI_SUPPORT - if (globalParams->getPSOPI()) { -@@ -1383,6 +1496,13 @@ - } - #endif - } -+ if (customCodeCbk) { -+ if ((s = (*customCodeCbk)(this, psOutCustomDocSetup, 0, -+ customCodeCbkData))) { -+ writePS(s->getCString()); -+ delete s; -+ } -+ } - } - - void PSOutputDev::writePageTrailer() { -@@ -1558,18 +1681,15 @@ - } - - void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) { -- Ref fontFileID; -- GString *name; -- PSFontParam *fontParam; -+ GfxFontLoc *fontLoc; - GString *psName; -- char buf[16]; - GBool subst; -+ char buf[16]; - UnicodeMap *uMap; - char *charName; - double xs, ys; - int code; - double w1, w2; -- double *fm; - int i, j; - - // check if font is already set up -@@ -1587,119 +1707,120 @@ - } - fontIDs[fontIDLen++] = *font->getID(); - -+ psName = NULL; - xs = ys = 1; - subst = gFalse; - -- // check for resident 8-bit font -- if (font->getName() && -- (fontParam = globalParams->getPSFont(font->getName()))) { -- psName = new GString(fontParam->psFontName->getCString()); -- -- // check for embedded Type 1 font -- } else if (globalParams->getPSEmbedType1() && -- font->getType() == fontType1 && -- font->getEmbeddedFontID(&fontFileID)) { -- psName = filterPSName(font->getEmbeddedFontName()); -- setupEmbeddedType1Font(&fontFileID, psName); -- -- // check for embedded Type 1C font -- } else if (globalParams->getPSEmbedType1() && -- font->getType() == fontType1C && -- font->getEmbeddedFontID(&fontFileID)) { -- // use the PDF font name because the embedded font name might -- // not include the subset prefix -- psName = filterPSName(font->getOrigName()); -- setupEmbeddedType1CFont(font, &fontFileID, psName); -- -- // check for embedded OpenType - Type 1C font -- } else if (globalParams->getPSEmbedType1() && -- font->getType() == fontType1COT && -- font->getEmbeddedFontID(&fontFileID)) { -- // use the PDF font name because the embedded font name might -- // not include the subset prefix -- psName = filterPSName(font->getOrigName()); -- setupEmbeddedOpenTypeT1CFont(font, &fontFileID, psName); -- -- // check for external Type 1 font file -- } else if (globalParams->getPSEmbedType1() && -- font->getType() == fontType1 && -- font->getExtFontFile()) { -- // this assumes that the PS font name matches the PDF font name -- psName = font->getName()->copy(); -- setupExternalType1Font(font->getExtFontFile(), psName); -- -- // check for embedded TrueType font -- } else if (globalParams->getPSEmbedTrueType() && -- (font->getType() == fontTrueType || -- font->getType() == fontTrueTypeOT) && -- font->getEmbeddedFontID(&fontFileID)) { -- psName = filterPSName(font->getEmbeddedFontName()); -- setupEmbeddedTrueTypeFont(font, &fontFileID, psName); -- -- // check for external TrueType font file -- } else if (globalParams->getPSEmbedTrueType() && -- font->getType() == fontTrueType && -- font->getExtFontFile()) { -- psName = filterPSName(font->getName()); -- setupExternalTrueTypeFont(font, psName); -- -- // check for embedded CID PostScript font -- } else if (globalParams->getPSEmbedCIDPostScript() && -- font->getType() == fontCIDType0C && -- font->getEmbeddedFontID(&fontFileID)) { -- psName = filterPSName(font->getEmbeddedFontName()); -- setupEmbeddedCIDType0Font(font, &fontFileID, psName); -- -- // check for embedded CID TrueType font -- } else if (globalParams->getPSEmbedCIDTrueType() && -- (font->getType() == fontCIDType2 || -- font->getType() == fontCIDType2OT) && -- font->getEmbeddedFontID(&fontFileID)) { -- psName = filterPSName(font->getEmbeddedFontName()); -- //~ should check to see if font actually uses vertical mode -- setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName, gTrue); -- -- // check for embedded OpenType - CID CFF font -- } else if (globalParams->getPSEmbedCIDPostScript() && -- font->getType() == fontCIDType0COT && -- font->getEmbeddedFontID(&fontFileID)) { -- psName = filterPSName(font->getEmbeddedFontName()); -- setupEmbeddedOpenTypeCFFFont(font, &fontFileID, psName); -- -- // check for Type 3 font -- } else if (font->getType() == fontType3) { -+ if (font->getType() == fontType3) { - psName = GString::format("T3_{0:d}_{1:d}", - font->getID()->num, font->getID()->gen); - setupType3Font(font, psName, parentResDict); -- -- // do 8-bit font substitution -- } else if (!font->isCIDFont()) { -- subst = gTrue; -- name = font->getName(); -- psName = NULL; -- if (name) { -- for (i = 0; psFonts[i]; ++i) { -- if (name->cmp(psFonts[i]) == 0) { -- psName = new GString(psFonts[i]); -- break; -+ } else { -+ fontLoc = font->locateFont(xref, gTrue); -+ switch (fontLoc->locType) { -+ case gfxFontLocEmbedded: -+ switch (fontLoc->fontType) { -+ case fontType1: -+ // this assumes that the PS font name matches the PDF font name -+ psName = font->getEmbeddedFontName()->copy(); -+ setupEmbeddedType1Font(&fontLoc->embFontID, psName); -+ break; -+ case fontType1C: -+ psName = makePSFontName(font, &fontLoc->embFontID); -+ setupEmbeddedType1CFont(font, &fontLoc->embFontID, psName); -+ break; -+ case fontType1COT: -+ psName = makePSFontName(font, &fontLoc->embFontID); -+ setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID, psName); -+ break; -+ case fontTrueType: -+ case fontTrueTypeOT: -+ psName = makePSFontName(font, font->getID()); -+ setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID, psName); -+ break; -+ case fontCIDType0C: -+ psName = makePSFontName(font, &fontLoc->embFontID); -+ setupEmbeddedCIDType0Font(font, &fontLoc->embFontID, psName); -+ break; -+ case fontCIDType2: -+ case fontCIDType2OT: -+ psName = makePSFontName(font, font->getID()); -+ //~ should check to see if font actually uses vertical mode -+ setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID, psName, gTrue); -+ break; -+ case fontCIDType0COT: -+ psName = makePSFontName(font, &fontLoc->embFontID); -+ setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID, psName); -+ break; -+ default: -+ break; -+ } -+ break; -+ case gfxFontLocExternal: -+ //~ add cases for external 16-bit fonts -+ switch (fontLoc->fontType) { -+ case fontType1: -+ if (font->getName()) { -+ // this assumes that the PS font name matches the PDF font name -+ psName = font->getName()->copy(); -+ } else { -+ //~ this won't work -- the PS font name won't match -+ psName = makePSFontName(font, font->getID()); - } -+ setupExternalType1Font(fontLoc->path, psName); -+ break; -+ case fontTrueType: -+ case fontTrueTypeOT: -+ psName = makePSFontName(font, font->getID()); -+ setupExternalTrueTypeFont(font, fontLoc->path, psName); -+ break; -+ case fontCIDType2: -+ case fontCIDType2OT: -+ psName = makePSFontName(font, font->getID()); -+ //~ should check to see if font actually uses vertical mode -+ setupExternalCIDTrueTypeFont(font, fontLoc->path, psName, gTrue); -+ break; -+ default: -+ break; - } -+ break; -+ case gfxFontLocResident: -+ psName = fontLoc->path->copy(); -+ break; - } -+ - if (!psName) { -- if (font->isFixedWidth()) { -- i = 8; -- } else if (font->isSerif()) { -- i = 4; -+ if (font->isCIDFont()) { -+ error(errSyntaxError, -1, -+ "Couldn't find a font to substitute for '{0:s}' ('{1:s}' character collection)", -+ font->getName() ? font->getName()->getCString() -+ : "(unnamed)", -+ ((GfxCIDFont *)font)->getCollection() -+ ? ((GfxCIDFont *)font)->getCollection()->getCString() -+ : "(unknown)"); -+ if (font16EncLen >= font16EncSize) { -+ font16EncSize += 16; -+ font16Enc = (PSFont16Enc *)greallocn(font16Enc, -+ font16EncSize, -+ sizeof(PSFont16Enc)); -+ } -+ font16Enc[font16EncLen].fontID = *font->getID(); -+ font16Enc[font16EncLen].enc = NULL; -+ ++font16EncLen; - } else { -- i = 0; -- } -- if (font->isBold()) { -- i += 2; -- } -- if (font->isItalic()) { -- i += 1; -+ error(errSyntaxError, -1, -+ "Couldn't find a font to substitute for '{0:s}'", -+ font->getName() ? font->getName()->getCString() -+ : "(unnamed)"); - } -- psName = new GString(psSubstFonts[i].psName); -+ delete fontLoc; -+ return; -+ } -+ -+ // scale substituted 8-bit fonts -+ if (fontLoc->locType == gfxFontLocResident && -+ fontLoc->substIdx >= 0) { -+ subst = gTrue; - for (code = 0; code < 256; ++code) { - if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) && - charName[0] == 'm' && charName[1] == '\0') { -@@ -1711,56 +1832,37 @@ - } else { - w1 = 0; - } -- w2 = psSubstFonts[i].mWidth; -+ w2 = psBase14SubstFonts[fontLoc->substIdx].mWidth; - xs = w1 / w2; - if (xs < 0.1) { - xs = 1; - } -- if (font->getType() == fontType3) { -- // This is a hack which makes it possible to substitute for some -- // Type 3 fonts. The problem is that it's impossible to know what -- // the base coordinate system used in the font is without actually -- // rendering the font. -- ys = xs; -- fm = font->getFontMatrix(); -- if (fm[0] != 0) { -- ys *= fm[3] / fm[0]; -- } -- } else { -- ys = 1; -- } - } - -- // do 16-bit font substitution -- } else if ((fontParam = globalParams-> -- getPSFont16(font->getName(), -- ((GfxCIDFont *)font)->getCollection(), -- font->getWMode()))) { -- subst = gTrue; -- psName = fontParam->psFontName->copy(); -- if (font16EncLen >= font16EncSize) { -- font16EncSize += 16; -- font16Enc = (PSFont16Enc *)greallocn(font16Enc, -- font16EncSize, sizeof(PSFont16Enc)); -- } -- font16Enc[font16EncLen].fontID = *font->getID(); -- font16Enc[font16EncLen].enc = fontParam->encoding->copy(); -- if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) { -- uMap->decRefCnt(); -+ // handle encodings for substituted CID fonts -+ if (fontLoc->locType == gfxFontLocResident && -+ fontLoc->fontType >= fontCIDType0) { -+ subst = gTrue; -+ if (font16EncLen >= font16EncSize) { -+ font16EncSize += 16; -+ font16Enc = (PSFont16Enc *)greallocn(font16Enc, -+ font16EncSize, -+ sizeof(PSFont16Enc)); -+ } -+ font16Enc[font16EncLen].fontID = *font->getID(); -+ if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) { -+ font16Enc[font16EncLen].enc = fontLoc->encoding->copy(); -+ uMap->decRefCnt(); -+ } else { -+ error(errSyntaxError, -1, -+ "Couldn't find Unicode map for 16-bit font encoding '{0:t}'", -+ fontLoc->encoding); -+ font16Enc[font16EncLen].enc = NULL; -+ } - ++font16EncLen; -- } else { -- error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'", -- font16Enc[font16EncLen].enc->getCString()); - } - -- // give up - can't do anything with this font -- } else { -- error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)", -- font->getName() ? font->getName()->getCString() : "(unnamed)", -- ((GfxCIDFont *)font)->getCollection() -- ? ((GfxCIDFont *)font)->getCollection()->getCString() -- : "(unknown)"); -- return; -+ delete fontLoc; - } - - // generate PostScript code to set up the font -@@ -1787,11 +1889,6 @@ - charName = buf; - } else { - charName = ((Gfx8BitFont *)font)->getCharName(i+j); -- // this is a kludge for broken PDF files that encode char 32 -- // as .notdef -- if (i+j == 32 && charName && !strcmp(charName, ".notdef")) { -- charName = "space"; -- } - } - writePS("/"); - writePSName(charName ? charName : (char *)".notdef"); -@@ -1821,36 +1918,30 @@ - int i; - - // check if font is already embedded -- for (i = 0; i < fontFileIDLen; ++i) { -- if (fontFileIDs[i].num == id->num && -- fontFileIDs[i].gen == id->gen) -- return; -- } -- -- // add entry to fontFileIDs list -- if (fontFileIDLen >= fontFileIDSize) { -- fontFileIDSize += 64; -- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); -+ if (fontNames->lookupInt(psName)) { -+ return; - } -- fontFileIDs[fontFileIDLen++] = *id; -+ fontNames->add(psName->copy(), 1); - - // get the font stream and info - refObj.initRef(id->num, id->gen); - refObj.fetch(xref, &strObj); - refObj.free(); - if (!strObj.isStream()) { - error(errSyntaxError, -1, "Embedded font file object is not a stream"); - goto err1; - } - if (!(dict = strObj.streamGetDict())) { - error(errSyntaxError, -1, - "Embedded font stream is missing its dictionary"); - goto err1; - } - dict->lookup("Length1", &obj1); - dict->lookup("Length2", &obj2); - dict->lookup("Length3", &obj3); - if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) { - error(errSyntaxError, -1, - "Missing length fields in embedded font stream dictionary"); - obj1.free(); - obj2.free(); - obj3.free(); -@@ -1947,22 +2039,12 @@ - void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) { - FILE *fontFile; - int c; -- int i; - - // check if font is already embedded -- for (i = 0; i < fontFileNameLen; ++i) { -- if (!fontFileNames[i]->cmp(fileName)) { -- return; -- } -- } -- -- // add entry to fontFileNames list -- if (fontFileNameLen >= fontFileNameSize) { -- fontFileNameSize += 64; -- fontFileNames = (GString **)greallocn(fontFileNames, -- fontFileNameSize, sizeof(GString *)); -+ if (fontNames->lookupInt(psName)) { -+ return; - } -- fontFileNames[fontFileNameLen++] = fileName->copy(); -+ fontNames->add(psName->copy(), 1); - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); -@@ -1992,18 +2074,22 @@ - int i; - - // check if font is already embedded -- for (i = 0; i < fontFileIDLen; ++i) { -- if (fontFileIDs[i].num == id->num && -- fontFileIDs[i].gen == id->gen) -+ for (i = 0; i < t1FontNameLen; ++i) { -+ if (t1FontNames[i].fontFileID.num == id->num && -+ t1FontNames[i].fontFileID.gen == id->gen) { -+ psName->clear(); -+ psName->insert(0, t1FontNames[i].psName); - return; -+ } - } -- -- // add entry to fontFileIDs list -- if (fontFileIDLen >= fontFileIDSize) { -- fontFileIDSize += 64; -- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); -- } -- fontFileIDs[fontFileIDLen++] = *id; -+ if (t1FontNameLen == t1FontNameSize) { -+ t1FontNameSize *= 2; -+ t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, -+ sizeof(PST1FontName)); -+ } -+ t1FontNames[t1FontNameLen].fontFileID = *id; -+ t1FontNames[t1FontNameLen].psName = psName->copy(); -+ ++t1FontNameLen; - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); -@@ -2012,13 +2098,14 @@ - embFontList->append("\n"); - - // convert it to a Type 1 font -- fontBuf = font->readEmbFontFile(xref, &fontLen); -- if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { -- ffT1C->convertToType1(psName->getCString(), NULL, gTrue, -- outputFunc, outputStream); -- delete ffT1C; -+ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { -+ if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { -+ ffT1C->convertToType1(psName->getCString(), NULL, gTrue, -+ outputFunc, outputStream); -+ delete ffT1C; -+ } -+ gfree(fontBuf); - } -- gfree(fontBuf); - - // ending comment - writePS("%%EndResource\n"); -@@ -2032,18 +2119,22 @@ - int i; - - // check if font is already embedded -- for (i = 0; i < fontFileIDLen; ++i) { -- if (fontFileIDs[i].num == id->num && -- fontFileIDs[i].gen == id->gen) -+ for (i = 0; i < t1FontNameLen; ++i) { -+ if (t1FontNames[i].fontFileID.num == id->num && -+ t1FontNames[i].fontFileID.gen == id->gen) { -+ psName->clear(); -+ psName->insert(0, t1FontNames[i].psName); - return; -+ } - } -- -- // add entry to fontFileIDs list -- if (fontFileIDLen >= fontFileIDSize) { -- fontFileIDSize += 64; -- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); -- } -- fontFileIDs[fontFileIDLen++] = *id; -+ if (t1FontNameLen == t1FontNameSize) { -+ t1FontNameSize *= 2; -+ t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, -+ sizeof(PST1FontName)); -+ } -+ t1FontNames[t1FontNameLen].fontFileID = *id; -+ t1FontNames[t1FontNameLen].psName = psName->copy(); -+ ++t1FontNameLen; - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); -@@ -2052,15 +2143,16 @@ - embFontList->append("\n"); - - // convert it to a Type 1 font -- fontBuf = font->readEmbFontFile(xref, &fontLen); -- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { -- if (ffTT->isOpenTypeCFF()) { -- ffTT->convertToType1(psName->getCString(), NULL, gTrue, -- outputFunc, outputStream); -+ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { -+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { -+ if (ffTT->isOpenTypeCFF()) { -+ ffTT->convertToType1(psName->getCString(), NULL, gTrue, -+ outputFunc, outputStream); -+ } -+ delete ffTT; - } -- delete ffTT; -+ gfree(fontBuf); - } -- gfree(fontBuf); - - // ending comment - writePS("%%EndResource\n"); -@@ -2071,26 +2163,7 @@ - char *fontBuf; - int fontLen; - FoFiTrueType *ffTT; - int *codeToGID; -- int i; -- -- // check if font is already embedded -- for (i = 0; i < fontFileIDLen; ++i) { -- if (fontFileIDs[i].num == id->num && -- fontFileIDs[i].gen == id->gen) { -- psName->appendf("_{0:d}", nextTrueTypeNum++); -- break; -- } -- } -- -- // add entry to fontFileIDs list -- if (i == fontFileIDLen) { -- if (fontFileIDLen >= fontFileIDSize) { -- fontFileIDSize += 64; -- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); -- } -- fontFileIDs[fontFileIDLen++] = *id; -- } - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); -@@ -2099,60 +2172,38 @@ - embFontList->append("\n"); - - // convert it to a Type 42 font -- fontBuf = font->readEmbFontFile(xref, &fontLen); -- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { -- codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); -- ffTT->convertToType42(psName->getCString(), -- ((Gfx8BitFont *)font)->getHasEncoding() -- ? ((Gfx8BitFont *)font)->getEncoding() -- : (char **)NULL, -- codeToGID, outputFunc, outputStream); -- if (codeToGID) { -- if (font8InfoLen >= font8InfoSize) { -- font8InfoSize += 16; -- font8Info = (PSFont8Info *)greallocn(font8Info, -- font8InfoSize, -- sizeof(PSFont8Info)); -+ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { -+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { -+ codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); -+ ffTT->convertToType42(psName->getCString(), -+ ((Gfx8BitFont *)font)->getHasEncoding() -+ ? ((Gfx8BitFont *)font)->getEncoding() -+ : (char **)NULL, -+ codeToGID, outputFunc, outputStream); -+ if (codeToGID) { -+ if (font8InfoLen >= font8InfoSize) { -+ font8InfoSize += 16; -+ font8Info = (PSFont8Info *)greallocn(font8Info, -+ font8InfoSize, -+ sizeof(PSFont8Info)); -+ } -+ font8Info[font8InfoLen].fontID = *font->getID(); -+ font8Info[font8InfoLen].codeToGID = codeToGID; -+ ++font8InfoLen; - } -- font8Info[font8InfoLen].fontID = *font->getID(); -- font8Info[font8InfoLen].codeToGID = codeToGID; -- ++font8InfoLen; -+ delete ffTT; - } -- delete ffTT; -+ gfree(fontBuf); - } -- gfree(fontBuf); - - // ending comment - writePS("%%EndResource\n"); - } - --void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *psName) { -- GString *fileName; -- char *fontBuf; -- int fontLen; -+void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *fileName, -+ GString *psName) { - FoFiTrueType *ffTT; - int *codeToGID; -- int i; -- -- // check if font is already embedded -- fileName = font->getExtFontFile(); -- for (i = 0; i < fontFileNameLen; ++i) { -- if (!fontFileNames[i]->cmp(fileName)) { -- psName->appendf("_{0:d}", nextTrueTypeNum++); -- break; -- } -- } -- -- // add entry to fontFileNames list -- if (i == fontFileNameLen) { -- if (fontFileNameLen >= fontFileNameSize) { -- fontFileNameSize += 64; -- fontFileNames = -- (GString **)greallocn(fontFileNames, -- fontFileNameSize, sizeof(GString *)); -- } -- fontFileNames[fontFileNameLen++] = fileName->copy(); -- } - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); -@@ -2161,8 +2212,7 @@ - embFontList->append("\n"); - - // convert it to a Type 42 font -- fontBuf = font->readExtFontFile(&fontLen); -- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { -+ if ((ffTT = FoFiTrueType::load(fileName->getCString()))) { - codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); - ffTT->convertToType42(psName->getCString(), - ((Gfx8BitFont *)font)->getHasEncoding() -@@ -2182,7 +2232,6 @@ - } - delete ffTT; - } -- gfree(fontBuf); - - // ending comment - writePS("%%EndResource\n"); -@@ -2196,18 +2245,22 @@ - int i; - - // check if font is already embedded -- for (i = 0; i < fontFileIDLen; ++i) { -- if (fontFileIDs[i].num == id->num && -- fontFileIDs[i].gen == id->gen) -+ for (i = 0; i < t1FontNameLen; ++i) { -+ if (t1FontNames[i].fontFileID.num == id->num && -+ t1FontNames[i].fontFileID.gen == id->gen) { -+ psName->clear(); -+ psName->insert(0, t1FontNames[i].psName); - return; -+ } - } -- -- // add entry to fontFileIDs list -- if (fontFileIDLen >= fontFileIDSize) { -- fontFileIDSize += 64; -- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); -- } -- fontFileIDs[fontFileIDLen++] = *id; -+ if (t1FontNameLen == t1FontNameSize) { -+ t1FontNameSize *= 2; -+ t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, -+ sizeof(PST1FontName)); -+ } -+ t1FontNames[t1FontNameLen].fontFileID = *id; -+ t1FontNames[t1FontNameLen].psName = psName->copy(); -+ ++t1FontNameLen; - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); -@@ -2216,18 +2269,21 @@ - embFontList->append("\n"); - - // convert it to a Type 0 font -- fontBuf = font->readEmbFontFile(xref, &fontLen); -- if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { -- if (globalParams->getPSLevel() >= psLevel3) { -- // Level 3: use a CID font -- ffT1C->convertToCIDType0(psName->getCString(), 0, NULL, -- outputFunc, outputStream); -- } else { -- // otherwise: use a non-CID composite font -- ffT1C->convertToType0(psName->getCString(), 0, NULL, -- outputFunc, outputStream); -+ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { -+ if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { -+ if (globalParams->getPSLevel() >= psLevel3) { -+ // Level 3: use a CID font -+ ffT1C->convertToCIDType0(psName->getCString(), NULL, 0, -+ outputFunc, outputStream); -+ } else { -+ // otherwise: use a non-CID composite font -+ ffT1C->convertToType0(psName->getCString(), NULL, 0, -+ outputFunc, outputStream); -+ } -+ delete ffT1C; - } -- delete ffT1C; -+ gfree(fontBuf); - } -- gfree(fontBuf); - - // ending comment - writePS("%%EndResource\n"); -@@ -2239,23 +2295,50 @@ - char *fontBuf; - int fontLen; - FoFiTrueType *ffTT; -- int i; - -- // check if font is already embedded -- for (i = 0; i < fontFileIDLen; ++i) { -- if (fontFileIDs[i].num == id->num && -- fontFileIDs[i].gen == id->gen) { -- psName->appendf("_{0:d}", nextTrueTypeNum++); -- break; -+ // beginning comment -+ writePSFmt("%%BeginResource: font {0:t}\n", psName); -+ embFontList->append("%%+ font "); -+ embFontList->append(psName->getCString()); -+ embFontList->append("\n"); -+ -+ // convert it to a Type 0 font -+ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { -+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { -+ if (globalParams->getPSLevel() >= psLevel3) { -+ // Level 3: use a CID font -+ ffTT->convertToCIDType2(psName->getCString(), -+ ((GfxCIDFont *)font)->getCIDToGID(), -+ ((GfxCIDFont *)font)->getCIDToGIDLen(), -+ needVerticalMetrics, -+ outputFunc, outputStream); -+ } else { -+ // otherwise: use a non-CID composite font -+ ffTT->convertToType0(psName->getCString(), -+ ((GfxCIDFont *)font)->getCIDToGID(), -+ ((GfxCIDFont *)font)->getCIDToGIDLen(), -+ needVerticalMetrics, -+ outputFunc, outputStream); -+ } -+ delete ffTT; - } -+ gfree(fontBuf); - } - -- // add entry to fontFileIDs list -- if (fontFileIDLen >= fontFileIDSize) { -- fontFileIDSize += 64; -- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); -- } -- fontFileIDs[fontFileIDLen++] = *id; -+ // ending comment -+ writePS("%%EndResource\n"); -+} -+ -+void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font, -+ GString *fileName, -+ GString *psName, -+ GBool needVerticalMetrics) { -+ FoFiTrueType *ffTT; -+ int *codeToGID; -+ int codeToGIDLen; -+ CharCodeToUnicode *ctu; -+ Unicode uBuf[8]; -+ int cmap, code; - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); -@@ -2264,26 +2347,62 @@ - embFontList->append("\n"); - - // convert it to a Type 0 font -- fontBuf = font->readEmbFontFile(xref, &fontLen); -- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { -- if (globalParams->getPSLevel() >= psLevel3) { -- // Level 3: use a CID font -- ffTT->convertToCIDType2(psName->getCString(), -- ((GfxCIDFont *)font)->getCIDToGID(), -- ((GfxCIDFont *)font)->getCIDToGIDLen(), -- needVerticalMetrics, -- outputFunc, outputStream); -+ //~ this should use fontNum to load the correct font -+ if ((ffTT = FoFiTrueType::load(fileName->getCString()))) { -+ -+ // check for embedding permission -+ if (ffTT->getEmbeddingRights() >= 1) { -+ -+ // create a CID-to-GID mapping, via Unicode -+ if ((ctu = ((GfxCIDFont *)font)->getToUnicode())) { -+ // look for a Unicode cmap -+ for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) { -+ if ((ffTT->getCmapPlatform(cmap) == 3 && -+ ffTT->getCmapEncoding(cmap) == 1) || -+ ffTT->getCmapPlatform(cmap) == 0) { -+ break; -+ } -+ } -+ if (cmap < ffTT->getNumCmaps()) { -+ // map CID -> Unicode -> GID -+ codeToGIDLen = ctu->getLength(); -+ codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); -+ for (code = 0; code < codeToGIDLen; ++code) { -+ if (ctu->mapToUnicode(code, uBuf, 8) > 0) { -+ codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]); -+ } else { -+ codeToGID[code] = 0; -+ } -+ } -+ if (globalParams->getPSLevel() >= psLevel3) { -+ // Level 3: use a CID font -+ ffTT->convertToCIDType2(psName->getCString(), -+ codeToGID, codeToGIDLen, -+ needVerticalMetrics, -+ outputFunc, outputStream); -+ } else { -+ // otherwise: use a non-CID composite font -+ ffTT->convertToType0(psName->getCString(), -+ codeToGID, codeToGIDLen, -+ needVerticalMetrics, -+ outputFunc, outputStream); -+ } -+ gfree(codeToGID); -+ } -+ ctu->decRefCnt(); -+ } else { -+ error(errSyntaxError, -1, -+ "Couldn't find a mapping to Unicode for font '{0:s}'", -+ font->getName() ? font->getName()->getCString() : "(unnamed)"); -+ } - } else { -- // otherwise: use a non-CID composite font -- ffTT->convertToType0(psName->getCString(), -- ((GfxCIDFont *)font)->getCIDToGID(), -- ((GfxCIDFont *)font)->getCIDToGIDLen(), -- needVerticalMetrics, -- outputFunc, outputStream); -+ error(errSyntaxError, -1, -+ "TrueType font '%s' does not allow embedding", -+ font->getName() ? font->getName()->getCString() : "(unnamed)"); -+ - } - delete ffTT; - } -- gfree(fontBuf); - - // ending comment - writePS("%%EndResource\n"); -@@ -2297,18 +2416,22 @@ - int i; - - // check if font is already embedded -- for (i = 0; i < fontFileIDLen; ++i) { -- if (fontFileIDs[i].num == id->num && -- fontFileIDs[i].gen == id->gen) -+ for (i = 0; i < t1FontNameLen; ++i) { -+ if (t1FontNames[i].fontFileID.num == id->num && -+ t1FontNames[i].fontFileID.gen == id->gen) { -+ psName->clear(); -+ psName->insert(0, t1FontNames[i].psName); - return; -+ } - } -- -- // add entry to fontFileIDs list -- if (fontFileIDLen >= fontFileIDSize) { -- fontFileIDSize += 64; -- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); -- } -- fontFileIDs[fontFileIDLen++] = *id; -+ if (t1FontNameLen == t1FontNameSize) { -+ t1FontNameSize *= 2; -+ t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, -+ sizeof(PST1FontName)); -+ } -+ t1FontNames[t1FontNameLen].fontFileID = *id; -+ t1FontNames[t1FontNameLen].psName = psName->copy(); -+ ++t1FontNameLen; - - // beginning comment - writePSFmt("%%BeginResource: font {0:t}\n", psName); -@@ -2317,21 +2440,27 @@ - embFontList->append("\n"); - - // convert it to a Type 0 font -- fontBuf = font->readEmbFontFile(xref, &fontLen); -- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { -- if (ffTT->isOpenTypeCFF()) { -- if (globalParams->getPSLevel() >= psLevel3) { -- // Level 3: use a CID font -- ffTT->convertToCIDType0(psName->getCString(), -- ((GfxCIDFont *)font)->getCIDToGID(), -- ((GfxCIDFont *)font)->getCIDToGIDLen(), -- outputFunc, outputStream); -- } else { -- // otherwise: use a non-CID composite font -- ffTT->convertToType0(psName->getCString(), -- ((GfxCIDFont *)font)->getCIDToGID(), -- ((GfxCIDFont *)font)->getCIDToGIDLen(), -- outputFunc, outputStream); -+ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { -+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { -+ if (ffTT->isOpenTypeCFF()) { -+ if (globalParams->getPSLevel() >= psLevel3) { -+ // Level 3: use a CID font -+ ffTT->convertToCIDType0(psName->getCString(), -+ ((GfxCIDFont *)font)->getCIDToGID(), -+ ((GfxCIDFont *)font)->getCIDToGIDLen(), -+ outputFunc, outputStream); -+ } else { -+ // otherwise: use a non-CID composite font -+ ffTT->convertToType0(psName->getCString(), -+ ((GfxCIDFont *)font)->getCIDToGID(), -+ ((GfxCIDFont *)font)->getCIDToGIDLen(), -+ outputFunc, outputStream); -+ } - } -+ delete ffTT; - } -- delete ffTT; -+ gfree(fontBuf); - } -- gfree(fontBuf); - - // ending comment - writePS("%%EndResource\n"); -@@ -2390,9 +2519,10 @@ - box.y1 = m[1]; - box.x2 = m[2]; - box.y2 = m[3]; - gfx = new Gfx(doc, this, resDict, &box, NULL); - inType3Char = gTrue; - for (i = 0; i < charProcs->getLength(); ++i) { -+ t3FillColorOnly = gFalse; - t3Cacheable = gFalse; - t3NeedsRestore = gFalse; - writePS("/"); -@@ -2430,9 +2560,45 @@ - writePS("%%EndResource\n"); - } - -+// Make a unique PS font name, based on the names given in the PDF -+// font object, and an object ID (font file object for -+GString *PSOutputDev::makePSFontName(GfxFont *font, Ref *id) { -+ GString *psName, *s; -+ -+ if ((s = font->getEmbeddedFontName())) { -+ psName = filterPSName(s); -+ if (!fontNames->lookupInt(psName)) { -+ fontNames->add(psName->copy(), 1); -+ return psName; -+ } -+ delete psName; -+ } -+ if ((s = font->getName())) { -+ psName = filterPSName(s); -+ if (!fontNames->lookupInt(psName)) { -+ fontNames->add(psName->copy(), 1); -+ return psName; -+ } -+ delete psName; -+ } -+ psName = GString::format("FF{0:d}_{1:d}", id->num, id->gen); -+ if ((s = font->getEmbeddedFontName())) { -+ s = filterPSName(s); -+ psName->append('_')->append(s); -+ delete s; -+ } else if ((s = font->getName())) { -+ s = filterPSName(s); -+ psName->append('_')->append(s); -+ delete s; -+ } -+ fontNames->add(psName->copy(), 1); -+ return psName; -+} -+ - void PSOutputDev::setupImages(Dict *resDict) { -- Object xObjDict, xObj, xObjRef, subtypeObj; -- int i; -+ Object xObjDict, xObj, xObjRef, subtypeObj, maskObj, maskRef; -+ Ref imgID; -+ int i, j; - - if (!(mode == psModeForm || inType3Char || preload)) { - return; -@@ -2447,9 +2613,32 @@ - xObj.streamGetDict()->lookup("Subtype", &subtypeObj); - if (subtypeObj.isName("Image")) { - if (xObjRef.isRef()) { -- setupImage(xObjRef.getRef(), xObj.getStream()); -+ imgID = xObjRef.getRef(); -+ for (j = 0; j < imgIDLen; ++j) { -+ if (imgIDs[j].num == imgID.num && imgIDs[j].gen == imgID.gen) { -+ break; -+ } -+ } -+ if (j == imgIDLen) { -+ if (imgIDLen >= imgIDSize) { -+ if (imgIDSize == 0) { -+ imgIDSize = 64; -+ } else { -+ imgIDSize *= 2; -+ } -+ imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref)); -+ } -+ imgIDs[imgIDLen++] = imgID; -+ setupImage(imgID, xObj.getStream(), gFalse); -+ if (level >= psLevel3 && -+ xObj.streamGetDict()->lookup("Mask", &maskObj)->isStream()) { -+ setupImage(imgID, maskObj.getStream(), gTrue); -+ } -+ maskObj.free(); -+ } - } else { - error(errSyntaxError, -1, - "Image in resource dict is not an indirect reference"); - } - } - subtypeObj.free(); -@@ -2461,30 +2650,12 @@ - xObjDict.free(); - } - --void PSOutputDev::setupImage(Ref id, Stream *str) { -+void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) { - GBool useRLE, useCompressed, useASCIIHex; - GString *s; - int c; - int size, line, col, i; - -- // check if image is already setup -- for (i = 0; i < imgIDLen; ++i) { -- if (imgIDs[i].num == id.num && imgIDs[i].gen == id.gen) { -- return; -- } -- } -- -- // add entry to imgIDs list -- if (imgIDLen >= imgIDSize) { -- if (imgIDSize == 0) { -- imgIDSize = 64; -- } else { -- imgIDSize *= 2; -- } -- imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref)); -- } -- imgIDs[imgIDLen++] = id; -- - // filters - //~ this does not correctly handle the DeviceN color space - //~ -- need to use DeviceNRecoder -@@ -2493,17 +2664,21 @@ - useCompressed = gFalse; - useASCIIHex = gTrue; - } else { -- s = str->getPSFilter(level < psLevel3 ? 2 : 3, ""); -- if (s) { -+ if (globalParams->getPSUncompressPreloadedImages()) { - useRLE = gFalse; -- useCompressed = gTrue; -- delete s; -- } else { -- useRLE = gTrue; - useCompressed = gFalse; -+ } else { -+ s = str->getPSFilter(level < psLevel3 ? 2 : 3, ""); -+ if (s) { -+ useRLE = gFalse; -+ useCompressed = gTrue; -+ delete s; -+ } else { -+ useRLE = gTrue; -+ useCompressed = gFalse; -+ } - } -- useASCIIHex = level == psLevel1 || level == psLevel1Sep || -- globalParams->getPSASCIIHex(); -+ useASCIIHex = globalParams->getPSASCIIHex(); - } - if (useCompressed) { - str = str->getUndecodedStream(); -@@ -2552,8 +2727,8 @@ - if (useRLE) { - ++size; - } -- writePSFmt("{0:d} array dup /ImData_{1:d}_{2:d} exch def\n", -- size, id.num, id.gen); -+ writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n", -+ size, mask ? "Mask" : "Im", id.num, id.gen); - str->close(); - - // write the data into the array -@@ -2722,12 +2898,14 @@ - int rotateA, GBool useMediaBox, GBool crop, - int sliceX, int sliceY, - int sliceW, int sliceH, - GBool printing, - GBool (*abortCheckCbk)(void *data), - void *abortCheckCbkData) { --#if HAVE_SPLASH - PreScanOutputDev *scan; - GBool rasterize; -+#if HAVE_SPLASH -+ GBool mono; -+ double dpi; - SplashOutputDev *splashOut; - SplashColor paperColor; - PDFRectangle box; -@@ -2737,43 +2915,33 @@ - Object obj; - Guchar *p; - Guchar col[4]; -+ double hDPI2, vDPI2; - double m0, m1, m2, m3, m4, m5; -+ int nStripes, stripeH, stripeY; - int c, w, h, x, y, comp, i; -+#endif - -- scan = new PreScanOutputDev(); -- page->displaySlice(scan, 72, 72, rotateA, useMediaBox, crop, -- sliceX, sliceY, sliceW, sliceH, -- printing, catalog, abortCheckCbk, abortCheckCbkData); -- rasterize = scan->usesTransparency(); -- delete scan; -+ if (globalParams->getPSAlwaysRasterize()) { -+ rasterize = gTrue; -+ } else { -+ scan = new PreScanOutputDev(); -+ page->displaySlice(scan, 72, 72, rotateA, useMediaBox, crop, -+ sliceX, sliceY, sliceW, sliceH, -+ printing, abortCheckCbk, abortCheckCbkData); -+ rasterize = scan->usesTransparency() || scan->usesPatternImageMask(); -+ delete scan; -+ } - if (!rasterize) { - return gTrue; - } - -- // rasterize the page -- if (level == psLevel1) { -- paperColor[0] = 0xff; -- splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, -- paperColor, gTrue, gFalse); --#if SPLASH_CMYK -- } else if (level == psLevel1Sep) { -- paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0; -- splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse, -- paperColor, gTrue, gFalse); --#endif -- } else { -- paperColor[0] = paperColor[1] = paperColor[2] = 0xff; -- splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, -- paperColor, gTrue, gFalse); -- } -- splashOut->startDoc(xref); -- page->displaySlice(splashOut, splashDPI, splashDPI, rotateA, -- useMediaBox, crop, -- sliceX, sliceY, sliceW, sliceH, -- printing, catalog, abortCheckCbk, abortCheckCbkData); -+#if HAVE_SPLASH -+ // get the rasterization parameters -+ dpi = globalParams->getPSRasterResolution(); -+ mono = globalParams->getPSRasterMono(); - - // start the PS page -- page->makeBox(splashDPI, splashDPI, rotateA, useMediaBox, gFalse, -+ page->makeBox(dpi, dpi, rotateA, useMediaBox, gFalse, - sliceX, sliceY, sliceW, sliceH, &box, &crop); - rotateA += page->getRotate(); - if (rotateA >= 360) { -@@ -2781,166 +2949,215 @@ - } else if (rotateA < 0) { - rotateA += 360; - } -- state = new GfxState(splashDPI, splashDPI, &box, rotateA, gFalse); -- startPage(page->getNum(), state); -- delete state; -- switch (rotateA) { -- case 0: -- default: // this should never happen -+ state = new GfxState(dpi, dpi, &box, rotateA, gFalse); -+ startPage(page->getNum(), state); -+ delete state; -+ -+ // set up the SplashOutputDev -+ if (mono || level == psLevel1) { -+ paperColor[0] = 0xff; -+ splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, -+ paperColor, gFalse, -+ globalParams->getAntialiasPrinting()); -+#if SPLASH_CMYK -+ } else if (level == psLevel1Sep) { -+ paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0; -+ splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse, -+ paperColor, gFalse, -+ globalParams->getAntialiasPrinting()); -+#endif -+ } else { -+ paperColor[0] = paperColor[1] = paperColor[2] = 0xff; -+ splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, -+ paperColor, gFalse, -+ globalParams->getAntialiasPrinting()); -+ } -+ splashOut->startDoc(xref); -+ -+ // break the page into stripes -+ hDPI2 = xScale * dpi; -+ vDPI2 = yScale * dpi; -+ if (sliceW < 0 || sliceH < 0) { -+ if (useMediaBox) { -+ box = *page->getMediaBox(); -+ } else { -+ box = *page->getCropBox(); -+ } -+ sliceX = sliceY = 0; -+ sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0); -+ sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0); -+ } -+ nStripes = (int)ceil((double)(sliceW * sliceH) / -+ (double)rasterizationSliceSize); -+ stripeH = (sliceH + nStripes - 1) / nStripes; -+ -+ // render the stripes -+ for (stripeY = sliceY; stripeY < sliceH; stripeY += stripeH) { -+ -+ // rasterize a stripe -+ page->makeBox(hDPI2, vDPI2, 0, useMediaBox, gFalse, -+ sliceX, stripeY, sliceW, stripeH, &box, &crop); - m0 = box.x2 - box.x1; - m1 = 0; - m2 = 0; - m3 = box.y2 - box.y1; - m4 = box.x1; - m5 = box.y1; -- break; -- case 90: -- m0 = 0; -- m1 = box.y2 - box.y1; -- m2 = -(box.x2 - box.x1); -- m3 = 0; -- m4 = box.x2; -- m5 = box.y1; -- break; -- case 180: -- m0 = -(box.x2 - box.x1); -- m1 = 0; -- m2 = 0; -- m3 = -(box.y2 - box.y1); -- m4 = box.x2; -- m5 = box.y2; -- break; -- case 270: -- m0 = 0; -- m1 = -(box.y2 - box.y1); -- m2 = box.x2 - box.x1; -- m3 = 0; -- m4 = box.x1; -- m5 = box.y2; -- break; -- } -- -- //~ need to add the process colors -- -- // draw the rasterized image -- bitmap = splashOut->getBitmap(); -- w = bitmap->getWidth(); -- h = bitmap->getHeight(); -- writePS("gsave\n"); -- writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] concat\n", -- m0, m1, m2, m3, m4, m5); -- switch (level) { -- case psLevel1: -- writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n", -- w, h, w, -h, h); -- p = bitmap->getDataPtr(); -- i = 0; -- for (y = 0; y < h; ++y) { -- for (x = 0; x < w; ++x) { -- writePSFmt("{0:02x}", *p++); -- if (++i == 32) { -- writePSChar('\n'); -- i = 0; -- } -- } -- } -- if (i != 0) { -- writePSChar('\n'); -- } -- break; -- case psLevel1Sep: -- writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n", -- w, h, w, -h, h); -- p = bitmap->getDataPtr(); -- i = 0; -- col[0] = col[1] = col[2] = col[3] = 0; -- for (y = 0; y < h; ++y) { -- for (comp = 0; comp < 4; ++comp) { -+ page->displaySlice(splashOut, hDPI2, vDPI2, -+ (360 - page->getRotate()) % 360, useMediaBox, crop, -+ sliceX, stripeY, sliceW, stripeH, -+ printing, abortCheckCbk, abortCheckCbkData); -+ -+ // draw the rasterized image -+ bitmap = splashOut->getBitmap(); -+ w = bitmap->getWidth(); -+ h = bitmap->getHeight(); -+ writePS("gsave\n"); -+ writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", -+ m0, m1, m2, m3, m4, m5); -+ switch (level) { -+ case psLevel1: -+ writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n", -+ w, h, w, -h, h); -+ p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); -+ i = 0; -+ for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { -- writePSFmt("{0:02x}", p[4*x + comp]); -- col[comp] |= p[4*x + comp]; -+ writePSFmt("{0:02x}", *p++); - if (++i == 32) { - writePSChar('\n'); - i = 0; - } - } - } -- p += bitmap->getRowSize(); -- } -- if (i != 0) { -+ if (i != 0) { -+ writePSChar('\n'); -+ } -+ break; -+ case psLevel1Sep: -+ writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n", -+ w, h, w, -h, h); -+ p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); -+ i = 0; -+ col[0] = col[1] = col[2] = col[3] = 0; -+ for (y = 0; y < h; ++y) { -+ for (comp = 0; comp < 4; ++comp) { -+ for (x = 0; x < w; ++x) { -+ writePSFmt("{0:02x}", p[4*x + comp]); -+ col[comp] |= p[4*x + comp]; -+ if (++i == 32) { -+ writePSChar('\n'); -+ i = 0; -+ } -+ } -+ } -+ p -= bitmap->getRowSize(); -+ } -+ if (i != 0) { -+ writePSChar('\n'); -+ } -+ if (col[0]) { -+ processColors |= psProcessCyan; -+ } -+ if (col[1]) { -+ processColors |= psProcessMagenta; -+ } -+ if (col[2]) { -+ processColors |= psProcessYellow; -+ } -+ if (col[3]) { -+ processColors |= psProcessBlack; -+ } -+ break; -+ case psLevel2: -+ case psLevel2Sep: -+ case psLevel3: -+ case psLevel3Sep: -+ if (mono) { -+ writePS("/DeviceGray setcolorspace\n"); -+ } else { -+ writePS("/DeviceRGB setcolorspace\n"); -+ } -+ writePS("<<\n /ImageType 1\n"); -+ writePSFmt(" /Width {0:d}\n", bitmap->getWidth()); -+ writePSFmt(" /Height {0:d}\n", bitmap->getHeight()); -+ writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h); -+ writePS(" /BitsPerComponent 8\n"); -+ if (mono) { -+ writePS(" /Decode [0 1]\n"); -+ } else { -+ writePS(" /Decode [0 1 0 1 0 1]\n"); -+ } -+ writePS(" /DataSource currentfile\n"); -+ if (globalParams->getPSASCIIHex()) { -+ writePS(" /ASCIIHexDecode filter\n"); -+ } else { -+ writePS(" /ASCII85Decode filter\n"); -+ } -+ writePS(" /RunLengthDecode filter\n"); -+ writePS(">>\n"); -+ writePS("image\n"); -+ obj.initNull(); -+ p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); -+ str0 = new MemStream((char *)p, 0, w * h * (mono ? 1 : 3), &obj); -+ str = new RunLengthEncoder(str0); -+ if (globalParams->getPSASCIIHex()) { -+ str = new ASCIIHexEncoder(str); -+ } else { -+ str = new ASCII85Encoder(str); -+ } -+ str->reset(); -+ while ((c = str->getChar()) != EOF) { -+ writePSChar(c); -+ } -+ str->close(); -+ delete str; -+ delete str0; - writePSChar('\n'); -+ processColors |= mono ? psProcessBlack : psProcessCMYK; -+ break; - } -- if (col[0]) { -- processColors |= psProcessCyan; -- } -- if (col[1]) { -- processColors |= psProcessMagenta; -- } -- if (col[2]) { -- processColors |= psProcessYellow; -- } -- if (col[3]) { -- processColors |= psProcessBlack; -- } -- break; -- case psLevel2: -- case psLevel2Sep: -- case psLevel3: -- case psLevel3Sep: -- writePS("/DeviceRGB setcolorspace\n"); -- writePS("<<\n /ImageType 1\n"); -- writePSFmt(" /Width {0:d}\n", bitmap->getWidth()); -- writePSFmt(" /Height {0:d}\n", bitmap->getHeight()); -- writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h); -- writePS(" /BitsPerComponent 8\n"); -- writePS(" /Decode [0 1 0 1 0 1]\n"); -- writePS(" /DataSource currentfile\n"); -- if (globalParams->getPSASCIIHex()) { -- writePS(" /ASCIIHexDecode filter\n"); -- } else { -- writePS(" /ASCII85Decode filter\n"); -- } -- writePS(" /RunLengthDecode filter\n"); -- writePS(">>\n"); -- writePS("image\n"); -- obj.initNull(); -- str0 = new MemStream((char *)bitmap->getDataPtr(), 0, w * h * 3, &obj); -- str = new RunLengthEncoder(str0); -- if (globalParams->getPSASCIIHex()) { -- str = new ASCIIHexEncoder(str); -- } else { -- str = new ASCII85Encoder(str); -- } -- str->reset(); -- while ((c = str->getChar()) != EOF) { -- writePSChar(c); -- } -- str->close(); -- delete str; -- delete str0; -- processColors |= psProcessCMYK; -- break; -+ writePS("grestore\n"); - } -+ - delete splashOut; -- writePS("grestore\n"); - - // finish the PS page - endPage(); - - return gFalse; --#else -+ -+#else // HAVE_SPLASH -+ -+ error(errSyntaxWarning, -1, -+ "PDF page uses transparency and PSOutputDev was built without" -+ " the Splash rasterizer - output may not be correct"); - return gTrue; --#endif -+#endif // HAVE_SPLASH - } - - void PSOutputDev::startPage(int pageNum, GfxState *state) { -- int x1, y1, x2, y2, width, height; -+ Page *page; -+ int x1, y1, x2, y2, width, height, t; - int imgWidth, imgHeight, imgWidth2, imgHeight2; - GBool landscape; -- -+ GString *s; - - if (mode == psModePS) { - writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage); -+ if (paperMatch) { -+ page = doc->getCatalog()->getPage(pageNum); -+ imgLLX = imgLLY = 0; -+ imgURX = (int)ceil(page->getMediaWidth()); -+ imgURY = (int)ceil(page->getMediaHeight()); -+ if (state->getRotate() == 90 || state->getRotate() == 270) { -+ t = imgURX; -+ imgURX = imgURY; -+ imgURY = t; -+ } -+ writePSFmt("%%PageMedia: {0:d}x{1:d}\n", imgURX, imgURY); -+ writePSFmt("%%PageBoundingBox: 0 0 {0:d} {1:d}\n", imgURX, imgURY); -+ } - writePS("%%BeginPageSetup\n"); - } - -@@ -2966,20 +3183,25 @@ - height = y2 - y1; - tx = ty = 0; - // rotation and portrait/landscape mode -- if (rotate0 >= 0) { -+ if (paperMatch) { -+ rotate = (360 - state->getRotate()) % 360; -+ landscape = gFalse; -+ } else if (rotate0 >= 0) { - rotate = (360 - rotate0) % 360; - landscape = gFalse; - } else { - rotate = (360 - state->getRotate()) % 360; - if (rotate == 0 || rotate == 180) { -- if (width > height && width > imgWidth) { -+ if ((width < height && imgWidth > imgHeight && height > imgHeight) || -+ (width > height && imgWidth < imgHeight && width > imgWidth)) { - rotate += 90; - landscape = gTrue; - } else { - landscape = gFalse; - } - } else { // rotate == 90 || rotate == 270 -- if (height > width && height > imgWidth) { -+ if ((height < width && imgWidth > imgHeight && width > imgHeight) || -+ (height > width && imgWidth < imgHeight && height > imgWidth)) { - rotate = 270 - rotate; - landscape = gTrue; - } else { -@@ -2989,6 +3211,9 @@ - } - writePSFmt("%%PageOrientation: {0:s}\n", - landscape ? "Landscape" : "Portrait"); -+ if (paperMatch) { -+ writePSFmt("{0:d} {1:d} pdfSetupPaper\n", imgURX, imgURY); -+ } - writePS("pdfStartPage\n"); - if (rotate == 0) { - imgWidth2 = imgWidth; -@@ -3015,9 +3240,9 @@ - xScale = xScale0; - yScale = yScale0; - } else if ((globalParams->getPSShrinkLarger() && -- (width > imgWidth2 || height > imgHeight2)) || -- (globalParams->getPSExpandSmaller() && -- (width < imgWidth2 && height < imgHeight2))) { -+ (width > imgWidth2 || height > imgHeight2)) || -+ (globalParams->getPSExpandSmaller() && -+ (width < imgWidth2 && height < imgHeight2))) { - xScale = (double)imgWidth2 / (double)width; - yScale = (double)imgHeight2 / (double)height; - if (yScale < xScale) { -@@ -3038,8 +3263,8 @@ - } - // center - if (tx0 >= 0 && ty0 >= 0) { -- tx += rotate == 0 ? tx0 : ty0; -- ty += rotate == 0 ? ty0 : -tx0; -+ tx += (rotate == 0 || rotate == 180) ? tx0 : ty0; -+ ty += (rotate == 0 || rotate == 180) ? ty0 : -tx0; - } else if (globalParams->getPSCenter()) { - if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { - tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2; -@@ -3049,22 +3274,21 @@ - ty += (imgHeight2 - yScale * height) / 2; - } - } -- tx += rotate == 0 ? imgLLX : imgLLY; -- ty += rotate == 0 ? imgLLY : -imgLLX; -+ tx += (rotate == 0 || rotate == 180) ? imgLLX : imgLLY; -+ ty += (rotate == 0 || rotate == 180) ? imgLLY : -imgLLX; - if (tx != 0 || ty != 0) { - writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); - } - if (xScale != 1 || yScale != 1) { - writePSFmt("{0:.6f} {1:.6f} scale\n", xScale, yScale); - } - if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { - writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re W\n", - clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0); - } else { - writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1); - } - -- writePS("%%EndPageSetup\n"); - ++seqPage; - break; - -@@ -3101,6 +3325,18 @@ - rotate = 0; - break; - } -+ -+ if (customCodeCbk) { -+ if ((s = (*customCodeCbk)(this, psOutCustomPageSetup, pageNum, -+ customCodeCbkData))) { -+ writePS(s->getCString()); -+ delete s; -+ } -+ } -+ -+ if (mode == psModePS) { -+ writePS("%%EndPageSetup\n"); -+ } - } - - void PSOutputDev::endPage() { -@@ -3458,25 +3693,33 @@ -+void PSOutputDev::saveTextPos(GfxState *state) { -+ writePS("currentpoint\n"); -+} -+ -+void PSOutputDev::restoreTextPos(GfxState *state) { -+ writePS("m\n"); -+} -+ - void PSOutputDev::stroke(GfxState *state) { - doPath(state->getPath()); -- if (t3String) { -- // if we're construct a cacheable Type 3 glyph, we need to do -+ if (inType3Char && t3FillColorOnly) { -+ // if we're constructing a cacheable Type 3 glyph, we need to do - // everything in the fill color - writePS("Sf\n"); - } else { -@@ -3494,19 +3737,19 @@ - writePS("f*\n"); - } - --void PSOutputDev::tilingPatternFill(GfxState *state, Object *str, -+void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str, - int paintType, Dict *resDict, - double *mat, double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep) { - PDFRectangle box; -- Gfx *gfx; -+ Gfx *gfx2; - - // define a Type 3 font - writePS("8 dict begin\n"); - writePS("/FontType 3 def\n"); - writePS("/FontMatrix [1 0 0 1 0 0] def\n"); - writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", - bbox[0], bbox[1], bbox[2], bbox[3]); - writePS("/Encoding 256 array def\n"); - writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); -@@ -3526,34 +3769,37 @@ - box.y1 = bbox[1]; - box.x2 = bbox[2]; - box.y2 = bbox[3]; - gfx = new Gfx(doc, this, resDict, &box, NULL); - writePS("/x {\n"); - if (paintType == 2) { - writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n", - xStep, bbox[0], bbox[1], bbox[2], bbox[3]); -+ t3FillColorOnly = gTrue; - } else { - if (x1 - 1 <= x0) { - writePS("1 0 setcharwidth\n"); - } else { - writePSFmt("{0:.6g} 0 setcharwidth\n", xStep); - } -+ t3FillColorOnly = gFalse; - } - inType3Char = gTrue; - ++numTilingPatterns; -- gfx->display(str); -+ gfx2->display(str); - --numTilingPatterns; - inType3Char = gFalse; - writePS("} def\n"); -- delete gfx; -+ delete gfx2; - writePS("end\n"); - writePS("currentdict end\n"); - writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns); - - // draw the tiles - writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns); -+ writePS("fCol\n"); - writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", - mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); - writePSFmt("{0:d} 1 {1:d} {{ {2:.6g} exch {3:.6g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n", - y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1); - writePS("grestore\n"); - } -@@ -3698,7 +3944,10 @@ - double xMin, yMin, xMax, yMax; - double x0, y0, r0, x1, y1, r1, t0, t1; - double xa, ya, ra; -- double sz, xz, yz, sMin, sMax, sa, ta; -+ double sz, sMin, sMax, h, ta; -+ double sLeft, sRight, sTop, sBottom, sZero, sDiag; -+ GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero; -+ GBool haveSMin, haveSMax; - double theta, alpha, a1, a2; - GBool enclosed; - int i; -@@ -3717,19 +3966,23 @@ - - // Compute the point at which r(s) = 0; check for the enclosed - // circles case; and compute the angles for the tangent lines. -- if (r0 == r1) { -- enclosed = x0 == x1 && y0 == y1; -+ h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); -+ if (h == 0) { -+ enclosed = gTrue; -+ theta = 0; // make gcc happy -+ sz = 0; // make gcc happy -+ } else if (r1 - r0 == 0) { -+ enclosed = gFalse; - theta = 0; - sz = 0; // make gcc happy -+ } else if (fabs(r1 - r0) >= h) { -+ enclosed = gTrue; -+ theta = 0; // make gcc happy -+ sz = 0; // make gcc happy - } else { -+ enclosed = gFalse; - sz = -r0 / (r1 - r0); -- xz = x0 + sz * (x1 - x0); -- yz = y0 + sz * (y1 - y0); -- enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0; -- theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz))); -- if (r0 > r1) { -- theta = -theta; -- } -+ theta = asin((r1 - r0) / h); - } - if (enclosed) { - a1 = 0; -@@ -3749,80 +4002,122 @@ - sMin = 0; - sMax = 1; - } else { -- sMin = 1; -- sMax = 0; -- // solve for x(s) + r(s) = xMin -- if ((x1 + r1) - (x0 + r0) != 0) { -- sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); -- if (sa < sMin) { -- sMin = sa; -- } else if (sa > sMax) { -- sMax = sa; -- } -- } -- // solve for x(s) - r(s) = xMax -- if ((x1 - r1) - (x0 - r0) != 0) { -- sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); -- if (sa < sMin) { -- sMin = sa; -- } else if (sa > sMax) { -- sMax = sa; -- } -- } -- // solve for y(s) + r(s) = yMin -- if ((y1 + r1) - (y0 + r0) != 0) { -- sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); -- if (sa < sMin) { -- sMin = sa; -- } else if (sa > sMax) { -- sMax = sa; -- } -- } -- // solve for y(s) - r(s) = yMax -- if ((y1 - r1) - (y0 - r0) != 0) { -- sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); -- if (sa < sMin) { -- sMin = sa; -- } else if (sa > sMax) { -- sMax = sa; -- } -- } -- // check against sz -- if (r0 < r1) { -- if (sMin < sz) { -- sMin = sz; -- } -- } else if (r0 > r1) { -- if (sMax > sz) { -- sMax = sz; -- } -+ // solve x(sLeft) + r(sLeft) = xMin -+ if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) { -+ sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); -+ } else { -+ sLeft = 0; // make gcc happy -+ } -+ // solve x(sRight) - r(sRight) = xMax -+ if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) { -+ sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); -+ } else { -+ sRight = 0; // make gcc happy -+ } -+ // solve y(sBottom) + r(sBottom) = yMin -+ if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) { -+ sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); -+ } else { -+ sBottom = 0; // make gcc happy -+ } -+ // solve y(sTop) - r(sTop) = yMax -+ if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) { -+ sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); -+ } else { -+ sTop = 0; // make gcc happy -+ } -+ // solve r(sZero) = 0 -+ if ((haveSZero = fabs(r1 - r0) > 0.000001)) { -+ sZero = -r0 / (r1 - r0); -+ } else { -+ sZero = 0; // make gcc happy -+ } -+ // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2) -+ if (haveSZero) { -+ sDiag = (sqrt((xMax - xMin) * (xMax - xMin) + -+ (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); -+ } else { -+ sDiag = 0; // make gcc happy - } -- // check the 'extend' flags -- if (!shading->getExtend0() && sMin < 0) { -+ // compute sMin -+ if (shading->getExtend0()) { -+ sMin = 0; -+ haveSMin = gFalse; -+ if (x0 < x1 && haveSLeft && sLeft < 0) { -+ sMin = sLeft; -+ haveSMin = gTrue; -+ } else if (x0 > x1 && haveSRight && sRight < 0) { -+ sMin = sRight; -+ haveSMin = gTrue; -+ } -+ if (y0 < y1 && haveSBottom && sBottom < 0) { -+ if (!haveSMin || sBottom > sMin) { -+ sMin = sBottom; -+ haveSMin = gTrue; -+ } -+ } else if (y0 > y1 && haveSTop && sTop < 0) { -+ if (!haveSMin || sTop > sMin) { -+ sMin = sTop; -+ haveSMin = gTrue; -+ } -+ } -+ if (haveSZero && sZero < 0) { -+ if (!haveSMin || sZero > sMin) { -+ sMin = sZero; -+ } -+ } -+ } else { - sMin = 0; - } -- if (!shading->getExtend1() && sMax > 1) { -+ // compute sMax -+ if (shading->getExtend1()) { -+ sMax = 1; -+ haveSMax = gFalse; -+ if (x1 < x0 && haveSLeft && sLeft > 1) { -+ sMax = sLeft; -+ haveSMax = gTrue; -+ } else if (x1 > x0 && haveSRight && sRight > 1) { -+ sMax = sRight; -+ haveSMax = gTrue; -+ } -+ if (y1 < y0 && haveSBottom && sBottom > 1) { -+ if (!haveSMax || sBottom < sMax) { -+ sMax = sBottom; -+ haveSMax = gTrue; -+ } -+ } else if (y1 > y0 && haveSTop && sTop > 1) { -+ if (!haveSMax || sTop < sMax) { -+ sMax = sTop; -+ haveSMax = gTrue; -+ } -+ } -+ if (haveSZero && sDiag > 1) { -+ if (!haveSMax || sDiag < sMax) { -+ sMax = sDiag; -+ } -+ } -+ } else { - sMax = 1; - } - } - - // generate the PS code -@@ -3970,15 +4265,16 @@ - GString *s2; -- double dx, dy, dx2, dy2, originX, originY; -+ double dx, dy, originX, originY; - char *p; - UnicodeMap *uMap; - CharCode code; - Unicode u[8]; - char buf[8]; -- int len, nChars, uLen, n, m, i, j; -+ double *dxdy; -+ int dxdySize, len, nChars, uLen, n, m, i, j; - - // check for invisible text -- this is used by Acrobat Capture - if (state->getRender() == 3) { -@@ -4003,6 +4299,10 @@ - for (i = 0; i < font16EncLen; ++i) { - if (font->getID()->num == font16Enc[i].fontID.num && - font->getID()->gen == font16Enc[i].fontID.gen) { -+ if (!font16Enc[i].enc) { -+ // font substitution failed, so don't output any text -+ return; -+ } - uMap = globalParams->getUnicodeMap(font16Enc[i].enc); - break; - } -@@ -4019,63 +4319,89 @@ - } - } - -- // compute width of chars in string, ignoring char spacing and word -- // spacing -- the Tj operator will adjust for the metrics of the -- // font that's actually used -- dx = dy = 0; -+ // compute the positioning (dx, dy) for each char in the string - nChars = 0; - p = s->getCString(); - len = s->getLength(); - s2 = new GString(); -+ dxdySize = font->isCIDFont() ? 8 : s->getLength(); -+ dxdy = (double *)gmallocn(2 * dxdySize, sizeof(double)); - while (len > 0) { - n = font->getNextChar(p, len, &code, - u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, -- &dx2, &dy2, &originX, &originY); -+ &dx, &dy, &originX, &originY); -+ dx *= state->getFontSize(); -+ dy *= state->getFontSize(); -+ if (wMode) { -+ dy += state->getCharSpace(); -+ if (n == 1 && *p == ' ') { -+ dy += state->getWordSpace(); -+ } -+ } else { -+ dx += state->getCharSpace(); -+ if (n == 1 && *p == ' ') { -+ dx += state->getWordSpace(); -+ } -+ } -+ dx *= state->getHorizScaling(); - if (font->isCIDFont()) { - if (uMap) { -+ if (nChars + uLen > dxdySize) { -+ do { -+ dxdySize *= 2; -+ } while (nChars + uLen > dxdySize); -+ dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); -+ } - for (i = 0; i < uLen; ++i) { - m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf)); - for (j = 0; j < m; ++j) { - s2->append(buf[j]); - } -+ //~ this really needs to get the number of chars in the target -+ //~ encoding - which may be more than the number of Unicode -+ //~ chars -+ dxdy[2 * nChars] = dx; -+ dxdy[2 * nChars + 1] = dy; -+ ++nChars; - } -- //~ this really needs to get the number of chars in the target -- //~ encoding - which may be more than the number of Unicode -- //~ chars -- nChars += uLen; - } else { -+ if (nChars + 1 > dxdySize) { -+ dxdySize *= 2; -+ dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); -+ } - s2->append((char)((code >> 8) & 0xff)); - s2->append((char)(code & 0xff)); -+ dxdy[2 * nChars] = dx; -+ dxdy[2 * nChars + 1] = dy; - ++nChars; - } - } else { - if (!codeToGID || codeToGID[code] >= 0) { - s2->append((char)code); -+ dxdy[2 * nChars] = dx; -+ dxdy[2 * nChars + 1] = dy; -+ ++nChars; - } - } -- dx += dx2; -- dy += dy2; - p += n; - len -= n; - } -- dx *= state->getFontSize() * state->getHorizScaling(); -- dy *= state->getFontSize(); - if (uMap) { - uMap->decRefCnt(); - } - -- if (s2->getLength() > 0) { -+ if (nChars > 0) { - writePSString(s2); -- if (font->isCIDFont()) { -- if (wMode) { -- writePSFmt(" {0:d} {1:.4g} Tj16V\n", nChars, dy); -- } else { -- writePSFmt(" {0:d} {1:.4g} Tj16\n", nChars, dx); -+ writePS("\n["); -+ for (i = 0; i < 2 * nChars; ++i) { -+ if (i > 0) { -+ writePS("\n"); - } -- } else { -- writePSFmt(" {0:.4g} Tj\n", dx); -+ writePSFmt("{0:.6g}", dxdy[i]); - } -+ writePS("] Tj\n"); - } -+ gfree(dxdy); - delete s2; - - if (state->getRender() & 4) { -@@ -4730,24 +5056,32 @@ - - // data source - if (mode == psModeForm || inType3Char || preload) { -- writePS(" /DataSource { 2 copy get exch 1 add exch }\n"); -+ writePS(" /DataSource { pdfImStr }\n"); - } else { - writePS(" /DataSource currentfile\n"); - } - - // filters -- s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, -- " "); -- if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || -- inlineImg || !s) { -- useRLE = gTrue; -- useASCII = !(mode == psModeForm || inType3Char || preload); -+ if ((mode == psModeForm || inType3Char || preload) && -+ globalParams->getPSUncompressPreloadedImages()) { -+ s = NULL; -+ useRLE = gFalse; - useCompressed = gFalse; -+ useASCII = gFalse; - } else { -- useRLE = gFalse; -- useASCII = str->isBinary() && -- !(mode == psModeForm || inType3Char || preload); -- useCompressed = gTrue; -+ s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, -+ " "); -+ if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || -+ inlineImg || !s) { -+ useRLE = gTrue; -+ useASCII = !(mode == psModeForm || inType3Char || preload); -+ useCompressed = gFalse; -+ } else { -+ useRLE = gFalse; -+ useASCII = str->isBinary() && -+ !(mode == psModeForm || inType3Char || preload); -+ useCompressed = gTrue; -+ } - } - if (useASCII) { - writePSFmt(" /ASCII{0:s}Decode filter\n", -@@ -4872,6 +5206,7 @@ - int n, numComps; - GBool useRLE, useASCII, useASCIIHex, useCompressed; - GBool maskUseRLE, maskUseASCII, maskUseCompressed; -+ GString *maskFilters; - GfxSeparationColorSpace *sepCS; - GfxColor color; - GfxCMYK cmyk; -@@ -4881,6 +5216,83 @@ - useASCIIHex = globalParams->getPSASCIIHex(); - useRLE = useASCII = useCompressed = gFalse; // make gcc happy - maskUseRLE = maskUseASCII = maskUseCompressed = gFalse; // make gcc happy -+ maskFilters = NULL; // make gcc happy -+ -+ // explicit masking -+ if (maskStr) { -+ -+ // mask data source -+ if ((mode == psModeForm || inType3Char || preload) && -+ globalParams->getPSUncompressPreloadedImages()) { -+ s = NULL; -+ maskUseRLE = gFalse; -+ maskUseCompressed = gFalse; -+ maskUseASCII = gFalse; -+ } else { -+ s = maskStr->getPSFilter(3, " "); -+ if (!s) { -+ maskUseRLE = gTrue; -+ maskUseASCII = !(mode == psModeForm || inType3Char || preload); -+ maskUseCompressed = gFalse; -+ } else { -+ maskUseRLE = gFalse; -+ maskUseASCII = maskStr->isBinary() && -+ !(mode == psModeForm || inType3Char || preload); -+ maskUseCompressed = gTrue; -+ } -+ } -+ maskFilters = new GString(); -+ if (maskUseASCII) { -+ maskFilters->appendf(" /ASCII{0:s}Decode filter\n", -+ useASCIIHex ? "Hex" : "85"); -+ } -+ if (maskUseRLE) { -+ maskFilters->append(" /RunLengthDecode filter\n"); -+ } -+ if (maskUseCompressed) { -+ maskFilters->append(s); -+ } -+ if (s) { -+ delete s; -+ } -+ if (mode == psModeForm || inType3Char || preload) { -+ writePSFmt("MaskData_{0:d}_{1:d} pdfMaskInit\n", -+ ref->getRefNum(), ref->getRefGen()); -+ } else { -+ writePS("currentfile\n"); -+ writePS(maskFilters->getCString()); -+ writePS("pdfMask\n"); -+ -+ // add RunLengthEncode and ASCIIHex/85 encode filters -+ if (maskUseCompressed) { -+ maskStr = maskStr->getUndecodedStream(); -+ } -+ if (maskUseRLE) { -+ maskStr = new RunLengthEncoder(maskStr); -+ } -+ if (maskUseASCII) { -+ if (useASCIIHex) { -+ maskStr = new ASCIIHexEncoder(maskStr); -+ } else { -+ maskStr = new ASCII85Encoder(maskStr); -+ } -+ } -+ -+ // copy the stream data -+ maskStr->reset(); -+ while ((c = maskStr->getChar()) != EOF) { -+ writePSChar(c); -+ } -+ maskStr->close(); -+ writePSChar('\n'); -+ writePS("%-EOD-\n"); -+ -+ // delete encoders -+ if (maskUseRLE || maskUseASCII) { -+ delete maskStr; -+ } -+ } -+ } - - // color space - if (colorMap) { -@@ -5015,24 +5427,32 @@ - - // data source - if (mode == psModeForm || inType3Char || preload) { -- writePS(" /DataSource { 2 copy get exch 1 add exch }\n"); -+ writePS(" /DataSource { pdfImStr }\n"); - } else { - writePS(" /DataSource currentfile\n"); - } - - // filters -- s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, -- " "); -- if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || -- inlineImg || !s) { -- useRLE = gTrue; -- useASCII = !(mode == psModeForm || inType3Char || preload); -+ if ((mode == psModeForm || inType3Char || preload) && -+ globalParams->getPSUncompressPreloadedImages()) { -+ s = NULL; -+ useRLE = gFalse; - useCompressed = gFalse; -+ useASCII = gFalse; - } else { -- useRLE = gFalse; -- useASCII = str->isBinary() && -- !(mode == psModeForm || inType3Char || preload); -- useCompressed = gTrue; -+ s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, -+ " "); -+ if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || -+ inlineImg || !s) { -+ useRLE = gTrue; -+ useASCII = !(mode == psModeForm || inType3Char || preload); -+ useCompressed = gFalse; -+ } else { -+ useRLE = gFalse; -+ useASCII = str->isBinary() && -+ !(mode == psModeForm || inType3Char || preload); -+ useCompressed = gTrue; -+ } - } - if (useASCII) { - writePSFmt(" /ASCII{0:s}Decode filter\n", -@@ -5065,30 +5485,13 @@ - maskInvert ? 1 : 0, maskInvert ? 0 : 1); - - // mask data source -- writePS(" /DataSource currentfile\n"); -- s = maskStr->getPSFilter(3, " "); -- if (!s) { -- maskUseRLE = gTrue; -- maskUseASCII = gTrue; -- maskUseCompressed = gFalse; -+ if (mode == psModeForm || inType3Char || preload) { -+ writePS(" /DataSource {pdfMaskSrc}\n"); -+ writePS(maskFilters->getCString()); - } else { -- maskUseRLE = gFalse; -- maskUseASCII = maskStr->isBinary(); -- maskUseCompressed = gTrue; -- } -- if (maskUseASCII) { -- writePSFmt(" /ASCII{0:s}Decode filter\n", -- useASCIIHex ? "Hex" : "85"); -- } -- if (maskUseRLE) { -- writePS(" /RunLengthDecode filter\n"); -- } -- if (maskUseCompressed) { -- writePS(s->getCString()); -- } -- if (s) { -- delete s; -+ writePS(" /DataSource maskStream\n"); - } -+ delete maskFilters; - - writePS(">>\n"); - writePS(">>\n"); -@@ -5116,39 +5519,6 @@ - - } - -- // explicit masking -- if (maskStr) { -- -- if (maskUseCompressed) { -- maskStr = maskStr->getUndecodedStream(); -- } -- -- // add RunLengthEncode and ASCIIHex/85 encode filters -- if (maskUseRLE) { -- maskStr = new RunLengthEncoder(maskStr); -- } -- if (maskUseASCII) { -- if (useASCIIHex) { -- maskStr = new ASCIIHexEncoder(maskStr); -- } else { -- maskStr = new ASCII85Encoder(maskStr); -- } -- } -- -- // copy the stream data -- maskStr->reset(); -- while ((c = maskStr->getChar()) != EOF) { -- writePSChar(c); -- } -- maskStr->close(); -- writePSChar('\n'); -- -- // delete encoders -- if (maskUseRLE || maskUseASCII) { -- delete maskStr; -- } -- } -- - // get rid of the array and index - if (mode == psModeForm || inType3Char || preload) { - writePS("pop pop\n"); -@@ -5196,6 +5566,13 @@ - delete str; - } - } -+ -+ // close the mask stream -+ if (maskStr) { -+ if (!(mode == psModeForm || inType3Char || preload)) { -+ writePS("pdfMaskEnd\n"); -+ } -+ } - } - - void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace, -@@ -5898,6 +6275,7 @@ - t3URY = ury; - t3String = new GString(); - writePS("q\n"); -+ t3FillColorOnly = gTrue; - t3Cacheable = gTrue; - t3NeedsRestore = gTrue; - } -diff -ru xpdf-3.02/xpdf/PSOutputDev.h xpdf-3.03/xpdf/PSOutputDev.h ---- xpdf-3.02/xpdf/PSOutputDev.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/PSOutputDev.h 2011-08-15 23:08:53.000000000 +0200 -@@ -21,15 +21,20 @@ - #include "GlobalParams.h" - #include "OutputDev.h" - -+class GHash; -+class PDFDoc; -+class XRef; - class Function; - class GfxPath; - class GfxFont; - class GfxColorSpace; - class GfxSeparationColorSpace; - class PDFRectangle; -+struct PST1FontName; - struct PSFont8Info; - struct PSFont16Enc; - class PSOutCustomColor; -+class PSOutputDev; - - //------------------------------------------------------------------------ - // PSOutputDev -@@ -48,25 +53,38 @@ - psGeneric // write to a generic stream - }; - --typedef void (*PSOutputFunc)(void *stream, const char *data, int len); -+enum PSOutCustomCodeLocation { -+ psOutCustomDocSetup, -+ psOutCustomPageSetup -+}; -+ -+typedef void (*PSOutputFunc)(void *stream, const char *data, int len); -+ -+typedef GString *(*PSOutCustomCodeCbk)(PSOutputDev *psOut, -+ PSOutCustomCodeLocation loc, int n, -+ void *data); - - class PSOutputDev: public OutputDev { - public: - - // Open a PostScript output file, and write the prolog. - PSOutputDev(char *fileName, PDFDoc *docA, - int firstPage, int lastPage, PSOutMode modeA, - int imgLLXA = 0, int imgLLYA = 0, - int imgURXA = 0, int imgURYA = 0, -- GBool manualCtrlA = gFalse); -+ GBool manualCtrlA = gFalse, -+ PSOutCustomCodeCbk customCodeCbkA = NULL, -+ void *customCodeCbkDataA = NULL); - - // Open a PSOutputDev that will write to a generic stream. - PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, - PDFDoc *docA, - int firstPage, int lastPage, PSOutMode modeA, - int imgLLXA = 0, int imgLLYA = 0, - int imgURXA = 0, int imgURYA = 0, -- GBool manualCtrlA = gFalse); -+ GBool manualCtrlA = gFalse, -+ PSOutCustomCodeCbk customCodeCbkA = NULL, -+ void *customCodeCbkDataA = NULL); - - // Destructor -- writes the trailer and closes the file. - virtual ~PSOutputDev(); -@@ -171,12 +189,14 @@ - virtual void updateHorizScaling(GfxState *state); - virtual void updateTextPos(GfxState *state); - virtual void updateTextShift(GfxState *state, double shift); -+ virtual void saveTextPos(GfxState *state); -+ virtual void restoreTextPos(GfxState *state); - - //----- path painting - virtual void stroke(GfxState *state); - virtual void fill(GfxState *state); - virtual void eoFill(GfxState *state); -- virtual void tilingPatternFill(GfxState *state, Object *str, -+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str, - int paintType, Dict *resDict, - double *mat, double *bbox, - int x0, int y0, int x1, int y1, -@@ -244,10 +264,10 @@ - private: - - void init(PSOutputFunc outputFuncA, void *outputStreamA, - PSFileType fileTypeA, PDFDoc *docA, - int firstPage, int lastPage, PSOutMode modeA, - int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, - GBool manualCtrlA); -@@ -256,14 +282,20 @@ - void setupEmbeddedType1CFont(GfxFont *font, Ref *id, GString *psName); - void setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GString *psName); - void setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GString *psName); -- void setupExternalTrueTypeFont(GfxFont *font, GString *psName); -+ void setupExternalTrueTypeFont(GfxFont *font, GString *fileName, -+ GString *psName); - void setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, GString *psName); - void setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GString *psName, - GBool needVerticalMetrics); -+ void setupExternalCIDTrueTypeFont(GfxFont *font, -+ GString *fileName, -+ GString *psName, -+ GBool needVerticalMetrics); - void setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GString *psName); - void setupType3Font(GfxFont *font, GString *psName, Dict *parentResDict); -+ GString *makePSFontName(GfxFont *font, Ref *id); - void setupImages(Dict *resDict); -- void setupImage(Ref id, Stream *str); -+ void setupImage(Ref id, Stream *str, GBool mask); - void setupForms(Dict *resDict); - void setupForm(Ref id, Object *strObj); - void addProcessColor(double c, double m, double y, double k); -@@ -308,6 +335,7 @@ - PSOutMode mode; // PostScript mode (PS, EPS, form) - int paperWidth; // width of paper, in pts - int paperHeight; // height of paper, in pts -+ GBool paperMatch; // true if paper size is set to match each page - int imgLLX, imgLLY, // imageable area, in pts - imgURX, imgURY; - GBool preload; // load all images into memory, and -@@ -322,20 +350,21 @@ - void *underlayCbkData; - void (*overlayCbk)(PSOutputDev *psOut, void *data); - void *overlayCbkData; -+ GString *(*customCodeCbk)(PSOutputDev *psOut, -+ PSOutCustomCodeLocation loc, int n, -+ void *data); -+ void *customCodeCbkData; - - PDFDoc *doc; - XRef *xref; // the xref table for this PDF file - - Ref *fontIDs; // list of object IDs of all used fonts - int fontIDLen; // number of entries in fontIDs array - int fontIDSize; // size of fontIDs array -- Ref *fontFileIDs; // list of object IDs of all embedded fonts -- int fontFileIDLen; // number of entries in fontFileIDs array -- int fontFileIDSize; // size of fontFileIDs array -- GString **fontFileNames; // list of names of all embedded external fonts -- int fontFileNameLen; // number of entries in fontFileNames array -- int fontFileNameSize; // size of fontFileNames array -- int nextTrueTypeNum; // next unique number to append to a TrueType -- // font name -+ GHash *fontNames; // all used font names -+ PST1FontName *t1FontNames; // font names for Type 1/1C fonts -+ int t1FontNameLen; // number of entries in t1FontNames array -+ int t1FontNameSize; // size of t1FontNames array - PSFont8Info *font8Info; // info for 8-bit fonts - int font8InfoLen; // number of entries in font8Info array - int font8InfoSize; // size of font8Info array -@@ -354,6 +383,8 @@ - int numTilingPatterns; // current number of nested tiling patterns - int nextFunc; // next unique number to use for a function - -+ GList *paperSizes; // list of used paper sizes, if paperMatch -+ // is true [PSOutPaperSize] - double tx0, ty0; // global translation - double xScale0, yScale0; // global scaling - int rotate0; // rotation angle (0, 90, 180, 270) -@@ -378,6 +409,7 @@ - GString *t3String; // Type 3 content string - double t3WX, t3WY, // Type 3 character parameters - t3LLX, t3LLY, t3URX, t3URY; -+ GBool t3FillColorOnly; // operators should only use the fill color - GBool t3Cacheable; // cleared if char is not cacheable - GBool t3NeedsRestore; // set if a 'q' operator was issued - -@@ -388,7 +420,6 @@ - - GBool ok; // set up ok? - -- - friend class WinPDFPrinter; - }; - -diff -ru xpdf-3.02/xpdf/SplashOutputDev.cc xpdf-3.03/xpdf/SplashOutputDev.cc ---- xpdf-3.02/xpdf/SplashOutputDev.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/SplashOutputDev.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -14,14 +14,18 @@ - - #include - #include -+#include - #include "gfile.h" - #include "GlobalParams.h" - #include "Error.h" - #include "Object.h" -+#include "Gfx.h" - #include "GfxFont.h" - #include "Link.h" - #include "CharCodeToUnicode.h" - #include "FontEncodingTables.h" -+#include "BuiltinFont.h" -+#include "BuiltinFontTables.h" - #include "FoFiTrueType.h" - #include "SplashBitmap.h" - #include "SplashGlyphBitmap.h" -@@ -45,6 +49,13 @@ - - //------------------------------------------------------------------------ - -+// Type 3 font cache size parameters -+#define type3FontCacheAssoc 8 -+#define type3FontCacheMaxSets 8 -+#define type3FontCacheSize (128*1024) -+ -+//------------------------------------------------------------------------ -+ - // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. - static inline Guchar div255(int x) { - return (Guchar)((x + (x >> 8) + 0x80) >> 8); -@@ -180,64 +191,100 @@ - } - } - --static void cvtRGBToHSV(Guchar r, Guchar g, Guchar b, int *h, int *s, int *v) { -- int cmax, cmid, cmin, x; -+static int getLum(int r, int g, int b) { -+ return (int)(0.3 * r + 0.59 * g + 0.11 * b); -+} - -- if (r >= g) { -- if (g >= b) { x = 0; cmax = r; cmid = g; cmin = b; } -- else if (b >= r) { x = 4; cmax = b; cmid = r; cmin = g; } -- else { x = 5; cmax = r; cmid = b; cmin = g; } -- } else { -- if (r >= b) { x = 1; cmax = g; cmid = r; cmin = b; } -- else if (g >= b) { x = 2; cmax = g; cmid = b; cmin = r; } -- else { x = 3; cmax = b; cmid = g; cmin = r; } -- } -- if (cmax == cmin) { -- *h = *s = 0; -+static int getSat(int r, int g, int b) { -+ int rgbMin, rgbMax; -+ -+ rgbMin = rgbMax = r; -+ if (g < rgbMin) { -+ rgbMin = g; -+ } else if (g > rgbMax) { -+ rgbMax = g; -+ } -+ if (b < rgbMin) { -+ rgbMin = b; -+ } else if (b > rgbMax) { -+ rgbMax = b; -+ } -+ return rgbMax - rgbMin; -+} -+ -+static void clipColor(int rIn, int gIn, int bIn, -+ Guchar *rOut, Guchar *gOut, Guchar *bOut) { -+ int lum, rgbMin, rgbMax; -+ -+ lum = getLum(rIn, gIn, bIn); -+ rgbMin = rgbMax = rIn; -+ if (gIn < rgbMin) { -+ rgbMin = gIn; -+ } else if (gIn > rgbMax) { -+ rgbMax = gIn; -+ } -+ if (bIn < rgbMin) { -+ rgbMin = bIn; -+ } else if (bIn > rgbMax) { -+ rgbMax = bIn; -+ } -+ if (rgbMin < 0) { -+ *rOut = (Guchar)(lum + ((rIn - lum) * lum) / (lum - rgbMin)); -+ *gOut = (Guchar)(lum + ((gIn - lum) * lum) / (lum - rgbMin)); -+ *bOut = (Guchar)(lum + ((bIn - lum) * lum) / (lum - rgbMin)); -+ } else if (rgbMax > 255) { -+ *rOut = (Guchar)(lum + ((rIn - lum) * (255 - lum)) / (rgbMax - lum)); -+ *gOut = (Guchar)(lum + ((gIn - lum) * (255 - lum)) / (rgbMax - lum)); -+ *bOut = (Guchar)(lum + ((bIn - lum) * (255 - lum)) / (rgbMax - lum)); - } else { -- *h = x * 60; -- if (x & 1) { -- *h += ((cmax - cmid) * 60) / (cmax - cmin); -- } else { -- *h += ((cmid - cmin) * 60) / (cmax - cmin); -- } -- *s = (255 * (cmax - cmin)) / cmax; -+ *rOut = rIn; -+ *gOut = gIn; -+ *bOut = bIn; - } -- *v = cmax; - } - --static void cvtHSVToRGB(int h, int s, int v, Guchar *r, Guchar *g, Guchar *b) { -- int x, f, cmax, cmid, cmin; -+static void setLum(Guchar rIn, Guchar gIn, Guchar bIn, int lum, -+ Guchar *rOut, Guchar *gOut, Guchar *bOut) { -+ int d; -+ -+ d = lum - getLum(rIn, gIn, bIn); -+ clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut); -+} -+ -+static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat, -+ Guchar *rOut, Guchar *gOut, Guchar *bOut) { -+ int rgbMin, rgbMid, rgbMax; -+ Guchar *minOut, *midOut, *maxOut; - -- if (s == 0) { -- *r = *g = *b = v; -+ if (rIn < gIn) { -+ rgbMin = rIn; minOut = rOut; -+ rgbMid = gIn; midOut = gOut; - } else { -- x = h / 60; -- f = h % 60; -- cmax = v; -- if (x & 1) { -- cmid = div255(v * 255 - ((s * f) / 60)); -- } else { -- cmid = div255(v * (255 - ((s * (60 - f)) / 60))); -- } -- cmin = div255(v * (255 - s)); -- switch (x) { -- case 0: *r = cmax; *g = cmid; *b = cmin; break; -- case 1: *g = cmax; *r = cmid; *b = cmin; break; -- case 2: *g = cmax; *b = cmid; *r = cmin; break; -- case 3: *b = cmax; *g = cmid; *r = cmin; break; -- case 4: *b = cmax; *r = cmid; *g = cmin; break; -- case 5: *r = cmax; *b = cmid; *g = cmin; break; -- } -+ rgbMin = gIn; minOut = gOut; -+ rgbMid = rIn; midOut = rOut; - } -+ if (bIn > rgbMid) { -+ rgbMax = bIn; maxOut = bOut; -+ } else if (bIn > rgbMin) { -+ rgbMax = rgbMid; maxOut = midOut; -+ rgbMid = bIn; midOut = bOut; -+ } else { -+ rgbMax = rgbMid; maxOut = midOut; -+ rgbMid = rgbMin; midOut = minOut; -+ rgbMin = bIn; minOut = bOut; -+ } -+ if (rgbMax > rgbMin) { -+ *midOut = (Guchar)((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin); -+ *maxOut = (Guchar)sat; -+ } else { -+ *midOut = *maxOut = 0; -+ } -+ *minOut = 0; - } - - static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { -- int hs, ss, vs, hd, sd, vd; --#if SPLASH_CMYK -- Guchar r, g, b; --#endif -+ Guchar r0, g0, b0, r1, g1, b1; - - switch (cm) { - case splashModeMono1: -@@ -246,25 +293,22 @@ - break; - case splashModeRGB8: - case splashModeBGR8: -- cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs); -- cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd); -- cvtHSVToRGB(hs, sd, vd, &blend[0], &blend[1], &blend[2]); -+ setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]), -+ &r0, &g0, &b0); -+ setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), -+ &blend[0], &blend[1], &blend[2]); - break; - #if SPLASH_CMYK - case splashModeCMYK8: -- //~ (0xff - ...) should be clipped -- cvtRGBToHSV(0xff - (src[0] + src[3]), -- 0xff - (src[1] + src[3]), -- 0xff - (src[2] + src[3]), &hs, &ss, &vs); -- cvtRGBToHSV(0xff - (dest[0] + dest[3]), -- 0xff - (dest[1] + dest[3]), -- 0xff - (dest[2] + dest[3]), &hd, &sd, &vd); -- cvtHSVToRGB(hs, sd, vd, &r, &g, &b); -- //~ should do black generation -- blend[0] = 0xff - r; -- blend[1] = 0xff - g; -- blend[2] = 0xff - b; -- blend[3] = 0; -+ // NB: inputs have already been converted to additive mode -+ setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]), -+ &r0, &g0, &b0); -+ setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), -+ &r1, &g1, &b1); -+ blend[0] = r1; -+ blend[1] = g1; -+ blend[2] = b1; -+ blend[3] = dest[3]; - break; - #endif - } -@@ -273,10 +317,7 @@ - static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, - SplashColorMode cm) { -- int hs, ss, vs, hd, sd, vd; --#if SPLASH_CMYK -- Guchar r, g, b; --#endif -+ Guchar r0, g0, b0, r1, g1, b1; - - switch (cm) { - case splashModeMono1: -@@ -285,25 +326,22 @@ - break; - case splashModeRGB8: - case splashModeBGR8: -- cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs); -- cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd); -- cvtHSVToRGB(hd, ss, vd, &blend[0], &blend[1], &blend[2]); -+ setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]), -+ &r0, &g0, &b0); -+ setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), -+ &blend[0], &blend[1], &blend[2]); - break; - #if SPLASH_CMYK - case splashModeCMYK8: -- //~ (0xff - ...) should be clipped -- cvtRGBToHSV(0xff - (src[0] + src[3]), -- 0xff - (src[1] + src[3]), -- 0xff - (src[2] + src[3]), &hs, &ss, &vs); -- cvtRGBToHSV(0xff - (dest[0] + dest[3]), -- 0xff - (dest[1] + dest[3]), -- 0xff - (dest[2] + dest[3]), &hd, &sd, &vd); -- cvtHSVToRGB(hd, ss, vd, &r, &g, &b); -- //~ should do black generation -- blend[0] = 0xff - r; -- blend[1] = 0xff - g; -- blend[2] = 0xff - b; -- blend[3] = 0; -+ // NB: inputs have already been converted to additive mode -+ setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]), -+ &r0, &g0, &b0); -+ setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), -+ &r1, &g1, &b1); -+ blend[0] = r1; -+ blend[1] = g1; -+ blend[2] = b1; -+ blend[3] = dest[3]; - break; - #endif - } -@@ -311,7 +349,6 @@ - - static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { -- int hs, ss, vs, hd, sd, vd; - #if SPLASH_CMYK - Guchar r, g, b; - #endif -@@ -323,25 +360,18 @@ - break; - case splashModeRGB8: - case splashModeBGR8: -- cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs); -- cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd); -- cvtHSVToRGB(hs, ss, vd, &blend[0], &blend[1], &blend[2]); -+ setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]), -+ &blend[0], &blend[1], &blend[2]); - break; - #if SPLASH_CMYK - case splashModeCMYK8: -- //~ (0xff - ...) should be clipped -- cvtRGBToHSV(0xff - (src[0] + src[3]), -- 0xff - (src[1] + src[3]), -- 0xff - (src[2] + src[3]), &hs, &ss, &vs); -- cvtRGBToHSV(0xff - (dest[0] + dest[3]), -- 0xff - (dest[1] + dest[3]), -- 0xff - (dest[2] + dest[3]), &hd, &sd, &vd); -- cvtHSVToRGB(hs, ss, vd, &r, &g, &b); -- //~ should do black generation -- blend[0] = 0xff - r; -- blend[1] = 0xff - g; -- blend[2] = 0xff - b; -- blend[3] = 0; -+ // NB: inputs have already been converted to additive mode -+ setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]), -+ &r, &g, &b); -+ blend[0] = r; -+ blend[1] = g; -+ blend[2] = b; -+ blend[3] = dest[3]; - break; - #endif - } -@@ -350,7 +380,6 @@ - static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, - SplashColorMode cm) { -- int hs, ss, vs, hd, sd, vd; - #if SPLASH_CMYK - Guchar r, g, b; - #endif -@@ -362,25 +391,18 @@ - break; - case splashModeRGB8: - case splashModeBGR8: -- cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs); -- cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd); -- cvtHSVToRGB(hd, sd, vs, &blend[0], &blend[1], &blend[2]); -+ setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]), -+ &blend[0], &blend[1], &blend[2]); - break; - #if SPLASH_CMYK - case splashModeCMYK8: -- //~ (0xff - ...) should be clipped -- cvtRGBToHSV(0xff - (src[0] + src[3]), -- 0xff - (src[1] + src[3]), -- 0xff - (src[2] + src[3]), &hs, &ss, &vs); -- cvtRGBToHSV(0xff - (dest[0] + dest[3]), -- 0xff - (dest[1] + dest[3]), -- 0xff - (dest[2] + dest[3]), &hd, &sd, &vd); -- cvtHSVToRGB(hd, sd, vs, &r, &g, &b); -- //~ should do black generation -- blend[0] = 0xff - r; -- blend[1] = 0xff - g; -- blend[2] = 0xff - b; -- blend[3] = 0; -+ // NB: inputs have already been converted to additive mode -+ setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]), -+ &r, &g, &b); -+ blend[0] = r; -+ blend[1] = g; -+ blend[2] = b; -+ blend[3] = src[3]; - break; - #endif - } -@@ -510,21 +503,23 @@ - glyphW = glyphWA; - glyphH = glyphHA; - validBBox = validBBoxA; -+ // sanity check for excessively large glyphs (which most likely -+ // indicate an incorrect BBox) -+ i = glyphW * glyphH; -+ if (i > 100000 || glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0) { -+ glyphW = glyphH = 100; -+ validBBox = gFalse; -+ } - if (aa) { - glyphSize = glyphW * glyphH; - } else { - glyphSize = ((glyphW + 7) >> 3) * glyphH; - } -- cacheAssoc = 8; -- if (glyphSize <= 256) { -- cacheSets = 8; -- } else if (glyphSize <= 512) { -- cacheSets = 4; -- } else if (glyphSize <= 1024) { -- cacheSets = 2; -- } else { -- cacheSets = 1; -- } -+ cacheAssoc = type3FontCacheAssoc; -+ for (cacheSets = type3FontCacheMaxSets; -+ cacheSets > 1 && -+ cacheSets * cacheAssoc * glyphSize > type3FontCacheSize; -+ cacheSets >>= 1) ; - cacheData = (Guchar *)gmallocn(cacheSets * cacheAssoc, glyphSize); - cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc, - sizeof(T3FontCacheTag)); -@@ -584,6 +579,7 @@ - colorMode = colorModeA; - bitmapRowPad = bitmapRowPadA; - bitmapTopDown = bitmapTopDownA; -+ bitmapUpsideDown = gFalse; - allowAntialias = allowAntialiasA; - vectorAntialias = allowAntialias && - globalParams->getVectorAntialias() && -@@ -591,12 +587,15 @@ - setupScreenParams(72.0, 72.0); - reverseVideo = reverseVideoA; - splashColorCopy(paperColor, paperColorA); -+ skipHorizText = gFalse; -+ skipRotatedText = gFalse; - - xref = NULL; - - bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, - colorMode != splashModeMono1, bitmapTopDown); - splash = new Splash(bitmap, vectorAntialias, &screenParams); -+ splash->setMinLineWidth(globalParams->getMinLineWidth()); - splash->clear(paperColor, 0); - - fontEngine = NULL; -@@ -609,6 +608,8 @@ - textClipPath = NULL; - - transpGroupStack = NULL; -+ -+ nestCount = 0; - } - - void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) { -@@ -723,15 +726,18 @@ - bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, - colorMode != splashModeMono1, bitmapTopDown); - } - splash = new Splash(bitmap, vectorAntialias, &screenParams); -+ splash->setMinLineWidth(globalParams->getMinLineWidth()); - if (state) { - ctm = state->getCTM(); - mat[0] = (SplashCoord)ctm[0]; -@@ -835,7 +841,10 @@ - } - - void SplashOutputDev::updateFlatness(GfxState *state) { -+#if 0 // Acrobat ignores the flatness setting, and always renders curves -+ // with a fairly small flatness value - splash->setFlatness(state->getFlatness()); -+#endif - } - - void SplashOutputDev::updateLineJoin(GfxState *state) { -@@ -868,14 +877,24 @@ - GfxCMYK cmyk; - #endif - -- state->getFillGray(&gray); -- state->getFillRGB(&rgb); -+ switch (colorMode) { -+ case splashModeMono1: -+ case splashModeMono8: -+ state->getFillGray(&gray); -+ splash->setFillPattern(getColor(gray)); -+ break; -+ case splashModeRGB8: -+ case splashModeBGR8: -+ state->getFillRGB(&rgb); -+ splash->setFillPattern(getColor(&rgb)); -+ break; - #if SPLASH_CMYK -- state->getFillCMYK(&cmyk); -- splash->setFillPattern(getColor(gray, &rgb, &cmyk)); --#else -- splash->setFillPattern(getColor(gray, &rgb)); -+ case splashModeCMYK8: -+ state->getFillCMYK(&cmyk); -+ splash->setFillPattern(getColor(&cmyk)); -+ break; - #endif -+ } - } - - void SplashOutputDev::updateStrokeColor(GfxState *state) { -@@ -885,28 +904,41 @@ - GfxCMYK cmyk; - #endif - -- state->getStrokeGray(&gray); -- state->getStrokeRGB(&rgb); -+ switch (colorMode) { -+ case splashModeMono1: -+ case splashModeMono8: -+ state->getStrokeGray(&gray); -+ splash->setStrokePattern(getColor(gray)); -+ break; -+ case splashModeRGB8: -+ case splashModeBGR8: -+ state->getStrokeRGB(&rgb); -+ splash->setStrokePattern(getColor(&rgb)); -+ break; - #if SPLASH_CMYK -- state->getStrokeCMYK(&cmyk); -- splash->setStrokePattern(getColor(gray, &rgb, &cmyk)); --#else -- splash->setStrokePattern(getColor(gray, &rgb)); -+ case splashModeCMYK8: -+ state->getStrokeCMYK(&cmyk); -+ splash->setStrokePattern(getColor(&cmyk)); -+ break; - #endif -+ } - } - --#if SPLASH_CMYK --SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb, -- GfxCMYK *cmyk) { --#else --SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb) { --#endif -- SplashPattern *pattern; -+SplashPattern *SplashOutputDev::getColor(GfxGray gray) { - SplashColor color; -- GfxColorComp r, g, b; - - if (reverseVideo) { - gray = gfxColorComp1 - gray; -+ } -+ color[0] = colToByte(gray); -+ return new SplashSolidColor(color); -+} -+ -+SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) { -+ GfxColorComp r, g, b; -+ SplashColor color; -+ -+ if (reverseVideo) { - r = gfxColorComp1 - rgb->r; - g = gfxColorComp1 - rgb->g; - b = gfxColorComp1 - rgb->b; -@@ -915,33 +947,58 @@ - g = rgb->g; - b = rgb->b; - } -+ color[0] = colToByte(r); -+ color[1] = colToByte(g); -+ color[2] = colToByte(b); -+ return new SplashSolidColor(color); -+} - -- pattern = NULL; // make gcc happy -- switch (colorMode) { -- case splashModeMono1: -- case splashModeMono8: -- color[0] = colToByte(gray); -- pattern = new SplashSolidColor(color); -- break; -- case splashModeRGB8: -- case splashModeBGR8: -- color[0] = colToByte(r); -- color[1] = colToByte(g); -- color[2] = colToByte(b); -- pattern = new SplashSolidColor(color); -- break; - #if SPLASH_CMYK -- case splashModeCMYK8: -- color[0] = colToByte(cmyk->c); -- color[1] = colToByte(cmyk->m); -- color[2] = colToByte(cmyk->y); -- color[3] = colToByte(cmyk->k); -- pattern = new SplashSolidColor(color); -- break; -+SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) { -+ SplashColor color; -+ -+ color[0] = colToByte(cmyk->c); -+ color[1] = colToByte(cmyk->m); -+ color[2] = colToByte(cmyk->y); -+ color[3] = colToByte(cmyk->k); -+ return new SplashSolidColor(color); -+} - #endif -- } - -- return pattern; - } - - void SplashOutputDev::updateBlendMode(GfxState *state) { -@@ -956,35 +1013,82 @@ - void SplashOutputDev::updateFont(GfxState *state) { - needFontUpdate = gTrue; - } - - void SplashOutputDev::doUpdateFont(GfxState *state) { - GfxFont *gfxFont; -+ GfxFontLoc *fontLoc; - GfxFontType fontType; - SplashOutFontFileID *id; - SplashFontFile *fontFile; -+ int fontNum; - FoFiTrueType *ff; - Ref embRef; - Object refObj, strObj; -- GString *tmpFileName, *fileName, *substName; -+ GString *tmpFileName, *fileName; - FILE *tmpFile; - int *codeToGID; -- DisplayFontParam *dfp; - CharCodeToUnicode *ctu; - double *textMat; -- double m11, m12, m21, m22, w1, w2, fontSize; -+ double m11, m12, m21, m22, fontSize; -+ double w, fontScaleMin, fontScaleAvg, fontScale; -+ Gushort ww; - SplashCoord mat[4]; - char *name; - Unicode uBuf[8]; -- int c, substIdx, n, code, cmap; -+ int c, substIdx, n, code, cmap, i; - - needFontUpdate = gFalse; - font = NULL; - tmpFileName = NULL; - substIdx = -1; -- dfp = NULL; - - if (!(gfxFont = state->getFont())) { - goto err1; -@@ -994,6 +1098,13 @@ - goto err1; - } - -+ // sanity-check the font size - skip anything larger than 10 inches -+ // (this avoids problems allocating memory for the font cache) -+ if (state->getTransformedFontSize() -+ > 10 * (state->getHDPI() + state->getVDPI())) { -+ goto err1; -+ } -+ - // check the font file cache - id = new SplashOutFontFileID(gfxFont->getID()); - if ((fontFile = fontEngine->getFontFile(id))) { -@@ -1001,19 +1112,32 @@ - - } else { - -- // if there is an embedded font, write it to disk -- if (gfxFont->getEmbeddedFontID(&embRef)) { -+ fileName = NULL; -+ fontNum = 0; -+ -+ if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) { -+ error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", -+ gfxFont->getName() ? gfxFont->getName()->getCString() -+ : "(unnamed)"); -+ goto err2; -+ } -+ -+ // embedded font -+ if (fontLoc->locType == gfxFontLocEmbedded) { -+ gfxFont->getEmbeddedFontID(&embRef); - if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) { -- error(-1, "Couldn't create temporary font file"); -+ error(errIO, -1, "Couldn't create temporary font file"); -+ delete fontLoc; - goto err2; - } - refObj.initRef(embRef.num, embRef.gen); - refObj.fetch(xref, &strObj); - refObj.free(); - if (!strObj.isStream()) { -- error(-1, "Embedded font object is wrong type"); -+ error(errSyntaxError, -1, "Embedded font object is wrong type"); - strObj.free(); - fclose(tmpFile); -+ delete fontLoc; - goto err2; - } - strObj.streamReset(); -@@ -1025,94 +1149,53 @@ - fclose(tmpFile); - fileName = tmpFileName; - -- // if there is an external font file, use it -- } else if (!(fileName = gfxFont->getExtFontFile())) { -- -- // look for a display font mapping or a substitute font -- if (gfxFont->isCIDFont()) { -- if (((GfxCIDFont *)gfxFont)->getCollection()) { -- dfp = globalParams-> -- getDisplayCIDFont(gfxFont->getName(), -- ((GfxCIDFont *)gfxFont)->getCollection()); -- } -- } else { -- if (gfxFont->getName()) { -- dfp = globalParams->getDisplayFont(gfxFont->getName()); -- } -- if (!dfp) { -- // 8-bit font substitution -- if (gfxFont->isFixedWidth()) { -- substIdx = 8; -- } else if (gfxFont->isSerif()) { -- substIdx = 4; -- } else { -- substIdx = 0; -- } -- if (gfxFont->isBold()) { -- substIdx += 2; -- } -- if (gfxFont->isItalic()) { -- substIdx += 1; -- } -- substName = new GString(splashOutSubstFonts[substIdx].name); -- dfp = globalParams->getDisplayFont(substName); -- delete substName; -- id->setSubstIdx(substIdx); -- } -- } -- if (!dfp) { -- error(-1, "Couldn't find a font for '%s'", -- gfxFont->getName() ? gfxFont->getName()->getCString() -- : "(unnamed)"); -- goto err2; -- } -- switch (dfp->kind) { -- case displayFontT1: -- fileName = dfp->t1.fileName; -- fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1; -- break; -- case displayFontTT: -- fileName = dfp->tt.fileName; -- fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType; -- break; -+ // external font -+ } else { // gfxFontLocExternal -+ fileName = fontLoc->path; -+ fontNum = fontLoc->fontNum; -+ if (fontLoc->substIdx >= 0) { -+ id->setSubstIdx(fontLoc->substIdx); - } - } - - // load the font file -- switch (fontType) { -+ switch (fontLoc->fontType) { - case fontType1: - if (!(fontFile = fontEngine->loadType1Font( -- id, -- fileName->getCString(), -- fileName == tmpFileName, -- (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { -- error(-1, "Couldn't create a font for '%s'", -+ id, -+ fileName->getCString(), -+ fileName == tmpFileName, -+ (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { -+ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); -+ delete fontLoc; - goto err2; - } - break; - case fontType1C: - if (!(fontFile = fontEngine->loadType1CFont( -- id, -- fileName->getCString(), -- fileName == tmpFileName, -- (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { -- error(-1, "Couldn't create a font for '%s'", -+ id, -+ fileName->getCString(), -+ fileName == tmpFileName, -+ (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { -+ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); -+ delete fontLoc; - goto err2; - } - break; - case fontType1COT: - if (!(fontFile = fontEngine->loadOpenTypeT1CFont( -- id, -- fileName->getCString(), -- fileName == tmpFileName, -- (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { -- error(-1, "Couldn't create a font for '%s'", -+ id, -+ fileName->getCString(), -+ fileName == tmpFileName, -+ (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { -+ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); -+ delete fontLoc; - goto err2; - } - break; -@@ -1122,18 +1205,33 @@ - codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff); - n = 256; - delete ff; -+ // if we're substituting for a non-TrueType font, we need to mark -+ // all notdef codes as "do not draw" (rather than drawing TrueType -+ // notdef glyphs) -+ if (gfxFont->getType() != fontTrueType && -+ gfxFont->getType() != fontTrueTypeOT) { -+ for (i = 0; i < 256; ++i) { -+ if (codeToGID[i] == 0) { -+ codeToGID[i] = -1; -+ } -+ } -+ } - } else { - codeToGID = NULL; - n = 0; - } - if (!(fontFile = fontEngine->loadTrueTypeFont( - id, -- fileName->getCString(), -+ fileName->getCString(), fontNum, - fileName == tmpFileName, -- codeToGID, n))) { -- error(-1, "Couldn't create a font for '%s'", -+ codeToGID, n, -+ gfxFont->getEmbeddedFontName() -+ ? gfxFont->getEmbeddedFontName()->getCString() -+ : (char *)NULL))) { -+ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); -+ delete fontLoc; - goto err2; - } - break; -@@ -1143,20 +1241,32 @@ - id, - fileName->getCString(), - fileName == tmpFileName))) { -- error(-1, "Couldn't create a font for '%s'", -+ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); -+ delete fontLoc; - goto err2; - } - break; - case fontCIDType0COT: - if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { - n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); - codeToGID = (int *)gmallocn(n, sizeof(int)); - memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), - n * sizeof(int)); - } else { - codeToGID = NULL; - n = 0; - } - if (!(fontFile = fontEngine->loadOpenTypeCFFFont( - id, - codeToGID, n))) { - error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); -+ delete fontLoc; - goto err2; - } - break; -@@ -1164,9 +1274,17 @@ - case fontCIDType2OT: - codeToGID = NULL; - n = 0; -- if (dfp) { -+ if (fontLoc->locType == gfxFontLocEmbedded) { -+ if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { -+ n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); -+ codeToGID = (int *)gmallocn(n, sizeof(int)); -+ memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), -+ n * sizeof(int)); -+ } -+ } else { - // create a CID-to-GID mapping, via Unicode - if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) { -+ //~ this should use fontNum to load the correct font - if ((ff = FoFiTrueType::load(fileName->getCString()))) { - // look for a Unicode cmap - for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) { -@@ -1179,12 +1297,12 @@ - if (cmap < ff->getNumCmaps()) { - // map CID -> Unicode -> GID - n = ctu->getLength(); -- codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort)); -+ codeToGID = (int *)gmallocn(n, sizeof(int)); - for (code = 0; code < n; ++code) { - if (ctu->mapToUnicode(code, uBuf, 8) > 0) { - codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]); - } else { -- codeToGID[code] = 0; -+ codeToGID[code] = -1; - } - } - } -@@ -1192,26 +1310,24 @@ - } - ctu->decRefCnt(); - } else { -- error(-1, "Couldn't find a mapping to Unicode for font '%s'", -+ error(errSyntaxError, -1, -+ "Couldn't find a mapping to Unicode for font '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); - } -- } else { -- if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { -- n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); -- codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort)); -- memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), -- n * sizeof(Gushort)); -- } - } - if (!(fontFile = fontEngine->loadTrueTypeFont( - id, -- fileName->getCString(), -+ fileName->getCString(), fontNum, - fileName == tmpFileName, -- codeToGID, n))) { -- error(-1, "Couldn't create a font for '%s'", -+ codeToGID, n, -+ gfxFont->getEmbeddedFontName() -+ ? gfxFont->getEmbeddedFontName()->getCString() -+ : (char *)NULL))) { -+ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); -+ delete fontLoc; - goto err2; - } - break; -@@ -1219,6 +1335,8 @@ - // this shouldn't happen - goto err2; - } -+ -+ delete fontLoc; - } - - // get the font matrix -@@ -1230,26 +1348,42 @@ - m22 = textMat[3] * fontSize; - - // for substituted fonts: adjust the font matrix -- compare the -- // width of 'm' in the original font and the substituted font -+ // widths of letters and digits (A-Z, a-z, 0-9) in the original font -+ // and the substituted font - substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx(); -- if (substIdx >= 0) { -+ if (substIdx >= 0 && substIdx < 12) { -+ fontScaleMin = 1; -+ fontScaleAvg = 0; -+ n = 0; - for (code = 0; code < 256; ++code) { - if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) && -- name[0] == 'm' && name[1] == '\0') { -- break; -+ name[0] && !name[1] && -+ ((name[0] >= 'A' && name[0] <= 'Z') || -+ (name[0] >= 'a' && name[0] <= 'z') || -+ (name[0] >= '0' && name[0] <= '9'))) { -+ w = ((Gfx8BitFont *)gfxFont)->getWidth(code); -+ builtinFontSubst[substIdx]->widths->getWidth(name, &ww); -+ if (w > 0.01 && ww > 10) { -+ w /= ww * 0.001; -+ if (w < fontScaleMin) { -+ fontScaleMin = w; -+ } -+ fontScaleAvg += w; -+ ++n; -+ } - } - } -- if (code < 256) { -- w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code); -- w2 = splashOutSubstFonts[substIdx].mWidth; -- if (!gfxFont->isSymbolic()) { -- // if real font is substantially narrower than substituted -- // font, reduce the font size accordingly -- if (w1 > 0.01 && w1 < 0.9 * w2) { -- w1 /= w2; -- m11 *= w1; -- m21 *= w1; -- } -+ // if real font is narrower than substituted font, reduce the font -+ // size accordingly -- this currently uses a scale factor halfway -+ // between the minimum and average computed scale factors, which -+ // is a bit of a kludge, but seems to produce mostly decent -+ // results -+ if (n) { -+ fontScaleAvg /= n; -+ if (fontScaleAvg < 1) { -+ fontScale = 0.5 * (fontScaleMin + fontScaleAvg); -+ m11 *= fontScale; -+ m12 *= fontScale; - } - } - } -@@ -1302,15 +1440,155 @@ - splash->fill(path, gTrue); - delete path; - } - -+void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str, -+ int paintType, Dict *resDict, -+ double *mat, double *bbox, -+ int x0, int y0, int x1, int y1, -+ double xStep, double yStep) { -+ double tileXMin, tileYMin, tileXMax, tileYMax, tx, ty; -+ int tileX0, tileY0, tileW, tileH, tileSize; -+ SplashBitmap *origBitmap, *tileBitmap; -+ Splash *origSplash; -+ SplashColor color; -+ double mat1[6]; -+ double xa, ya, xb, yb, xc, yc; -+ int x, y, xx, yy, i; -+ -+ // transform the four corners of the bbox from pattern space to -+ // device space and compute the device space bbox -+ state->transform(bbox[0] * mat[0] + bbox[1] * mat[2] + mat[4], -+ bbox[0] * mat[1] + bbox[1] * mat[3] + mat[5], -+ &tx, &ty); -+ tileXMin = tileXMax = tx; -+ tileYMin = tileYMax = ty; -+ state->transform(bbox[2] * mat[0] + bbox[1] * mat[2] + mat[4], -+ bbox[2] * mat[1] + bbox[1] * mat[3] + mat[5], -+ &tx, &ty); -+ if (tx < tileXMin) { -+ tileXMin = tx; -+ } else if (tx > tileXMax) { -+ tileXMax = tx; -+ } -+ if (ty < tileYMin) { -+ tileYMin = ty; -+ } else if (ty > tileYMax) { -+ tileYMax = ty; -+ } -+ state->transform(bbox[2] * mat[0] + bbox[3] * mat[2] + mat[4], -+ bbox[2] * mat[1] + bbox[3] * mat[3] + mat[5], -+ &tx, &ty); -+ if (tx < tileXMin) { -+ tileXMin = tx; -+ } else if (tx > tileXMax) { -+ tileXMax = tx; -+ } -+ if (ty < tileYMin) { -+ tileYMin = ty; -+ } else if (ty > tileYMax) { -+ tileYMax = ty; -+ } -+ state->transform(bbox[0] * mat[0] + bbox[3] * mat[2] + mat[4], -+ bbox[0] * mat[1] + bbox[3] * mat[3] + mat[5], -+ &tx, &ty); -+ if (tx < tileXMin) { -+ tileXMin = tx; -+ } else if (tx > tileXMax) { -+ tileXMax = tx; -+ } -+ if (ty < tileYMin) { -+ tileYMin = ty; -+ } else if (ty > tileYMax) { -+ tileYMax = ty; -+ } -+ if (tileXMin == tileXMax || tileYMin == tileYMax) { -+ return; -+ } -+ -+ tileX0 = (int)floor(tileXMin); -+ tileY0 = (int)floor(tileYMin); -+ tileW = (int)ceil(tileXMax) - tileX0; -+ tileH = (int)ceil(tileYMax) - tileY0; -+ -+ // check for an excessively large tile size -+ tileSize = tileW * tileH; -+ if (tileSize > 1000000 || tileSize < 0) { -+ mat1[0] = mat[0]; -+ mat1[1] = mat[1]; -+ mat1[2] = mat[2]; -+ mat1[3] = mat[3]; -+ for (y = y0; y < y1; ++y) { -+ for (x = x0; x < x1; ++x) { -+ xa = x * xStep; -+ ya = y * yStep; -+ mat1[4] = xa * mat[0] + ya * mat[2] + mat[4]; -+ mat1[5] = xa * mat[1] + ya * mat[3] + mat[5]; -+ gfx->drawForm(str, resDict, mat1, bbox); -+ } -+ } -+ return; -+ } -+ -+ // create a temporary bitmap -+ origBitmap = bitmap; -+ origSplash = splash; -+ bitmap = tileBitmap = new SplashBitmap(tileW, tileH, bitmapRowPad, -+ colorMode, gTrue, bitmapTopDown); -+ splash = new Splash(bitmap, vectorAntialias, origSplash->getScreen()); -+ splash->setMinLineWidth(globalParams->getMinLineWidth()); -+ for (i = 0; i < splashMaxColorComps; ++i) { -+ color[i] = 0; -+ } -+ splash->clear(color); -+ ++nestCount; -+ -+ // copy the fill color (for uncolored tiling patterns) -+ // (and stroke color, to handle buggy PDF files) -+ splash->setFillPattern(origSplash->getFillPattern()->copy()); -+ splash->setStrokePattern(origSplash->getStrokePattern()->copy()); -+ -+ // render the tile -+ state->shiftCTM(-tileX0, -tileY0); -+ updateCTM(state, 0, 0, 0, 0, 0, 0); -+ gfx->drawForm(str, resDict, mat, bbox); -+ state->shiftCTM(tileX0, tileY0); -+ updateCTM(state, 0, 0, 0, 0, 0, 0); -+ -+ // restore the original bitmap -+ --nestCount; -+ delete splash; -+ bitmap = origBitmap; -+ splash = origSplash; -+ splash->setOverprintMask(0xffffffff); -+ -+ // draw the tiles -+ for (y = y0; y < y1; ++y) { -+ for (x = x0; x < x1; ++x) { -+ xa = x * xStep; -+ ya = y * yStep; -+ xb = xa * mat[0] + ya * mat[2]; -+ yb = xa * mat[1] + ya * mat[3]; -+ state->transformDelta(xb, yb, &xc, &yc); -+ xx = (int)(xc + tileX0 + 0.5); -+ yy = (int)(yc + tileY0 + 0.5); -+ splash->composite(tileBitmap, 0, 0, xx, yy, tileW, tileH, -+ gFalse, gFalse); -+ } -+ } -+ -+ delete tileBitmap; -+} -+ - void SplashOutputDev::clip(GfxState *state) { - SplashPath *path; - -@@ -1375,6 +1655,18 @@ - Unicode *u, int uLen) { - SplashPath *path; - int render; -+ GBool doFill, doStroke, doClip, strokeAdjust; -+ double m[4]; -+ GBool horiz; -+ -+ if (skipHorizText || skipRotatedText) { -+ state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); -+ horiz = m[0] > 0 && fabs(m[1]) < 0.001 && -+ fabs(m[2]) < 0.001 && m[3] < 0; -+ if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { -+ return; -+ } -+ } - - // check for invisible text -- this is used by Acrobat Capture - render = state->getRender(); -@@ -1392,36 +1684,76 @@ - x -= originX; - y -= originY; - -- // fill -- if (!(render & 1)) { -- if (!state->getFillColorSpace()->isNonMarking()) { -- splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font); -+ doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking(); -+ doStroke = ((render & 3) == 1 || (render & 3) == 2) && -+ !state->getStrokeColorSpace()->isNonMarking(); -+ doClip = render & 4; -+ -+ path = NULL; -+ if (doStroke || doClip) { -+ if ((path = font->getGlyphPath(code))) { -+ path->offset((SplashCoord)x, (SplashCoord)y); - } - } - -+ // don't use stroke adjustment when stroking text -- the results -+ // tend to be ugly (because characters with horizontal upper or -+ // lower edges get misaligned relative to the other characters) -+ strokeAdjust = gFalse; // make gcc happy -+ if (doStroke) { -+ strokeAdjust = splash->getStrokeAdjust(); -+ splash->setStrokeAdjust(gFalse); -+ } -+ -+ // fill and stroke -+ if (doFill && doStroke) { -+ if (path) { -+ setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), -+ state->getOverprintMode(), state->getFillColor()); -+ splash->fill(path, gFalse); -+ setOverprintMask(state->getStrokeColorSpace(), -+ state->getStrokeOverprint(), -+ state->getOverprintMode(), -+ state->getStrokeColor()); -+ splash->stroke(path); -+ } -+ -+ // fill -+ } else if (doFill) { -+ setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), -+ state->getOverprintMode(), state->getFillColor()); -+ splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font); -+ - // stroke -- if ((render & 3) == 1 || (render & 3) == 2) { -- if (!state->getStrokeColorSpace()->isNonMarking()) { -- if ((path = font->getGlyphPath(code))) { -- path->offset((SplashCoord)x, (SplashCoord)y); -- splash->stroke(path); -- delete path; -- } -+ } else if (doStroke) { -+ if (path) { -+ setOverprintMask(state->getStrokeColorSpace(), -+ state->getStrokeOverprint(), -+ state->getOverprintMode(), -+ state->getStrokeColor()); -+ splash->stroke(path); - } - } - - // clip -- if (render & 4) { -- if ((path = font->getGlyphPath(code))) { -- path->offset((SplashCoord)x, (SplashCoord)y); -+ if (doClip) { -+ if (path) { - if (textClipPath) { - textClipPath->append(path); -- delete path; - } else { - textClipPath = path; -+ path = NULL; - } - } - } -+ -+ if (doStroke) { -+ splash->setStrokeAdjust(strokeAdjust); -+ } -+ -+ if (path) { -+ delete path; -+ } - } - - GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, -@@ -1433,9 +1765,20 @@ - T3FontCache *t3Font; - T3GlyphStack *t3gs; - GBool validBBox; -+ double m[4]; -+ GBool horiz; - double x1, y1, xMin, yMin, xMax, yMax, xt, yt; - int i, j; - -+ if (skipHorizText || skipRotatedText) { -+ state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); -+ horiz = m[0] > 0 && fabs(m[1]) < 0.001 && -+ fabs(m[2]) < 0.001 && m[3] < 0; -+ if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { -+ return gTrue; -+ } -+ } -+ - if (!(gfxFont = state->getFont())) { - return gFalse; - } -@@ -1517,10 +1860,10 @@ - validBBox = gTrue; - } - t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3], -- (int)floor(xMin - xt), -- (int)floor(yMin - yt), -- (int)ceil(xMax) - (int)floor(xMin) + 3, -- (int)ceil(yMax) - (int)floor(yMin) + 3, -+ (int)floor(xMin - xt) - 2, -+ (int)floor(yMin - yt) - 2, -+ (int)ceil(xMax) - (int)floor(xMin) + 4, -+ (int)ceil(yMax) - (int)floor(yMin) + 4, - validBBox, - colorMode != splashModeMono1); - } -@@ -1547,6 +1890,8 @@ - t3GlyphStack->cacheTag = NULL; - t3GlyphStack->cacheData = NULL; - -+ haveT3Dx = gFalse; -+ - return gFalse; - } - -@@ -1555,6 +1900,7 @@ - double *ctm; - - if (t3GlyphStack->cacheTag) { -+ --nestCount; - memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(), - t3GlyphStack->cache->glyphSize); - delete bitmap; -@@ -1574,6 +1920,7 @@ - } - - void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) { -+ haveT3Dx = gTrue; - } - - void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, -@@ -1584,6 +1931,12 @@ - double xt, yt, xMin, xMax, yMin, yMax, x1, y1; - int i, j; - -+ // ignore multiple d0/d1 operators -+ if (haveT3Dx) { -+ return; -+ } -+ haveT3Dx = gTrue; -+ - t3Font = t3GlyphStack->cache; - - // check for a valid bbox -@@ -1662,7 +2015,7 @@ - t3GlyphStack->origSplash->getScreen()); - color[0] = 0; - splash->clear(color); -- color[0] = 1; -+ color[0] = 0xff; - } else { - bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, - splashModeMono8, gFalse); -@@ -1672,18 +2025,23 @@ - splash->clear(color); - color[0] = 0xff; - } -+ splash->setMinLineWidth(globalParams->getMinLineWidth()); - splash->setFillPattern(new SplashSolidColor(color)); - splash->setStrokePattern(new SplashSolidColor(color)); - //~ this should copy other state from t3GlyphStack->origSplash? -+ //~ [this is likely the same situation as in beginTransparencyGroup()] - state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], - -t3Font->glyphX, -t3Font->glyphY); - updateCTM(state, 0, 0, 0, 0, 0, 0); -+ ++nestCount; - } - -@@ -1717,9 +2075,10 @@ - if (imgMaskData->y == imgMaskData->height) { - return gFalse; - } -- for (x = 0, p = imgMaskData->imgStr->getLine(), q = line; -- x < imgMaskData->width; -- ++x) { -+ if (!(p = imgMaskData->imgStr->getLine())) { -+ return gFalse; -+ } -+ for (x = 0, q = line; x < imgMaskData->width; ++x) { - *q++ = *p++ ^ imgMaskData->invert; - } - ++imgMaskData->y; -@@ -1736,6 +2095,8 @@ - if (state->getFillColorSpace()->isNonMarking()) { - return; - } -+ setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), -+ state->getOverprintMode(), state->getFillColor()); - - ctm = state->getCTM(); - mat[0] = ctm[0]; -@@ -1765,6 +2126,46 @@ - str->close(); - } - -+void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, -+ Object *ref, Stream *str, -+ int width, int height, -+ GBool invert, -+ GBool inlineImg) { -+ double *ctm; -+ SplashCoord mat[6]; -+ SplashOutImageMaskData imgMaskData; -+ SplashBitmap *maskBitmap; -+ Splash *maskSplash; -+ SplashColor maskColor; -+ -+ ctm = state->getCTM(); -+ mat[0] = ctm[0]; -+ mat[1] = ctm[1]; -+ mat[2] = -ctm[2]; -+ mat[3] = -ctm[3]; -+ mat[4] = ctm[2] + ctm[4]; -+ mat[5] = ctm[3] + ctm[5]; -+ imgMaskData.imgStr = new ImageStream(str, width, 1, 1); -+ imgMaskData.imgStr->reset(); -+ imgMaskData.invert = invert ? 0 : 1; -+ imgMaskData.width = width; -+ imgMaskData.height = height; -+ imgMaskData.y = 0; -+ maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), -+ 1, splashModeMono8, gFalse); -+ maskSplash = new Splash(maskBitmap, gTrue); -+ maskColor[0] = 0; -+ maskSplash->clear(maskColor); -+ maskColor[0] = 0xff; -+ maskSplash->setFillPattern(new SplashSolidColor(maskColor)); -+ maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, -+ width, height, mat, gFalse); -+ delete imgMaskData.imgStr; -+ str->close(); -+ delete maskSplash; -+ splash->setSoftMask(maskBitmap); -+} -+ - struct SplashOutImageData { - ImageStream *imgStr; - GfxImageColorMap *colorMap; -@@ -1789,6 +2190,9 @@ - if (imgData->y == imgData->height) { - return gFalse; - } -+ if (!(p = imgData->imgStr->getLine())) { -+ return gFalse; -+ } - - nComps = imgData->colorMap->getNumPixelComps(); - -@@ -1796,17 +2200,13 @@ - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: -- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine; -- x < imgData->width; -- ++x, ++p) { -+ for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { - *q++ = imgData->lookup[*p]; - } - break; - case splashModeRGB8: - case splashModeBGR8: -- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine; -- x < imgData->width; -- ++x, ++p) { -+ for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { - col = &imgData->lookup[3 * *p]; - *q++ = col[0]; - *q++ = col[1]; -@@ -1815,9 +2215,7 @@ - break; - #if SPLASH_CMYK - case splashModeCMYK8: -- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine; -- x < imgData->width; -- ++x, ++p) { -+ for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { - col = &imgData->lookup[4 * *p]; - *q++ = col[0]; - *q++ = col[1]; -@@ -1831,18 +2229,14 @@ - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: -- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine; -- x < imgData->width; -- ++x, p += nComps) { -+ for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getGray(p, &gray); - *q++ = colToByte(gray); - } - break; - case splashModeRGB8: - case splashModeBGR8: -- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine; -- x < imgData->width; -- ++x, p += nComps) { -+ for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getRGB(p, &rgb); - *q++ = colToByte(rgb.r); - *q++ = colToByte(rgb.g); -@@ -1851,9 +2245,7 @@ - break; - #if SPLASH_CMYK - case splashModeCMYK8: -- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine; -- x < imgData->width; -- ++x, p += nComps) { -+ for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { - imgData->colorMap->getCMYK(p, &cmyk); - *q++ = colToByte(cmyk.c); - *q++ = colToByte(cmyk.m); -@@ -1885,10 +2277,13 @@ - if (imgData->y == imgData->height) { - return gFalse; - } -+ if (!(p = imgData->imgStr->getLine())) { -+ return gFalse; -+ } - - nComps = imgData->colorMap->getNumPixelComps(); - -- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, aq = alphaLine; -+ for (x = 0, q = colorLine, aq = alphaLine; - x < imgData->width; - ++x, p += nComps) { - alpha = 0; -@@ -1904,7 +2299,6 @@ - case splashModeMono1: - case splashModeMono8: - *q++ = imgData->lookup[*p]; -- *aq++ = alpha; - break; - case splashModeRGB8: - case splashModeBGR8: -@@ -1912,7 +2306,6 @@ - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; -- *aq++ = alpha; - break; - #if SPLASH_CMYK - case splashModeCMYK8: -@@ -1921,17 +2314,16 @@ - *q++ = col[1]; - *q++ = col[2]; - *q++ = col[3]; -- *aq++ = alpha; - break; - #endif - } -+ *aq++ = alpha; - } else { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData->colorMap->getGray(p, &gray); - *q++ = colToByte(gray); -- *aq++ = alpha; - break; - case splashModeRGB8: - case splashModeBGR8: -@@ -1939,7 +2331,6 @@ - *q++ = colToByte(rgb.r); - *q++ = colToByte(rgb.g); - *q++ = colToByte(rgb.b); -- *aq++ = alpha; - break; - #if SPLASH_CMYK - case splashModeCMYK8: -@@ -1948,10 +2339,10 @@ - *q++ = colToByte(cmyk.m); - *q++ = colToByte(cmyk.y); - *q++ = colToByte(cmyk.k); -- *aq++ = alpha; - break; - #endif - } -+ *aq++ = alpha; - } - } - -@@ -1976,6 +2367,9 @@ - Guchar pix; - int n, i; - -+ setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), -+ state->getOverprintMode(), NULL); -+ - ctm = state->getCTM(); - mat[0] = ctm[0]; - mat[1] = ctm[1]; -@@ -2012,7 +2406,7 @@ - break; - case splashModeRGB8: - case splashModeBGR8: -- imgData.lookup = (SplashColorPtr)gmalloc(3 * n); -+ imgData.lookup = (SplashColorPtr)gmallocn(n, 3); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getRGB(&pix, &rgb); -@@ -2023,7 +2417,7 @@ - break; - #if SPLASH_CMYK - case splashModeCMYK8: -- imgData.lookup = (SplashColorPtr)gmalloc(4 * n); -+ imgData.lookup = (SplashColorPtr)gmallocn(n, 4); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - colorMap->getCMYK(&pix, &cmyk); -@@ -2034,7 +2428,6 @@ - } - break; - #endif -- break; - } - } - -@@ -2071,7 +2464,6 @@ - Guchar *alphaLine) { - SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data; - Guchar *p, *aq; -- SplashColor maskColor; - SplashColorPtr q, col; - GfxRGB rgb; - GfxGray gray; -@@ -2079,25 +2471,35 @@ - GfxCMYK cmyk; - #endif - Guchar alpha; -+ Guchar *maskPtr; -+ int maskBit; - int nComps, x; - - if (imgData->y == imgData->height) { - return gFalse; - } -+ if (!(p = imgData->imgStr->getLine())) { -+ return gFalse; -+ } - - nComps = imgData->colorMap->getNumPixelComps(); - -- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, aq = alphaLine; -+ maskPtr = imgData->mask->getDataPtr() + -+ imgData->y * imgData->mask->getRowSize(); -+ maskBit = 0x80; -+ for (x = 0, q = colorLine, aq = alphaLine; - x < imgData->width; - ++x, p += nComps) { -- imgData->mask->getPixel(x, imgData->y, maskColor); -- alpha = maskColor[0] ? 0xff : 0x00; -+ alpha = (*maskPtr & maskBit) ? 0xff : 0x00; -+ if (!(maskBit >>= 1)) { -+ ++maskPtr; -+ maskBit = 0x80; -+ } - if (imgData->lookup) { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - *q++ = imgData->lookup[*p]; -- *aq++ = alpha; - break; - case splashModeRGB8: - case splashModeBGR8: -@@ -2105,7 +2507,6 @@ - *q++ = col[0]; - *q++ = col[1]; - *q++ = col[2]; -- *aq++ = alpha; - break; - #if SPLASH_CMYK - case splashModeCMYK8: -@@ -2114,17 +2515,16 @@ - *q++ = col[1]; - *q++ = col[2]; - *q++ = col[3]; -- *aq++ = alpha; - break; - #endif - } -+ *aq++ = alpha; - } else { - switch (imgData->colorMode) { - case splashModeMono1: - case splashModeMono8: - imgData->colorMap->getGray(p, &gray); - *q++ = colToByte(gray); -- *aq++ = alpha; - break; - case splashModeRGB8: - case splashModeBGR8: -@@ -2132,7 +2532,6 @@ - *q++ = colToByte(rgb.r); - *q++ = colToByte(rgb.g); - *q++ = colToByte(rgb.b); -- *aq++ = alpha; - break; - #if SPLASH_CMYK - case splashModeCMYK8: -@@ -2141,10 +2540,10 @@ - *q++ = colToByte(cmyk.m); - *q++ = colToByte(cmyk.y); - *q++ = colToByte(cmyk.k); -- *aq++ = alpha; - break; - #endif - } -+ *aq++ = alpha; - } - } - -@@ -2175,6 +2574,9 @@ - Guchar pix; - int n, i; - -+ setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), -+ state->getOverprintMode(), NULL); -+ - // If the mask is higher resolution than the image, use - // drawSoftMaskedImage() instead. - if (maskWidth > width || maskHeight > height) { -@@ -2318,6 +2720,9 @@ - Guchar pix; - int n, i; - -+ setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), -+ state->getOverprintMode(), NULL); -+ - ctm = state->getCTM(); - mat[0] = ctm[0]; - mat[1] = ctm[1]; -@@ -2433,7 +2838,7 @@ - SplashTransparencyGroup *transpGroup; - SplashColor color; - double xMin, yMin, xMax, yMax, x, y; -- int tx, ty, w, h; -+ int tx, ty, w, h, i; - - // transform the bbox - state->transform(bbox[0], bbox[1], &x, &y); -@@ -2475,14 +2880,14 @@ - tx = (int)floor(xMin); - if (tx < 0) { - tx = 0; -- } else if (tx > bitmap->getWidth()) { -- tx = bitmap->getWidth(); -+ } else if (tx >= bitmap->getWidth()) { -+ tx = bitmap->getWidth() - 1; - } - ty = (int)floor(yMin); - if (ty < 0) { - ty = 0; -- } else if (ty > bitmap->getHeight()) { -- ty = bitmap->getHeight(); -+ } else if (ty >= bitmap->getHeight()) { -+ ty = bitmap->getHeight() - 1; - } - w = (int)ceil(xMax) - tx + 1; - if (tx + w > bitmap->getWidth()) { -@@ -2512,31 +2917,47 @@ - transpGroup->origBitmap = bitmap; - transpGroup->origSplash = splash; - -- //~ this ignores the blendingColorSpace arg -+ //~ this handles the blendingColorSpace arg for soft masks, but -+ //~ not yet for transparency groups -+ -+ // switch to the blending color space -+ if (forSoftMask && isolated && blendingColorSpace) { -+ if (blendingColorSpace->getMode() == csDeviceGray || -+ blendingColorSpace->getMode() == csCalGray || -+ (blendingColorSpace->getMode() == csICCBased && -+ blendingColorSpace->getNComps() == 1)) { -+ colorMode = splashModeMono8; -+ } else if (blendingColorSpace->getMode() == csDeviceRGB || -+ blendingColorSpace->getMode() == csCalRGB || -+ (blendingColorSpace->getMode() == csICCBased && -+ blendingColorSpace->getNComps() == 3)) { -+ //~ does this need to use BGR8? -+ colorMode = splashModeRGB8; -+#if SPLASH_CMYK -+ } else if (blendingColorSpace->getMode() == csDeviceCMYK || -+ (blendingColorSpace->getMode() == csICCBased && -+ blendingColorSpace->getNComps() == 4)) { -+ colorMode = splashModeCMYK8; -+#endif -+ } -+ } - - // create the temporary bitmap - bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue, - bitmapTopDown); - splash = new Splash(bitmap, vectorAntialias, - transpGroup->origSplash->getScreen()); -+ splash->setMinLineWidth(globalParams->getMinLineWidth()); -+ //~ Acrobat apparently copies at least the fill and stroke colors, and -+ //~ maybe other state(?) -- but not the clipping path (and not sure -+ //~ what else) -+ //~ [this is likely the same situation as in type3D1()] -+ splash->setFillPattern(transpGroup->origSplash->getFillPattern()->copy()); -+ splash->setStrokePattern( -+ transpGroup->origSplash->getStrokePattern()->copy()); - if (isolated) { -- switch (colorMode) { -- case splashModeMono1: -- case splashModeMono8: -- color[0] = 0; -- break; -- case splashModeRGB8: -- case splashModeBGR8: -- color[0] = color[1] = color[2] = 0; -- break; --#if SPLASH_CMYK -- case splashModeCMYK8: -- color[0] = color[1] = color[2] = color[3] = 0; -- break; --#endif -- default: -- // make gcc happy -- break; -+ for (i = 0; i < splashMaxColorComps; ++i) { -+ color[i] = 0; - } - splash->clear(color, 0); - } else { -@@ -2546,16 +2967,16 @@ - transpGroup->tBitmap = bitmap; - state->shiftCTM(-tx, -ty); - updateCTM(state, 0, 0, 0, 0, 0, 0); -+ ++nestCount; - } - - void SplashOutputDev::endTransparencyGroup(GfxState *state) { -- double *ctm; -- - // restore state -+ --nestCount; - delete splash; - bitmap = transpGroupStack->origBitmap; -+ colorMode = bitmap->getMode(); - splash = transpGroupStack->origSplash; -- ctm = state->getCTM(); - state->shiftCTM(transpGroupStack->tx, transpGroupStack->ty); - updateCTM(state, 0, 0, 0, 0, 0, 0); - } -@@ -2573,9 +2994,12 @@ - - // paint the transparency group onto the parent bitmap - // - the clip path was set in the parent's state) -- splash->composite(tBitmap, 0, 0, tx, ty, -- tBitmap->getWidth(), tBitmap->getHeight(), -- gFalse, !isolated); -+ if (tx < bitmap->getWidth() && ty < bitmap->getHeight()) { -+ splash->setOverprintMask(0xffffffff); -+ splash->composite(tBitmap, 0, 0, tx, ty, -+ tBitmap->getWidth(), tBitmap->getHeight(), -+ gFalse, !isolated); -+ } - - // pop the stack - transpGroup = transpGroupStack; -@@ -2606,13 +3030,13 @@ - tBitmap = transpGroupStack->tBitmap; - - // composite with backdrop color -- if (!alpha && colorMode != splashModeMono1) { -+ if (!alpha && tBitmap->getMode() != splashModeMono1) { - //~ need to correctly handle the case where no blending color - //~ space is given - tSplash = new Splash(tBitmap, vectorAntialias, - transpGroupStack->origSplash->getScreen()); - if (transpGroupStack->blendingColorSpace) { -- switch (colorMode) { -+ switch (tBitmap->getMode()) { - case splashModeMono1: - // transparency is not supported in mono1 mode - break; -@@ -2648,36 +3072,38 @@ - 1, splashModeMono8, gFalse); - memset(softMask->getDataPtr(), 0, - softMask->getRowSize() * softMask->getHeight()); -- p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx; -- for (y = 0; y < tBitmap->getHeight(); ++y) { -- for (x = 0; x < tBitmap->getWidth(); ++x) { -- tBitmap->getPixel(x, y, color); -- if (alpha) { -- //~ unimplemented -- } else { -- // convert to luminosity -- switch (colorMode) { -- case splashModeMono1: -- case splashModeMono8: -- lum = color[0] / 255.0; -- break; -- case splashModeRGB8: -- case splashModeBGR8: -- lum = (0.3 / 255.0) * color[0] + -- (0.59 / 255.0) * color[1] + -- (0.11 / 255.0) * color[2]; -- break; --#if SPLASH_CMYK -- case splashModeCMYK8: -- lum = (1 - color[4] / 255.0) -- - (0.3 / 255.0) * color[0] -- - (0.59 / 255.0) * color[1] -- - (0.11 / 255.0) * color[2]; -- if (lum < 0) { -- lum = 0; -- } -- break; -+ if (tx < softMask->getWidth() && ty < softMask->getHeight()) { -+ p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx; -+ for (y = 0; y < tBitmap->getHeight(); ++y) { -+ for (x = 0; x < tBitmap->getWidth(); ++x) { -+ if (alpha) { -+ lum = tBitmap->getAlpha(x, y) / 255.0; -+ } else { -+ tBitmap->getPixel(x, y, color); -+ // convert to luminosity -+ switch (tBitmap->getMode()) { -+ case splashModeMono1: -+ case splashModeMono8: -+ lum = color[0] / 255.0; -+ break; -+ case splashModeRGB8: -+ case splashModeBGR8: -+ lum = (0.3 / 255.0) * color[0] + -+ (0.59 / 255.0) * color[1] + -+ (0.11 / 255.0) * color[2]; -+ break; -+#if SPLASH_CMYK -+ case splashModeCMYK8: -+ lum = (1 - color[3] / 255.0) -+ - (0.3 / 255.0) * color[0] -+ - (0.59 / 255.0) * color[1] -+ - (0.11 / 255.0) * color[2]; -+ if (lum < 0) { -+ lum = 0; -+ } -+ break; - #endif -+ } - } - if (transferFunc) { - transferFunc->transform(&lum, &lum2); -@@ -2686,8 +3112,8 @@ - } - p[x] = (int)(lum2 * 255.0 + 0.5); - } -+ p += softMask->getRowSize(); - } -- p += softMask->getRowSize(); - } - splash->setSoftMask(softMask); - -@@ -2743,39 +3169,49 @@ - rgb.r = byteToCol(r); - rgb.g = byteToCol(g); - rgb.b = byteToCol(b); -- gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5); -- if (gray > gfxColorComp1) { -- gray = gfxColorComp1; -- } -+ switch (colorMode) { -+ case splashModeMono1: -+ case splashModeMono8: -+ gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5); -+ if (gray > gfxColorComp1) { -+ gray = gfxColorComp1; -+ } -+ splash->setFillPattern(getColor(gray)); -+ break; -+ case splashModeRGB8: -+ case splashModeBGR8: -+ splash->setFillPattern(getColor(&rgb)); -+ break; - #if SPLASH_CMYK -- cmyk.c = gfxColorComp1 - rgb.r; -- cmyk.m = gfxColorComp1 - rgb.g; -- cmyk.y = gfxColorComp1 - rgb.b; -- cmyk.k = 0; -- splash->setFillPattern(getColor(gray, &rgb, &cmyk)); --#else -- splash->setFillPattern(getColor(gray, &rgb)); -+ case splashModeCMYK8: -+ cmyk.c = gfxColorComp1 - rgb.r; -+ cmyk.m = gfxColorComp1 - rgb.g; -+ cmyk.y = gfxColorComp1 - rgb.b; -+ cmyk.k = 0; -+ splash->setFillPattern(getColor(&cmyk)); -+ break; - #endif -+ } - } - --SplashFont *SplashOutputDev::getFont(GString *name, double *textMatA) { -- DisplayFontParam *dfp; -+SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) { - Ref ref; - SplashOutFontFileID *id; -+ GfxFontLoc *fontLoc; - SplashFontFile *fontFile; - SplashFont *fontObj; - FoFiTrueType *ff; -- Gushort *codeToGID; -+ int *codeToGID; - Unicode u; - SplashCoord textMat[4]; - int cmap, i; - -- for (i = 0; i < 16; ++i) { -- if (!name->cmp(splashOutSubstFonts[i].name)) { -+ for (i = 0; i < nBuiltinFonts; ++i) { -+ if (!name->cmp(builtinFonts[i].name)) { - break; - } - } -- if (i == 16) { -+ if (i == nBuiltinFonts) { - return NULL; - } - ref.num = i; -@@ -2788,12 +3224,16 @@ - - // load the font file - } else { -- dfp = globalParams->getDisplayFont(name); -- if (dfp && dfp->kind == displayFontT1) { -- fontFile = fontEngine->loadType1Font(id, dfp->t1.fileName->getCString(), -+ if (!(fontLoc = GfxFont::locateBase14Font(name))) { -+ return NULL; -+ } -+ if (fontLoc->fontType == fontType1) { -+ fontFile = fontEngine->loadType1Font(id, fontLoc->path->getCString(), - gFalse, winAnsiEncoding); -- } else if (dfp && dfp->kind == displayFontTT) { -- if (!(ff = FoFiTrueType::load(dfp->tt.fileName->getCString()))) { -+ } else if (fontLoc->fontType == fontTrueType) { -+ if (!(ff = FoFiTrueType::load(fontLoc->path->getCString()))) { -+ delete fontLoc; -+ delete id; - return NULL; - } - for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) { -@@ -2805,9 +3245,11 @@ - } - if (cmap == ff->getNumCmaps()) { - delete ff; -+ delete fontLoc; -+ delete id; - return NULL; - } -- codeToGID = (Gushort *)gmallocn(256, sizeof(Gushort)); -+ codeToGID = (int *)gmallocn(256, sizeof(int)); - for (i = 0; i < 256; ++i) { - codeToGID[i] = 0; - if (winAnsiEncoding[i] && -@@ -2817,11 +3259,18 @@ - } - delete ff; - fontFile = fontEngine->loadTrueTypeFont(id, -- dfp->tt.fileName->getCString(), -- gFalse, codeToGID, 256); -+ fontLoc->path->getCString(), -+ fontLoc->fontNum, -+ gFalse, codeToGID, 256, NULL); - } else { -+ delete fontLoc; -+ delete id; - return NULL; - } -+ delete fontLoc; -+ } -+ if (!fontFile) { -+ return NULL; - } - - // create the scaled font -@@ -2835,11 +3284,7 @@ - } - - #if 1 //~tmp: turn off anti-aliasing temporarily --GBool SplashOutputDev::getVectorAntialias() { -- return splash->getVectorAntialias(); --} -- --void SplashOutputDev::setVectorAntialias(GBool vaa) { -- splash->setVectorAntialias(vaa); -+void SplashOutputDev::setInShading(GBool sh) { -+ splash->setInShading(sh); - } - #endif -diff -ru xpdf-3.02/xpdf/SplashOutputDev.h xpdf-3.03/xpdf/SplashOutputDev.h ---- xpdf-3.02/xpdf/SplashOutputDev.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/SplashOutputDev.h 2011-08-15 23:08:53.000000000 +0200 -@@ -58,15 +58,21 @@ - - // Does this device use upside-down coordinates? - // (Upside-down means (0,0) is the top left corner of the page.) -- virtual GBool upsideDown() { return gTrue; } -+ virtual GBool upsideDown() { return bitmapTopDown ^ bitmapUpsideDown; } - - // Does this device use drawChar() or drawString()? - virtual GBool useDrawChar() { return gTrue; } - -+ // Does this device use tilingPatternFill()? If this returns false, -+ // tiling pattern fills will be reduced to a series of other drawing -+ // operations. -+ virtual GBool useTilingPatternFill() { return gTrue; } -+ - // Does this device use beginType3Char/endType3Char? Otherwise, - // text in Type 3 fonts will be drawn with drawChar/drawString. - virtual GBool interpretType3Chars() { return gTrue; } - -+ - //----- initialization and control - - // Start a page. -@@ -103,6 +110,11 @@ - virtual void stroke(GfxState *state); - virtual void fill(GfxState *state); - virtual void eoFill(GfxState *state); -+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str, -+ int paintType, Dict *resDict, -+ double *mat, double *bbox, -+ int x0, int y0, int x1, int y1, -+ double xStep, double yStep); - - //----- path clipping - virtual void clip(GfxState *state); -@@ -124,6 +136,10 @@ - virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, - GBool inlineImg); -+ virtual void setSoftMaskFromImageMask(GfxState *state, -+ Object *ref, Stream *str, -+ int width, int height, GBool invert, -+ GBool inlineImg); - virtual void drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, - int *maskColors, GBool inlineImg); -@@ -174,6 +190,10 @@ - // caller. - SplashBitmap *takeBitmap(); - -+ // Set this flag to true to generate an upside-down bitmap (useful -+ // for Windows BMP files). -+ void setBitmapUpsideDown(GBool f) { bitmapUpsideDown = f; } -+ - // Get the Splash object. - Splash *getSplash() { return splash; } - -@@ -187,26 +207,36 @@ - void setFillColor(int r, int g, int b); - - // Get a font object for a Base-14 font, using the Latin-1 encoding. -- SplashFont *getFont(GString *name, double *textMatA); -+ SplashFont *getFont(GString *name, SplashCoord *textMatA); - - SplashFont *getCurrentFont() { return font; } - -+ // If is true, don't draw horizontal text. -+ // If is true, don't draw rotated (non-horizontal) text. -+ void setSkipText(GBool skipHorizTextA, GBool skipRotatedTextA) -+ { skipHorizText = skipHorizTextA; skipRotatedText = skipRotatedTextA; } -+ -+ int getNestCount() { return nestCount; } -+ -+ - #if 1 //~tmp: turn off anti-aliasing temporarily -- virtual GBool getVectorAntialias(); -- virtual void setVectorAntialias(GBool vaa); -+ virtual void setInShading(GBool sh); - #endif - - private: - - void setupScreenParams(double hDPI, double vDPI); -+ SplashPattern *getColor(GfxGray gray); -+ SplashPattern *getColor(GfxRGB *rgb); - #if SPLASH_CMYK -- SplashPattern *getColor(GfxGray gray, GfxRGB *rgb, GfxCMYK *cmyk); --#else -- SplashPattern *getColor(GfxGray gray, GfxRGB *rgb); -+ SplashPattern *getColor(GfxCMYK *cmyk); - #endif -@@ -219,11 +249,14 @@ - SplashColorMode colorMode; - int bitmapRowPad; - GBool bitmapTopDown; -+ GBool bitmapUpsideDown; - GBool allowAntialias; - GBool vectorAntialias; - GBool reverseVideo; // reverse video mode - SplashColor paperColor; // paper color - SplashScreenParams screenParams; -+ GBool skipHorizText; -+ GBool skipRotatedText; - - XRef *xref; // xref table for current document - -@@ -235,6 +268,7 @@ - t3FontCache[splashOutT3FontCacheSize]; - int nT3Fonts; // number of valid entries in t3FontCache - T3GlyphStack *t3GlyphStack; // Type 3 glyph context stack -+ GBool haveT3Dx; // set after seeing a d0/d1 operator - - SplashFont *font; // current font - GBool needFontUpdate; // set when the font needs to be updated -@@ -242,6 +276,8 @@ - - SplashTransparencyGroup * // transparency group stack - transpGroupStack; -+ -+ int nestCount; - }; - - #endif -diff -ru xpdf-3.02/xpdf/TextOutputDev.cc xpdf-3.03/xpdf/TextOutputDev.cc ---- xpdf-3.02/xpdf/TextOutputDev.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/TextOutputDev.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -545,7 +618,7 @@ - - // insert the new word - if (cursor && wordBaseIdx == cursorBaseIdx && -- word->primaryCmp(cursor) > 0) { -+ word->primaryCmp(cursor) >= 0) { - w0 = cursor; - w1 = cursor->next; - } else { -@@ -928,7 +1001,7 @@ - xMax = blk->xMin + d1 * (blk->xMax - blk->xMin); - yMin = blk->yMin + d2 * (blk->yMax - blk->yMin); - yMax = blk->yMin + d3 * (blk->yMax - blk->yMin); -- base = blk->yMin + base * (blk->yMax - blk->yMin); -+ base = blk->yMin + d4 * (blk->yMax - blk->yMin); - break; - case 1: - xMin = blk->xMax - d3 * (blk->xMax - blk->xMin); -@@ -1150,15 +1223,15 @@ - } - } - --void TextBlock::coalesce(UnicodeMap *uMap) { -+void TextBlock::coalesce(UnicodeMap *uMap, double fixedPitch) { - TextWord *word0, *word1, *word2, *bestWord0, *bestWord1, *lastWord; - TextLine *line, *line0, *line1; - int poolMinBaseIdx, startBaseIdx, minBaseIdx, maxBaseIdx; - int baseIdx, bestWordBaseIdx, idx0, idx1; - double minBase, maxBase; -- double fontSize, delta, priDelta, secDelta; -+ double fontSize, wordSpacing, delta, priDelta, secDelta; - TextLine **lineArray; -- GBool found; -+ GBool found, overlap; - int col1, col2; - int i, j, k; - -@@ -1168,11 +1241,7 @@ - while (word0) { - priDelta = dupMaxPriDelta * word0->fontSize; - secDelta = dupMaxSecDelta * word0->fontSize; -- if (rot == 0 || rot == 3) { -- maxBaseIdx = pool->getBaseIdx(word0->base + secDelta); -- } else { -- maxBaseIdx = pool->getBaseIdx(word0->base - secDelta); -- } -+ maxBaseIdx = pool->getBaseIdx(word0->base + secDelta); - found = gFalse; - word1 = word2 = NULL; // make gcc happy - for (idx1 = idx0; idx1 <= maxBaseIdx; ++idx1) { -@@ -1269,6 +1338,7 @@ - maxBase = word0->base + maxIntraLineDelta * fontSize; - minBaseIdx = pool->getBaseIdx(minBase); - maxBaseIdx = pool->getBaseIdx(maxBase); -+ wordSpacing = fixedPitch ? fixedPitch : maxWordSpacing * fontSize; - - // find the rest of the words in this line - while (1) { -@@ -1277,25 +1347,32 @@ - // this line - bestWordBaseIdx = 0; - bestWord0 = bestWord1 = NULL; -- for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) { -+ overlap = gFalse; -+ for (baseIdx = minBaseIdx; -+ !overlap && baseIdx <= maxBaseIdx; -+ ++baseIdx) { - for (word0 = NULL, word1 = pool->getPool(baseIdx); - word1; - word0 = word1, word1 = word1->next) { - if (word1->base >= minBase && -- word1->base <= maxBase && -- (delta = lastWord->primaryDelta(word1)) >= -- minCharSpacing * fontSize) { -- if (delta < maxWordSpacing * fontSize && -- (!bestWord1 || word1->primaryCmp(bestWord1) < 0)) { -- bestWordBaseIdx = baseIdx; -- bestWord0 = word0; -- bestWord1 = word1; -+ word1->base <= maxBase) { -+ delta = lastWord->primaryDelta(word1); -+ if (delta < minCharSpacing * fontSize) { -+ overlap = gTrue; -+ break; -+ } else { -+ if (delta < wordSpacing && -+ (!bestWord1 || word1->primaryCmp(bestWord1) < 0)) { -+ bestWordBaseIdx = baseIdx; -+ bestWord0 = word0; -+ bestWord1 = word1; -+ } -+ break; - } -- break; - } - } - } -- if (!bestWord1) { -+ if (overlap || !bestWord1) { - break; - } - -@@ -1342,52 +1419,79 @@ - - // column assignment - nColumns = 0; -- for (i = 0; i < nLines; ++i) { -- line0 = lineArray[i]; -- col1 = 0; -- for (j = 0; j < i; ++j) { -- line1 = lineArray[j]; -- if (line1->primaryDelta(line0) >= 0) { -- col2 = line1->col[line1->len] + 1; -- } else { -- k = 0; // make gcc happy -- switch (rot) { -- case 0: -- for (k = 0; -- k < line1->len && -- line0->xMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]); -- ++k) ; -- break; -- case 1: -- for (k = 0; -- k < line1->len && -- line0->yMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]); -- ++k) ; -- break; -- case 2: -- for (k = 0; -- k < line1->len && -- line0->xMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]); -- ++k) ; -- break; -- case 3: -- for (k = 0; -- k < line1->len && -- line0->yMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]); -- ++k) ; -- break; -- } -- col2 = line1->col[k]; -+ if (fixedPitch) { -+ for (i = 0; i < nLines; ++i) { -+ line0 = lineArray[i]; -+ col1 = 0; // make gcc happy -+ switch (rot) { -+ case 0: -+ col1 = (int)((line0->xMin - xMin) / fixedPitch + 0.5); -+ break; -+ case 1: -+ col1 = (int)((line0->yMin - yMin) / fixedPitch + 0.5); -+ break; -+ case 2: -+ col1 = (int)((xMax - line0->xMax) / fixedPitch + 0.5); -+ break; -+ case 3: -+ col1 = (int)((yMax - line0->yMax) / fixedPitch + 0.5); -+ break; - } -- if (col2 > col1) { -- col1 = col2; -+ for (k = 0; k <= line0->len; ++k) { -+ line0->col[k] += col1; -+ } -+ if (line0->col[line0->len] > nColumns) { -+ nColumns = line0->col[line0->len]; - } - } -- for (k = 0; k <= line0->len; ++k) { -- line0->col[k] += col1; -- } -- if (line0->col[line0->len] > nColumns) { -- nColumns = line0->col[line0->len]; -+ } else { -+ for (i = 0; i < nLines; ++i) { -+ line0 = lineArray[i]; -+ col1 = 0; -+ for (j = 0; j < i; ++j) { -+ line1 = lineArray[j]; -+ if (line1->primaryDelta(line0) >= 0) { -+ col2 = line1->col[line1->len] + 1; -+ } else { -+ k = 0; // make gcc happy -+ switch (rot) { -+ case 0: -+ for (k = 0; -+ k < line1->len && -+ line0->xMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]); -+ ++k) ; -+ break; -+ case 1: -+ for (k = 0; -+ k < line1->len && -+ line0->yMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]); -+ ++k) ; -+ break; -+ case 2: -+ for (k = 0; -+ k < line1->len && -+ line0->xMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]); -+ ++k) ; -+ break; -+ case 3: -+ for (k = 0; -+ k < line1->len && -+ line0->yMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]); -+ ++k) ; -+ break; -+ } -+ col2 = line1->col[k]; -+ } -+ if (col2 > col1) { -+ col1 = col2; -+ } -+ } -+ for (k = 0; k <= line0->len; ++k) { -+ line0->col[k] += col1; -+ } -+ if (line0->col[line0->len] > nColumns) { -+ nColumns = line0->col[line0->len]; -+ } - } - } - gfree(lineArray); -@@ -1744,6 +1848,9 @@ - nest = 0; - nTinyChars = 0; - lastCharOverlap = gFalse; -+ actualText = NULL; -+ actualTextLen = 0; -+ actualTextNBytes = 0; - if (!rawOrder) { - for (rot = 0; rot < 4; ++rot) { - pools[rot] = new TextPool(); -@@ -1799,6 +1906,7 @@ - delete curWord; - curWord = NULL; - } -+ gfree(actualText); - if (rawOrder) { - while (rawWords) { - word = rawWords; -@@ -1817,6 +1925,8 @@ - gfree(blocks); - } - deleteGList(fonts, TextFontInfo); -+ deleteGList(underlines, TextUnderline); -+ deleteGList(links, TextLink); - - curWord = NULL; - charPos = 0; -@@ -1824,6 +1934,9 @@ - curFontSize = 0; - nest = 0; - nTinyChars = 0; -+ actualText = NULL; -+ actualTextLen = 0; -+ actualTextNBytes = 0; - if (!rawOrder) { - for (rot = 0; rot < 4; ++rot) { - pools[rot] = new TextPool(); -@@ -1834,6 +1947,8 @@ - rawWords = NULL; - rawLastWord = NULL; - fonts = new GList(); -+ underlines = new GList(); -+ links = new GList(); - } - - void TextPage::updateFont(GfxState *state) { -@@ -1993,7 +2124,7 @@ - // (2) this character overlaps the previous one (duplicated text), or - // (3) the previous character was an overlap (we want each duplicated - // character to be in a word by itself at this stage), -- // (4) the font size has changed -+ // (4) the font or font size has changed - if (curWord && curWord->len > 0) { - base = sp = delta = 0; // make gcc happy - switch (curWord->rot) { -@@ -2024,6 +2155,7 @@ - sp < -minDupBreakOverlap * curWord->fontSize || - sp > minWordBreakSpace * curWord->fontSize || - fabs(base - curWord->base) > 0.5 || -+ curFont != curWord->font || - curFontSize != curWord->fontSize) { - endWord(); - } -@@ -2057,15 +2189,46 @@ - } - } - charPos += nBytes; - } - -+void TextPage::incCharCount(int nChars) { -+ charPos += nChars; -+} -+ -@@ -2109,7 +2272,7 @@ - links->append(new TextLink(xMin, yMin, xMax, yMax, link)); - } - --void TextPage::coalesce(GBool physLayout, GBool doHTML) { -+void TextPage::coalesce(GBool physLayout, double fixedPitch, GBool doHTML) { - UnicodeMap *uMap; - TextPool *pool; - TextWord *word0, *word1, *word2; -@@ -2139,7 +2302,7 @@ - blkList = NULL; - lastBlk = NULL; - nBlocks = 0; -- primaryRot = -1; -+ primaryRot = 0; - - #if 0 // for debugging - printf("*** initial words ***\n"); -@@ -2603,7 +2766,7 @@ - //~ addition to primary rotation - - // coalesce the block, and add it to the list -- blk->coalesce(uMap); -+ blk->coalesce(uMap, fixedPitch); - if (lastBlk) { - lastBlk->next = blk; - } else { -@@ -2611,11 +2774,12 @@ - } - lastBlk = blk; - count[rot] += blk->charCount; -- if (primaryRot < 0 || count[rot] > count[primaryRot]) { -- primaryRot = rot; -- } - ++nBlocks; - } -+ -+ if (count[rot] > count[primaryRot]) { -+ primaryRot = rot; -+ } - } - - #if 0 // for debugging -@@ -2674,76 +2838,108 @@ - - //----- column assignment - -- // sort blocks into xy order for column assignment -- blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *)); -- for (blk = blkList, i = 0; blk; blk = blk->next, ++i) { -- blocks[i] = blk; -- } -- qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpXYPrimaryRot); -+ if (physLayout && fixedPitch) { - -- // column assignment -- for (i = 0; i < nBlocks; ++i) { -- blk0 = blocks[i]; -- col1 = 0; -- for (j = 0; j < i; ++j) { -- blk1 = blocks[j]; -- col2 = 0; // make gcc happy -+ blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *)); -+ for (blk = blkList, i = 0; blk; blk = blk->next, ++i) { -+ blocks[i] = blk; -+ col1 = 0; // make gcc happy - switch (primaryRot) { - case 0: -- if (blk0->xMin > blk1->xMax) { -- col2 = blk1->col + blk1->nColumns + 3; -- } else if (blk1->xMax == blk1->xMin) { -- col2 = blk1->col; -- } else { -- col2 = blk1->col + (int)(((blk0->xMin - blk1->xMin) / -- (blk1->xMax - blk1->xMin)) * -- blk1->nColumns); -- } -+ col1 = (int)(blk->xMin / fixedPitch + 0.5); - break; - case 1: -- if (blk0->yMin > blk1->yMax) { -- col2 = blk1->col + blk1->nColumns + 3; -- } else if (blk1->yMax == blk1->yMin) { -- col2 = blk1->col; -- } else { -- col2 = blk1->col + (int)(((blk0->yMin - blk1->yMin) / -- (blk1->yMax - blk1->yMin)) * -- blk1->nColumns); -- } -+ col1 = (int)(blk->yMin / fixedPitch + 0.5); - break; - case 2: -- if (blk0->xMax < blk1->xMin) { -- col2 = blk1->col + blk1->nColumns + 3; -- } else if (blk1->xMin == blk1->xMax) { -- col2 = blk1->col; -- } else { -- col2 = blk1->col + (int)(((blk0->xMax - blk1->xMax) / -- (blk1->xMin - blk1->xMax)) * -- blk1->nColumns); -- } -+ col1 = (int)((pageWidth - blk->xMax) / fixedPitch + 0.5); - break; - case 3: -- if (blk0->yMax < blk1->yMin) { -- col2 = blk1->col + blk1->nColumns + 3; -- } else if (blk1->yMin == blk1->yMax) { -- col2 = blk1->col; -- } else { -- col2 = blk1->col + (int)(((blk0->yMax - blk1->yMax) / -- (blk1->yMin - blk1->yMax)) * -- blk1->nColumns); -- } -+ col1 = (int)((pageHeight - blk->yMax) / fixedPitch + 0.5); - break; - } -- if (col2 > col1) { -- col1 = col2; -+ blk->col = col1; -+ for (line = blk->lines; line; line = line->next) { -+ for (j = 0; j <= line->len; ++j) { -+ line->col[j] += col1; -+ } - } - } -- blk0->col = col1; -- for (line = blk0->lines; line; line = line->next) { -- for (j = 0; j <= line->len; ++j) { -- line->col[j] += col1; -+ -+ } else { -+ -+ // sort blocks into xy order for column assignment -+ blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *)); -+ for (blk = blkList, i = 0; blk; blk = blk->next, ++i) { -+ blocks[i] = blk; -+ } -+ qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpXYPrimaryRot); -+ -+ // column assignment -+ for (i = 0; i < nBlocks; ++i) { -+ blk0 = blocks[i]; -+ col1 = 0; -+ for (j = 0; j < i; ++j) { -+ blk1 = blocks[j]; -+ col2 = 0; // make gcc happy -+ switch (primaryRot) { -+ case 0: -+ if (blk0->xMin > blk1->xMax) { -+ col2 = blk1->col + blk1->nColumns + 3; -+ } else if (blk1->xMax == blk1->xMin) { -+ col2 = blk1->col; -+ } else { -+ col2 = blk1->col + (int)(((blk0->xMin - blk1->xMin) / -+ (blk1->xMax - blk1->xMin)) * -+ blk1->nColumns); -+ } -+ break; -+ case 1: -+ if (blk0->yMin > blk1->yMax) { -+ col2 = blk1->col + blk1->nColumns + 3; -+ } else if (blk1->yMax == blk1->yMin) { -+ col2 = blk1->col; -+ } else { -+ col2 = blk1->col + (int)(((blk0->yMin - blk1->yMin) / -+ (blk1->yMax - blk1->yMin)) * -+ blk1->nColumns); -+ } -+ break; -+ case 2: -+ if (blk0->xMax < blk1->xMin) { -+ col2 = blk1->col + blk1->nColumns + 3; -+ } else if (blk1->xMin == blk1->xMax) { -+ col2 = blk1->col; -+ } else { -+ col2 = blk1->col + (int)(((blk0->xMax - blk1->xMax) / -+ (blk1->xMin - blk1->xMax)) * -+ blk1->nColumns); -+ } -+ break; -+ case 3: -+ if (blk0->yMax < blk1->yMin) { -+ col2 = blk1->col + blk1->nColumns + 3; -+ } else if (blk1->yMin == blk1->yMax) { -+ col2 = blk1->col; -+ } else { -+ col2 = blk1->col + (int)(((blk0->yMax - blk1->yMax) / -+ (blk1->yMin - blk1->yMax)) * -+ blk1->nColumns); -+ } -+ break; -+ } -+ if (col2 > col1) { -+ col1 = col2; -+ } -+ } -+ blk0->col = col1; -+ for (line = blk0->lines; line; line = line->next) { -+ for (j = 0; j <= line->len; ++j) { -+ line->col[j] += col1; -+ } - } - } -+ - } - - #if 0 // for debugging -@@ -2753,7 +2949,7 @@ - blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax, blk->col, - blk->nColumns); - for (line = blk->lines; line; line = line->next) { -- printf(" line:\n"); -+ printf(" line: col[0]=%d\n", line->col[0]); - for (word0 = line->words; word0; word0 = word0->next) { - printf(" word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '", - word0->xMin, word0->xMax, word0->yMin, word0->yMax, -@@ -2932,6 +3128,7 @@ - GBool startAtTop, GBool stopAtBottom, - GBool startAtLast, GBool stopAtLast, - GBool caseSensitive, GBool backward, -+ GBool wholeWord, - double *xMin, double *yMin, - double *xMax, double *yMax) { - TextBlock *blk; -@@ -2989,25 +3186,35 @@ - blk = blocks[i]; - - // check: is the block above the top limit? -- if (!startAtTop && (backward ? blk->yMin > yStart : blk->yMax < yStart)) { -+ // (this only works if the page's primary rotation is zero -- -+ // otherwise the blocks won't be sorted in the useful order) -+ if (!startAtTop && primaryRot == 0 && -+ (backward ? blk->yMin > yStart : blk->yMax < yStart)) { - continue; - } - - // check: is the block below the bottom limit? -- if (!stopAtBottom && (backward ? blk->yMax < yStop : blk->yMin > yStop)) { -+ // (this only works if the page's primary rotation is zero -- -+ // otherwise the blocks won't be sorted in the useful order) -+ if (!stopAtBottom && primaryRot == 0 && -+ (backward ? blk->yMax < yStop : blk->yMin > yStop)) { - break; - } - - for (line = blk->lines; line; line = line->next) { - - // check: is the line above the top limit? -- if (!startAtTop && -+ // (this only works if the page's primary rotation is zero -- -+ // otherwise the lines won't be sorted in the useful order) -+ if (!startAtTop && primaryRot == 0 && - (backward ? line->yMin > yStart : line->yMin < yStart)) { - continue; - } - - // check: is the line below the bottom limit? -- if (!stopAtBottom && -+ // (this only works if the page's primary rotation is zero -- -+ // otherwise the lines won't be sorted in the useful order) -+ if (!stopAtBottom && primaryRot == 0 && - (backward ? line->yMin < yStop : line->yMin > yStop)) { - continue; - } -@@ -3030,68 +3237,72 @@ - j = backward ? m - len : 0; - p = txt + j; - while (backward ? j >= 0 : j <= m - len) { -- -- // compare the strings -- for (k = 0; k < len; ++k) { -- if (p[k] != s2[k]) { -- break; -+ if (!wholeWord || -+ ((j == 0 || !unicodeTypeAlphaNum(txt[j - 1])) && -+ (j + len == m || !unicodeTypeAlphaNum(txt[j + len])))) { -+ -+ // compare the strings -+ for (k = 0; k < len; ++k) { -+ if (p[k] != s2[k]) { -+ break; -+ } - } -- } - -- // found it -- if (k == len) { -- switch (line->rot) { -- case 0: -- xMin1 = line->edge[j]; -- xMax1 = line->edge[j + len]; -- yMin1 = line->yMin; -- yMax1 = line->yMax; -- break; -- case 1: -- xMin1 = line->xMin; -- xMax1 = line->xMax; -- yMin1 = line->edge[j]; -- yMax1 = line->edge[j + len]; -- break; -- case 2: -- xMin1 = line->edge[j + len]; -- xMax1 = line->edge[j]; -- yMin1 = line->yMin; -- yMax1 = line->yMax; -- break; -- case 3: -- xMin1 = line->xMin; -- xMax1 = line->xMax; -- yMin1 = line->edge[j + len]; -- yMax1 = line->edge[j]; -- break; -- } -- if (backward) { -- if ((startAtTop || -- yMin1 < yStart || (yMin1 == yStart && xMin1 < xStart)) && -- (stopAtBottom || -- yMin1 > yStop || (yMin1 == yStop && xMin1 > xStop))) { -- if (!found || -- yMin1 > yMin0 || (yMin1 == yMin0 && xMin1 > xMin0)) { -- xMin0 = xMin1; -- xMax0 = xMax1; -- yMin0 = yMin1; -- yMax0 = yMax1; -- found = gTrue; -- } -+ // found it -+ if (k == len) { -+ switch (line->rot) { -+ case 0: -+ xMin1 = line->edge[j]; -+ xMax1 = line->edge[j + len]; -+ yMin1 = line->yMin; -+ yMax1 = line->yMax; -+ break; -+ case 1: -+ xMin1 = line->xMin; -+ xMax1 = line->xMax; -+ yMin1 = line->edge[j]; -+ yMax1 = line->edge[j + len]; -+ break; -+ case 2: -+ xMin1 = line->edge[j + len]; -+ xMax1 = line->edge[j]; -+ yMin1 = line->yMin; -+ yMax1 = line->yMax; -+ break; -+ case 3: -+ xMin1 = line->xMin; -+ xMax1 = line->xMax; -+ yMin1 = line->edge[j + len]; -+ yMax1 = line->edge[j]; -+ break; - } -- } else { -- if ((startAtTop || -- yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) && -- (stopAtBottom || -- yMin1 < yStop || (yMin1 == yStop && xMin1 < xStop))) { -- if (!found || -- yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) { -- xMin0 = xMin1; -- xMax0 = xMax1; -- yMin0 = yMin1; -- yMax0 = yMax1; -- found = gTrue; -+ if (backward) { -+ if ((startAtTop || -+ yMin1 < yStart || (yMin1 == yStart && xMin1 < xStart)) && -+ (stopAtBottom || -+ yMin1 > yStop || (yMin1 == yStop && xMin1 > xStop))) { -+ if (!found || -+ yMin1 > yMin0 || (yMin1 == yMin0 && xMin1 > xMin0)) { -+ xMin0 = xMin1; -+ xMax0 = xMax1; -+ yMin0 = yMin1; -+ yMax0 = yMax1; -+ found = gTrue; -+ } -+ } -+ } else { -+ if ((startAtTop || -+ yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) && -+ (stopAtBottom || -+ yMin1 < yStop || (yMin1 == yStop && xMin1 < xStop))) { -+ if (!found || -+ yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) { -+ xMin0 = xMin1; -+ xMax0 = xMax1; -+ yMin0 = yMin1; -+ yMax0 = yMax1; -+ found = gTrue; -+ } - } - } - } -@@ -3820,10 +4038,20 @@ - fwrite(text, 1, len, (FILE *)stream); - } - - TextOutputDev::TextOutputDev(char *fileName, GBool physLayoutA, -- GBool rawOrderA, GBool append) { -+ double fixedPitchA, GBool rawOrderA, -+ GBool append) { - text = NULL; - physLayout = physLayoutA; -+ fixedPitch = physLayout ? fixedPitchA : 0; - rawOrder = rawOrderA; - doHTML = gFalse; - ok = gTrue; -@@ -3854,11 +4074,13 @@ - } - - TextOutputDev::TextOutputDev(TextOutputFunc func, void *stream, -- GBool physLayoutA, GBool rawOrderA) { -+ GBool physLayoutA, double fixedPitchA, -+ GBool rawOrderA) { - outputFunc = func; - outputStream = stream; - needClose = gFalse; - physLayout = physLayoutA; -+ fixedPitch = physLayout ? fixedPitchA : 0; - rawOrder = rawOrderA; - doHTML = gFalse; - text = new TextPage(rawOrderA); -@@ -3883,12 +4105,16 @@ - - void TextOutputDev::endPage() { - text->endPage(); -- text->coalesce(physLayout, doHTML); -+ text->coalesce(physLayout, fixedPitch, doHTML); - if (outputStream) { - text->dump(outputStream, outputFunc, physLayout); - } - } - -+void TextOutputDev::restoreState(GfxState *state) { -+ text->updateFont(state); -+} -+ - void TextOutputDev::updateFont(GfxState *state) { - text->updateFont(state); - } -@@ -3903,7 +4129,19 @@ - double dx, double dy, - double originX, double originY, - CharCode c, int nBytes, Unicode *u, int uLen) { -- text->addChar(state, x, y, dx, dy, c, nBytes, u, uLen); -+ text->addChar(state, x - originX, y - originY, dx, dy, c, nBytes, u, uLen); -+} -+ -+void TextOutputDev::incCharCount(int nChars) { -+ text->incCharCount(nChars); -+} -+ -@@ -4057,10 +4295,12 @@ - GBool startAtTop, GBool stopAtBottom, - GBool startAtLast, GBool stopAtLast, - GBool caseSensitive, GBool backward, -+ GBool wholeWord, - double *xMin, double *yMin, - double *xMax, double *yMax) { - return text->findText(s, len, startAtTop, stopAtBottom, -- startAtLast, stopAtLast, caseSensitive, backward, -+ startAtLast, stopAtLast, -+ caseSensitive, backward, wholeWord, - xMin, yMin, xMax, yMax); - } - -diff -ru xpdf-3.02/xpdf/TextOutputDev.h xpdf-3.03/xpdf/TextOutputDev.h ---- xpdf-3.02/xpdf/TextOutputDev.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/TextOutputDev.h 2011-08-15 23:08:53.000000000 +0200 -@@ -280,7 +281,7 @@ - - void addWord(TextWord *word); - -- void coalesce(UnicodeMap *uMap); -+ void coalesce(UnicodeMap *uMap, double fixedPitch); - - // Update this block's priMin and priMax values, looking at . - void updatePriMinMax(TextBlock *blk); -@@ -429,6 +430,15 @@ - double dx, double dy, - CharCode c, int nBytes, Unicode *u, int uLen); - -+ // Add invisible characters. -+ void incCharCount(int nChars); -+ -@@ -442,7 +452,7 @@ - void addLink(int xMin, int yMin, int xMax, int yMax, Link *link); - - // Coalesce strings that look like parts of the same line. -- void coalesce(GBool physLayout, GBool doHTML); -+ void coalesce(GBool physLayout, double fixedPitch, GBool doHTML); - - // Find a string. If is true, starts looking at the - // top of the page; else if is true, starts looking -@@ -455,6 +465,7 @@ - GBool startAtTop, GBool stopAtBottom, - GBool startAtLast, GBool stopAtLast, - GBool caseSensitive, GBool backward, -+ GBool wholeWord, - double *xMin, double *yMin, - double *xMax, double *yMax); - -@@ -502,6 +513,13 @@ - int nTinyChars; // number of "tiny" chars seen so far - GBool lastCharOverlap; // set if the last added char overlapped the - // previous char -@@ -544,14 +562,16 @@ - // is maintained. If is true, the text is kept in - // content stream order. - TextOutputDev(char *fileName, GBool physLayoutA, -- GBool rawOrderA, GBool append); -+ double fixedPitchA, GBool rawOrderA, -+ GBool append); - - // Create a TextOutputDev which will write to a generic stream. If - // is true, the original physical layout of the text - // is maintained. If is true, the text is kept in - // content stream order. - TextOutputDev(TextOutputFunc func, void *stream, -- GBool physLayoutA, GBool rawOrderA); -+ GBool physLayoutA, double fixedPitchA, -+ GBool rawOrderA); - - // Destructor. - virtual ~TextOutputDev(); -@@ -575,6 +595,10 @@ - // Does this device need non-text content? - virtual GBool needNonText() { return gFalse; } - -+ // Does this device require incCharCount to be called for text on -+ // non-shown layers? -+ virtual GBool needCharCount() { return gTrue; } -+ - //----- initialization and control - - // Start a page. -@@ -583,6 +607,9 @@ - // End a page. - virtual void endPage(); - -+ //----- save/restore graphics state -+ virtual void restoreState(GfxState *state); -+ - //----- update text state - virtual void updateFont(GfxState *state); - -@@ -593,6 +620,9 @@ - double dx, double dy, - double originX, double originY, - CharCode c, int nBytes, Unicode *u, int uLen); -+ virtual void incCharCount(int nChars); -@@ -615,6 +645,7 @@ - GBool startAtTop, GBool stopAtBottom, - GBool startAtLast, GBool stopAtLast, - GBool caseSensitive, GBool backward, -+ GBool wholeWord, - double *xMin, double *yMin, - double *xMax, double *yMax); - -@@ -653,6 +684,9 @@ - TextPage *text; // text for the current page - GBool physLayout; // maintain original physical layout when - // dumping text -+ double fixedPitch; // if physLayout is true and this is non-zero, -+ // assume fixed-pitch characters with this -+ // width - GBool rawOrder; // keep text in content stream order - GBool doHTML; // extra processing for HTML conversion - GBool ok; // set up ok? -diff -ru xpdf-3.02/xpdf/XRef.cc xpdf-3.03/xpdf/XRef.cc ---- xpdf-3.02/xpdf/XRef.cc 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/XRef.cc 2011-08-15 23:08:53.000000000 +0200 -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include - #include "gmem.h" - #include "Object.h" - #include "Stream.h" -@@ -190,13 +202,14 @@ - // XRef - //------------------------------------------------------------------------ - --XRef::XRef(BaseStream *strA) { -+XRef::XRef(BaseStream *strA, GBool repair) { - Guint pos; - Object obj; - - ok = gTrue; - errCode = errNone; - size = 0; -+ last = -1; - entries = NULL; - streamEnds = NULL; - streamEndsLen = 0; -@@ -206,30 +219,32 @@ - permFlags = defPermFlags; - ownerPasswordOk = gFalse; - -- // read the trailer - str = strA; - start = str->getStart(); -- pos = getStartXref(); - -- // if there was a problem with the 'startxref' position, try to -- // reconstruct the xref table -- if (pos == 0) { -+ // if the 'repair' flag is set, try to reconstruct the xref table -+ if (repair) { - if (!(ok = constructXRef())) { - errCode = errDamaged; - return; - } - -- // read the xref table -+ // if the 'repair' flag is not set, read the xref table - } else { -- while (readXRef(&pos)) ; - -- // if there was a problem with the xref table, -- // try to reconstruct it -+ // read the trailer -+ pos = getStartXref(); -+ if (pos == 0) { -+ errCode = errDamaged; -+ ok = gFalse; -+ return; -+ } -+ -+ // read the xref table -+ while (readXRef(&pos)) ; - if (!ok) { -- if (!(ok = constructXRef())) { -- errCode = errDamaged; -- return; -- } -+ errCode = errDamaged; -+ return; - } - } - -@@ -288,7 +303,7 @@ - if (i < 0) { - return 0; - } -- for (p = &buf[i+9]; isspace(*p); ++p) ; -+ for (p = &buf[i+9]; isspace(*p & 0xff); ++p) ; - lastXRefPos = strToUnsigned(p); - - return lastXRefPos; -@@ -307,7 +322,7 @@ - new Lexer(NULL, - str->makeSubStream(start + *pos, gFalse, 0, &obj)), - gTrue); -- parser->getObj(&obj); -+ parser->getObj(&obj, gTrue); - - // parse an old-style xref table - if (obj.isCmd("xref")) { -@@ -317,11 +332,11 @@ - // parse an xref stream - } else if (obj.isInt()) { - obj.free(); -- if (!parser->getObj(&obj)->isInt()) { -+ if (!parser->getObj(&obj, gTrue)->isInt()) { - goto err1; - } - obj.free(); -- if (!parser->getObj(&obj)->isCmd("obj")) { -+ if (!parser->getObj(&obj, gTrue)->isCmd("obj")) { - goto err1; - } - obj.free(); -@@ -353,7 +368,7 @@ - int first, n, newSize, i; - - while (1) { -- parser->getObj(&obj); -+ parser->getObj(&obj, gTrue); - if (obj.isCmd("trailer")) { - obj.free(); - break; -@@ -363,7 +378,7 @@ - } - first = obj.getInt(); - obj.free(); -- if (!parser->getObj(&obj)->isInt()) { -+ if (!parser->getObj(&obj, gTrue)->isInt()) { - goto err1; - } - n = obj.getInt(); -@@ -386,17 +401,17 @@ - size = newSize; - } - for (i = first; i < first + n; ++i) { -- if (!parser->getObj(&obj)->isInt()) { -+ if (!parser->getObj(&obj, gTrue)->isInt()) { - goto err1; - } - entry.offset = (Guint)obj.getInt(); - obj.free(); -- if (!parser->getObj(&obj)->isInt()) { -+ if (!parser->getObj(&obj, gTrue)->isInt()) { - goto err1; - } - entry.gen = obj.getInt(); - obj.free(); -- parser->getObj(&obj); -+ parser->getObj(&obj, gTrue); - if (obj.isCmd("n")) { - entry.type = xrefEntryUncompressed; - } else if (obj.isCmd("f")) { -@@ -417,6 +432,9 @@ - entries[0] = entries[1]; - entries[1].offset = 0xffffffff; - } -+ if (i > last) { -+ last = i; -+ } - } - } - } -@@ -429,13 +447,25 @@ - // get the 'Prev' pointer - obj.getDict()->lookupNF("Prev", &obj2); - if (obj2.isInt()) { -- *pos = (Guint)obj2.getInt(); -- more = gTrue; -+ pos2 = (Guint)obj2.getInt(); -+ if (pos2 != *pos) { -+ *pos = pos2; -+ more = gTrue; -+ } else { -+ error(errSyntaxWarning, -1, "Infinite loop in xref table"); -+ more = gFalse; -+ } - } else if (obj2.isRef()) { - // certain buggy PDF generators generate "/Prev NNN 0 R" instead - // of "/Prev NNN" -- *pos = (Guint)obj2.getRefNum(); -- more = gTrue; -+ pos2 = (Guint)obj2.getRefNum(); -+ if (pos2 != *pos) { -+ *pos = pos2; -+ more = gTrue; -+ } else { -+ error(errSyntaxWarning, -1, "Infinite loop in xref table"); -+ more = gFalse; -+ } - } else { - more = gFalse; - } -@@ -624,6 +654,9 @@ - default: - return gFalse; - } -+ if (i > last) { -+ last = i; -+ } - } - } - -@@ -647,7 +680,6 @@ - size = 0; - entries = NULL; - -- error(-1, "PDF file is damaged - attempting to reconstruct xref table..."); - gotRoot = gFalse; - streamEndsLen = streamEndsSize = 0; - -@@ -687,30 +719,30 @@ - delete parser; - - // look for object -- } else if (isdigit(*p)) { -+ } else if (isdigit(*p & 0xff)) { - num = atoi(p); - if (num > 0) { - do { - ++p; -- } while (*p && isdigit(*p)); -- if (isspace(*p)) { -+ } while (*p && isdigit(*p & 0xff)); -+ if (isspace(*p & 0xff)) { - do { - ++p; -- } while (*p && isspace(*p)); -- if (isdigit(*p)) { -+ } while (*p && isspace(*p & 0xff)); -+ if (isdigit(*p & 0xff)) { - gen = atoi(p); - do { - ++p; -- } while (*p && isdigit(*p)); -- if (isspace(*p)) { -+ } while (*p && isdigit(*p & 0xff)); -+ if (isspace(*p & 0xff)) { - do { - ++p; -- } while (*p && isspace(*p)); -+ } while (*p && isspace(*p & 0xff)); - if (!strncmp(p, "obj", 3)) { - if (num >= size) { - newSize = (num + 1 + 255) & ~255; - if (newSize < 0) { -- error(-1, "Bad object number"); -+ error(errSyntaxError, -1, "Bad object number"); - return gFalse; - } - entries = (XRefEntry *) -@@ -726,6 +758,9 @@ - entries[num].offset = pos - start; - entries[num].gen = gen; - entries[num].type = xrefEntryUncompressed; -+ if (num > last) { -+ last = num; -+ } - } - } - } -diff -ru xpdf-3.02/xpdf/XRef.h xpdf-3.03/xpdf/XRef.h ---- xpdf-3.02/xpdf/XRef.h 2007-02-27 23:05:52.000000000 +0100 -+++ xpdf-3.03/xpdf/XRef.h 2011-08-15 23:08:53.000000000 +0200 -@@ -43,7 +43,7 @@ - public: - - // Constructor. Read xref table from stream. -- XRef(BaseStream *strA); -+ XRef(BaseStream *strA, GBool repair); - - // Destructor. - ~XRef(); -@@ -67,19 +67,20 @@ - // Get catalog object. - Object *getCatalog(Object *obj) { return fetch(rootNum, rootGen, obj); } - - // Fetch an indirect reference. -- Object *fetch(int num, int gen, Object *obj); -+ Object *fetch(int num, int gen, Object *obj, int recursion = 0); - - // Return the document's Info dictionary (if any). - Object *getDocInfo(Object *obj); - Object *getDocInfoNF(Object *obj); - - // Return the number of objects in the xref table. -- int getNumObjects() { return size; } -+ int getNumObjects() { return last + 1; } - - // Return the offset of the last xref table. - Guint getLastXRefPos() { return lastXRefPos; } -@@ -104,6 +105,7 @@ - // at beginning of file) - XRefEntry *entries; // xref entries - int size; // size of array -+ int last; // last used index in - int rootNum, rootGen; // catalog dict - GBool ok; // true if xref table is valid - int errCode; // error code (if is false) +diff -ru xpdf-3.02/doc/pdftotext.1 xpdf-3.03/doc/pdftotext.1 +--- xpdf-3.02/doc/pdftotext.1 2007-02-27 23:05:51.000000000 +0100 ++++ xpdf-3.03/doc/pdftotext.1 2011-08-15 23:08:53.000000000 +0200 +@@ -49,6 +49,10 @@ + text. The default is to \'undo' physical layout (columns, + hyphenation, etc.) and output the text in reading order. + .TP ++.BI \-fixed " number" ++Assume fixed-pitch (or tabular) text, with the specified character ++width (in points). This forces physical layout mode. ++.TP + .B \-raw + Keep the text in content stream order. This is a hack which often + "undoes" column formatting, etc. Use of raw mode is no longer +diff -ru xpdf-3.02/xpdf/Gfx.cc xpdf-3.03/xpdf/Gfx.cc +--- xpdf-3.02/xpdf/Gfx.cc 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/Gfx.cc 2011-08-15 23:08:53.000000000 +0200 +@@ -18,9 +18,12 @@ + #include + #include + #include "gmem.h" ++#include "GString.h" ++#include "GList.h" + #include "GlobalParams.h" + #include "CharTypes.h" + #include "Object.h" + #include "PDFDoc.h" + #include "Array.h" + #include "Dict.h" + #include "Stream.h" +@@ -31,7 +34,9 @@ + #include "OutputDev.h" + #include "Page.h" + #include "Annot.h" ++#include "OptionalContent.h" + #include "Error.h" ++#include "PDFDocEncoding.h" + #include "Gfx.h" + + // the MSVC math.h doesn't define this +@@ -461,6 +487,10 @@ + baseMatrix[i] = state->getCTM()[i]; + } + formDepth = 0; ++ textClipBBoxEmpty = gTrue; ++ markedContentStack = new GList(); ++ ocState = gTrue; ++ parser = NULL; + abortCheckCbk = abortCheckCbkA; + abortCheckCbkData = abortCheckCbkDataA; + +@@ -500,6 +531,10 @@ + baseMatrix[i] = state->getCTM()[i]; + } + formDepth = 0; ++ textClipBBoxEmpty = gTrue; ++ markedContentStack = new GList(); ++ ocState = gTrue; ++ parser = NULL; + abortCheckCbk = abortCheckCbkA; + abortCheckCbkData = abortCheckCbkDataA; + +@@ -517,18 +552,17 @@ + } + + Gfx::~Gfx() { +- while (state->hasSaves()) { +- restoreState(); +- } + if (!subPage) { + out->endPage(); + } ++ while (state->hasSaves()) { ++ restoreState(); ++ } ++ delete state; + while (res) { + popResources(); + } +- if (state) { +- delete state; +- } ++ deleteGList(markedContentStack, GfxMarkedContent); + } + + void Gfx::display(Object *obj, GBool topLevel) { +@@ -562,7 +596,8 @@ + int lastAbortCheck; + + // scan a sequence of objects +- updateLevel = lastAbortCheck = 0; ++ updateLevel = 1; // make sure even empty pages trigger a call to dump() ++ lastAbortCheck = 0; + numArgs = 0; + parser->getObj(&obj); + while (!obj.isEOF()) { +@@ -695,6 +734,7 @@ + + a = -1; + b = numOps; ++ cmp = 0; // make gcc happy + // invariant: opTab[a] < name < opTab[b] + while (b - a > 1) { + m = (a + b) / 2; +@@ -801,6 +841,7 @@ + + void Gfx::opSetExtGState(Object args[], int numArgs) { + Object obj1, obj2, obj3, obj4, obj5; ++ Object args2[2]; + GfxBlendMode mode; + GBool haveFillOP; + Function *funcs[4]; +@@ -808,9 +849,10 @@ + GBool haveBackdropColor; + GfxColorSpace *blendingColorSpace; + GBool alpha, isolated, knockout; ++ double opac; + int i; + + if (!res->lookupGState(args[0].getName(), &obj1)) { + return; + } + if (!obj1.isDict()) { +@@ -824,28 +867,77 @@ + printf("\n"); + } + ++ // parameters that are also set by individual PDF operators ++ if (obj1.dictLookup("LW", &obj2)->isNum()) { ++ opSetLineWidth(&obj2, 1); ++ } ++ obj2.free(); ++ if (obj1.dictLookup("LC", &obj2)->isInt()) { ++ opSetLineCap(&obj2, 1); ++ } ++ obj2.free(); ++ if (obj1.dictLookup("LJ", &obj2)->isInt()) { ++ opSetLineJoin(&obj2, 1); ++ } ++ obj2.free(); ++ if (obj1.dictLookup("ML", &obj2)->isNum()) { ++ opSetMiterLimit(&obj2, 1); ++ } ++ obj2.free(); ++ if (obj1.dictLookup("D", &obj2)->isArray() && ++ obj2.arrayGetLength() == 2) { ++ obj2.arrayGet(0, &args2[0]); ++ obj2.arrayGet(1, &args2[1]); ++ if (args2[0].isArray() && args2[1].isNum()) { ++ opSetDash(args2, 2); ++ } ++ args2[0].free(); ++ args2[1].free(); ++ } ++ obj2.free(); ++#if 0 //~ need to add a new version of GfxResources::lookupFont() that ++ //~ takes an indirect ref instead of a name ++ if (obj1.dictLookup("Font", &obj2)->isArray() && ++ obj2.arrayGetLength() == 2) { ++ obj2.arrayGet(0, &args2[0]); ++ obj2.arrayGet(1, &args2[1]); ++ if (args2[0].isDict() && args2[1].isNum()) { ++ opSetFont(args2, 2); ++ } ++ args2[0].free(); ++ args2[1].free(); ++ } ++ obj2.free(); ++#endif ++ if (obj1.dictLookup("FL", &obj2)->isNum()) { ++ opSetFlat(&obj2, 1); ++ } ++ obj2.free(); ++ + // 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(errSyntaxError, getPos(), "Invalid blend mode in ExtGState"); + } + } + obj2.free(); + if (obj1.dictLookup("ca", &obj2)->isNum()) { +- state->setFillOpacity(obj2.getNum()); ++ opac = obj2.getNum(); ++ state->setFillOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac); + out->updateFillOpacity(state); + } + obj2.free(); + if (obj1.dictLookup("CA", &obj2)->isNum()) { +- state->setStrokeOpacity(obj2.getNum()); ++ opac = obj2.getNum(); ++ state->setStrokeOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac); + out->updateStrokeOpacity(state); + } + obj2.free(); + +- // fill/stroke overprint ++ // fill/stroke overprint, overprint mode + if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) { + state->setFillOverprint(obj2.getBool()); + out->updateFillOverprint(state); +@@ -860,6 +952,11 @@ + } + } + obj2.free(); ++ if (obj1.dictLookup("OPM", &obj2)->isInt()) { ++ state->setOverprintMode(obj2.getInt()); ++ out->updateOverprintMode(state); ++ } ++ obj2.free(); + + // stroke adjust + if (obj1.dictLookup("SA", &obj2)->isBool()) { +@@ -915,13 +1012,18 @@ + obj3.free(); + funcs[0] = NULL; + if (!obj2.dictLookup("TR", &obj3)->isNull()) { +- funcs[0] = Function::parse(&obj3); +- if (funcs[0]->getInputSize() != 1 || +- funcs[0]->getOutputSize() != 1) { +- error(getPos(), +- "Invalid transfer function in soft mask in ExtGState"); +- delete funcs[0]; ++ if (obj3.isName("Default") || ++ obj3.isName("Identity")) { + funcs[0] = NULL; ++ } else { ++ funcs[0] = Function::parse(&obj3); ++ if (funcs[0]->getInputSize() != 1 || ++ funcs[0]->getOutputSize() != 1) { ++ error(errSyntaxError, getPos(), ++ "Invalid transfer function in soft mask in ExtGState"); ++ delete funcs[0]; ++ funcs[0] = NULL; ++ } + } + } + obj3.free(); +@@ -1045,9 +1149,9 @@ + + // draw it + ++formDepth; +- doForm1(str, resDict, m, bbox, gTrue, gTrue, +- blendingColorSpace, isolated, knockout, +- alpha, transferFunc, backdropColor); ++ drawForm(str, resDict, m, bbox, gTrue, gTrue, ++ blendingColorSpace, isolated, knockout, ++ alpha, transferFunc, backdropColor); + --formDepth; + + if (blendingColorSpace) { +@@ -1402,14 +1512,16 @@ + + void Gfx::opStroke(Object args[], int numArgs) { + if (!state->isCurPt()) { +- //error(getPos(), "No path in stroke"); ++ //error(errSyntaxError, getPos(), "No path in stroke"); + return; + } + if (state->isPath()) { +- if (state->getStrokeColorSpace()->getMode() == csPattern) { +- doPatternStroke(); +- } else { +- out->stroke(state); ++ if (ocState) { ++ if (state->getStrokeColorSpace()->getMode() == csPattern) { ++ doPatternStroke(); ++ } else { ++ out->stroke(state); ++ } + } + } + doEndPath(); +@@ -1417,15 +1529,17 @@ + + void Gfx::opCloseStroke(Object args[], int numArgs) { + if (!state->isCurPt()) { +- //error(getPos(), "No path in closepath/stroke"); ++ //error(errSyntaxError, getPos(), "No path in closepath/stroke"); + return; + } + if (state->isPath()) { + state->closePath(); +- if (state->getStrokeColorSpace()->getMode() == csPattern) { +- doPatternStroke(); +- } else { +- out->stroke(state); ++ if (ocState) { ++ if (state->getStrokeColorSpace()->getMode() == csPattern) { ++ doPatternStroke(); ++ } else { ++ out->stroke(state); ++ } + } + } + doEndPath(); +@@ -1433,14 +1547,16 @@ + + void Gfx::opFill(Object args[], int numArgs) { + if (!state->isCurPt()) { +- //error(getPos(), "No path in fill"); ++ //error(errSyntaxError, getPos(), "No path in fill"); + return; + } + if (state->isPath()) { +- if (state->getFillColorSpace()->getMode() == csPattern) { +- doPatternFill(gFalse); +- } else { +- out->fill(state); ++ if (ocState) { ++ if (state->getFillColorSpace()->getMode() == csPattern) { ++ doPatternFill(gFalse); ++ } else { ++ out->fill(state); ++ } + } + } + doEndPath(); +@@ -1448,14 +1564,16 @@ + + void Gfx::opEOFill(Object args[], int numArgs) { + if (!state->isCurPt()) { +- //error(getPos(), "No path in eofill"); ++ //error(errSyntaxError, getPos(), "No path in eofill"); + return; + } + if (state->isPath()) { +- if (state->getFillColorSpace()->getMode() == csPattern) { +- doPatternFill(gTrue); +- } else { +- out->eoFill(state); ++ if (ocState) { ++ if (state->getFillColorSpace()->getMode() == csPattern) { ++ doPatternFill(gTrue); ++ } else { ++ out->eoFill(state); ++ } + } + } + doEndPath(); +@@ -1463,19 +1581,21 @@ + + void Gfx::opFillStroke(Object args[], int numArgs) { + if (!state->isCurPt()) { +- //error(getPos(), "No path in fill/stroke"); ++ //error(errSyntaxError, getPos(), "No path in fill/stroke"); + return; + } + if (state->isPath()) { +- if (state->getFillColorSpace()->getMode() == csPattern) { +- doPatternFill(gFalse); +- } else { +- out->fill(state); +- } +- if (state->getStrokeColorSpace()->getMode() == csPattern) { +- doPatternStroke(); +- } else { +- out->stroke(state); ++ if (ocState) { ++ if (state->getFillColorSpace()->getMode() == csPattern) { ++ doPatternFill(gFalse); ++ } else { ++ out->fill(state); ++ } ++ if (state->getStrokeColorSpace()->getMode() == csPattern) { ++ doPatternStroke(); ++ } else { ++ out->stroke(state); ++ } + } + } + doEndPath(); +@@ -1483,20 +1603,22 @@ + + void Gfx::opCloseFillStroke(Object args[], int numArgs) { + if (!state->isCurPt()) { +- //error(getPos(), "No path in closepath/fill/stroke"); ++ //error(errSyntaxError, getPos(), "No path in closepath/fill/stroke"); + return; + } + if (state->isPath()) { + state->closePath(); +- if (state->getFillColorSpace()->getMode() == csPattern) { +- doPatternFill(gFalse); +- } else { +- out->fill(state); +- } +- if (state->getStrokeColorSpace()->getMode() == csPattern) { +- doPatternStroke(); +- } else { +- out->stroke(state); ++ if (ocState) { ++ if (state->getFillColorSpace()->getMode() == csPattern) { ++ doPatternFill(gFalse); ++ } else { ++ out->fill(state); ++ } ++ if (state->getStrokeColorSpace()->getMode() == csPattern) { ++ doPatternStroke(); ++ } else { ++ out->stroke(state); ++ } + } + } + doEndPath(); +@@ -1504,19 +1626,21 @@ + + void Gfx::opEOFillStroke(Object args[], int numArgs) { + if (!state->isCurPt()) { +- //error(getPos(), "No path in eofill/stroke"); ++ //error(errSyntaxError, getPos(), "No path in eofill/stroke"); + return; + } + if (state->isPath()) { +- if (state->getFillColorSpace()->getMode() == csPattern) { +- doPatternFill(gTrue); +- } else { +- out->eoFill(state); +- } +- if (state->getStrokeColorSpace()->getMode() == csPattern) { +- doPatternStroke(); +- } else { +- out->stroke(state); ++ if (ocState) { ++ if (state->getFillColorSpace()->getMode() == csPattern) { ++ doPatternFill(gTrue); ++ } else { ++ out->eoFill(state); ++ } ++ if (state->getStrokeColorSpace()->getMode() == csPattern) { ++ doPatternStroke(); ++ } else { ++ out->stroke(state); ++ } + } + } + doEndPath(); +@@ -1524,20 +1648,22 @@ + + void Gfx::opCloseEOFillStroke(Object args[], int numArgs) { + if (!state->isCurPt()) { +- //error(getPos(), "No path in closepath/eofill/stroke"); ++ //error(errSyntaxError, getPos(), "No path in closepath/eofill/stroke"); + return; + } + if (state->isPath()) { + state->closePath(); +- if (state->getFillColorSpace()->getMode() == csPattern) { +- doPatternFill(gTrue); +- } else { +- out->eoFill(state); +- } +- if (state->getStrokeColorSpace()->getMode() == csPattern) { +- doPatternStroke(); +- } else { +- out->stroke(state); ++ if (ocState) { ++ if (state->getFillColorSpace()->getMode() == csPattern) { ++ doPatternFill(gTrue); ++ } else { ++ out->eoFill(state); ++ } ++ if (state->getStrokeColorSpace()->getMode() == csPattern) { ++ doPatternStroke(); ++ } else { ++ out->stroke(state); ++ } + } + } + doEndPath(); +@@ -1558,13 +1684,13 @@ + } + switch (pattern->getType()) { + case 1: +- doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill); ++ doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill, gFalse); + break; + case 2: +- doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill); ++ doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill, gFalse); + break; + default: +- error(getPos(), "Unimplemented pattern type (%d) in fill", ++ error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill", + pattern->getType()); + break; + } +@@ -1585,23 +1711,68 @@ + } + switch (pattern->getType()) { + case 1: +- doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse); ++ doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse, gFalse); ++ break; ++ case 2: ++ doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse, gFalse); ++ break; ++ default: ++ error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in stroke", ++ pattern->getType()); ++ break; ++ } ++} ++ ++void Gfx::doPatternText() { ++ GfxPattern *pattern; ++ ++ // this is a bit of a kludge -- patterns can be really slow, so we ++ // skip them if we're only doing text extraction, since they almost ++ // certainly don't contain any text ++ if (!out->needNonText()) { ++ return; ++ } ++ ++ if (!(pattern = state->getFillPattern())) { ++ return; ++ } ++ switch (pattern->getType()) { ++ case 1: ++ doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, gFalse, gTrue); + break; + case 2: +- doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse); ++ doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, gFalse, gTrue); + break; + default: +- error(getPos(), "Unimplemented pattern type (%d) in stroke", ++ error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill", + pattern->getType()); + break; + } + } + ++void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height, ++ GBool invert, GBool inlineImg) { ++ saveState(); ++ ++ out->setSoftMaskFromImageMask(state, ref, str, ++ width, height, invert, inlineImg); ++ ++ state->clearPath(); ++ state->moveTo(0, 0); ++ state->lineTo(1, 0); ++ state->lineTo(1, 1); ++ state->lineTo(0, 1); ++ state->closePath(); ++ doPatternFill(gTrue); ++ ++ restoreState(); ++} ++ + void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, +- GBool stroke, GBool eoFill) { ++ GBool stroke, GBool eoFill, GBool text) { + GfxPatternColorSpace *patCS; + GfxColorSpace *cs; +- GfxPath *savedPath; ++ GfxState *savedState; + double xMin, yMin, xMax, yMax, x, y, x1, y1; + double cxMin, cyMin, cxMax, cyMax; + int xi0, yi0, xi1, yi1, xi, yi; +@@ -1652,28 +1823,27 @@ + imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det; + + // save current graphics state +- savedPath = state->getPath()->copy(); +- saveState(); ++ savedState = saveStateStack(); + + // set underlying color space (for uncolored tiling patterns); set + // various other parameters (stroke color, line width) to match + // Adobe's behavior ++ state->setFillPattern(NULL); ++ state->setStrokePattern(NULL); + 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()); ++ out->updateFillColor(state); ++ out->updateStrokeColor(state); + } else { + state->setFillColorSpace(new GfxDeviceGrayColorSpace()); + out->updateFillColorSpace(state); + state->setStrokeColorSpace(new GfxDeviceGrayColorSpace()); + out->updateStrokeColorSpace(state); + } +- state->setFillPattern(NULL); +- out->updateFillColor(state); +- state->setStrokePattern(NULL); +- out->updateStrokeColor(state); + if (!stroke) { + state->setLineWidth(0); + out->updateLineWidth(state); +@@ -1683,7 +1853,7 @@ + if (stroke) { + state->clipToStrokePath(); + out->clipToStrokePath(state); +- } else { ++ } else if (!text) { + state->clip(); + if (eoFill) { + out->eoClip(state); +@@ -1754,7 +1924,7 @@ + if (out->useTilingPatternFill()) { + m1[4] = m[4]; + m1[5] = m[5]; +- out->tilingPatternFill(state, tPat->getContentStream(), ++ out->tilingPatternFill(state, this, tPat->getContentStream(), + tPat->getPaintType(), tPat->getResDict(), + m1, tPat->getBBox(), + xi0, yi0, xi1, yi1, xstep, ystep); +@@ -1765,22 +1935,21 @@ + 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()); ++ drawForm(tPat->getContentStream(), tPat->getResDict(), ++ m1, tPat->getBBox()); + } + } + } + + // restore graphics state + err: +- restoreState(); +- state->setPath(savedPath); ++ restoreStateStack(savedState); + } + + void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, +- GBool stroke, GBool eoFill) { ++ GBool stroke, GBool eoFill, GBool text) { + GfxShading *shading; +- GfxPath *savedPath; ++ GfxState *savedState; + double *ctm, *btm, *ptm; + double m[6], ictm[6], m1[6]; + double xMin, yMin, xMax, yMax; +@@ -1789,27 +1958,13 @@ + shading = sPat->getShading(); + + // save current graphics state +- savedPath = state->getPath()->copy(); +- saveState(); +- +- // clip to bbox +- if (shading->getHasBBox()) { +- shading->getBBox(&xMin, &yMin, &xMax, &yMax); +- state->moveTo(xMin, yMin); +- state->lineTo(xMax, yMin); +- state->lineTo(xMax, yMax); +- state->lineTo(xMin, yMax); +- state->closePath(); +- state->clip(); +- out->clip(state); +- state->setPath(savedPath->copy()); +- } ++ savedState = saveStateStack(); + + // clip to current path + if (stroke) { + state->clipToStrokePath(); + out->clipToStrokePath(state); +- } else { ++ } else if (!text) { + state->clip(); + if (eoFill) { + out->eoClip(state); +@@ -1817,17 +1972,6 @@ + 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 +@@ -1861,11 +2005,39 @@ + 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]); + +-#if 1 //~tmp: turn off anti-aliasing temporarily +- GBool vaa = out->getVectorAntialias(); +- if (vaa) { +- out->setVectorAntialias(gFalse); ++ // clip to bbox ++ if (shading->getHasBBox()) { ++ shading->getBBox(&xMin, &yMin, &xMax, &yMax); ++ state->moveTo(xMin, yMin); ++ state->lineTo(xMax, yMin); ++ state->lineTo(xMax, yMax); ++ state->lineTo(xMin, yMax); ++ state->closePath(); ++ state->clip(); ++ out->clip(state); ++ state->clearPath(); ++ } ++ ++ // 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); ++ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); ++ state->moveTo(xMin, yMin); ++ state->lineTo(xMax, yMin); ++ state->lineTo(xMax, yMax); ++ state->lineTo(xMin, yMax); ++ state->closePath(); ++ out->fill(state); ++ state->clearPath(); + } ++ ++#if 1 //~tmp: turn off anti-aliasing temporarily ++ out->setInShading(gTrue); + #endif + + // do shading type-specific operations +@@ -1890,28 +2062,28 @@ + } + + #if 1 //~tmp: turn off anti-aliasing temporarily +- if (vaa) { +- out->setVectorAntialias(gTrue); +- } ++ out->setInShading(gFalse); + #endif + + // restore graphics state +- restoreState(); +- state->setPath(savedPath); ++ restoreStateStack(savedState); + } + + void Gfx::opShFill(Object args[], int numArgs) { + GfxShading *shading; +- GfxPath *savedPath; ++ GfxState *savedState; + double xMin, yMin, xMax, yMax; + ++ if (!ocState) { ++ return; ++ } ++ + if (!(shading = res->lookupShading(args[0].getName()))) { + return; + } + + // save current graphics state +- savedPath = state->getPath()->copy(); +- saveState(); ++ savedState = saveStateStack(); + + // clip to bbox + if (shading->getHasBBox()) { +@@ -1931,10 +2103,7 @@ + out->updateFillColorSpace(state); + + #if 1 //~tmp: turn off anti-aliasing temporarily +- GBool vaa = out->getVectorAntialias(); +- if (vaa) { +- out->setVectorAntialias(gFalse); +- } ++ out->setInShading(gTrue); + #endif + + // do shading type-specific operations +@@ -1959,14 +2128,11 @@ + } + + #if 1 //~tmp: turn off anti-aliasing temporarily +- if (vaa) { +- out->setVectorAntialias(gTrue); +- } ++ out->setInShading(gFalse); + #endif + + // restore graphics state +- restoreState(); +- state->setPath(savedPath); ++ restoreStateStack(savedState); + + delete shading; + } +@@ -2100,16 +2266,16 @@ + double xMin, yMin, xMax, yMax; + double x0, y0, x1, y1; + double dx, dy, mul; +- GBool dxZero, dyZero; ++ GBool dxdyZero, horiz; + double tMin, tMax, t, tx, ty; +- double s[4], sMin, sMax, tmp; ++ double sMin, sMax, tmp; + double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1; + double t0, t1, tt; + double ta[axialMaxSplits + 1]; + int next[axialMaxSplits + 1]; + GfxColor color0, color1; + int nComps; +- int i, j, k, kk; ++ int i, j, k; + + if (out->useShadedFills() && + out->axialShadedFill(state, shading)) { +@@ -2124,9 +2290,9 @@ + shading->getCoords(&x0, &y0, &x1, &y1); + dx = x1 - x0; + dy = y1 - y0; +- dxZero = fabs(dx) < 0.01; +- dyZero = fabs(dy) < 0.01; +- if (dxZero && dyZero) { ++ dxdyZero = fabs(dx) < 0.01 && fabs(dy) < 0.01; ++ horiz = fabs(dy) < fabs(dx); ++ if (dxdyZero) { + tMin = tMax = 0; + } else { + mul = 1 / (dx * dx + dy * dy); +@@ -2170,18 +2336,16 @@ + // 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: ++ // (xMin, yMin, xMax, yMax). For -1 < |dy/dx| < 1, look at the ++ // intersection with yMin, yMax: + // +- // s0 = (xMin - tx) / -dy +- // s1 = (xMax - tx) / -dy +- // s2 = (yMin - ty) / dx +- // s3 = (yMax - ty) / dx ++ // s0 = (yMin - ty) / dx ++ // s1 = (yMax - ty) / dx + // +- // and we want the middle two s values. ++ // else look at the intersection with xMin, xMax: + // +- // In the case where dx = 0, take s0 and s1; in the case where dy = +- // 0, take s2 and s3. ++ // s0 = (xMin - tx) / -dy ++ // s1 = (xMax - tx) / -dy + // + // Each filled polygon is bounded by two of these line segments + // perpdendicular to the t axis. +@@ -2190,13 +2354,10 @@ + // 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 ++ // set up + nComps = shading->getColorSpace()->getNComps(); + ta[0] = tMin; +- next[0] = axialMaxSplits / 2; +- ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax); +- next[axialMaxSplits / 2] = axialMaxSplits; ++ next[0] = axialMaxSplits; + ta[axialMaxSplits] = tMax; + + // compute the color at t = tMin +@@ -2214,32 +2375,19 @@ + // bounding box + tx = x0 + tMin * dx; + ty = y0 + tMin * dy; +- if (dxZero && dyZero) { ++ if (dxdyZero) { + sMin = sMax = 0; +- } else 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; ++ if (horiz) { ++ sMin = (yMin - ty) / dx; ++ sMax = (yMax - ty) / dx; ++ } else { ++ sMin = (xMin - tx) / -dy; ++ sMax = (xMax - tx) / -dy; ++ } ++ if (sMin > sMax) { ++ tmp = sMin; sMin = sMax; sMax = tmp; + } +- sMin = s[1]; +- sMax = s[2]; + } + ux0 = tx - sMin * dy; + uy0 = ty + sMin * dx; +@@ -2260,15 +2408,19 @@ + } 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) { ++ // require at least two splits (to avoid problems where the ++ // color doesn't change smoothly along the t axis) ++ if (j - i <= axialMaxSplits / 4) { ++ shading->getColor(tt, &color1); ++ for (k = 0; k < nComps; ++k) { ++ if (abs(color1.c[k] - color0.c[k]) > axialColorDelta) { ++ break; ++ } ++ } ++ if (k == nComps) { + break; + } + } +- if (k == nComps) { +- break; +- } + k = (i + j) / 2; + ta[k] = 0.5 * (ta[i] + ta[j]); + next[i] = k; +@@ -2286,32 +2438,19 @@ + // bounding box + tx = x0 + ta[j] * dx; + ty = y0 + ta[j] * dy; +- if (dxZero && dyZero) { ++ if (dxdyZero) { + sMin = sMax = 0; +- } else 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; ++ if (horiz) { ++ sMin = (yMin - ty) / dx; ++ sMax = (yMax - ty) / dx; ++ } else { ++ sMin = (xMin - tx) / -dy; ++ sMax = (xMax - tx) / -dy; ++ } ++ if (sMin > sMax) { ++ tmp = sMin; sMin = sMax; sMax = tmp; + } +- sMin = s[1]; +- sMax = s[2]; + } + ux1 = tx - sMin * dy; + uy1 = ty + sMin * dx; +@@ -2348,7 +2487,10 @@ + GfxColor colorA, colorB; + double xa, ya, xb, yb, ra, rb; + double ta, tb, sa, sb; +- double sz, xz, yz, sMin, sMax; ++ double sz, sMin, sMax, h; ++ double sLeft, sRight, sTop, sBottom, sZero, sDiag; ++ GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero; ++ GBool haveSMin, haveSMax; + GBool enclosed; + int ia, ib, k, n; + double *ctm; +@@ -2367,23 +2509,23 @@ + + // Compute the point at which r(s) = 0; check for the enclosed + // circles case; and compute the angles for the tangent lines. +- if (x0 == x1 && y0 == y1) { ++ h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); ++ if (h == 0) { + enclosed = gTrue; + theta = 0; // make gcc happy + sz = 0; // make gcc happy +- } else if (r0 == r1) { ++ } else if (r1 - r0 == 0) { + enclosed = gFalse; + theta = 0; + sz = 0; // make gcc happy ++ } else if (fabs(r1 - r0) >= h) { ++ enclosed = gTrue; ++ theta = 0; // make gcc happy ++ sz = 0; // make gcc happy + } else { ++ enclosed = gFalse; + sz = -r0 / (r1 - r0); +- xz = x0 + sz * (x1 - x0); +- yz = y0 + sz * (y1 - y0); +- enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0; +- theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz))); +- if (r0 > r1) { +- theta = -theta; +- } ++ theta = asin((r1 - r0) / h); + } + if (enclosed) { + alpha = 0; +@@ -2397,59 +2539,101 @@ + sMin = 0; + sMax = 1; + } else { +- sMin = 1; +- sMax = 0; +- // solve for x(s) + r(s) = xMin +- if ((x1 + r1) - (x0 + r0) != 0) { +- sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); +- if (sa < sMin) { +- sMin = sa; +- } else if (sa > sMax) { +- sMax = sa; +- } +- } +- // solve for x(s) - r(s) = xMax +- if ((x1 - r1) - (x0 - r0) != 0) { +- sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); +- if (sa < sMin) { +- sMin = sa; +- } else if (sa > sMax) { +- sMax = sa; +- } +- } +- // solve for y(s) + r(s) = yMin +- if ((y1 + r1) - (y0 + r0) != 0) { +- sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); +- if (sa < sMin) { +- sMin = sa; +- } else if (sa > sMax) { +- sMax = sa; +- } +- } +- // solve for y(s) - r(s) = yMax +- if ((y1 - r1) - (y0 - r0) != 0) { +- sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); +- if (sa < sMin) { +- sMin = sa; +- } else if (sa > sMax) { +- sMax = sa; +- } +- } +- // check against sz +- if (r0 < r1) { +- if (sMin < sz) { +- sMin = sz; +- } +- } else if (r0 > r1) { +- if (sMax > sz) { +- sMax = sz; +- } ++ // solve x(sLeft) + r(sLeft) = xMin ++ if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) { ++ sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); ++ } else { ++ sLeft = 0; // make gcc happy + } +- // check the 'extend' flags +- if (!shading->getExtend0() && sMin < 0) { ++ // solve x(sRight) - r(sRight) = xMax ++ if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) { ++ sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); ++ } else { ++ sRight = 0; // make gcc happy ++ } ++ // solve y(sBottom) + r(sBottom) = yMin ++ if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) { ++ sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); ++ } else { ++ sBottom = 0; // make gcc happy ++ } ++ // solve y(sTop) - r(sTop) = yMax ++ if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) { ++ sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); ++ } else { ++ sTop = 0; // make gcc happy ++ } ++ // solve r(sZero) = 0 ++ if ((haveSZero = fabs(r1 - r0) > 0.000001)) { ++ sZero = -r0 / (r1 - r0); ++ } else { ++ sZero = 0; // make gcc happy ++ } ++ // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2) ++ if (haveSZero) { ++ sDiag = (sqrt((xMax - xMin) * (xMax - xMin) + ++ (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); ++ } else { ++ sDiag = 0; // make gcc happy ++ } ++ // compute sMin ++ if (shading->getExtend0()) { ++ sMin = 0; ++ haveSMin = gFalse; ++ if (x0 < x1 && haveSLeft && sLeft < 0) { ++ sMin = sLeft; ++ haveSMin = gTrue; ++ } else if (x0 > x1 && haveSRight && sRight < 0) { ++ sMin = sRight; ++ haveSMin = gTrue; ++ } ++ if (y0 < y1 && haveSBottom && sBottom < 0) { ++ if (!haveSMin || sBottom > sMin) { ++ sMin = sBottom; ++ haveSMin = gTrue; ++ } ++ } else if (y0 > y1 && haveSTop && sTop < 0) { ++ if (!haveSMin || sTop > sMin) { ++ sMin = sTop; ++ haveSMin = gTrue; ++ } ++ } ++ if (haveSZero && sZero < 0) { ++ if (!haveSMin || sZero > sMin) { ++ sMin = sZero; ++ } ++ } ++ } else { + sMin = 0; + } +- if (!shading->getExtend1() && sMax > 1) { ++ // compute sMax ++ if (shading->getExtend1()) { ++ sMax = 1; ++ haveSMax = gFalse; ++ if (x1 < x0 && haveSLeft && sLeft > 1) { ++ sMax = sLeft; ++ haveSMax = gTrue; ++ } else if (x1 > x0 && haveSRight && sRight > 1) { ++ sMax = sRight; ++ haveSMax = gTrue; ++ } ++ if (y1 < y0 && haveSBottom && sBottom > 1) { ++ if (!haveSMax || sBottom < sMax) { ++ sMax = sBottom; ++ haveSMax = gTrue; ++ } ++ } else if (y1 > y0 && haveSTop && sTop > 1) { ++ if (!haveSMax || sTop < sMax) { ++ sMax = sTop; ++ haveSMax = gTrue; ++ } ++ } ++ if (haveSZero && sDiag > 1) { ++ if (!haveSMax || sDiag < sMax) { ++ sMax = sDiag; ++ } ++ } ++ } else { + sMax = 1; + } + } +@@ -2922,6 +3106,7 @@ + out->updateTextMat(state); + out->updateTextPos(state); + fontChanged = gTrue; ++ textClipBBoxEmpty = gTrue; + } + + void Gfx::opEndText(Object args[], int numArgs) { +@@ -3028,19 +3213,23 @@ + + void Gfx::opShowText(Object args[], int numArgs) { + if (!state->getFont()) { +- error(getPos(), "No font in show"); ++ error(errSyntaxError, getPos(), "No font in show"); + return; + } + if (fontChanged) { + out->updateFont(state); + fontChanged = gFalse; + } +- out->beginStringOp(state); +- doShowText(args[0].getString()); +- out->endStringOp(state); ++ if (ocState) { ++ out->beginStringOp(state); ++ doShowText(args[0].getString()); ++ out->endStringOp(state); ++ } else { ++ doIncCharCount(args[0].getString()); ++ } + } + + void Gfx::opMoveShowText(Object args[], int numArgs) { + double tx, ty; + + if (!state->getFont()) { +@@ -3055,12 +3244,16 @@ + ty = state->getLineY() - state->getLeading(); + state->textMoveTo(tx, ty); + out->updateTextPos(state); +- out->beginStringOp(state); +- doShowText(args[0].getString()); +- out->endStringOp(state); ++ if (ocState) { ++ out->beginStringOp(state); ++ doShowText(args[0].getString()); ++ out->endStringOp(state); ++ } else { ++ doIncCharCount(args[0].getString()); ++ } + } + + void Gfx::opMoveSetShowText(Object args[], int numArgs) { + double tx, ty; + + if (!state->getFont()) { +@@ -3079,9 +3272,13 @@ + out->updateWordSpace(state); + out->updateCharSpace(state); + out->updateTextPos(state); +- out->beginStringOp(state); +- doShowText(args[2].getString()); +- out->endStringOp(state); ++ if (ocState) { ++ out->beginStringOp(state); ++ doShowText(args[2].getString()); ++ out->endStringOp(state); ++ } else { ++ doIncCharCount(args[2].getString()); ++ } + } + + void Gfx::opShowSpaceText(Object args[], int numArgs) { +@@ -3091,37 +3288,48 @@ + int i; + + if (!state->getFont()) { +- error(getPos(), "No font in show/space"); ++ error(errSyntaxError, 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 * +- fabs(state->getFontSize())); ++ if (ocState) { ++ 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()) { ++ if (wMode) { ++ state->textShift(0, -obj.getNum() * 0.001 * ++ state->getFontSize()); ++ } else { ++ state->textShift(-obj.getNum() * 0.001 * ++ state->getFontSize() * ++ state->getHorizScaling(), 0); ++ } ++ out->updateTextShift(state, obj.getNum()); ++ } else if (obj.isString()) { ++ doShowText(obj.getString()); + } else { +- state->textShift(-obj.getNum() * 0.001 * +- fabs(state->getFontSize()), 0); ++ error(errSyntaxError, getPos(), ++ "Element of show/space array must be number or string"); + } +- out->updateTextShift(state, obj.getNum()); +- } else if (obj.isString()) { +- doShowText(obj.getString()); +- } else { +- error(getPos(), "Element of show/space array must be number or string"); ++ obj.free(); ++ } ++ out->endStringOp(state); ++ } else { ++ a = args[0].getArray(); ++ for (i = 0; i < a->getLength(); ++i) { ++ a->get(i, &obj); ++ if (obj.isString()) { ++ doIncCharCount(obj.getString()); ++ } ++ obj.free(); + } +- obj.free(); + } +- out->endStringOp(state); + } + + void Gfx::doShowText(GString *s) { +@@ -3130,14 +3338,18 @@ + double riseX, riseY; + CharCode code; + Unicode u[8]; +- double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY; ++ double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, ddx, ddy; + double originX, originY, tOriginX, tOriginY; ++ double x0, y0, x1, y1; + double oldCTM[6], newCTM[6]; + double *mat; + Object charProc; + Dict *resDict; + Parser *oldParser; ++ GfxState *savedState; + char *p; ++ int render; ++ GBool patternFill; + int len, n, uLen, nChars, nSpaces, i; + + font = state->getFont(); +@@ -3147,6 +3359,28 @@ + out->beginString(state, s); + } + ++ // if we're doing a pattern fill, set up clipping ++ render = state->getRender(); ++ if (!(render & 1) && ++ state->getFillColorSpace()->getMode() == csPattern) { ++ patternFill = gTrue; ++ saveState(); ++ // disable fill, enable clipping, leave stroke unchanged ++ if ((render ^ (render >> 1)) & 1) { ++ render = 5; ++ } else { ++ render = 7; ++ } ++ state->setRender(render); ++ out->updateRender(state); ++ } else { ++ patternFill = gFalse; ++ } ++ ++ state->textTransformDelta(0, state->getRise(), &riseX, &riseY); ++ x0 = state->getCurX() + riseX; ++ y0 = state->getCurY() + riseY; ++ + // handle a Type 3 char + if (font->getType() == fontType3 && out->interpretType3Chars()) { + mat = state->getCTM(); +@@ -3169,11 +3403,8 @@ + newCTM[3] *= state->getFontSize(); + newCTM[0] *= state->getHorizScaling(); + newCTM[2] *= state->getHorizScaling(); +- state->textTransformDelta(0, state->getRise(), &riseX, &riseY); + curX = state->getCurX(); + curY = state->getCurY(); +- lineX = state->getLineX(); +- lineY = state->getLineY(); + oldParser = parser; + p = s->getCString(); + len = s->getLength(); +@@ -3189,11 +3420,12 @@ + dy *= state->getFontSize(); + state->textTransformDelta(dx, dy, &tdx, &tdy); + state->transform(curX + riseX, curY + riseY, &x, &y); +- saveState(); ++ savedState = saveStateStack(); + state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y); + //~ the CTM concat values here are wrong (but never used) + out->updateCTM(state, 1, 0, 0, 1, 0, 0); +- if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy, ++ state->transformDelta(dx, dy, &ddx, &ddy); ++ if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy, + code, u, uLen)) { + ((Gfx8BitFont *)font)->getCharProc(code, &charProc); + if ((resDict = ((Gfx8BitFont *)font)->getResources())) { +@@ -3210,20 +3443,16 @@ + } + charProc.free(); + } +- restoreState(); +- // GfxState::restore() does *not* restore the current position, +- // so we deal with it here using (curX, curY) and (lineX, lineY) ++ restoreStateStack(savedState); + curX += tdx; + curY += tdy; + state->moveTo(curX, curY); +- state->textSetPos(lineX, lineY); + p += n; + len -= n; + } + parser = oldParser; + + } else if (out->useDrawChar()) { +- state->textTransformDelta(0, state->getRise(), &riseX, &riseY); + p = s->getCString(); + len = s->getLength(); + while (len > 0) { +@@ -3294,9 +3523,52 @@ + out->endString(state); + } + ++ if (patternFill) { ++ out->saveTextPos(state); ++ // tell the OutputDev to do the clipping ++ out->endTextObject(state); ++ // set up a clipping bbox so doPatternText will work -- assume ++ // that the text bounding box does not extend past the baseline in ++ // any direction by more than twice the font size ++ x1 = state->getCurX() + riseX; ++ y1 = state->getCurY() + riseY; ++ if (x0 > x1) { ++ x = x0; x0 = x1; x1 = x; ++ } ++ if (y0 > y1) { ++ y = y0; y0 = y1; y1 = y; ++ } ++ state->textTransformDelta(0, state->getFontSize(), &dx, &dy); ++ state->textTransformDelta(state->getFontSize(), 0, &dx2, &dy2); ++ dx = fabs(dx); ++ dx2 = fabs(dx2); ++ if (dx2 > dx) { ++ dx = dx2; ++ } ++ dy = fabs(dy); ++ dy2 = fabs(dy2); ++ if (dy2 > dy) { ++ dy = dy2; ++ } ++ state->clipToRect(x0 - 2 * dx, y0 - 2 * dy, x1 + 2 * dx, y1 + 2 * dy); ++ // set render mode to fill-only ++ state->setRender(0); ++ out->updateRender(state); ++ doPatternText(); ++ restoreState(); ++ out->restoreTextPos(state); ++ } ++ + updateLevel += 10 * s->getLength(); + } + ++// NB: this is only called when ocState is false. ++void Gfx::doIncCharCount(GString *s) { ++ if (out->needCharCount()) { ++ out->incCharCount(s->getLength()); ++ } ++} ++ + //------------------------------------------------------------------------ + // XObject operators + //------------------------------------------------------------------------ +@@ -3308,8 +3580,11 @@ + Object opiDict; + #endif + ++ if (!ocState && !out->needCharCount()) { ++ return; ++ } + name = args[0].getName(); + if (!res->lookupXObject(name, &obj1)) { + return; + } + if (!obj1.isStream()) { +@@ -3373,7 +3650,7 @@ + GBool maskInvert; + Stream *maskStr; + Object obj1, obj2; +- int i; ++ int i, n; + + // get info from the stream + bits = 0; +@@ -3389,19 +3666,27 @@ + obj1.free(); + dict->lookup("W", &obj1); + } +- if (!obj1.isInt()) ++ if (!obj1.isInt()) { + goto err2; ++ } + width = obj1.getInt(); + obj1.free(); ++ if (width <= 0) { ++ goto err1; ++ } + dict->lookup("Height", &obj1); + if (obj1.isNull()) { + obj1.free(); + dict->lookup("H", &obj1); + } +- if (!obj1.isInt()) ++ if (!obj1.isInt()) { + goto err2; ++ } + height = obj1.getInt(); + obj1.free(); ++ if (height <= 0) { ++ goto err1; ++ } + + // image or mask? + dict->lookup("ImageMask", &obj1); +@@ -3447,16 +3732,30 @@ + } + if (obj1.isArray()) { + obj1.arrayGet(0, &obj2); +- if (obj2.isInt() && obj2.getInt() == 1) +- invert = gTrue; ++ invert = obj2.isNum() && obj2.getNum() == 1; + obj2.free(); + } else if (!obj1.isNull()) { + goto err2; + } + obj1.free(); + ++ // if drawing is disabled, skip over inline image data ++ if (!ocState) { ++ str->reset(); ++ n = height * ((width + 7) / 8); ++ for (i = 0; i < n; ++i) { ++ str->getChar(); ++ } ++ str->close(); ++ + // draw it +- out->drawImageMask(state, ref, str, width, height, invert, inlineImg); ++ } else { ++ if (state->getFillColorSpace()->getMode() == csPattern) { ++ doPatternImageMask(ref, str, width, height, invert, inlineImg); ++ } else { ++ out->drawImageMask(state, ref, str, width, height, invert, inlineImg); ++ } ++ } + + } else { + +@@ -3581,14 +3880,36 @@ + haveSoftMask = gTrue; + } else if (maskObj.isArray()) { + // color key mask ++ haveColorKeyMask = gTrue; + for (i = 0; +- i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps; +- ++i) { ++ i+1 < maskObj.arrayGetLength() && i+1 < 2*gfxColorMaxComps; ++ i += 2) { + maskObj.arrayGet(i, &obj1); ++ if (!obj1.isInt()) { ++ obj1.free(); ++ haveColorKeyMask = gFalse; ++ break; ++ } + maskColors[i] = obj1.getInt(); + obj1.free(); ++ if (maskColors[i] < 0 || maskColors[i] >= (1 << bits)) { ++ haveColorKeyMask = gFalse; ++ break; ++ } ++ maskObj.arrayGet(i+1, &obj1); ++ if (!obj1.isInt()) { ++ obj1.free(); ++ haveColorKeyMask = gFalse; ++ break; ++ } ++ maskColors[i+1] = obj1.getInt(); ++ obj1.free(); ++ if (maskColors[i+1] < 0 || maskColors[i+1] >= (1 << bits) || ++ maskColors[i] > maskColors[i+1]) { ++ haveColorKeyMask = gFalse; ++ break; ++ } + } +- haveColorKeyMask = gTrue; + } else if (maskObj.isStream()) { + // explicit mask + if (inlineImg) { +@@ -3633,9 +3954,7 @@ + } + if (obj1.isArray()) { + obj1.arrayGet(0, &obj2); +- if (obj2.isInt() && obj2.getInt() == 1) { +- maskInvert = gTrue; +- } ++ maskInvert = obj2.isNum() && obj2.getNum() == 1; + obj2.free(); + } else if (!obj1.isNull()) { + goto err2; +@@ -3644,20 +3963,32 @@ + haveExplicitMask = gTrue; + } + ++ // if drawing is disabled, skip over inline image data ++ if (!ocState) { ++ str->reset(); ++ n = height * ((width * colorMap->getNumPixelComps() * ++ colorMap->getBits() + 7) / 8); ++ for (i = 0; i < n; ++i) { ++ str->getChar(); ++ } ++ str->close(); ++ + // draw it +- 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); ++ 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; + ++ delete colorMap; + maskObj.free(); + smaskObj.free(); + } +@@ -3683,11 +4014,12 @@ + double m[6], bbox[4]; + Object resObj; + Dict *resDict; ++ GBool oc, ocSaved; + Object obj1, obj2, obj3; + int i; + + // check for excessive recursion + if (formDepth > 100) { + return; + } + +@@ -3697,7 +4029,20 @@ + // check form type + dict->lookup("FormType", &obj1); + if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) { + error(errSyntaxError, getPos(), "Unknown form type"); ++ } ++ obj1.free(); ++ ++ // check for optional content key ++ ocSaved = ocState; ++ dict->lookupNF("OC", &obj1); ++ if (doc->getOptionalContent()->evalOCObject(&obj1, &oc) && !oc) { ++ obj1.free(); ++ if (out->needCharCount()) { ++ ocState = gFalse; ++ } else { ++ return; ++ } + } + obj1.free(); + +@@ -3705,7 +4050,8 @@ + dict->lookup("BBox", &bboxObj); + if (!bboxObj.isArray()) { + bboxObj.free(); + error(errSyntaxError, getPos(), "Bad form bounding box"); ++ ocState = ocSaved; + return; + } + for (i = 0; i < 4; ++i) { +@@ -3759,23 +4105,26 @@ + + // draw it + ++formDepth; +- doForm1(str, resDict, m, bbox, +- transpGroup, gFalse, blendingColorSpace, isolated, knockout); ++ drawForm(str, resDict, m, bbox, ++ transpGroup, gFalse, blendingColorSpace, isolated, knockout); + --formDepth; + + if (blendingColorSpace) { + delete blendingColorSpace; + } + resObj.free(); ++ ++ ocState = ocSaved; + } + +-void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox, +- GBool transpGroup, GBool softMask, +- GfxColorSpace *blendingColorSpace, +- GBool isolated, GBool knockout, +- GBool alpha, Function *transferFunc, +- GfxColor *backdropColor) { ++void Gfx::drawForm(Object *str, Dict *resDict, double *matrix, double *bbox, ++ GBool transpGroup, GBool softMask, ++ GfxColorSpace *blendingColorSpace, ++ GBool isolated, GBool knockout, ++ GBool alpha, Function *transferFunc, ++ GfxColor *backdropColor) { + Parser *oldParser; ++ GfxState *savedState; + double oldBaseMatrix[6]; + int i; + +@@ -3783,7 +4132,7 @@ + pushResources(resDict); + + // save current graphics state +- saveState(); ++ savedState = saveStateStack(); + + // kill any pre-existing path + state->clearPath(); +@@ -3847,7 +4196,7 @@ + parser = oldParser; + + // restore graphics state +- restoreState(); ++ restoreStateStack(savedState); + + // pop resource stack + popResources(); +@@ -3869,6 +4218,9 @@ + Stream *str; + int c1, c2; + ++ // NB: this function is run even if ocState is false -- doImage() is ++ // responsible for skipping over the inline image data ++ + // build dict/stream + str = buildImageStream(); + +@@ -3921,18 +4274,23 @@ + obj.free(); + + // make stream +- str = new EmbedStream(parser->getStream(), &dict, gFalse, 0); ++ if (!(str = parser->getStream())) { ++ error(errSyntaxError, getPos(), "Invalid inline image data"); ++ dict.free(); ++ return NULL; ++ } ++ str = new EmbedStream(str, &dict, gFalse, 0); + str = str->addFilters(&dict); + + return str; + } + + void Gfx::opImageData(Object args[], int numArgs) { + error(errInternal, getPos(), "Got 'ID' operator"); + } + + void Gfx::opEndImage(Object args[], int numArgs) { + error(errInternal, getPos(), "Got 'EI' operator"); + } + + //------------------------------------------------------------------------ +@@ -3967,23 +4325,88 @@ + //------------------------------------------------------------------------ + + void Gfx::opBeginMarkedContent(Object args[], int numArgs) { ++ GfxMarkedContent *mc; ++ Object obj; ++ GBool ocStateNew; ++ GString *s; ++ Unicode *u; ++ int uLen, i; ++ GfxMarkedContentKind mcKind; ++ + if (printCommands) { + printf(" marked content: %s ", args[0].getName()); +- if (numArgs == 2) +- args[2].print(stdout); ++ if (numArgs == 2) { ++ args[1].print(stdout); ++ } + printf("\n"); + fflush(stdout); + } ++ mcKind = gfxMCOther; ++ if (args[0].isName("OC") && numArgs == 2 && args[1].isName() && ++ res->lookupPropertiesNF(args[1].getName(), &obj)) { ++ if (doc->getOptionalContent()->evalOCObject(&obj, &ocStateNew)) { ++ ocState = ocStateNew; ++ } ++ obj.free(); ++ mcKind = gfxMCOptionalContent; + } + + void Gfx::opEndMarkedContent(Object args[], int numArgs) { ++ GfxMarkedContent *mc; ++ GfxMarkedContentKind mcKind; ++ ++ if (markedContentStack->getLength() > 0) { ++ mc = (GfxMarkedContent *) ++ markedContentStack->del(markedContentStack->getLength() - 1); ++ mcKind = mc->kind; ++ delete mc; ++ if (mcKind == gfxMCOptionalContent) { ++ if (markedContentStack->getLength() > 0) { ++ mc = (GfxMarkedContent *) ++ markedContentStack->get(markedContentStack->getLength() - 1); ++ ocState = mc->ocState; ++ } else { ++ ocState = gTrue; ++ } + + + void Gfx::opMarkPoint(Object args[], int numArgs) { + if (printCommands) { + printf(" mark point: %s ", args[0].getName()); + if (numArgs == 2) +- args[2].print(stdout); ++ args[1].print(stdout); + printf("\n"); + fflush(stdout); + } +@@ -3996,45 +4419,23 @@ + dict->lookup("Resources", &resObj); + resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL; + + // draw it +- doForm1(str, resDict, m, bbox); ++ drawForm(str, resDict, m, bbox); + + resObj.free(); + } +@@ -4168,6 +4598,27 @@ + out->restoreState(state); + } + ++// Create a new state stack, and initialize it with a copy of the ++// current state. ++GfxState *Gfx::saveStateStack() { ++ GfxState *oldState; ++ ++ out->saveState(state); ++ oldState = state; ++ state = state->copy(gTrue); ++ return oldState; ++} ++ ++// Switch back to the previous state stack. ++void Gfx::restoreStateStack(GfxState *oldState) { ++ while (state->hasSaves()) { ++ restoreState(); ++ } ++ delete state; ++ state = oldState; ++ out->restoreState(state); ++} ++ + void Gfx::pushResources(Dict *resDict) { + res = new GfxResources(xref, resDict, res); + } +diff -ru xpdf-3.02/xpdf/GfxFont.cc xpdf-3.03/xpdf/GfxFont.cc +--- xpdf-3.02/xpdf/GfxFont.cc 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/GfxFont.cc 2011-08-15 23:08:53.000000000 +0200 +@@ -16,6 +16,11 @@ + #include + #include + #include ++#include ++#include ++#if HAVE_STD_SORT ++#include ++#endif + #include "gmem.h" + #include "Error.h" + #include "Object.h" +@@ -25,6 +30,7 @@ + #include "CharCodeToUnicode.h" + #include "FontEncodingTables.h" + #include "BuiltinFontTables.h" ++#include "FoFiIdentifier.h" + #include "FoFiType1.h" + #include "FoFiType1C.h" + #include "FoFiTrueType.h" +@@ -93,15 +107,66 @@ + GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) { + GString *nameA; ++ Ref embFontIDA; ++ GfxFontType typeA; + GfxFont *font; + Object obj1; + +@@ -113,53 +178,235 @@ + } + obj1.free(); + +- // get font type ++ // get embedded font ID and font type ++ typeA = getFontType(xref, fontDict, &embFontIDA); ++ ++ // create the font object + font = NULL; +- fontDict->lookup("Subtype", &obj1); +- if (obj1.isName("Type1") || obj1.isName("MMType1")) { +- font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1, fontDict); +- } else if (obj1.isName("Type1C")) { +- font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1C, fontDict); +- } else if (obj1.isName("Type3")) { +- font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType3, fontDict); +- } else if (obj1.isName("TrueType")) { +- font = new Gfx8BitFont(xref, tagA, idA, nameA, fontTrueType, fontDict); +- } else if (obj1.isName("Type0")) { +- font = new GfxCIDFont(xref, tagA, idA, nameA, fontDict); ++ if (typeA < fontCIDType0) { ++ font = new Gfx8BitFont(xref, tagA, idA, nameA, typeA, embFontIDA, ++ fontDict); + } else { +- error(-1, "Unknown font type: '%s'", +- obj1.isName() ? obj1.getName() : "???"); +- font = new Gfx8BitFont(xref, tagA, idA, nameA, fontUnknownType, fontDict); ++ font = new GfxCIDFont(xref, tagA, idA, nameA, typeA, embFontIDA, ++ fontDict); + } +- obj1.free(); + + return font; + } + +-GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA) { ++GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA, ++ GfxFontType typeA, Ref embFontIDA) { + ok = gFalse; + tag = new GString(tagA); + id = idA; + name = nameA; ++ type = typeA; ++ embFontID = embFontIDA; + embFontName = NULL; +- extFontFile = NULL; + } + + GfxFont::~GfxFont() { + delete tag; + if (name) { + delete name; + } + if (embFontName) { + delete embFontName; + } +- if (extFontFile) { +- delete extFontFile; ++} ++ ++// This function extracts three pieces of information: ++// 1. the "expected" font type, i.e., the font type implied by ++// Font.Subtype, DescendantFont.Subtype, and ++// FontDescriptor.FontFile3.Subtype ++// 2. the embedded font object ID ++// 3. the actual font type - determined by examining the embedded font ++// if there is one, otherwise equal to the expected font type ++// If the expected and actual font types don't match, a warning ++// message is printed. The expected font type is not used for ++// anything else. ++GfxFontType GfxFont::getFontType(XRef *xref, Dict *fontDict, Ref *embID) { ++ GfxFontType t, expectedType; ++ FoFiIdentifierType fft; ++ Dict *fontDict2; ++ Object subtype, fontDesc, obj1, obj2, obj3, obj4; ++ GBool isType0, err; ++ ++ t = fontUnknownType; ++ embID->num = embID->gen = -1; ++ err = gFalse; ++ ++ fontDict->lookup("Subtype", &subtype); ++ expectedType = fontUnknownType; ++ isType0 = gFalse; ++ if (subtype.isName("Type1") || subtype.isName("MMType1")) { ++ expectedType = fontType1; ++ } else if (subtype.isName("Type1C")) { ++ expectedType = fontType1C; ++ } else if (subtype.isName("Type3")) { ++ expectedType = fontType3; ++ } else if (subtype.isName("TrueType")) { ++ expectedType = fontTrueType; ++ } else if (subtype.isName("Type0")) { ++ isType0 = gTrue; ++ } else { ++ error(errSyntaxWarning, -1, "Unknown font type: '{0:s}'", ++ subtype.isName() ? subtype.getName() : "???"); ++ } ++ subtype.free(); ++ ++ fontDict2 = fontDict; ++ if (fontDict->lookup("DescendantFonts", &obj1)->isArray()) { ++ if (obj1.arrayGetLength() == 0) { ++ error(errSyntaxWarning, -1, "Empty DescendantFonts array in font"); ++ obj2.initNull(); ++ } else if (obj1.arrayGet(0, &obj2)->isDict()) { ++ if (!isType0) { ++ error(errSyntaxWarning, -1, "Non-CID font with DescendantFonts array"); ++ } ++ fontDict2 = obj2.getDict(); ++ fontDict2->lookup("Subtype", &subtype); ++ if (subtype.isName("CIDFontType0")) { ++ if (isType0) { ++ expectedType = fontCIDType0; ++ } ++ } else if (subtype.isName("CIDFontType2")) { ++ if (isType0) { ++ expectedType = fontCIDType2; ++ } ++ } ++ subtype.free(); ++ } ++ } else { ++ obj2.initNull(); ++ } ++ ++ if (fontDict2->lookup("FontDescriptor", &fontDesc)->isDict()) { ++ if (fontDesc.dictLookupNF("FontFile", &obj3)->isRef()) { ++ *embID = obj3.getRef(); ++ if (expectedType != fontType1) { ++ err = gTrue; ++ } ++ } ++ obj3.free(); ++ if (embID->num == -1 && ++ fontDesc.dictLookupNF("FontFile2", &obj3)->isRef()) { ++ *embID = obj3.getRef(); ++ if (isType0) { ++ expectedType = fontCIDType2; ++ } else if (expectedType != fontTrueType) { ++ err = gTrue; ++ } ++ } ++ obj3.free(); ++ if (embID->num == -1 && ++ fontDesc.dictLookupNF("FontFile3", &obj3)->isRef()) { ++ *embID = obj3.getRef(); ++ if (obj3.fetch(xref, &obj4)->isStream()) { ++ obj4.streamGetDict()->lookup("Subtype", &subtype); ++ if (subtype.isName("Type1")) { ++ if (expectedType != fontType1) { ++ err = gTrue; ++ expectedType = isType0 ? fontCIDType0 : fontType1; ++ } ++ } else if (subtype.isName("Type1C")) { ++ if (expectedType == fontType1) { ++ expectedType = fontType1C; ++ } else if (expectedType != fontType1C) { ++ err = gTrue; ++ expectedType = isType0 ? fontCIDType0C : fontType1C; ++ } ++ } else if (subtype.isName("TrueType")) { ++ if (expectedType != fontTrueType) { ++ err = gTrue; ++ expectedType = isType0 ? fontCIDType2 : fontTrueType; ++ } ++ } else if (subtype.isName("CIDFontType0C")) { ++ if (expectedType == fontCIDType0) { ++ expectedType = fontCIDType0C; ++ } else { ++ err = gTrue; ++ expectedType = isType0 ? fontCIDType0C : fontType1C; ++ } ++ } else if (subtype.isName("OpenType")) { ++ if (expectedType == fontTrueType) { ++ expectedType = fontTrueTypeOT; ++ } else if (expectedType == fontType1) { ++ expectedType = fontType1COT; ++ } else if (expectedType == fontCIDType0) { ++ expectedType = fontCIDType0COT; ++ } else if (expectedType == fontCIDType2) { ++ expectedType = fontCIDType2OT; ++ } else { ++ err = gTrue; ++ } ++ } else { ++ error(errSyntaxError, -1, "Unknown font type '{0:s}'", ++ subtype.isName() ? subtype.getName() : "???"); ++ } ++ subtype.free(); ++ } ++ obj4.free(); ++ } ++ obj3.free(); ++ } ++ fontDesc.free(); ++ ++ t = fontUnknownType; ++ if (embID->num >= 0) { ++ obj3.initRef(embID->num, embID->gen); ++ obj3.fetch(xref, &obj4); ++ if (obj4.isStream()) { ++ obj4.streamReset(); ++ fft = FoFiIdentifier::identifyStream(&readFromStream, obj4.getStream()); ++ obj4.streamClose(); ++ switch (fft) { ++ case fofiIdType1PFA: ++ case fofiIdType1PFB: ++ t = fontType1; ++ break; ++ case fofiIdCFF8Bit: ++ t = isType0 ? fontCIDType0C : fontType1C; ++ break; ++ case fofiIdCFFCID: ++ t = fontCIDType0C; ++ break; ++ case fofiIdTrueType: ++ case fofiIdTrueTypeCollection: ++ t = isType0 ? fontCIDType2 : fontTrueType; ++ break; ++ case fofiIdOpenTypeCFF8Bit: ++ t = isType0 ? fontCIDType0COT : fontType1COT; ++ break; ++ case fofiIdOpenTypeCFFCID: ++ t = fontCIDType0COT; ++ break; ++ default: ++ error(errSyntaxError, -1, "Embedded font file may be invalid"); ++ break; ++ } ++ } ++ obj4.free(); ++ obj3.free(); ++ } ++ ++ if (t == fontUnknownType) { ++ t = expectedType; + } ++ ++ if (t != expectedType) { ++ err = gTrue; ++ } ++ ++ if (err) { ++ error(errSyntaxWarning, -1, ++ "Mismatch between font type and embedded font file"); ++ } ++ ++ obj2.free(); ++ obj1.free(); ++ ++ return t; + } + + void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) { +@@ -170,8 +417,6 @@ + // assume Times-Roman by default (for substitution purposes) + flags = fontSerif; + +- embFontID.num = -1; +- embFontID.gen = -1; + missingWidth = 0; + + if (fontDict->lookup("FontDescriptor", &obj1)->isDict()) { +@@ -189,75 +434,6 @@ + } + obj2.free(); + +- // look for embedded font file +- if (obj1.dictLookupNF("FontFile", &obj2)->isRef()) { +- embFontID = obj2.getRef(); +- if (type != fontType1) { +- error(-1, "Mismatch between font type and embedded font file"); +- type = fontType1; +- } +- } +- obj2.free(); +- if (embFontID.num == -1 && +- obj1.dictLookupNF("FontFile2", &obj2)->isRef()) { +- embFontID = obj2.getRef(); +- if (type != fontTrueType && type != fontCIDType2) { +- error(-1, "Mismatch between font type and embedded font file"); +- type = type == fontCIDType0 ? fontCIDType2 : fontTrueType; +- } +- } +- obj2.free(); +- if (embFontID.num == -1 && +- obj1.dictLookupNF("FontFile3", &obj2)->isRef()) { +- if (obj2.fetch(xref, &obj3)->isStream()) { +- obj3.streamGetDict()->lookup("Subtype", &obj4); +- if (obj4.isName("Type1")) { +- embFontID = obj2.getRef(); +- if (type != fontType1) { +- error(-1, "Mismatch between font type and embedded font file"); +- type = fontType1; +- } +- } else if (obj4.isName("Type1C")) { +- embFontID = obj2.getRef(); +- if (type != fontType1 && type != fontType1C) { +- error(-1, "Mismatch between font type and embedded font file"); +- } +- type = fontType1C; +- } else if (obj4.isName("TrueType")) { +- embFontID = obj2.getRef(); +- if (type != fontTrueType) { +- error(-1, "Mismatch between font type and embedded font file"); +- type = fontTrueType; +- } +- } else if (obj4.isName("CIDFontType0C")) { +- embFontID = obj2.getRef(); +- if (type != fontCIDType0) { +- error(-1, "Mismatch between font type and embedded font file"); +- } +- type = fontCIDType0C; +- } else if (obj4.isName("OpenType")) { +- embFontID = obj2.getRef(); +- if (type == fontTrueType) { +- type = fontTrueTypeOT; +- } else if (type == fontType1) { +- type = fontType1COT; +- } else if (type == fontCIDType0) { +- type = fontCIDType0COT; +- } else if (type == fontCIDType2) { +- type = fontCIDType2OT; +- } else { +- error(-1, "Mismatch between font type and embedded font file"); +- } +- } else { +- error(-1, "Unknown embedded font type '%s'", +- obj4.isName() ? obj4.getName() : "???"); +- } +- obj4.free(); +- } +- obj3.free(); +- } +- obj2.free(); +- + // look for MissingWidth + obj1.dictLookup("MissingWidth", &obj2); + if (obj2.isNum()) { +@@ -269,8 +445,13 @@ + obj1.dictLookup("Ascent", &obj2); + if (obj2.isNum()) { + t = 0.001 * obj2.getNum(); +- // some broken font descriptors set ascent and descent to 0 +- if (t != 0) { ++ // some broken font descriptors specify a negative ascent ++ if (t < 0) { ++ t = -t; ++ } ++ // some broken font descriptors set ascent and descent to 0; ++ // others set it to ridiculous values (e.g., 32768) ++ if (t != 0 && t < 3) { + ascent = t; + } + } +@@ -278,14 +459,14 @@ + obj1.dictLookup("Descent", &obj2); + if (obj2.isNum()) { + t = 0.001 * obj2.getNum(); ++ // some broken font descriptors specify a positive descent ++ if (t > 0) { ++ t = -t; ++ } + // some broken font descriptors set ascent and descent to 0 +- if (t != 0) { ++ if (t != 0 && t > -3) { + descent = t; + } +- // some broken font descriptors specify a positive descent +- if (descent > 0) { +- descent = -descent; +- } + } + obj2.free(); + +@@ -330,37 +511,280 @@ + return ctu; + } + +-void GfxFont::findExtFontFile() { +- static char *type1Exts[] = { ".pfa", ".pfb", ".ps", "", NULL }; +- static char *ttExts[] = { ".ttf", NULL }; ++GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) { ++ GfxFontLoc *fontLoc; ++ SysFontType sysFontType; ++ GString *path, *base14Name, *substName; ++ PSFontParam16 *psFont16; ++ Object refObj, embFontObj; ++ int substIdx, fontNum; ++ GBool embed; + +- if (name) { +- if (type == fontType1) { +- extFontFile = globalParams->findFontFile(name, type1Exts); +- } else if (type == fontTrueType) { +- extFontFile = globalParams->findFontFile(name, ttExts); ++ if (type == fontType3) { ++ return NULL; ++ } ++ ++ //----- embedded font ++ if (embFontID.num >= 0) { ++ embed = gTrue; ++ refObj.initRef(embFontID.num, embFontID.gen); ++ refObj.fetch(xref, &embFontObj); ++ if (!embFontObj.isStream()) { ++ error(errSyntaxError, -1, "Embedded font object is wrong type"); ++ embed = gFalse; ++ } ++ embFontObj.free(); ++ refObj.free(); ++ if (embed) { ++ if (ps) { ++ switch (type) { ++ case fontType1: ++ case fontType1C: ++ case fontType1COT: ++ embed = globalParams->getPSEmbedType1(); ++ break; ++ case fontTrueType: ++ case fontTrueTypeOT: ++ embed = globalParams->getPSEmbedTrueType(); ++ break; ++ case fontCIDType0C: ++ case fontCIDType0COT: ++ embed = globalParams->getPSEmbedCIDPostScript(); ++ break; ++ case fontCIDType2: ++ case fontCIDType2OT: ++ embed = globalParams->getPSEmbedCIDTrueType(); ++ break; ++ default: ++ break; ++ } ++ } ++ if (embed) { ++ fontLoc = new GfxFontLoc(); ++ fontLoc->locType = gfxFontLocEmbedded; ++ fontLoc->fontType = type; ++ fontLoc->embFontID = embFontID; ++ return fontLoc; ++ } ++ } ++ } ++ ++ //----- PS passthrough ++ if (ps && !isCIDFont() && globalParams->getPSFontPassthrough()) { ++ fontLoc = new GfxFontLoc(); ++ fontLoc->locType = gfxFontLocResident; ++ fontLoc->fontType = fontType1; ++ fontLoc->path = name->copy(); ++ return fontLoc; ++ } ++ ++ //----- PS resident Base-14 font ++ if (ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) { ++ fontLoc = new GfxFontLoc(); ++ fontLoc->locType = gfxFontLocResident; ++ fontLoc->fontType = fontType1; ++ fontLoc->path = new GString(((Gfx8BitFont *)this)->base14->base14Name); ++ return fontLoc; ++ } ++ ++ //----- external font file (fontFile, fontDir) ++ if ((path = globalParams->findFontFile(name))) { ++ if ((fontLoc = getExternalFont(path, isCIDFont()))) { ++ return fontLoc; ++ } ++ } ++ ++ //----- external font file for Base-14 font ++ if (!ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) { ++ base14Name = new GString(((Gfx8BitFont *)this)->base14->base14Name); ++ if ((path = globalParams->findFontFile(base14Name))) { ++ if ((fontLoc = getExternalFont(path, gFalse))) { ++ delete base14Name; ++ return fontLoc; ++ } ++ } ++ delete base14Name; ++ } ++ ++ //----- system font ++ if ((path = globalParams->findSystemFontFile(name, &sysFontType, ++ &fontNum))) { ++ if (isCIDFont()) { ++ if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) { ++ fontLoc = new GfxFontLoc(); ++ fontLoc->locType = gfxFontLocExternal; ++ fontLoc->fontType = fontCIDType2; ++ fontLoc->path = path; ++ fontLoc->fontNum = fontNum; ++ return fontLoc; ++ } ++ } else { ++ if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) { ++ fontLoc = new GfxFontLoc(); ++ fontLoc->locType = gfxFontLocExternal; ++ fontLoc->fontType = fontTrueType; ++ fontLoc->path = path; ++ return fontLoc; ++ } else if (sysFontType == sysFontPFA || sysFontType == sysFontPFB) { ++ fontLoc = new GfxFontLoc(); ++ fontLoc->locType = gfxFontLocExternal; ++ fontLoc->fontType = fontType1; ++ fontLoc->path = path; ++ fontLoc->fontNum = fontNum; ++ return fontLoc; ++ } ++ } ++ delete path; ++ } ++ ++ if (!isCIDFont()) { ++ ++ //----- 8-bit PS resident font ++ if (ps) { ++ if ((path = globalParams->getPSResidentFont(name))) { ++ fontLoc = new GfxFontLoc(); ++ fontLoc->locType = gfxFontLocResident; ++ fontLoc->fontType = fontType1; ++ fontLoc->path = path; ++ return fontLoc; ++ } ++ } ++ ++ //----- 8-bit font substitution ++ if (flags & fontFixedWidth) { ++ substIdx = 0; ++ } else if (flags & fontSerif) { ++ substIdx = 8; ++ } else { ++ substIdx = 4; ++ } ++ if (isBold()) { ++ substIdx += 2; ++ } ++ if (isItalic()) { ++ substIdx += 1; ++ } ++ substName = new GString(base14SubstFonts[substIdx]); ++ if (ps) { ++ error(errSyntaxWarning, -1, "Substituting font '{0:s}' for '{1:t}'", ++ base14SubstFonts[substIdx], name); ++ fontLoc = new GfxFontLoc(); ++ fontLoc->locType = gfxFontLocResident; ++ fontLoc->fontType = fontType1; ++ fontLoc->path = substName; ++ fontLoc->substIdx = substIdx; ++ return fontLoc; ++ } else { ++ path = globalParams->findFontFile(substName); ++ delete substName; ++ if (path) { ++ if ((fontLoc = getExternalFont(path, gFalse))) { ++ error(errSyntaxWarning, -1, "Substituting font '{0:s}' for '{1:t}'", ++ base14SubstFonts[substIdx], name); ++ fontLoc->substIdx = substIdx; ++ return fontLoc; ++ } ++ } ++ } ++ ++ // failed to find a substitute font ++ return NULL; ++ } ++ ++ //----- 16-bit PS resident font ++ if (ps && ((psFont16 = globalParams->getPSResidentFont16( ++ name, ++ ((GfxCIDFont *)this)->getWMode())))) { ++ fontLoc = new GfxFontLoc(); ++ fontLoc->locType = gfxFontLocResident; ++ fontLoc->fontType = fontCIDType0; // this is not used ++ fontLoc->path = psFont16->psFontName->copy(); ++ fontLoc->encoding = psFont16->encoding->copy(); ++ fontLoc->wMode = psFont16->wMode; ++ return fontLoc; ++ } ++ if (ps && ((psFont16 = globalParams->getPSResidentFontCC( ++ ((GfxCIDFont *)this)->getCollection(), ++ ((GfxCIDFont *)this)->getWMode())))) { ++ error(errSyntaxWarning, -1, "Substituting font '{0:t}' for '{1:t}'", ++ psFont16->psFontName, name); ++ fontLoc = new GfxFontLoc(); ++ fontLoc->locType = gfxFontLocResident; ++ fontLoc->fontType = fontCIDType0; // this is not used ++ fontLoc->path = psFont16->psFontName->copy(); ++ fontLoc->encoding = psFont16->encoding->copy(); ++ fontLoc->wMode = psFont16->wMode; ++ return fontLoc; ++ } ++ ++ //----- CID font substitution ++ if ((path = globalParams->findCCFontFile( ++ ((GfxCIDFont *)this)->getCollection()))) { ++ if ((fontLoc = getExternalFont(path, gTrue))) { ++ error(errSyntaxWarning, -1, "Substituting font '{0:t}' for '{1:t}'", ++ fontLoc->path, name); ++ return fontLoc; + } + } ++ ++ // failed to find a substitute font ++ return NULL; + } + +-char *GfxFont::readExtFontFile(int *len) { +- FILE *f; +- char *buf; ++GfxFontLoc *GfxFont::locateBase14Font(GString *base14Name) { ++ GString *path; + +- if (!(f = fopen(extFontFile->getCString(), "rb"))) { +- error(-1, "External font file '%s' vanished", extFontFile->getCString()); ++ path = globalParams->findFontFile(base14Name); ++ if (!path) { + return NULL; + } +- fseek(f, 0, SEEK_END); +- *len = (int)ftell(f); +- fseek(f, 0, SEEK_SET); +- buf = (char *)gmalloc(*len); +- if ((int)fread(buf, 1, *len, f) != *len) { +- error(-1, "Error reading external font file '%s'", +- extFontFile->getCString()); ++ return getExternalFont(path, gFalse); ++} ++ ++GfxFontLoc *GfxFont::getExternalFont(GString *path, GBool cid) { ++ FoFiIdentifierType fft; ++ GfxFontType fontType; ++ GfxFontLoc *fontLoc; ++ ++ fft = FoFiIdentifier::identifyFile(path->getCString()); ++ switch (fft) { ++ case fofiIdType1PFA: ++ case fofiIdType1PFB: ++ fontType = fontType1; ++ break; ++ case fofiIdCFF8Bit: ++ fontType = fontType1C; ++ break; ++ case fofiIdCFFCID: ++ fontType = fontCIDType0C; ++ break; ++ case fofiIdTrueType: ++ case fofiIdTrueTypeCollection: ++ fontType = cid ? fontCIDType2 : fontTrueType; ++ break; ++ case fofiIdOpenTypeCFF8Bit: ++ fontType = fontType1COT; ++ break; ++ case fofiIdOpenTypeCFFCID: ++ fontType = fontCIDType0COT; ++ break; ++ case fofiIdUnknown: ++ case fofiIdError: ++ default: ++ fontType = fontUnknownType; ++ break; ++ } ++ if (fontType == fontUnknownType || ++ (cid ? (fontType < fontCIDType0) ++ : (fontType >= fontCIDType0))) { ++ delete path; ++ return NULL; + } +- fclose(f); +- return buf; ++ fontLoc = new GfxFontLoc(); ++ fontLoc->locType = gfxFontLocExternal; ++ fontLoc->fontType = fontType; ++ fontLoc->path = path; ++ return fontLoc; + } + + char *GfxFont::readEmbFontFile(XRef *xref, int *len) { +@@ -386,6 +810,10 @@ + str->reset(); + while ((c = str->getChar()) != EOF) { + if (i == size) { ++ if (size > INT_MAX - 4096) { ++ error(errSyntaxError, -1, "Embedded font file is too large"); ++ break; ++ } + size += 4096; + buf = (char *)grealloc(buf, size); + } +@@ -405,8 +833,8 @@ + //------------------------------------------------------------------------ + + Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA, +- GfxFontType typeA, Dict *fontDict): +- GfxFont(tagA, idA, nameA) ++ GfxFontType typeA, Ref embFontIDA, Dict *fontDict): ++ GfxFont(tagA, idA, nameA, typeA, embFontIDA) + { + GString *name2; + BuiltinFont *builtinFont; +@@ -428,11 +856,11 @@ + Object obj1, obj2, obj3; + int n, i, a, b, m; + +- type = typeA; + ctu = NULL; + + // do font name substitution for various aliases of the Base 14 font + // names + base14 = NULL; + if (name) { + name2 = name->copy(); + i = 0; +@@ -499,9 +927,6 @@ + fontBBox[3] = 0.001 * builtinFont->bbox[3]; + } + +- // look for an external font file +- findExtFontFile(); +- + // get font matrix + fontMat[0] = fontMat[3] = 1; + fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0; +@@ -581,54 +1007,45 @@ + baseEnc = winAnsiEncoding; + } + +- // check embedded or external font file for base encoding ++ // check embedded font file for base encoding + // (only for Type 1 fonts - trying to get an encoding out of a + // TrueType font is a losing proposition) + ffT1 = NULL; + ffT1C = NULL; + buf = NULL; +- if (type == fontType1 && (extFontFile || embFontID.num >= 0)) { +- if (extFontFile) { +- ffT1 = FoFiType1::load(extFontFile->getCString()); +- } else { +- buf = readEmbFontFile(xref, &len); +- ffT1 = FoFiType1::make(buf, len); +- } +- if (ffT1) { +- if (ffT1->getName()) { +- if (embFontName) { +- delete embFontName; ++ if (type == fontType1 && embFontID.num >= 0) { ++ if ((buf = readEmbFontFile(xref, &len))) { ++ if ((ffT1 = FoFiType1::make(buf, len))) { ++ if (ffT1->getName()) { ++ if (embFontName) { ++ delete embFontName; ++ } ++ embFontName = new GString(ffT1->getName()); ++ } ++ if (!baseEnc) { ++ baseEnc = (const char **)ffT1->getEncoding(); ++ baseEncFromFontFile = gTrue; + } +- embFontName = new GString(ffT1->getName()); +- } +- if (!baseEnc) { +- baseEnc = (const char **)ffT1->getEncoding(); +- baseEncFromFontFile = gTrue; + } ++ gfree(buf); + } +- } else if (type == fontType1C && (extFontFile || embFontID.num >= 0)) { +- if (extFontFile) { +- ffT1C = FoFiType1C::load(extFontFile->getCString()); +- } else { +- buf = readEmbFontFile(xref, &len); +- ffT1C = FoFiType1C::make(buf, len); +- } +- if (ffT1C) { +- if (ffT1C->getName()) { +- if (embFontName) { +- delete embFontName; ++ } else if (type == fontType1C && embFontID.num >= 0) { ++ if ((buf = readEmbFontFile(xref, &len))) { ++ if ((ffT1C = FoFiType1C::make(buf, len))) { ++ if (ffT1C->getName()) { ++ if (embFontName) { ++ delete embFontName; ++ } ++ embFontName = new GString(ffT1C->getName()); ++ } ++ if (!baseEnc) { ++ baseEnc = (const char **)ffT1C->getEncoding(); ++ baseEncFromFontFile = gTrue; + } +- embFontName = new GString(ffT1C->getName()); +- } +- if (!baseEnc) { +- baseEnc = (const char **)ffT1C->getEncoding(); +- baseEncFromFontFile = gTrue; + } ++ gfree(buf); + } + } +- if (buf) { +- gfree(buf); +- } + + // get default base encoding + if (!baseEnc) { +@@ -644,7 +1061,7 @@ + + // copy the base encoding + for (i = 0; i < 256; ++i) { +- enc[i] = baseEnc[i]; ++ enc[i] = (char *)baseEnc[i]; + if ((encFree[i] = baseEncFromFontFile) && enc[i]) { + enc[i] = copyString(baseEnc[i]); + } +@@ -654,11 +1071,10 @@ + // T1C->T1 conversion (since the 'seac' operator depends on having + // the accents in the encoding), so we fill in any gaps from + // StandardEncoding +- if (type == fontType1C && (extFontFile || embFontID.num >= 0) && +- baseEncFromFontFile) { ++ if (type == fontType1C && embFontID.num >= 0 && baseEncFromFontFile) { + for (i = 0; i < 256; ++i) { + if (!enc[i] && standardEncoding[i]) { + enc[i] = (char *)standardEncoding[i]; + encFree[i] = gFalse; + } + } +@@ -734,14 +1151,21 @@ + } + + // pass 2: try to fill in the missing chars, looking for names of +- // the form 'Axx', 'xx', 'Ann', 'ABnn', or 'nn', where 'A' and 'B' +- // are any letters, 'xx' is two hex digits, and 'nn' is 2-4 +- // decimal digits ++ // any of the following forms: ++ // - 'xx' ++ // - 'Axx' ++ // - 'nn' ++ // - 'Ann' ++ // - 'ABnn' ++ // - 'unixxxx' (possibly followed by garbage - some Arabic files ++ // use 'uni0628.medi', etc.) ++ // where 'A' and 'B' are any letters, 'xx' is two hex digits, 'xxxx' ++ // is four hex digits, and 'nn' is 2-4 decimal digits + if (missing && globalParams->getMapNumericCharNames()) { + for (code = 0; code < 256; ++code) { + if ((charName = enc[code]) && !toUnicode[code] && + strcmp(charName, ".notdef")) { + n = strlen(charName); + code2 = -1; + if (hex && n == 3 && isalpha(charName[0]) && + isxdigit(charName[1]) && isxdigit(charName[2])) { +@@ -758,8 +1182,13 @@ + } else if (n >= 4 && n <= 6 && + isdigit(charName[2]) && isdigit(charName[3])) { + code2 = atoi(charName+2); ++ } else if (n >= 7 && charName[0] == 'u' && charName[1] == 'n' && ++ charName[2] == 'i' && ++ isxdigit(charName[3]) && isxdigit(charName[4]) && ++ isxdigit(charName[5]) && isxdigit(charName[6])) { ++ sscanf(charName + 3, "%x", &code2); + } +- if (code2 >= 0 && code2 <= 0xff) { ++ if (code2 >= 0 && code2 <= 0xffff) { + toUnicode[code] = (Unicode)code2; + } + } +@@ -835,7 +1264,7 @@ + obj1.arrayGet(code - firstChar, &obj2); + if (obj2.isNum()) { + widths[code] = obj2.getNum() * mul; +- if (widths[code] != widths[firstChar]) { ++ if (fabs(widths[code] - widths[firstChar]) > 0.00001) { + flags &= ~fontFixedWidth; + } + } +@@ -945,9 +1374,10 @@ + // TrueType font has a Macintosh Roman cmap, use it, and + // reverse map the char names through MacRomanEncoding to + // get char codes. +- // 1b. If the TrueType font has a Microsoft Unicode cmap or a +- // non-Microsoft Unicode cmap, use it, and use the Unicode +- // indexes, not the char codes. ++ // 1b. If the PDF font is not symbolic or the PDF font is not ++ // embedded, and the TrueType font has a Microsoft Unicode ++ // cmap or a non-Microsoft Unicode cmap, use it, and use the ++ // Unicode indexes, not the char codes. + // 1c. If the PDF font is symbolic and the TrueType font has a + // Microsoft Symbol cmap, use it, and use char codes + // directly (possibly with an offset of 0xf000). +@@ -983,7 +1413,8 @@ + if (usesMacRomanEnc && macRomanCmap >= 0) { + cmap = macRomanCmap; + useMacRoman = gTrue; +- } else if (unicodeCmap >= 0) { ++ } else if ((!(flags & fontSymbolic) || embFontID.num < 0) && ++ unicodeCmap >= 0) { + cmap = unicodeCmap; + useUnicode = gTrue; + } else if ((flags & fontSymbolic) && msSymbolCmap >= 0) { +@@ -1010,6 +1441,8 @@ + if ((code = globalParams->getMacRomanCharCode(charName))) { + map[i] = ff->mapCodeToGID(cmap, code); + } ++ } else { ++ map[i] = -1; + } + } + +@@ -1020,6 +1453,8 @@ + (u = globalParams->mapNameToUnicode(charName))) || + (n = ctu->mapToUnicode((CharCode)i, &u, 1))) { + map[i] = ff->mapCodeToGID(cmap, u); ++ } else { ++ map[i] = -1; + } + } + +@@ -1035,8 +1470,8 @@ + + // try the TrueType 'post' table to handle any unmapped characters + for (i = 0; i < 256; ++i) { +- if (!map[i] && (charName = enc[i])) { ++ if (map[i] <= 0 && (charName = enc[i])) { + map[i] = ff->mapNameToGID(charName); + } + } + +@@ -1077,12 +1530,11 @@ + GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA, +- Dict *fontDict): +- GfxFont(tagA, idA, nameA) ++ GfxFontType typeA, Ref embFontIDA, Dict *fontDict): ++ GfxFont(tagA, idA, nameA, typeA, embFontIDA) + { + Dict *desFontDict; +- GString *collection, *cMapName; + Object desFontDictObj; + Object obj1, obj2, obj3, obj4, obj5, obj6; + CharCodeToUnicode *utu; +@@ -1091,8 +1545,10 @@ + ascent = 0.95; + descent = -0.35; + fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0; ++ collection = NULL; + cMap = NULL; + ctu = NULL; ++ ctuUsesCharCode = gTrue; + widths.defWidth = 1.0; + widths.defHeight = -1.0; + widths.defVY = 0.880; +@@ -1104,52 +1560,38 @@ + cidToGIDLen = 0; + + // get the descendant font +- if (!fontDict->lookup("DescendantFonts", &obj1)->isArray()) { +- error(-1, "Missing DescendantFonts entry in Type 0 font"); ++ if (!fontDict->lookup("DescendantFonts", &obj1)->isArray() || ++ obj1.arrayGetLength() == 0) { ++ error(errSyntaxError, -1, ++ "Missing or empty DescendantFonts entry in Type 0 font"); + obj1.free(); ++ + goto err1; + } + if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) { +- error(-1, "Bad descendant font in Type 0 font"); +- goto err3; ++ error(errSyntaxError, -1, "Bad descendant font in Type 0 font"); ++ goto err2; + } + obj1.free(); + desFontDict = desFontDictObj.getDict(); + +- // font type +- if (!desFontDict->lookup("Subtype", &obj1)) { +- error(-1, "Missing Subtype entry in Type 0 descendant font"); +- goto err3; +- } +- if (obj1.isName("CIDFontType0")) { +- type = fontCIDType0; +- } else if (obj1.isName("CIDFontType2")) { +- type = fontCIDType2; +- } else { +- error(-1, "Unknown Type 0 descendant font type '%s'", +- obj1.isName() ? obj1.getName() : "???"); +- goto err3; +- } +- obj1.free(); +- + // get info from font descriptor + readFontDescriptor(xref, desFontDict); + +- // look for an external font file +- findExtFontFile(); +- + //----- encoding info ----- + + // char collection + if (!desFontDict->lookup("CIDSystemInfo", &obj1)->isDict()) { +- error(-1, "Missing CIDSystemInfo dictionary in Type 0 descendant font"); +- goto err3; ++ error(errSyntaxError, -1, ++ "Missing CIDSystemInfo dictionary in Type 0 descendant font"); ++ goto err2; + } + obj1.dictLookup("Registry", &obj2); + obj1.dictLookup("Ordering", &obj3); + if (!obj2.isString() || !obj3.isString()) { +- error(-1, "Invalid CIDSystemInfo dictionary in Type 0 descendant font"); +- goto err4; ++ error(errSyntaxError, -1, ++ "Invalid CIDSystemInfo dictionary in Type 0 descendant font"); ++ goto err3; + } + collection = obj2.getString()->copy()->append('-')->append(obj3.getString()); + obj3.free(); +@@ -1158,19 +1600,18 @@ + + // look for a ToUnicode CMap + if (!(ctu = readToUnicodeCMap(fontDict, 16, NULL))) { ++ ctuUsesCharCode = gFalse; + +- // the "Adobe-Identity" and "Adobe-UCS" collections don't have +- // cidToUnicode files +- if (collection->cmp("Adobe-Identity") && +- collection->cmp("Adobe-UCS")) { +- +- // look for a user-supplied .cidToUnicode file +- if (!(ctu = globalParams->getCIDToUnicode(collection))) { +- error(-1, "Unknown character collection '%s'", +- collection->getCString()); +- // fall-through, assuming the Identity mapping -- this appears +- // to match Adobe's behavior +- } ++ // use an identity mapping for the "Adobe-Identity" and ++ // "Adobe-UCS" collections ++ if (!collection->cmp("Adobe-Identity") || ++ !collection->cmp("Adobe-UCS")) { ++ ctu = CharCodeToUnicode::makeIdentityMapping(); ++ ++ // look for a user-supplied .cidToUnicode file ++ } else if (!(ctu = globalParams->getCIDToUnicode(collection))) { ++ error(errSyntaxError, -1, ++ "Unknown character collection '{0:t}'", collection); + } + } + +@@ -1193,43 +1634,35 @@ + } + + // encoding (i.e., CMap) +- //~ need to handle a CMap stream here +- //~ also need to deal with the UseCMap entry in the stream dict +- if (!fontDict->lookup("Encoding", &obj1)->isName()) { +- error(-1, "Missing or invalid Encoding entry in Type 0 font"); +- delete collection; +- goto err3; ++ if (fontDict->lookup("Encoding", &obj1)->isNull()) { ++ error(errSyntaxError, -1, "Missing Encoding entry in Type 0 font"); ++ goto err2; + } +- cMapName = new GString(obj1.getName()); +- obj1.free(); +- if (!(cMap = globalParams->getCMap(collection, cMapName))) { +- error(-1, "Unknown CMap '%s' for character collection '%s'", +- cMapName->getCString(), collection->getCString()); +- delete collection; +- delete cMapName; ++ if (!(cMap = CMap::parse(NULL, collection, &obj1))) { + goto err2; + } +- delete collection; +- delete cMapName; ++ obj1.free(); + +- // CIDToGIDMap (for embedded TrueType fonts) +- if (type == fontCIDType2) { ++ // CIDToGIDMap ++ // (the PDF spec only allows these for TrueType fonts, but Acrobat ++ // apparently also allows them for OpenType CFF fonts) ++ if (type == fontCIDType2 || type == fontCIDType0COT) { + desFontDict->lookup("CIDToGIDMap", &obj1); + if (obj1.isStream()) { + cidToGIDLen = 0; + i = 64; +@@ -1387,17 +1830,19 @@ + ok = gTrue; + return; + +- err4: ++ err3: + obj3.free(); + obj2.free(); +- err3: +- obj1.free(); + err2: ++ obj1.free(); + desFontDictObj.free(); + err1:; + } + + GfxCIDFont::~GfxCIDFont() { ++ if (collection) { ++ delete collection; ++ } + if (cMap) { + cMap->decRefCnt(); + } +@@ -1425,12 +1871,16 @@ + return 1; + } + + *code = (CharCode)(cid = cMap->getCID(s, len, &c, &n)); + if (ctu) { +- *uLen = ctu->mapToUnicode(cid, u, uSize); ++ *uLen = ctu->mapToUnicode(ctuUsesCharCode ? c : cid, u, uSize); + } else { + *uLen = 0; + } ++ if (!*uLen && uSize >= 1 && globalParams->getMapUnknownCharNames()) { ++ u[0] = *code; ++ *uLen = 1; ++ } + + // horizontal + if (cMap->getWMode() == 0) { +diff -ru xpdf-3.02/xpdf/GfxFont.h xpdf-3.03/xpdf/GfxFont.h +--- xpdf-3.02/xpdf/GfxFont.h 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/GfxFont.h 2011-08-15 23:08:53.000000000 +0200 +@@ -91,7 +127,8 @@ + // Build a GfxFont object. + static GfxFont *makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict); + +- GfxFont(char *tagA, Ref idA, GString *nameA); ++ GfxFont(char *tagA, Ref idA, GString *nameA, ++ GfxFontType typeA, Ref embFontIDA); + + virtual ~GfxFont(); + +@@ -126,10 +160,6 @@ + // NULL if there is no embedded font. + GString *getEmbeddedFontName() { return embFontName; } + +- // Get the name of the external font file. Returns NULL if there +- // is no external font file. +- GString *getExtFontFile() { return extFontFile; } +- + // Get font descriptor flags. + int getFlags() { return flags; } + GBool isFixedWidth() { return flags & fontFixedWidth; } +@@ -151,8 +181,14 @@ + // Return the writing mode (0=horizontal, 1=vertical). + virtual int getWMode() { return 0; } + +- // Read an external or embedded font file into a buffer. +- char *readExtFontFile(int *len); ++ // Locate the font file for this font. If is true, includes PS ++ // printer-resident fonts. Returns NULL on failure. ++ GfxFontLoc *locateFont(XRef *xref, GBool ps); ++ ++ // Locate a Base-14 font file for a specified font name. ++ static GfxFontLoc *locateBase14Font(GString *base14Name); ++ ++ // Read an embedded font file into a buffer. + char *readEmbFontFile(XRef *xref, int *len); + + // Get the next char from a string of bytes, returning the +@@ -167,22 +203,21 @@ + + protected: + ++ static GfxFontType getFontType(XRef *xref, Dict *fontDict, Ref *embID); + void readFontDescriptor(XRef *xref, Dict *fontDict); + CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits, + CharCodeToUnicode *ctu); +- void findExtFontFile(); ++ static GfxFontLoc *getExternalFont(GString *path, GBool cid); + + GString *tag; // PDF font tag + Ref id; // reference (used as unique ID) + GString *name; // font name + GfxFontType type; // type of font + int flags; // font descriptor flags + GString *embFontName; // name of embedded font + Ref embFontID; // ref to embedded font file stream +- GString *extFontFile; // external font file name +- double fontMat[6]; // font matrix (Type 3 only) +- double fontBBox[4]; // font bounding box (Type 3 only) ++ double fontMat[6]; // font matrix ++ double fontBBox[4]; // font bounding box + double missingWidth; // "default" width + double ascent; // max height above baseline + double descent; // max depth below baseline +@@ -197,7 +232,7 @@ + public: + + Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA, +- GfxFontType typeA, Dict *fontDict); ++ GfxFontType typeA, Ref embFontIDA, Dict *fontDict); + + virtual ~Gfx8BitFont(); + +@@ -247,6 +283,8 @@ + double widths[256]; // character widths + Object charProcs; // Type 3 CharProcs dictionary + Object resources; // Type 3 Resources dictionary ++ ++ friend class GfxFont; + }; + + //------------------------------------------------------------------------ +@@ -257,7 +295,7 @@ + public: + + GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA, +- Dict *fontDict); ++ GfxFontType typeA, Ref embFontIDA, Dict *fontDict); + + virtual ~GfxCIDFont(); + +@@ -278,15 +316,18 @@ + + private: + ++ GString *collection; // collection name + CMap *cMap; // char code --> CID +- CharCodeToUnicode *ctu; // CID --> Unicode ++ CharCodeToUnicode *ctu; // CID/char code --> Unicode ++ GBool ctuUsesCharCode; // true: ctu maps char code to Unicode; ++ // false: ctu maps CID to Unicode + GfxFontCIDWidths widths; // character widths +diff -ru xpdf-3.02/xpdf/Gfx.h xpdf-3.03/xpdf/Gfx.h +--- xpdf-3.02/xpdf/Gfx.h 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/Gfx.h 2011-08-15 23:08:53.000000000 +0200 +@@ -18,6 +18,8 @@ + #include "gtypes.h" + + class GString; ++class GList; + class PDFDoc; + class XRef; + class Array; + class Stream; +@@ -141,8 +169,16 @@ + // Get the current graphics state object. + GfxState *getState() { return state; } + ++ void drawForm(Object *str, Dict *resDict, double *matrix, double *bbox, ++ GBool transpGroup = gFalse, GBool softMask = gFalse, ++ GfxColorSpace *blendingColorSpace = NULL, ++ GBool isolated = gFalse, GBool knockout = gFalse, ++ GBool alpha = gFalse, Function *transferFunc = NULL, ++ GfxColor *backdropColor = NULL); ++ + private: + + PDFDoc *doc; + XRef *xref; // the xref table for this PDF file + OutputDev *out; // output device + GBool subPage; // is this a sub-page object? +@@ -157,6 +193,12 @@ + double baseMatrix[6]; // default matrix for most recent + // page/form/pattern + int formDepth; ++ double textClipBBox[4]; // text clipping bounding box ++ GBool textClipBBoxEmpty; // true if textClipBBox has not been ++ // initialized yet ++ GBool ocState; // true if drawing is enabled, false if ++ // disabled ++ GList *markedContentStack; // BMC/BDC/EMC stack [GfxMarkedContent] + + Parser *parser; // parser for page content stream(s) + +@@ -224,10 +266,13 @@ + void opCloseEOFillStroke(Object args[], int numArgs); + void doPatternFill(GBool eoFill); + void doPatternStroke(); ++ void doPatternText(); ++ void doPatternImageMask(Object *ref, Stream *str, int width, int height, ++ GBool invert, GBool inlineImg); + void doTilingPatternFill(GfxTilingPattern *tPat, +- GBool stroke, GBool eoFill); ++ GBool stroke, GBool eoFill, GBool text); + void doShadingPatternFill(GfxShadingPattern *sPat, +- GBool stroke, GBool eoFill); ++ GBool stroke, GBool eoFill, GBool text); + void opShFill(Object args[], int numArgs); + void doFunctionShFill(GfxFunctionShading *shading); + void doFunctionShFill1(GfxFunctionShading *shading, +@@ -274,17 +319,12 @@ + void opMoveSetShowText(Object args[], int numArgs); + void opShowSpaceText(Object args[], int numArgs); + void doShowText(GString *s); ++ void doIncCharCount(GString *s); + + // XObject operators + void opXObject(Object args[], int numArgs); + void doImage(Object *ref, Stream *str, GBool inlineImg); + void doForm(Object *str); +- void doForm1(Object *str, Dict *resDict, double *matrix, double *bbox, +- GBool transpGroup = gFalse, GBool softMask = gFalse, +- GfxColorSpace *blendingColorSpace = NULL, +- GBool isolated = gFalse, GBool knockout = gFalse, +- GBool alpha = gFalse, Function *transferFunc = NULL, +- GfxColor *backdropColor = NULL); + + // in-line image operators + void opBeginImage(Object args[], int numArgs); +@@ -305,6 +345,8 @@ + void opEndMarkedContent(Object args[], int numArgs); + void opMarkPoint(Object args[], int numArgs); + ++ GfxState *saveStateStack(); ++ void restoreStateStack(GfxState *oldState); + void pushResources(Dict *resDict); + void popResources(); + }; +diff -ru xpdf-3.02/xpdf/GfxState.cc xpdf-3.03/xpdf/GfxState.cc +--- xpdf-3.02/xpdf/GfxState.cc 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/GfxState.cc 2011-08-15 23:08:53.000000000 +0200 +@@ -819,26 +854,27 @@ + obj1.free(); + arr->get(1, &obj1); + if (!obj1.isStream()) { + error(errSyntaxError, -1, "Bad ICCBased color space (stream)"); + obj1.free(); + return NULL; + } + dict = obj1.streamGetDict(); + if (!dict->lookup("N", &obj2)->isInt()) { + error(errSyntaxError, -1, "Bad ICCBased color space (N)"); + obj2.free(); + obj1.free(); + return NULL; + } + nCompsA = obj2.getInt(); + obj2.free(); +- if (nCompsA > gfxColorMaxComps) { +- error(-1, "ICCBased color space with too many (%d > %d) components", +- nCompsA, gfxColorMaxComps); +- nCompsA = gfxColorMaxComps; ++ if (nCompsA > 4) { ++ error(errSyntaxError, -1, ++ "ICCBased color space with too many ({0:d} > 4) components", ++ nCompsA); ++ nCompsA = 4; + } + if (dict->lookup("Alternate", &obj2)->isNull() || +@@ -986,8 +1025,9 @@ + 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; ++ error(errSyntaxError, -1, ++ "Bad Indexed color space (lookup table stream too short)"); ++ cs->indexHigh = indexHighA = i - 1; + } + cs->lookup[i*n + j] = (Guchar)x; + } +@@ -995,8 +1035,9 @@ + 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; ++ error(errSyntaxError, -1, ++ "Bad Indexed color space (lookup table string too short)"); ++ cs->indexHigh = indexHighA = obj1.getString()->getLength() / n - 1; + } + s = obj1.getString()->getCString(); + for (i = 0; i <= indexHighA; ++i) { +@@ -1254,14 +1354,7 @@ + goto err4; + } + obj1.free(); +- cs = new GfxDeviceNColorSpace(nCompsA, altA, funcA); +- cs->nonMarking = gTrue; +- for (i = 0; i < nCompsA; ++i) { +- cs->names[i] = namesA[i]; +- if (namesA[i]->cmp("None")) { +- cs->nonMarking = gFalse; +- } +- } ++ cs = new GfxDeviceNColorSpace(nCompsA, namesA, altA, funcA); + return cs; + + err4: +@@ -3187,7 +3299,7 @@ + GfxIndexedColorSpace *indexedCS; + GfxSeparationColorSpace *sepCS; + int maxPixel, indexHigh; +- Guchar *lookup2; ++ Guchar *indexedLookup; + Function *sepFunc; + Object obj; + double x[gfxColorMaxComps]; +@@ -3204,6 +3316,7 @@ + // initialize + for (k = 0; k < gfxColorMaxComps; ++k) { + lookup[k] = NULL; ++ lookup2[k] = NULL; + } + + // get decode map +@@ -3236,10 +3353,18 @@ + // 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. +- // ++ for (k = 0; k < nComps; ++k) { ++ lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1, ++ sizeof(GfxColorComp)); ++ for (i = 0; i <= maxPixel; ++i) { ++ lookup[k][i] = dblToCol(decodeLow[k] + ++ (i * decodeRange[k]) / maxPixel); ++ } ++ } ++ + // Optimization: for Indexed and Separation color spaces (which have +- // only one component), we store color values in the lookup table +- // rather than component values. ++ // only one component), we pre-compute a second lookup table with ++ // color values + colorSpace2 = NULL; + nComps2 = 0; + if (colorSpace->getMode() == csIndexed) { +@@ -3250,20 +3375,22 @@ + colorSpace2 = indexedCS->getBase(); + indexHigh = indexedCS->getIndexHigh(); + nComps2 = colorSpace2->getNComps(); +- lookup2 = indexedCS->getLookup(); ++ indexedLookup = indexedCS->getLookup(); + colorSpace2->getDefaultRanges(x, y, indexHigh); + for (k = 0; k < nComps2; ++k) { +- lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1, +- sizeof(GfxColorComp)); +- 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; +- } +- lookup[k][i] = +- dblToCol(x[k] + (lookup2[j*nComps2 + k] / 255.0) * y[k]); ++ lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1, ++ sizeof(GfxColorComp)); ++ } ++ 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) { ++ lookup2[k][i] = ++ dblToCol(x[k] + (indexedLookup[j*nComps2 + k] / 255.0) * y[k]); + } + } + } else if (colorSpace->getMode() == csSeparation) { +@@ -3272,21 +3399,14 @@ + nComps2 = colorSpace2->getNComps(); + sepFunc = sepCS->getFunc(); + for (k = 0; k < nComps2; ++k) { +- lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1, +- sizeof(GfxColorComp)); +- for (i = 0; i <= maxPixel; ++i) { +- x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel; +- sepFunc->transform(x, y); +- lookup[k][i] = dblToCol(y[k]); +- } ++ lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1, ++ sizeof(GfxColorComp)); + } +- } else { +- for (k = 0; k < nComps; ++k) { +- lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1, +- sizeof(GfxColorComp)); +- for (i = 0; i <= maxPixel; ++i) { +- lookup[k][i] = dblToCol(decodeLow[k] + +- (i * decodeRange[k]) / maxPixel); ++ for (i = 0; i <= maxPixel; ++i) { ++ x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel; ++ sepFunc->transform(x, y); ++ for (k = 0; k < nComps2; ++k) { ++ lookup2[k][i] = dblToCol(y[k]); + } + } + } +@@ -3309,24 +3429,24 @@ + colorSpace2 = NULL; + for (k = 0; k < gfxColorMaxComps; ++k) { + lookup[k] = NULL; ++ lookup2[k] = NULL; + } + n = 1 << bits; ++ for (k = 0; k < nComps; ++k) { ++ lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); ++ memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp)); ++ } + if (colorSpace->getMode() == csIndexed) { + colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase(); + for (k = 0; k < nComps2; ++k) { +- lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); +- memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp)); ++ lookup2[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); ++ memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp)); + } + } else if (colorSpace->getMode() == csSeparation) { + colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt(); + for (k = 0; k < nComps2; ++k) { +- lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); +- memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp)); +- } +- } else { +- for (k = 0; k < nComps; ++k) { +- lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); +- memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp)); ++ lookup2[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); ++ memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp)); + } + } + for (i = 0; i < nComps; ++i) { +@@ -3342,6 +3462,7 @@ + delete colorSpace; + for (i = 0; i < gfxColorMaxComps; ++i) { + gfree(lookup[i]); ++ gfree(lookup2[i]); + } + } + +@@ -3351,7 +3472,7 @@ + + if (colorSpace2) { + for (i = 0; i < nComps2; ++i) { +- color.c[i] = lookup[i][x[0]]; ++ color.c[i] = lookup2[i][x[0]]; + } + colorSpace2->getGray(&color, gray); + } else { +@@ -3368,7 +3489,7 @@ + + if (colorSpace2) { + for (i = 0; i < nComps2; ++i) { +- color.c[i] = lookup[i][x[0]]; ++ color.c[i] = lookup2[i][x[0]]; + } + colorSpace2->getRGB(&color, rgb); + } else { +@@ -3385,7 +3506,7 @@ + + if (colorSpace2) { + for (i = 0; i < nComps2; ++i) { +- color.c[i] = lookup[i][x[0]]; ++ color.c[i] = lookup2[i][x[0]]; + } + colorSpace2->getCMYK(&color, cmyk); + } else { +@@ -3405,6 +3527,88 @@ + } + } + ++void GfxImageColorMap::getGrayByteLine(Guchar *in, Guchar *out, int n) { ++ GfxColor color; ++ GfxGray gray; ++ int i, j; ++ ++ if (colorSpace2) { ++ for (j = 0; j < n; ++j) { ++ for (i = 0; i < nComps2; ++i) { ++ color.c[i] = lookup2[i][in[j]]; ++ } ++ colorSpace2->getGray(&color, &gray); ++ out[j] = colToByte(gray); ++ } ++ } else { ++ for (j = 0; j < n; ++j) { ++ for (i = 0; i < nComps; ++i) { ++ color.c[i] = lookup[i][in[j * nComps + i]]; ++ } ++ colorSpace->getGray(&color, &gray); ++ out[j] = colToByte(gray); ++ } ++ } ++} ++ ++void GfxImageColorMap::getRGBByteLine(Guchar *in, Guchar *out, int n) { ++ GfxColor color; ++ GfxRGB rgb; ++ int i, j; ++ ++ if (colorSpace2) { ++ for (j = 0; j < n; ++j) { ++ for (i = 0; i < nComps2; ++i) { ++ color.c[i] = lookup2[i][in[j]]; ++ } ++ colorSpace2->getRGB(&color, &rgb); ++ out[j*3] = colToByte(rgb.r); ++ out[j*3 + 1] = colToByte(rgb.g); ++ out[j*3 + 2] = colToByte(rgb.b); ++ } ++ } else { ++ for (j = 0; j < n; ++j) { ++ for (i = 0; i < nComps; ++i) { ++ color.c[i] = lookup[i][in[j * nComps + i]]; ++ } ++ colorSpace->getRGB(&color, &rgb); ++ out[j*3] = colToByte(rgb.r); ++ out[j*3 + 1] = colToByte(rgb.g); ++ out[j*3 + 2] = colToByte(rgb.b); ++ } ++ } ++} ++ ++void GfxImageColorMap::getCMYKByteLine(Guchar *in, Guchar *out, int n) { ++ GfxColor color; ++ GfxCMYK cmyk; ++ int i, j; ++ ++ if (colorSpace2) { ++ for (j = 0; j < n; ++j) { ++ for (i = 0; i < nComps2; ++i) { ++ color.c[i] = lookup2[i][in[j]]; ++ } ++ colorSpace2->getCMYK(&color, &cmyk); ++ out[j*4] = colToByte(cmyk.c); ++ out[j*4 + 1] = colToByte(cmyk.m); ++ out[j*4 + 2] = colToByte(cmyk.y); ++ out[j*4 + 3] = colToByte(cmyk.k); ++ } ++ } else { ++ for (j = 0; j < n; ++j) { ++ for (i = 0; i < nComps; ++i) { ++ color.c[i] = lookup[i][in[j * nComps + i]]; ++ } ++ colorSpace->getCMYK(&color, &cmyk); ++ out[j*4] = colToByte(cmyk.c); ++ out[j*4 + 1] = colToByte(cmyk.m); ++ out[j*4 + 2] = colToByte(cmyk.y); ++ out[j*4 + 3] = colToByte(cmyk.k); ++ } ++ } ++} ++ + //------------------------------------------------------------------------ + // GfxSubpath and GfxPath + //------------------------------------------------------------------------ +@@ -3526,13 +3730,18 @@ + } + + void GfxPath::lineTo(double x, double y) { +- if (justMoved) { ++ if (justMoved || (n > 0 && subpaths[n-1]->isClosed())) { + if (n >= size) { + size *= 2; + subpaths = (GfxSubpath **) + greallocn(subpaths, size, sizeof(GfxSubpath *)); + } +- subpaths[n] = new GfxSubpath(firstX, firstY); ++ if (justMoved) { ++ subpaths[n] = new GfxSubpath(firstX, firstY); ++ } else { ++ subpaths[n] = new GfxSubpath(subpaths[n-1]->getLastX(), ++ subpaths[n-1]->getLastY()); ++ } + ++n; + justMoved = gFalse; + } +@@ -3541,13 +3750,18 @@ + + void GfxPath::curveTo(double x1, double y1, double x2, double y2, + double x3, double y3) { +- if (justMoved) { ++ if (justMoved || (n > 0 && subpaths[n-1]->isClosed())) { + if (n >= size) { + size *= 2; + subpaths = (GfxSubpath **) + greallocn(subpaths, size, sizeof(GfxSubpath *)); + } +- subpaths[n] = new GfxSubpath(firstX, firstY); ++ if (justMoved) { ++ subpaths[n] = new GfxSubpath(firstX, firstY); ++ } else { ++ subpaths[n] = new GfxSubpath(subpaths[n-1]->getLastX(), ++ subpaths[n-1]->getLastY()); ++ } + ++n; + justMoved = gFalse; + } +@@ -3658,6 +3872,7 @@ + strokeOpacity = 1; + fillOverprint = gFalse; + strokeOverprint = gFalse; ++ overprintMode = 0; + transfer[0] = transfer[1] = transfer[2] = transfer[3] = NULL; + + lineWidth = 1; +@@ -3719,13 +3934,10 @@ + // this gets set to NULL by restore() + delete path; + } +- if (saved) { +- delete saved; +- } + } + + // Used for copy(); +-GfxState::GfxState(GfxState *state) { ++GfxState::GfxState(GfxState *state, GBool copyPath) { + int i; + + memcpy(this, state, sizeof(GfxState)); +@@ -3750,6 +3962,9 @@ + lineDash = (double *)gmallocn(lineDashLength, sizeof(double)); + memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double)); + } ++ if (copyPath) { ++ path = state->path->copy(); ++ } + saved = NULL; + } + +@@ -4056,6 +4271,60 @@ + } + } + ++void GfxState::clipToRect(double xMin, double yMin, double xMax, double yMax) { ++ double x, y, xMin1, yMin1, xMax1, yMax1; ++ ++ transform(xMin, yMin, &x, &y); ++ xMin1 = xMax1 = x; ++ yMin1 = yMax1 = y; ++ transform(xMax, yMin, &x, &y); ++ if (x < xMin1) { ++ xMin1 = x; ++ } else if (x > xMax1) { ++ xMax1 = x; ++ } ++ if (y < yMin1) { ++ yMin1 = y; ++ } else if (y > yMax1) { ++ yMax1 = y; ++ } ++ transform(xMax, yMax, &x, &y); ++ if (x < xMin1) { ++ xMin1 = x; ++ } else if (x > xMax1) { ++ xMax1 = x; ++ } ++ if (y < yMin1) { ++ yMin1 = y; ++ } else if (y > yMax1) { ++ yMax1 = y; ++ } ++ transform(xMin, yMax, &x, &y); ++ if (x < xMin1) { ++ xMin1 = x; ++ } else if (x > xMax1) { ++ xMax1 = x; ++ } ++ if (y < yMin1) { ++ yMin1 = y; ++ } else if (y > yMax1) { ++ yMax1 = y; ++ } ++ ++ if (xMin1 > clipXMin) { ++ clipXMin = xMin1; ++ } ++ if (yMin1 > clipYMin) { ++ clipYMin = yMin1; ++ } ++ if (xMax1 < clipXMax) { ++ clipXMax = xMax1; ++ } ++ if (yMax1 < clipYMax) { ++ clipYMax = yMax1; ++ } ++} ++ + void GfxState::textShift(double tx, double ty) { + double dx, dy; + +diff -ru xpdf-3.02/xpdf/GfxState.h xpdf-3.03/xpdf/GfxState.h +--- xpdf-3.02/xpdf/GfxState.h 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/GfxState.h 2011-08-15 23:08:53.000000000 +0200 +@@ -878,6 +894,11 @@ + void getCMYK(Guchar *x, GfxCMYK *cmyk); + void getColor(Guchar *x, GfxColor *color); + ++ // Convert a line of pixels to 8-bit colors. ++ void getGrayByteLine(Guchar *in, Guchar *out, int n); ++ void getRGBByteLine(Guchar *in, Guchar *out, int n); ++ void getCMYKByteLine(Guchar *in, Guchar *out, int n); ++ + private: + + GfxImageColorMap(GfxImageColorMap *colorMap); +@@ -889,6 +910,8 @@ + int nComps2; // number of components in colorSpace2 + GfxColorComp * // lookup table + lookup[gfxColorMaxComps]; ++ GfxColorComp * // optimized case lookup table ++ lookup2[gfxColorMaxComps]; + double // minimum values for each component + decodeLow[gfxColorMaxComps]; + double // max - min value for each component +@@ -1023,7 +1046,8 @@ + ~GfxState(); + + // Copy. +- GfxState *copy() { return new GfxState(this); } ++ GfxState *copy(GBool copyPath = gFalse) ++ { return new GfxState(this, copyPath); } + + // Accessors. + double getHDPI() { return hDPI; } +@@ -1059,6 +1083,7 @@ + double getStrokeOpacity() { return strokeOpacity; } + GBool getFillOverprint() { return fillOverprint; } + GBool getStrokeOverprint() { return strokeOverprint; } ++ int getOverprintMode() { return overprintMode; } + Function **getTransfer() { return transfer; } + double getLineWidth() { return lineWidth; } + void getLineDash(double **dash, int *length, double *start) +@@ -1127,6 +1152,7 @@ + void setStrokeOpacity(double opac) { strokeOpacity = opac; } + void setFillOverprint(GBool op) { fillOverprint = op; } + void setStrokeOverprint(GBool op) { strokeOverprint = op; } ++ void setOverprintMode(int opm) { overprintMode = opm; } + void setTransfer(Function **funcs); + void setLineWidth(double width) { lineWidth = width; } + void setLineDash(double *dash, int length, double start); +@@ -1169,6 +1195,7 @@ + // Update clip region. + void clip(); + void clipToStrokePath(); ++ void clipToRect(double xMin, double yMin, double xMax, double yMax); + + // Text position. + void textSetPos(double tx, double ty) { lineX = tx; lineY = ty; } +@@ -1204,6 +1231,7 @@ + double strokeOpacity; // stroke opacity + GBool fillOverprint; // fill overprint + GBool strokeOverprint; // stroke overprint ++ int overprintMode; // overprint mode ("OPM") + Function *transfer[4]; // transfer function (entries may be: all + // NULL = identity; last three NULL = + // single function; all four non-NULL = +@@ -1238,7 +1266,7 @@ + + GfxState *saved; // next GfxState on stack + +- GfxState(GfxState *state); ++ GfxState(GfxState *state, GBool copyPath); + }; + + #endif +diff -ru xpdf-3.02/xpdf/GlobalParams.cc xpdf-3.03/xpdf/GlobalParams.cc +--- xpdf-3.02/xpdf/GlobalParams.cc 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/GlobalParams.cc 2011-08-15 23:08:53.000000000 +0200 +@@ -124,215 +124,138 @@ + GlobalParams *globalParams = NULL; + + //------------------------------------------------------------------------ +-// DisplayFontParam ++// PSFontParam16 + //------------------------------------------------------------------------ + +-DisplayFontParam::DisplayFontParam(GString *nameA, +- DisplayFontParamKind kindA) { ++PSFontParam16::PSFontParam16(GString *nameA, int wModeA, ++ GString *psFontNameA, GString *encodingA) { + name = nameA; +- kind = kindA; +- switch (kind) { +- case displayFontT1: +- t1.fileName = NULL; +- break; +- case displayFontTT: +- tt.fileName = NULL; +- break; +- } ++ wMode = wModeA; ++ psFontName = psFontNameA; ++ encoding = encodingA; + } + +-DisplayFontParam::~DisplayFontParam() { ++PSFontParam16::~PSFontParam16() { + delete name; +- switch (kind) { +- case displayFontT1: +- if (t1.fileName) { +- delete t1.fileName; +- } +- break; +- case displayFontTT: +- if (tt.fileName) { +- delete tt.fileName; +- } +- break; +- } ++ delete psFontName; ++ delete encoding; + } + +-#ifdef WIN32 +- + //------------------------------------------------------------------------ +-// WinFontInfo ++// SysFontInfo + //------------------------------------------------------------------------ + +-class WinFontInfo: public DisplayFontParam { ++class SysFontInfo { + public: + +- GBool bold, italic; ++ GString *name; ++ GBool bold; ++ GBool italic; ++ GString *path; ++ SysFontType type; ++ int fontNum; // for TrueType collections + +- static WinFontInfo *make(GString *nameA, GBool boldA, GBool italicA, +- HKEY regKey, char *winFontDir); +- WinFontInfo(GString *nameA, GBool boldA, GBool italicA, +- GString *fileNameA); +- virtual ~WinFontInfo(); +- GBool equals(WinFontInfo *fi); ++ SysFontInfo(GString *nameA, GBool boldA, GBool italicA, ++ GString *pathA, SysFontType typeA, int fontNumA); ++ ~SysFontInfo(); ++ GBool match(SysFontInfo *fi); ++ GBool match(GString *nameA, GBool boldA, GBool italicA); + }; + +-WinFontInfo *WinFontInfo::make(GString *nameA, GBool boldA, GBool italicA, +- HKEY regKey, char *winFontDir) { +- GString *regName; +- GString *fileNameA; +- char buf[MAX_PATH]; +- DWORD n; +- char c; +- int i; +- +- //----- find the font file +- fileNameA = NULL; +- regName = nameA->copy(); +- if (boldA) { +- regName->append(" Bold"); +- } +- if (italicA) { +- regName->append(" Italic"); +- } +- regName->append(" (TrueType)"); +- n = sizeof(buf); +- if (RegQueryValueEx(regKey, regName->getCString(), NULL, NULL, +- (LPBYTE)buf, &n) == ERROR_SUCCESS) { +- fileNameA = new GString(winFontDir); +- fileNameA->append('\\')->append(buf); +- } +- delete regName; +- if (!fileNameA) { +- delete nameA; +- return NULL; +- } +- +- //----- normalize the font name +- i = 0; +- while (i < nameA->getLength()) { +- c = nameA->getChar(i); +- if (c == ' ' || c == ',' || c == '-') { +- nameA->del(i); +- } else { +- ++i; +- } +- } +- +- return new WinFontInfo(nameA, boldA, italicA, fileNameA); +-} +- +-WinFontInfo::WinFontInfo(GString *nameA, GBool boldA, GBool italicA, +- GString *fileNameA): +- DisplayFontParam(nameA, displayFontTT) +-{ ++SysFontInfo::SysFontInfo(GString *nameA, GBool boldA, GBool italicA, ++ GString *pathA, SysFontType typeA, int fontNumA) { ++ name = nameA; + bold = boldA; + italic = italicA; +- tt.fileName = fileNameA; ++ path = pathA; ++ type = typeA; ++ fontNum = fontNumA; ++} ++ ++SysFontInfo::~SysFontInfo() { ++ delete name; ++ delete path; + } + +-WinFontInfo::~WinFontInfo() { ++GBool SysFontInfo::match(SysFontInfo *fi) { ++ return !strcasecmp(name->getCString(), fi->name->getCString()) && ++ bold == fi->bold && italic == fi->italic; + } + +-GBool WinFontInfo::equals(WinFontInfo *fi) { +- return !name->cmp(fi->name) && bold == fi->bold && italic == fi->italic; ++GBool SysFontInfo::match(GString *nameA, GBool boldA, GBool italicA) { ++ return !strcasecmp(name->getCString(), nameA->getCString()) && ++ bold == boldA && italic == italicA; + } + + //------------------------------------------------------------------------ +-// WinFontList ++// SysFontList + //------------------------------------------------------------------------ + +-class WinFontList { ++class SysFontList { + public: + +- WinFontList(char *winFontDirA); +- ~WinFontList(); +- WinFontInfo *find(GString *font); ++ SysFontList(); ++ ~SysFontList(); ++ SysFontInfo *find(GString *name); ++ ++#ifdef WIN32 ++ void scanWindowsFonts(char *winFontDir); ++#endif + + private: + +- void add(WinFontInfo *fi); +- static int CALLBACK enumFunc1(CONST LOGFONT *font, +- CONST TEXTMETRIC *metrics, +- DWORD type, LPARAM data); +- static int CALLBACK enumFunc2(CONST LOGFONT *font, +- CONST TEXTMETRIC *metrics, +- DWORD type, LPARAM data); +- +- GList *fonts; // [WinFontInfo] +- HDC dc; // (only used during enumeration) +- HKEY regKey; // (only used during enumeration) +- char *winFontDir; // (only used during enumeration) +-}; ++#ifdef WIN32 ++ SysFontInfo *makeWindowsFont(char *name, int fontNum, ++ char *path); ++#endif + +-WinFontList::WinFontList(char *winFontDirA) { +- OSVERSIONINFO version; +- char *path; ++ GList *fonts; // [SysFontInfo] ++}; + ++SysFontList::SysFontList() { + fonts = new GList(); +- dc = GetDC(NULL); +- winFontDir = winFontDirA; +- version.dwOSVersionInfoSize = sizeof(version); +- GetVersionEx(&version); +- if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { +- path = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\"; +- } else { +- path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\"; +- } +- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, +- KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, +- ®Key) == ERROR_SUCCESS) { +- EnumFonts(dc, NULL, &WinFontList::enumFunc1, (LPARAM)this); +- RegCloseKey(regKey); +- } +- ReleaseDC(NULL, dc); + } + +-WinFontList::~WinFontList() { +- deleteGList(fonts, WinFontInfo); ++SysFontList::~SysFontList() { ++ deleteGList(fonts, SysFontInfo); + } + +-void WinFontList::add(WinFontInfo *fi) { +- int i; +- +- for (i = 0; i < fonts->getLength(); ++i) { +- if (((WinFontInfo *)fonts->get(i))->equals(fi)) { +- delete fi; +- return; +- } +- } +- fonts->append(fi); +-} +- +-WinFontInfo *WinFontList::find(GString *font) { +- GString *name; ++SysFontInfo *SysFontList::find(GString *name) { ++ GString *name2; + GBool bold, italic; +- WinFontInfo *fi; ++ SysFontInfo *fi; + char c; + int n, i; + +- name = font->copy(); ++ name2 = name->copy(); + + // remove space, comma, dash chars + i = 0; +- while (i < name->getLength()) { +- c = name->getChar(i); ++ while (i < name2->getLength()) { ++ c = name2->getChar(i); + if (c == ' ' || c == ',' || c == '-') { +- name->del(i); ++ name2->del(i); + } else { + ++i; + } + } +- n = name->getLength(); ++ n = name2->getLength(); + + // remove trailing "MT" (Foo-MT, Foo-BoldMT, etc.) +- if (!strcmp(name->getCString() + n - 2, "MT")) { +- name->del(n - 2, 2); ++ if (n > 2 && !strcmp(name2->getCString() + n - 2, "MT")) { ++ name2->del(n - 2, 2); + n -= 2; + } + ++ // look for "Regular" ++ if (n > 7 && !strcmp(name2->getCString() + n - 7, "Regular")) { ++ name2->del(n - 7, 7); ++ n -= 7; ++ } ++ + // look for "Italic" +- if (!strcmp(name->getCString() + n - 6, "Italic")) { +- name->del(n - 6, 6); ++ if (n > 6 && !strcmp(name2->getCString() + n - 6, "Italic")) { ++ name2->del(n - 6, 6); + italic = gTrue; + n -= 6; + } else { +@@ -340,8 +263,8 @@ + } + + // look for "Bold" +- if (!strcmp(name->getCString() + n - 4, "Bold")) { +- name->del(n - 4, 4); ++ if (n > 4 && !strcmp(name2->getCString() + n - 4, "Bold")) { ++ name2->del(n - 4, 4); + bold = gTrue; + n -= 4; + } else { +@@ -349,84 +272,183 @@ + } + + // remove trailing "MT" (FooMT-Bold, etc.) +- if (!strcmp(name->getCString() + n - 2, "MT")) { +- name->del(n - 2, 2); ++ if (n > 2 && !strcmp(name2->getCString() + n - 2, "MT")) { ++ name2->del(n - 2, 2); + n -= 2; + } + + // remove trailing "PS" +- if (!strcmp(name->getCString() + n - 2, "PS")) { +- name->del(n - 2, 2); ++ if (n > 2 && !strcmp(name2->getCString() + n - 2, "PS")) { ++ name2->del(n - 2, 2); + n -= 2; + } + ++ // remove trailing "IdentityH" ++ if (n > 9 && !strcmp(name2->getCString() + n - 9, "IdentityH")) { ++ name2->del(n - 9, 9); ++ n -= 9; ++ } ++ + // search for the font + fi = NULL; + for (i = 0; i < fonts->getLength(); ++i) { +- fi = (WinFontInfo *)fonts->get(i); +- if (!fi->name->cmp(name) && fi->bold == bold && fi->italic == italic) { ++ fi = (SysFontInfo *)fonts->get(i); ++ if (fi->match(name2, bold, italic)) { + break; + } + fi = NULL; + } ++ if (!fi && bold) { ++ // try ignoring the bold flag ++ for (i = 0; i < fonts->getLength(); ++i) { ++ fi = (SysFontInfo *)fonts->get(i); ++ if (fi->match(name2, gFalse, italic)) { ++ break; ++ } ++ fi = NULL; ++ } ++ } ++ if (!fi && (bold || italic)) { ++ // try ignoring the bold and italic flags ++ for (i = 0; i < fonts->getLength(); ++i) { ++ fi = (SysFontInfo *)fonts->get(i); ++ if (fi->match(name2, gFalse, gFalse)) { ++ break; ++ } ++ fi = NULL; ++ } ++ } + +- delete name; ++ delete name2; + return fi; + } + +-int CALLBACK WinFontList::enumFunc1(CONST LOGFONT *font, +- CONST TEXTMETRIC *metrics, +- DWORD type, LPARAM data) { +- WinFontList *fl = (WinFontList *)data; +- +- EnumFonts(fl->dc, font->lfFaceName, &WinFontList::enumFunc2, (LPARAM)fl); +- return 1; +-} +- +-int CALLBACK WinFontList::enumFunc2(CONST LOGFONT *font, +- CONST TEXTMETRIC *metrics, +- DWORD type, LPARAM data) { +- WinFontList *fl = (WinFontList *)data; +- WinFontInfo *fi; +- +- if (type & TRUETYPE_FONTTYPE) { +- if ((fi = WinFontInfo::make(new GString(font->lfFaceName), +- font->lfWeight >= 600, +- font->lfItalic ? gTrue : gFalse, +- fl->regKey, fl->winFontDir))) { +- fl->add(fi); ++#ifdef WIN32 ++void SysFontList::scanWindowsFonts(char *winFontDir) { ++ OSVERSIONINFO version; ++ char *path; ++ DWORD idx, valNameLen, dataLen, type; ++ HKEY regKey; ++ char valName[1024], data[1024]; ++ int n, fontNum; ++ char *p0, *p1; ++ GString *fontPath; ++ ++ version.dwOSVersionInfoSize = sizeof(version); ++ GetVersionEx(&version); ++ if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { ++ path = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\"; ++ } else { ++ path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\"; ++ } ++ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, ++ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, ++ ®Key) == ERROR_SUCCESS) { ++ idx = 0; ++ while (1) { ++ valNameLen = sizeof(valName) - 1; ++ dataLen = sizeof(data) - 1; ++ if (RegEnumValue(regKey, idx, valName, &valNameLen, NULL, ++ &type, (LPBYTE)data, &dataLen) != ERROR_SUCCESS) { ++ break; ++ } ++ if (type == REG_SZ && ++ valNameLen > 0 && valNameLen < sizeof(valName) && ++ dataLen > 0 && dataLen < sizeof(data)) { ++ valName[valNameLen] = '\0'; ++ data[dataLen] = '\0'; ++ n = strlen(data); ++ if (!strcasecmp(data + n - 4, ".ttf") || ++ !strcasecmp(data + n - 4, ".ttc")) { ++ fontPath = new GString(data); ++ if (!(dataLen >= 3 && data[1] == ':' && data[2] == '\\')) { ++ fontPath->insert(0, '\\'); ++ fontPath->insert(0, winFontDir); ++ } ++ p0 = valName; ++ fontNum = 0; ++ while (*p0) { ++ p1 = strstr(p0, " & "); ++ if (p1) { ++ *p1 = '\0'; ++ p1 = p1 + 3; ++ } else { ++ p1 = p0 + strlen(p0); ++ } ++ fonts->append(makeWindowsFont(p0, fontNum, ++ fontPath->getCString())); ++ p0 = p1; ++ ++fontNum; ++ } ++ delete fontPath; ++ } ++ } ++ ++idx; + } ++ RegCloseKey(regKey); + } +- return 1; + } + +-#endif // WIN32 ++SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum, ++ char *path) { ++ int n; ++ GBool bold, italic; ++ GString *s; ++ char c; ++ int i; ++ SysFontType type; + +-//------------------------------------------------------------------------ +-// PSFontParam +-//------------------------------------------------------------------------ ++ n = strlen(name); ++ bold = italic = gFalse; + +-PSFontParam::PSFontParam(GString *pdfFontNameA, int wModeA, +- GString *psFontNameA, GString *encodingA) { +- pdfFontName = pdfFontNameA; +- wMode = wModeA; +- psFontName = psFontNameA; +- encoding = encodingA; +-} ++ // remove trailing ' (TrueType)' ++ if (n > 11 && !strncmp(name + n - 11, " (TrueType)", 11)) { ++ n -= 11; ++ } + +-PSFontParam::~PSFontParam() { +- delete pdfFontName; +- delete psFontName; +- if (encoding) { +- delete encoding; ++ // remove trailing ' Italic' ++ if (n > 7 && !strncmp(name + n - 7, " Italic", 7)) { ++ n -= 7; ++ italic = gTrue; ++ } ++ ++ // remove trailing ' Bold' ++ if (n > 5 && !strncmp(name + n - 5, " Bold", 5)) { ++ n -= 5; ++ bold = gTrue; + } ++ ++ // remove trailing ' Regular' ++ if (n > 5 && !strncmp(name + n - 8, " Regular", 8)) { ++ n -= 8; ++ } ++ ++ //----- normalize the font name ++ s = new GString(name, n); ++ i = 0; ++ while (i < s->getLength()) { ++ c = s->getChar(i); ++ if (c == ' ' || c == ',' || c == '-') { ++ s->del(i); ++ } else { ++ ++i; ++ } ++ } ++ ++ if (!strcasecmp(path + strlen(path) - 4, ".ttc")) { ++ type = sysFontTTC; ++ } else { ++ type = sysFontTTF; ++ } ++ return new SysFontInfo(s, bold, italic, new GString(path), type, fontNum); + } ++#endif + + //------------------------------------------------------------------------ + // KeyBinding + //------------------------------------------------------------------------ + +-KeyBinding::KeyBinding(int codeA, int modsA, int contextA, char *cmd0) { ++KeyBinding::KeyBinding(int codeA, int modsA, int contextA, const char *cmd0) { + code = codeA; + mods = modsA; + context = contextA; +@@ -637,9 +657,10 @@ + unicodeMaps = new GHash(gTrue); + cMapDirs = new GHash(gTrue); + toUnicodeDirs = new GList(); +- displayFonts = new GHash(); +- displayCIDFonts = new GHash(); +- displayNamedCIDFonts = new GHash(); ++ fontFiles = new GHash(gTrue); ++ fontDirs = new GList(); ++ ccFontFiles = new GHash(gTrue); ++ sysFonts = new SysFontList(); + #if HAVE_PAPER_H + char *paperName; + const struct paper *paperType; +@@ -668,16 +689,21 @@ + psDuplex = gFalse; + psLevel = psLevel2; + psFile = NULL; +- psFonts = new GHash(); +- psNamedFonts16 = new GList(); +- psFonts16 = new GList(); ++ psResidentFonts = new GHash(gTrue); ++ psResidentFonts16 = new GList(); ++ psResidentFontsCC = new GList(); + psEmbedType1 = gTrue; + psEmbedTrueType = gTrue; + psEmbedCIDPostScript = gTrue; + psEmbedCIDTrueType = gTrue; ++ psFontPassthrough = gFalse; + psPreload = gFalse; + psOPI = gFalse; + psASCIIHex = gFalse; ++ psUncompressPreloadedImages = gFalse; ++ psRasterResolution = 300; ++ psRasterMono = gFalse; ++ psAlwaysRasterize = gFalse; + textEncoding = new GString("Latin1"); + #if defined(WIN32) + textEOL = eolDOS; +@@ -688,13 +714,14 @@ + #endif + textPageBreaks = gTrue; + textKeepTinyChars = gFalse; +- fontDirs = new GList(); + initialZoom = new GString("125"); + continuousView = gFalse; + enableT1lib = gTrue; + enableFreeType = gTrue; ++ disableFreeTypeHinting = gFalse; + antialias = gTrue; + vectorAntialias = gTrue; ++ antialiasPrinting = gFalse; + strokeAdjust = gTrue; + screenType = screenUnset; + screenSize = -1; +@@ -702,6 +729,10 @@ + screenGamma = 1.0; + screenBlackThreshold = 0.0; + screenWhiteThreshold = 1.0; ++ minLineWidth = 0.0; ++ overprintPreview = gFalse; + urlCommand = NULL; + movieCommand = NULL; + mapNumericCharNames = gTrue; +@@ -716,10 +747,6 @@ + unicodeMapCache = new UnicodeMapCache(); + cMapCache = new CMapCache(); + +-#ifdef WIN32 +- winFontList = NULL; +-#endif +- + #ifdef ENABLE_PLUGINS + plugins = new GList(); + securityHandlers = new GList(); +@@ -764,7 +791,7 @@ + } + } + if (!f) { +-#if defined(WIN32) && !defined(__CYGWIN32__) ++#ifdef WIN32 + char buf[512]; + i = GetModuleFileName(NULL, buf, sizeof(buf)); + if (i <= 0 || i >= sizeof(buf)) { +@@ -1762,23 +1833,21 @@ + deleteGHash(residentUnicodeMaps, UnicodeMap); + deleteGHash(unicodeMaps, GString); + deleteGList(toUnicodeDirs, GString); +- deleteGHash(displayFonts, DisplayFontParam); +- deleteGHash(displayCIDFonts, DisplayFontParam); +- deleteGHash(displayNamedCIDFonts, DisplayFontParam); +-#ifdef WIN32 +- if (winFontList) { +- delete winFontList; +- } +-#endif ++ deleteGHash(fontFiles, GString); ++ deleteGList(fontDirs, GString); ++ deleteGHash(ccFontFiles, GString); ++ delete sysFonts; + if (psFile) { + delete psFile; + } +- deleteGHash(psFonts, PSFontParam); +- deleteGList(psNamedFonts16, PSFontParam); +- deleteGList(psFonts16, PSFontParam); ++ deleteGHash(psResidentFonts, GString); ++ deleteGList(psResidentFonts16, PSFontParam16); ++ deleteGList(psResidentFontsCC, PSFontParam16); + delete textEncoding; +- deleteGList(fontDirs, GString); + delete initialZoom; +@@ -1829,8 +1898,6 @@ + char winFontDir[MAX_PATH]; + #endif + FILE *f; +- DisplayFontParamKind kind; +- DisplayFontParam *dfp; + int i, j; + + #ifdef WIN32 +@@ -1850,16 +1917,13 @@ + } + #endif + for (i = 0; displayFontTab[i].name; ++i) { +- fontName = new GString(displayFontTab[i].name); +- if (getDisplayFont(fontName)) { +- delete fontName; ++ if (fontFiles->lookup(displayFontTab[i].name)) { + continue; + } ++ fontName = new GString(displayFontTab[i].name); + fileName = NULL; +- kind = displayFontT1; // make gcc happy + if (dir) { + fileName = appendToPath(new GString(dir), displayFontTab[i].t1FileName); +- kind = displayFontT1; + if ((f = fopen(fileName->getCString(), "rb"))) { + fclose(f); + } else { +@@ -1871,7 +1935,6 @@ + if (!fileName && winFontDir[0] && displayFontTab[i].ttFileName) { + fileName = appendToPath(new GString(winFontDir), + displayFontTab[i].ttFileName); +- kind = displayFontTT; + if ((f = fopen(fileName->getCString(), "rb"))) { + fclose(f); + } else { +@@ -1886,7 +1949,6 @@ + for (j = 0; !fileName && displayFontDirs[j]; ++j) { + fileName = appendToPath(new GString(displayFontDirs[j]), + displayFontTab[i].ttFileName); +- kind = displayFontTT; + if ((f = fopen(fileName->getCString(), "rb"))) { + fclose(f); + } else { +@@ -1895,11 +1957,10 @@ + } + } + } +-#else ++#else // WIN32 + for (j = 0; !fileName && displayFontDirs[j]; ++j) { + fileName = appendToPath(new GString(displayFontDirs[j]), + displayFontTab[i].t1FileName); +- kind = displayFontT1; + if ((f = fopen(fileName->getCString(), "rb"))) { + fclose(f); + } else { +@@ -1907,20 +1968,19 @@ + fileName = NULL; + } + } +-#endif ++#endif // WIN32 + if (!fileName) { +- error(-1, "No display font for '%s'", displayFontTab[i].name); ++ error(errConfig, -1, "No display font for '{0:s}'", ++ displayFontTab[i].name); + delete fontName; + continue; + } +- dfp = new DisplayFontParam(fontName, kind); +- dfp->t1.fileName = fileName; +- globalParams->addDisplayFont(dfp); ++ addFontFile(fontName, fileName); + } + + #ifdef WIN32 + if (winFontDir[0]) { +- winFontList = new WinFontList(winFontDir); ++ sysFonts->scanWindowsFonts(winFontDir); + } + #endif + } +@@ -2020,31 +2080,72 @@ + return NULL; + } + +-DisplayFontParam *GlobalParams::getDisplayFont(GString *fontName) { +- DisplayFontParam *dfp; ++GString *GlobalParams::findFontFile(GString *fontName) { ++ static const char *exts[] = { ".pfa", ".pfb", ".ttf", ".ttc" }; ++ GString *path, *dir; ++#ifdef WIN32 ++ GString *fontNameU; ++#endif ++ const char *ext; ++ FILE *f; ++ int i, j; + + lockGlobalParams; +- dfp = (DisplayFontParam *)displayFonts->lookup(fontName); +-#ifdef WIN32 +- if (!dfp && winFontList) { +- dfp = winFontList->find(fontName); ++ if ((path = (GString *)fontFiles->lookup(fontName))) { ++ path = path->copy(); ++ unlockGlobalParams; ++ return path; + } ++ for (i = 0; i < fontDirs->getLength(); ++i) { ++ dir = (GString *)fontDirs->get(i); ++ for (j = 0; j < (int)(sizeof(exts) / sizeof(exts[0])); ++j) { ++ ext = exts[j]; ++#ifdef WIN32 ++ fontNameU = fileNameToUTF8(fontName->getCString()); ++ path = appendToPath(dir->copy(), fontNameU->getCString()); ++ delete fontNameU; ++#else ++ path = appendToPath(dir->copy(), fontName->getCString()); + #endif ++ path->append(ext); ++ if ((f = openFile(path->getCString(), "rb"))) { ++ fclose(f); ++ unlockGlobalParams; ++ return path; ++ } ++ delete path; ++ } ++ } ++ unlockGlobalParams; ++ return NULL; ++} ++ ++GString *GlobalParams::findSystemFontFile(GString *fontName, ++ SysFontType *type, ++ int *fontNum) { ++ SysFontInfo *fi; ++ GString *path; ++ ++ path = NULL; ++ lockGlobalParams; ++ if ((fi = sysFonts->find(fontName))) { ++ path = fi->path->copy(); ++ *type = fi->type; ++ *fontNum = fi->fontNum; ++ } + unlockGlobalParams; +- return dfp; ++ return path; + } + +-DisplayFontParam *GlobalParams::getDisplayCIDFont(GString *fontName, +- GString *collection) { +- DisplayFontParam *dfp; ++GString *GlobalParams::findCCFontFile(GString *collection) { ++ GString *path; + + lockGlobalParams; +- if (!fontName || +- !(dfp = (DisplayFontParam *)displayNamedCIDFonts->lookup(fontName))) { +- dfp = (DisplayFontParam *)displayCIDFonts->lookup(collection); ++ if ((path = (GString *)ccFontFiles->lookup(collection))) { ++ path = path->copy(); + } + unlockGlobalParams; +- return dfp; ++ return path; + } + + GString *GlobalParams::getPSFile() { +@@ -2137,41 +2238,62 @@ + return level; + } + +-PSFontParam *GlobalParams::getPSFont(GString *fontName) { +- PSFontParam *p; ++GString *GlobalParams::getPSResidentFont(GString *fontName) { ++ GString *psName; + + lockGlobalParams; +- p = (PSFontParam *)psFonts->lookup(fontName); ++ psName = (GString *)psResidentFonts->lookup(fontName); + unlockGlobalParams; +- return p; ++ return psName; + } + +-PSFontParam *GlobalParams::getPSFont16(GString *fontName, +- GString *collection, int wMode) { +- PSFontParam *p; ++GList *GlobalParams::getPSResidentFonts() { ++ GList *names; ++ GHashIter *iter; ++ GString *name; ++ GString *psName; ++ ++ names = new GList(); ++ lockGlobalParams; ++ psResidentFonts->startIter(&iter); ++ while (psResidentFonts->getNext(&iter, &name, (void **)&psName)) { ++ names->append(psName->copy()); ++ } ++ unlockGlobalParams; ++ return names; ++} ++ ++PSFontParam16 *GlobalParams::getPSResidentFont16(GString *fontName, ++ int wMode) { ++ PSFontParam16 *p; + int i; + + lockGlobalParams; + p = NULL; +- if (fontName) { +- for (i = 0; i < psNamedFonts16->getLength(); ++i) { +- p = (PSFontParam *)psNamedFonts16->get(i); +- if (!p->pdfFontName->cmp(fontName) && +- p->wMode == wMode) { +- break; +- } +- p = NULL; ++ for (i = 0; i < psResidentFonts16->getLength(); ++i) { ++ p = (PSFontParam16 *)psResidentFonts16->get(i); ++ if (!(p->name->cmp(fontName)) && p->wMode == wMode) { ++ break; + } ++ p = NULL; + } +- if (!p && collection) { +- for (i = 0; i < psFonts16->getLength(); ++i) { +- p = (PSFontParam *)psFonts16->get(i); +- if (!p->pdfFontName->cmp(collection) && +- p->wMode == wMode) { +- break; +- } +- p = NULL; ++ unlockGlobalParams; ++ return p; ++} ++ ++PSFontParam16 *GlobalParams::getPSResidentFontCC(GString *collection, ++ int wMode) { ++ PSFontParam16 *p; ++ int i; ++ ++ lockGlobalParams; ++ p = NULL; ++ for (i = 0; i < psResidentFontsCC->getLength(); ++i) { ++ p = (PSFontParam16 *)psResidentFontsCC->get(i); ++ if (!(p->name->cmp(collection)) && p->wMode == wMode) { ++ break; + } ++ p = NULL; + } + unlockGlobalParams; + return p; +@@ -2213,6 +2335,15 @@ + return e; + } + ++GBool GlobalParams::getPSFontPassthrough() { ++ GBool e; ++ ++ lockGlobalParams; ++ e = psFontPassthrough; ++ unlockGlobalParams; ++ return e; ++} ++ + GBool GlobalParams::getPSPreload() { + GBool preload; + +@@ -2240,6 +2371,42 @@ + return ah; + } + ++GBool GlobalParams::getPSUncompressPreloadedImages() { ++ GBool ah; ++ ++ lockGlobalParams; ++ ah = psUncompressPreloadedImages; ++ unlockGlobalParams; ++ return ah; ++} ++ ++double GlobalParams::getPSRasterResolution() { ++ double res; ++ ++ lockGlobalParams; ++ res = psRasterResolution; ++ unlockGlobalParams; ++ return res; ++} ++ ++GBool GlobalParams::getPSRasterMono() { ++ GBool mono; ++ ++ lockGlobalParams; ++ mono = psRasterMono; ++ unlockGlobalParams; ++ return mono; ++} ++ ++GBool GlobalParams::getPSAlwaysRasterize() { ++ GBool rast; ++ ++ lockGlobalParams; ++ rast = psAlwaysRasterize; ++ unlockGlobalParams; ++ return rast; ++} ++ + GString *GlobalParams::getTextEncodingName() { + GString *s; + +@@ -2276,30 +2443,6 @@ + return tiny; + } + +-GString *GlobalParams::findFontFile(GString *fontName, char **exts) { +- GString *dir, *fileName; +- char **ext; +- FILE *f; +- int i; +- +- lockGlobalParams; +- for (i = 0; i < fontDirs->getLength(); ++i) { +- dir = (GString *)fontDirs->get(i); +- for (ext = exts; *ext; ++ext) { +- fileName = appendToPath(dir->copy(), fontName->getCString()); +- fileName->append(*ext); +- if ((f = fopen(fileName->getCString(), "rb"))) { +- fclose(f); +- unlockGlobalParams; +- return fileName; +- } +- delete fileName; +- } +- } +- unlockGlobalParams; +- return NULL; +-} +- + GString *GlobalParams::getInitialZoom() { + GString *s; + +@@ -2355,6 +2507,15 @@ + return f; + } + ++GBool GlobalParams::getAntialiasPrinting() { ++ GBool f; ++ ++ lockGlobalParams; ++ f = antialiasPrinting; ++ unlockGlobalParams; ++ return f; ++} ++ + GBool GlobalParams::getStrokeAdjust() { + GBool f; + +@@ -2418,6 +2579,24 @@ + return thresh; + } + ++double GlobalParams::getMinLineWidth() { ++ double w; ++ ++ lockGlobalParams; ++ w = minLineWidth; ++ unlockGlobalParams; ++ return w; ++} ++ + GBool GlobalParams::getMapNumericCharNames() { + GBool map; + +@@ -2552,14 +2731,9 @@ + // functions to set parameters + //------------------------------------------------------------------------ + +-void GlobalParams::addDisplayFont(DisplayFontParam *param) { +- DisplayFontParam *old; +- ++void GlobalParams::addFontFile(GString *fontName, GString *path) { + lockGlobalParams; +- if ((old = (DisplayFontParam *)displayFonts->remove(param->name))) { +- delete old; +- } +- displayFonts->add(param->name, param); ++ fontFiles->add(fontName, path); + unlockGlobalParams; + } + +@@ -2684,6 +2858,12 @@ + unlockGlobalParams; + } + ++void GlobalParams::setPSFontPassthrough(GBool passthrough) { ++ lockGlobalParams; ++ psFontPassthrough = passthrough; ++ unlockGlobalParams; ++} ++ + void GlobalParams::setPSPreload(GBool preload) { + lockGlobalParams; + psPreload = preload; +diff -ru xpdf-3.02/xpdf/GlobalParams.h xpdf-3.03/xpdf/GlobalParams.h +--- xpdf-3.02/xpdf/GlobalParams.h 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/GlobalParams.h 2011-08-15 23:08:53.000000000 +0200 +@@ -35,9 +35,7 @@ + class CMapCache; + struct XpdfSecurityHandler; + class GlobalParams; +-#ifdef WIN32 +-class WinFontList; +-#endif ++class SysFontList; + + //------------------------------------------------------------------------ + +@@ -46,51 +44,27 @@ + + //------------------------------------------------------------------------ + +-enum DisplayFontParamKind { +- displayFontT1, +- displayFontTT +-}; +- +-struct DisplayFontParamT1 { +- GString *fileName; +-}; +- +-struct DisplayFontParamTT { +- GString *fileName; +-}; +- +-class DisplayFontParam { +-public: +- +- GString *name; // font name for 8-bit fonts and named +- // CID fonts; collection name for +- // generic CID fonts +- DisplayFontParamKind kind; +- union { +- DisplayFontParamT1 t1; +- DisplayFontParamTT tt; +- }; +- +- DisplayFontParam(GString *nameA, DisplayFontParamKind kindA); +- virtual ~DisplayFontParam(); ++enum SysFontType { ++ sysFontPFA, ++ sysFontPFB, ++ sysFontTTF, ++ sysFontTTC + }; + + //------------------------------------------------------------------------ + +-class PSFontParam { ++class PSFontParam16 { + public: + +- GString *pdfFontName; // PDF font name for 8-bit fonts and +- // named 16-bit fonts; char collection +- // name for generic 16-bit fonts +- int wMode; // writing mode (0=horiz, 1=vert) for +- // 16-bit fonts ++ GString *name; // PDF font name for psResidentFont16; ++ // char collection name for psResidentFontCC ++ int wMode; // writing mode (0=horiz, 1=vert) + GString *psFontName; // PostScript font name +- GString *encoding; // encoding, for 16-bit fonts only ++ GString *encoding; // encoding + +- PSFontParam(GString *pdfFontNameA, int wModeA, +- GString *psFontNameA, GString *encodingA); +- ~PSFontParam(); ++ PSFontParam16(GString *nameA, int wModeA, ++ GString *psFontNameA, GString *encodingA); ++ ~PSFontParam16(); + }; + + //------------------------------------------------------------------------ +@@ -212,9 +191,11 @@ + UnicodeMap *getResidentUnicodeMap(GString *encodingName); + FILE *getUnicodeMapFile(GString *encodingName); + FILE *findCMapFile(GString *collection, GString *cMapName); + FILE *findToUnicodeFile(GString *name); +- DisplayFontParam *getDisplayFont(GString *fontName); +- DisplayFontParam *getDisplayCIDFont(GString *fontName, GString *collection); ++ GString *findFontFile(GString *fontName); ++ GString *findSystemFontFile(GString *fontName, SysFontType *type, ++ int *fontNum); ++ GString *findCCFontFile(GString *collection); + GString *getPSFile(); + int getPSPaperWidth(); + int getPSPaperHeight(); +@@ -225,26 +206,34 @@ + GBool getPSShrinkLarger(); + GBool getPSCenter(); + PSLevel getPSLevel(); +- PSFontParam *getPSFont(GString *fontName); +- PSFontParam *getPSFont16(GString *fontName, GString *collection, int wMode); ++ GString *getPSResidentFont(GString *fontName); ++ GList *getPSResidentFonts(); ++ PSFontParam16 *getPSResidentFont16(GString *fontName, int wMode); ++ PSFontParam16 *getPSResidentFontCC(GString *collection, int wMode); + GBool getPSEmbedType1(); + GBool getPSEmbedTrueType(); + GBool getPSEmbedCIDPostScript(); + GBool getPSEmbedCIDTrueType(); ++ GBool getPSFontPassthrough(); + GBool getPSPreload(); + GBool getPSOPI(); + GBool getPSASCIIHex(); ++ GBool getPSUncompressPreloadedImages(); ++ double getPSRasterResolution(); ++ GBool getPSRasterMono(); ++ GBool getPSAlwaysRasterize(); + GString *getTextEncodingName(); + EndOfLineKind getTextEOL(); + GBool getTextPageBreaks(); + GBool getTextKeepTinyChars(); +- GString *findFontFile(GString *fontName, char **exts); + GString *getInitialZoom(); + GBool getContinuousView(); + GBool getEnableT1lib(); + GBool getEnableFreeType(); + GBool getAntialias(); + GBool getVectorAntialias(); ++ GBool getAntialiasPrinting(); + GBool getStrokeAdjust(); + ScreenType getScreenType(); + int getScreenSize(); +@@ -252,6 +241,10 @@ + double getScreenGamma(); + double getScreenBlackThreshold(); + double getScreenWhiteThreshold(); ++ double getMinLineWidth(); ++ GBool getDrawAnnotations(); ++ GBool getOverprintPreview() { return overprintPreview; } + GString *getURLCommand() { return urlCommand; } + GString *getMovieCommand() { return movieCommand; } + GBool getMapNumericCharNames(); +@@ -268,7 +261,7 @@ + + //----- functions to set parameters + +- void addDisplayFont(DisplayFontParam *param); ++ void addFontFile(GString *fontName, GString *path); + void setPSFile(char *file); + GBool setPSPaperSize(char *size); + void setPSPaperWidth(int width); +@@ -284,6 +277,7 @@ + void setPSEmbedTrueType(GBool embed); + void setPSEmbedCIDPostScript(GBool embed); + void setPSEmbedCIDTrueType(GBool embed); ++ void setPSFontPassthrough(GBool passthrough); + void setPSPreload(GBool preload); + void setPSOPI(GBool opi); + void setPSASCIIHex(GBool hex); +@@ -380,15 +374,12 @@ + GHash *cMapDirs; // list of CMap dirs, indexed by collection + // name [GList[GString]] + GList *toUnicodeDirs; // list of ToUnicode CMap dirs [GString] +- GHash *displayFonts; // display font info, indexed by font name +- // [DisplayFontParam] +-#ifdef WIN32 +- WinFontList *winFontList; // system TrueType fonts +-#endif +- GHash *displayCIDFonts; // display CID font info, indexed by +- // collection [DisplayFontParam] +- GHash *displayNamedCIDFonts; // display CID font info, indexed by +- // font name [DisplayFontParam] ++ GHash *fontFiles; // font files: font name mapped to path ++ // [GString] ++ GList *fontDirs; // list of font dirs [GString] ++ GHash *ccFontFiles; // character collection font files: ++ // collection name mapped to path [GString] ++ SysFontList *sysFonts; // system fonts + GString *psFile; // PostScript file or command (for xpdf) + int psPaperWidth; // paper size, in PostScript points, for + int psPaperHeight; // PostScript output +@@ -402,31 +393,44 @@ + GBool psCenter; // center pages on the paper + GBool psDuplex; // enable duplexing in PostScript? + PSLevel psLevel; // PostScript level to generate +- GHash *psFonts; // PostScript font info, indexed by PDF +- // font name [PSFontParam] +- GList *psNamedFonts16; // named 16-bit fonts [PSFontParam] +- GList *psFonts16; // generic 16-bit fonts [PSFontParam] ++ GHash *psResidentFonts; // 8-bit fonts resident in printer: ++ // PDF font name mapped to PS font name ++ // [GString] ++ GList *psResidentFonts16; // 16-bit fonts resident in printer: ++ // PDF font name mapped to font info ++ // [PSFontParam16] ++ GList *psResidentFontsCC; // 16-bit character collection fonts ++ // resident in printer: collection name ++ // mapped to font info [PSFontParam16] + GBool psEmbedType1; // embed Type 1 fonts? + GBool psEmbedTrueType; // embed TrueType fonts? + GBool psEmbedCIDPostScript; // embed CID PostScript fonts? + GBool psEmbedCIDTrueType; // embed CID TrueType fonts? ++ GBool psFontPassthrough; // pass all fonts through as-is? + GBool psPreload; // preload PostScript images and forms into + // memory + GBool psOPI; // generate PostScript OPI comments? + GBool psASCIIHex; // use ASCIIHex instead of ASCII85? ++ GBool psUncompressPreloadedImages; // uncompress all preloaded images ++ double psRasterResolution; // PostScript rasterization resolution (dpi) ++ GBool psRasterMono; // true to do PostScript rasterization ++ // in monochrome (gray); false to do it ++ // in color (RGB/CMYK) ++ GBool psAlwaysRasterize; // force PostScript rasterization + GString *textEncoding; // encoding (unicodeMap) to use for text + // output + EndOfLineKind textEOL; // type of EOL marker to use for text + // output + GBool textPageBreaks; // insert end-of-page markers? + GBool textKeepTinyChars; // keep all characters in text output +- GList *fontDirs; // list of font dirs [GString] + GString *initialZoom; // initial zoom level + GBool continuousView; // continuous view mode + GBool enableT1lib; // t1lib enable flag + GBool enableFreeType; // FreeType enable flag + GBool antialias; // font anti-aliasing enable flag + GBool vectorAntialias; // vector anti-aliasing enable flag ++ GBool antialiasPrinting; // allow anti-aliasing when printing + GBool strokeAdjust; // stroke adjustment enable flag + ScreenType screenType; // halftone screen type + int screenSize; // screen matrix size +@@ -434,6 +438,10 @@ + double screenGamma; // screen gamma correction + double screenBlackThreshold; // screen black clamping threshold + double screenWhiteThreshold; // screen white clamping threshold ++ double minLineWidth; // minimum line width ++ GBool overprintPreview; // enable overprint preview + GString *urlCommand; // command executed for URL links + GString *movieCommand; // command executed for movie annotations + GBool mapNumericCharNames; // map numeric char names (from font subsets)? +Només a xpdf-3.03/xpdf: OptionalContent.cc +Només a xpdf-3.03/xpdf: OptionalContent.h +diff -ru xpdf-3.02/xpdf/OutputDev.cc xpdf-3.03/xpdf/OutputDev.cc +--- xpdf-3.02/xpdf/OutputDev.cc 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/OutputDev.cc 2011-08-15 23:08:53.000000000 +0200 +@@ -65,6 +65,7 @@ + updateStrokeOpacity(state); + updateFillOverprint(state); + updateStrokeOverprint(state); ++ updateOverprintMode(state); + updateTransfer(state); + updateFont(state); + } +@@ -89,6 +90,13 @@ + } + } + ++void OutputDev::setSoftMaskFromImageMask(GfxState *state, ++ Object *ref, Stream *str, ++ int width, int height, GBool invert, ++ GBool inlineImg) { ++ drawImageMask(state, ref, str, width, height, invert, inlineImg); ++} ++ + void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, GfxImageColorMap *colorMap, + int *maskColors, GBool inlineImg) { +diff -ru xpdf-3.02/xpdf/OutputDev.h xpdf-3.03/xpdf/OutputDev.h +--- xpdf-3.02/xpdf/OutputDev.h 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/OutputDev.h 2011-08-15 23:08:53.000000000 +0200 +@@ -19,6 +19,7 @@ + #include "CharTypes.h" + + class GString; ++class Gfx; + class GfxState; + struct GfxColor; + class GfxColorSpace; +@@ -76,6 +77,11 @@ + // Does this device need non-text content? + virtual GBool needNonText() { return gTrue; } + ++ // Does this device require incCharCount to be called for text on ++ // non-shown layers? ++ virtual GBool needCharCount() { return gFalse; } ++ ++ + //----- initialization and control + + // Set default transform matrix. +@@ -135,6 +141,7 @@ + virtual void updateStrokeOpacity(GfxState *state) {} + virtual void updateFillOverprint(GfxState *state) {} + virtual void updateStrokeOverprint(GfxState *state) {} ++ virtual void updateOverprintMode(GfxState *state) {} + virtual void updateTransfer(GfxState *state) {} + + //----- update text state +@@ -147,12 +154,14 @@ + virtual void updateHorizScaling(GfxState *state) {} + virtual void updateTextPos(GfxState *state) {} + virtual void updateTextShift(GfxState *state, double shift) {} ++ virtual void saveTextPos(GfxState *state) {} ++ virtual void restoreTextPos(GfxState *state) {} + + //----- path painting + virtual void stroke(GfxState *state) {} + virtual void fill(GfxState *state) {} + virtual void eoFill(GfxState *state) {} +@@ -185,11 +194,18 @@ + CharCode code, Unicode *u, int uLen); + virtual void endType3Char(GfxState *state) {} + virtual void endTextObject(GfxState *state) {} ++ virtual void incCharCount(int nChars) {} + + //----- image drawing + virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, + int width, int height, GBool invert, + GBool inlineImg); ++ virtual void setSoftMaskFromImageMask(GfxState *state, ++ Object *ref, Stream *str, ++ int width, int height, GBool invert, ++ GBool inlineImg); + virtual void drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, GfxImageColorMap *colorMap, + int *maskColors, GBool inlineImg); +@@ -234,11 +250,10 @@ + virtual void clearSoftMask(GfxState *state) {} + + //----- links + virtual void processLink(Link *link) {} + + #if 1 //~tmp: turn off anti-aliasing temporarily +- virtual GBool getVectorAntialias() { return gFalse; } +- virtual void setVectorAntialias(GBool vaa) {} ++ virtual void setInShading(GBool sh) {} + #endif + + private: +diff -ru xpdf-3.02/xpdf/PDFDoc.cc xpdf-3.03/xpdf/PDFDoc.cc +--- xpdf-3.02/xpdf/PDFDoc.cc 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/PDFDoc.cc 2011-08-15 23:08:53.000000000 +0200 +@@ -36,6 +36,7 @@ + #ifndef DISABLE_OUTLINE + #include "Outline.h" + #endif ++#include "OptionalContent.h" + #include "PDFDoc.h" + + //------------------------------------------------------------------------ +@@ -174,6 +199,7 @@ + #ifndef DISABLE_OUTLINE + outline = NULL; + #endif ++ optContent = NULL; + ok = setup(ownerPassword, userPassword); + } + +@@ -183,38 +209,71 @@ + // check header + checkHeader(); + ++ // read the xref and catalog ++ if (!PDFDoc::setup2(ownerPassword, userPassword, gFalse)) { ++ if (errCode == errDamaged || errCode == errBadCatalog) { ++ // try repairing the xref table ++ error(errSyntaxWarning, -1, ++ "PDF file is damaged - attempting to reconstruct xref table..."); ++ if (!PDFDoc::setup2(ownerPassword, userPassword, gTrue)) { ++ return gFalse; ++ } ++ } else { ++ return gFalse; ++ } ++ } ++ ++#ifndef DISABLE_OUTLINE ++ // read outline ++ outline = new Outline(catalog->getOutline(), xref); ++#endif ++ ++ // read the optional content info ++ optContent = new OptionalContent(this); ++ ++ // done ++ return gTrue; ++} ++ ++GBool PDFDoc::setup2(GString *ownerPassword, GString *userPassword, ++ GBool repairXRef) { + // read xref table +- xref = new XRef(str); ++ xref = new XRef(str, repairXRef); + if (!xref->isOk()) { +- error(-1, "Couldn't read xref table"); ++ error(errSyntaxError, -1, "Couldn't read xref table"); + errCode = xref->getErrorCode(); ++ delete xref; ++ xref = NULL; + return gFalse; + } + + // check for encryption + if (!checkEncryption(ownerPassword, userPassword)) { + errCode = errEncrypted; ++ delete xref; ++ xref = NULL; + return gFalse; + } + + // read catalog + catalog = new Catalog(this); + if (!catalog->isOk()) { +- error(-1, "Couldn't read page catalog"); ++ error(errSyntaxError, -1, "Couldn't read page catalog"); + errCode = errBadCatalog; ++ delete catalog; ++ catalog = NULL; ++ delete xref; ++ xref = NULL; + return gFalse; + } + +-#ifndef DISABLE_OUTLINE +- // read outline +- outline = new Outline(catalog->getOutline(), xref); +-#endif +- +- // done + return gTrue; + } + + PDFDoc::~PDFDoc() { ++ if (optContent) { ++ delete optContent; ++ } + #ifndef DISABLE_OUTLINE + if (outline) { + delete outline; +@@ -280,7 +345,10 @@ + xref->getTrailerDict()->dictLookup("Encrypt", &encrypt); + if ((encrypted = encrypt.isDict())) { + if ((secHdlr = SecurityHandler::make(this, &encrypt))) { +- if (secHdlr->checkEncryption(ownerPassword, userPassword)) { ++ if (secHdlr->isUnencrypted()) { ++ // no encryption ++ ret = gTrue; ++ } else if (secHdlr->checkEncryption(ownerPassword, userPassword)) { + // authorization succeeded + xref->setEncryption(secHdlr->getPermissionFlags(), + secHdlr->getOwnerPasswordOk(), +@@ -315,7 +383,7 @@ + printf("***** page %d *****\n", page); + } + catalog->getPage(page)->display(out, hDPI, vDPI, +- rotate, useMediaBox, crop, printing, catalog, ++ rotate, useMediaBox, crop, printing, + abortCheckCbk, abortCheckCbkData); + } + +@@ -329,6 +397,7 @@ + for (page = firstPage; page <= lastPage; ++page) { + displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, printing, + abortCheckCbk, abortCheckCbkData); ++ catalog->doneWithPage(page); + } + } + +diff -ru xpdf-3.02/xpdf/PDFDoc.h xpdf-3.03/xpdf/PDFDoc.h +--- xpdf-3.02/xpdf/PDFDoc.h 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/PDFDoc.h 2011-08-15 23:08:53.000000000 +0200 +@@ -27,6 +27,8 @@ + class LinkAction; + class LinkDest; + class Outline; ++class OptionalContent; + + //------------------------------------------------------------------------ + // PDFDoc +@@ -128,6 +133,9 @@ + Outline *getOutline() { return outline; } + #endif + ++ // Return the OptionalContent object. ++ OptionalContent *getOptionalContent() { return optContent; } ++ + // Is the file encrypted? + GBool isEncrypted() { return xref->isEncrypted(); } + +@@ -154,27 +162,44 @@ + private: + + GBool setup(GString *ownerPassword, GString *userPassword); ++ GBool setup2(GString *ownerPassword, GString *userPassword, ++ GBool repairXRef); + void checkHeader(); + GBool checkEncryption(GString *ownerPassword, GString *userPassword); + + GString *fileName; + #ifdef WIN32 + wchar_t *fileNameU; + #endif + FILE *file; + BaseStream *str; + void *guiData; + double pdfVersion; + XRef *xref; + Catalog *catalog; + #ifndef DISABLE_OUTLINE + Outline *outline; + #endif +- ++ OptionalContent *optContent; + + GBool ok; + int errCode; +diff -ru xpdf-3.02/xpdf/pdftotext.cc xpdf-3.03/xpdf/pdftotext.cc +--- xpdf-3.02/xpdf/pdftotext.cc 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/pdftotext.cc 2011-08-15 23:08:53.000000000 +0200 +@@ -35,7 +35,8 @@ + + static int firstPage = 1; + static int lastPage = 0; + static GBool physLayout = gFalse; ++static double fixedPitch = 0; + static GBool rawOrder = gFalse; + static GBool htmlMeta = gFalse; + static char textEncName[128] = ""; +@@ -55,6 +58,8 @@ + "last page to convert"}, + {"-layout", argFlag, &physLayout, 0, + "maintain original physical layout"}, ++ {"-fixed", argFP, &fixedPitch, 0, ++ "assume fixed-pitch (or tabular) text"}, + {"-raw", argFlag, &rawOrder, 0, + "keep strings in content stream order"}, + {"-htmlmeta", argFlag, &htmlMeta, 0, +@@ -112,6 +117,9 @@ + goto err0; + } + fileName = new GString(argv[1]); ++ if (fixedPitch) { ++ physLayout = gTrue; ++ } + + // read config file + globalParams = new GlobalParams(cfgFileName); +@@ -232,7 +241,7 @@ + + // write text file + textOut = new TextOutputDev(textFileName->getCString(), +- physLayout, rawOrder, htmlMeta); ++ physLayout, fixedPitch, rawOrder, htmlMeta); + if (textOut->isOk()) { + doc->displayPages(textOut, firstPage, lastPage, 72, 72, 0, + gFalse, gTrue, gFalse); +diff -ru xpdf-3.02/xpdf/PreScanOutputDev.cc xpdf-3.03/xpdf/PreScanOutputDev.cc +--- xpdf-3.02/xpdf/PreScanOutputDev.cc 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/PreScanOutputDev.cc 2011-08-15 23:08:53.000000000 +0200 +@@ -14,6 +14,8 @@ + + #include + #include "GlobalParams.h" ++#include "Page.h" ++#include "Gfx.h" + #include "GfxFont.h" + #include "Link.h" + #include "PreScanOutputDev.h" +@@ -58,6 +60,62 @@ + state->getFillOpacity(), state->getBlendMode()); + } + ++GBool PreScanOutputDev::functionShadedFill(GfxState *state, ++ GfxFunctionShading *shading) { ++ if (shading->getColorSpace()->getMode() != csDeviceGray && ++ shading->getColorSpace()->getMode() != csCalGray) { ++ gray = gFalse; ++ } ++ mono = gFalse; ++ if (state->getFillOpacity() != 1 || ++ state->getBlendMode() != gfxBlendNormal) { ++ transparency = gTrue; ++ } ++ return gTrue; ++} ++ ++GBool PreScanOutputDev::axialShadedFill(GfxState *state, ++ GfxAxialShading *shading) { ++ if (shading->getColorSpace()->getMode() != csDeviceGray && ++ shading->getColorSpace()->getMode() != csCalGray) { ++ gray = gFalse; ++ } ++ mono = gFalse; ++ if (state->getFillOpacity() != 1 || ++ state->getBlendMode() != gfxBlendNormal) { ++ transparency = gTrue; ++ } ++ return gTrue; ++} ++ ++GBool PreScanOutputDev::radialShadedFill(GfxState *state, ++ GfxRadialShading *shading) { ++ if (shading->getColorSpace()->getMode() != csDeviceGray && ++ shading->getColorSpace()->getMode() != csCalGray) { ++ gray = gFalse; ++ } ++ mono = gFalse; ++ if (state->getFillOpacity() != 1 || ++ state->getBlendMode() != gfxBlendNormal) { ++ transparency = gTrue; ++ } ++ return gTrue; ++} ++ + void PreScanOutputDev::clip(GfxState *state) { + //~ check for a rectangle "near" the edge of the page; + //~ else set gdi to false +@@ -71,8 +129,6 @@ + int render; + GfxFont *font; + double m11, m12, m21, m22; +- Ref embRef; +- DisplayFontParam *dfp; + GBool simpleTTF; + + render = state->getRender(); +@@ -87,18 +143,14 @@ + + font = state->getFont(); + state->getFontTransMat(&m11, &m12, &m21, &m22); ++ //~ this should check for external fonts that are non-TrueType + simpleTTF = fabs(m11 + m22) < 0.01 && + m11 > 0 && + fabs(m12) < 0.01 && + fabs(m21) < 0.01 && + fabs(state->getHorizScaling() - 1) < 0.001 && + (font->getType() == fontTrueType || +- font->getType() == fontTrueTypeOT) && +- (font->getEmbeddedFontID(&embRef) || +- font->getExtFontFile() || +- (font->getName() && +- (dfp = globalParams->getDisplayFont(font->getName())) && +- dfp->kind == displayFontTT)); ++ font->getType() == fontTrueTypeOT); + if (simpleTTF) { + //~ need to create a FoFiTrueType object, and check for a Unicode cmap + } +@@ -127,6 +179,9 @@ + + check(state->getFillColorSpace(), state->getFillColor(), + state->getFillOpacity(), state->getBlendMode()); ++ if (state->getFillColorSpace()->getMode() == csPattern) { ++ patternImgMask = gTrue; ++ } + gdi = gFalse; + + if (inlineImg) { +@@ -149,12 +204,17 @@ + if (colorSpace->getMode() == csIndexed) { + colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); + } +- if (colorSpace->getMode() != csDeviceGray && +- colorSpace->getMode() != csCalGray) { ++ if (colorSpace->getMode() == csDeviceGray || ++ colorSpace->getMode() == csCalGray) { ++ if (colorMap->getBits() > 1) { ++ mono = gFalse; ++ } ++ } else { + gray = gFalse; ++ mono = gFalse; + } +- mono = gFalse; +- if (state->getBlendMode() != gfxBlendNormal) { ++ if (state->getFillOpacity() != 1 || ++ state->getBlendMode() != gfxBlendNormal) { + transparency = gTrue; + } + gdi = gFalse; +@@ -182,12 +242,17 @@ + if (colorSpace->getMode() == csIndexed) { + colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); + } +- if (colorSpace->getMode() != csDeviceGray && +- colorSpace->getMode() != csCalGray) { ++ if (colorSpace->getMode() == csDeviceGray || ++ colorSpace->getMode() == csCalGray) { ++ if (colorMap->getBits() > 1) { ++ mono = gFalse; ++ } ++ } else { + gray = gFalse; ++ mono = gFalse; + } +- mono = gFalse; +- if (state->getBlendMode() != gfxBlendNormal) { ++ if (state->getFillOpacity() != 1 || ++ state->getBlendMode() != gfxBlendNormal) { + transparency = gTrue; + } + gdi = gFalse; +@@ -253,5 +318,6 @@ + mono = gTrue; + gray = gTrue; + transparency = gFalse; ++ patternImgMask = gFalse; + gdi = gTrue; + } +diff -ru xpdf-3.02/xpdf/PreScanOutputDev.h xpdf-3.03/xpdf/PreScanOutputDev.h +--- xpdf-3.02/xpdf/PreScanOutputDev.h 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/PreScanOutputDev.h 2011-08-15 23:08:53.000000000 +0200 +@@ -41,6 +41,16 @@ + // Does this device use drawChar() or drawString()? + virtual GBool useDrawChar() { return gTrue; } + ++ // Does this device use functionShadedFill(), axialShadedFill(), and ++ // radialShadedFill()? If this returns false, these shaded fills ++ // will be reduced to a series of other drawing operations. ++ virtual GBool useShadedFills() { return gTrue; } ++ + // Does this device use beginType3Char/endType3Char? Otherwise, + // text in Type 3 fonts will be drawn with drawChar/drawString. + virtual GBool interpretType3Chars() { return gTrue; } +@@ -57,6 +67,15 @@ + virtual void stroke(GfxState *state); + virtual void fill(GfxState *state); + virtual void eoFill(GfxState *state); ++ virtual GBool functionShadedFill(GfxState *state, ++ GfxFunctionShading *shading); ++ virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading); ++ virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading); + + //----- path clipping + virtual void clip(GfxState *state); +@@ -110,6 +129,11 @@ + GBool usesTransparency() { return transparency; } + + // Returns true if the operations performed since the last call to ++ // clearStats() included any image mask fills with a pattern color ++ // space. ++ GBool usesPatternImageMask() { return patternImgMask; } ++ ++ // Returns true if the operations performed since the last call to + // clearStats() are all rasterizable by GDI calls in GDIOutputDev. + GBool isAllGDI() { return gdi; } + +@@ -124,6 +148,7 @@ + GBool mono; + GBool gray; + GBool transparency; ++ GBool patternImgMask; + GBool gdi; + }; + +diff -ru xpdf-3.02/xpdf/PSOutputDev.cc xpdf-3.03/xpdf/PSOutputDev.cc +--- xpdf-3.02/xpdf/PSOutputDev.cc 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/PSOutputDev.cc 2011-08-15 23:08:53.000000000 +0200 +@@ -19,6 +19,7 @@ + #include + #include "GString.h" + #include "GList.h" ++#include "GHash.h" + #include "config.h" + #include "GlobalParams.h" + #include "Object.h" +@@ -34,8 +35,10 @@ + #include "Page.h" + #include "Stream.h" + #include "Annot.h" ++#include "PDFDoc.h" + #include "XRef.h" + #include "PreScanOutputDev.h" ++#include "CharCodeToUnicode.h" + #if HAVE_SPLASH + # include "Splash.h" + # include "SplashBitmap.h" +@@ -55,8 +58,8 @@ + + //------------------------------------------------------------------------ + +-// Resolution at which pages with transparency will be rasterized. +-#define splashDPI 300 ++// Max size of a slice when rasterizing pages, in pixels. ++#define rasterizationSliceSize 20000000 + + //------------------------------------------------------------------------ + // PostScript prolog and setup +@@ -82,16 +85,24 @@ + " } for", + "~123sn", + "/pdfSetup {", +- " 3 1 roll 2 array astore", + " /setpagedevice where {", +- " pop 3 dict begin", ++ " pop 2 dict begin", ++ " /Policies 1 dict dup begin /PageSize 6 def end def", ++ " { /Duplex true def } if", ++ " currentdict end setpagedevice", ++ " } {", ++ " pop", ++ " } ifelse", ++ "} def", ++ "/pdfSetupPaper {", ++ " 2 array astore", ++ " /setpagedevice where {", ++ " pop 2 dict begin", + " /PageSize exch def", + " /ImagingBBox null def", +- " /Policies 1 dict dup begin /PageSize 3 def end def", +- " { /Duplex true def } if", + " currentdict end setpagedevice", + " } {", +- " pop pop", ++ " pop", + " } ifelse", + "} def", + "~1sn", +@@ -377,82 +388,82 @@ + "/Td { pdfTextMat transform moveto } def", + "/Tm { /pdfTextMat exch def } def", + "% text string operators", ++ "/xyshow where {", ++ " pop", ++ " /xyshow2 {", ++ " dup length array", ++ " 0 2 2 index length 1 sub {", ++ " 2 index 1 index 2 copy get 3 1 roll 1 add get", ++ " pdfTextMat dtransform", ++ " 4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put", ++ " } for", ++ " exch pop", ++ " xyshow", ++ " } def", ++ "}{", ++ " /xyshow2 {", ++ " currentfont /FontType get 0 eq {", ++ " 0 2 3 index length 1 sub {", ++ " currentpoint 4 index 3 index 2 getinterval show moveto", ++ " 2 copy get 2 index 3 2 roll 1 add get", ++ " pdfTextMat dtransform rmoveto", ++ " } for", ++ " } {", ++ " 0 1 3 index length 1 sub {", ++ " currentpoint 4 index 3 index 1 getinterval show moveto", ++ " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", ++ " pdfTextMat dtransform rmoveto", ++ " } for", ++ " } ifelse", ++ " pop pop", ++ " } def", ++ "} ifelse", + "/cshow where {", + " pop", +- " /cshow2 {", +- " dup {", +- " pop pop", +- " 1 string dup 0 3 index put 3 index exec", ++ " /xycp {", // xycharpath ++ " 0 3 2 roll", ++ " {", ++ " pop pop currentpoint 3 2 roll", ++ " 1 string dup 0 4 3 roll put false charpath moveto", ++ " 2 copy get 2 index 2 index 1 add get", ++ " pdfTextMat dtransform rmoveto", ++ " 2 add", + " } exch cshow", + " pop pop", + " } def", + "}{", +- " /cshow2 {", ++ " /xycp {", // xycharpath + " currentfont /FontType get 0 eq {", +- " 0 2 2 index length 1 sub {", +- " 2 copy get exch 1 add 2 index exch get", +- " 2 copy exch 256 mul add", +- " 2 string dup 0 6 5 roll put dup 1 5 4 roll put", +- " 3 index exec", ++ " 0 2 3 index length 1 sub {", ++ " currentpoint 4 index 3 index 2 getinterval false charpath moveto", ++ " 2 copy get 2 index 3 2 roll 1 add get", ++ " pdfTextMat dtransform rmoveto", + " } for", + " } {", +- " dup {", +- " 1 string dup 0 3 index put 3 index exec", +- " } forall", ++ " 0 1 3 index length 1 sub {", ++ " currentpoint 4 index 3 index 1 getinterval false charpath moveto", ++ " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", ++ " pdfTextMat dtransform rmoveto", ++ " } for", + " } ifelse", + " pop pop", + " } def", + "} ifelse", +- "/awcp {", // awidthcharpath +- " exch {", +- " false charpath", +- " 5 index 5 index rmoveto", +- " 6 index eq { 7 index 7 index rmoveto } if", +- " } exch cshow2", +- " 6 {pop} repeat", +- "} def", + "/Tj {", + " fCol", // because stringwidth has to draw Type 3 chars +- " 1 index stringwidth pdfTextMat idtransform pop", +- " sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse", +- " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32", +- " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0", +- " pdfTextMat dtransform", +- " 6 5 roll Tj1", +- "} def", +- "/Tj16 {", +- " fCol", // because stringwidth has to draw Type 3 chars +- " 2 index stringwidth pdfTextMat idtransform pop", +- " sub exch div", +- " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32", +- " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0", +- " pdfTextMat dtransform", +- " 6 5 roll Tj1", +- "} def", +- "/Tj16V {", +- " fCol", // because stringwidth has to draw Type 3 chars +- " 2 index stringwidth pdfTextMat idtransform exch pop", +- " sub exch div", +- " 0 pdfWordSpacing pdfTextMat dtransform 32", +- " 4 3 roll pdfCharSpacing add 0 exch", +- " pdfTextMat dtransform", +- " 6 5 roll Tj1", +- "} def", +- "/Tj1 {", + " 0 pdfTextRise pdfTextMat dtransform rmoveto", +- " currentpoint 8 2 roll", ++ " currentpoint 4 2 roll", + " pdfTextRender 1 and 0 eq {", +- " 6 copy awidthshow", ++ " 2 copy xyshow2", + " } if", + " pdfTextRender 3 and dup 1 eq exch 2 eq or {", +- " 7 index 7 index moveto", +- " 6 copy", ++ " 3 index 3 index moveto", ++ " 2 copy", + " currentfont /FontType get 3 eq { fCol } { sCol } ifelse", +- " false awcp currentpoint stroke moveto", ++ " xycp currentpoint stroke moveto", + " } if", + " pdfTextRender 4 and 0 ne {", +- " 8 6 roll moveto", +- " false awcp", ++ " 4 2 roll moveto xycp", + " /pdfTextClipPath [ pdfTextClipPath aload pop", + " {/moveto cvx}", + " {/lineto cvx}", +@@ -461,13 +472,13 @@ + " pathforall ] def", + " currentpoint newpath moveto", + " } {", +- " 8 {pop} repeat", ++ " pop pop pop pop", + " } ifelse", + " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", + "} def", +- "/TJm { pdfFontSize 0.001 mul mul neg 0", ++ "/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0", + " pdfTextMat dtransform rmoveto } def", +- "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch", ++ "/TJmV { 0.001 mul pdfFontSize mul neg 0 exch", + " pdfTextMat dtransform rmoveto } def", + "/Tclip { pdfTextClipPath cvx exec clip newpath", + " /pdfTextClipPath [] def } def", +@@ -495,19 +506,52 @@ + " fCol /pdfImBuf1 4 index 7 add 8 idiv string def", + " { currentfile pdfImBuf1 readhexstring pop } imagemask", + "} def", ++ "/pdfImStr {", ++ " 2 copy exch length lt {", ++ " 2 copy get exch 1 add exch", ++ " } {", ++ " ()", ++ " } ifelse", ++ "} def", + "/pdfImM1a {", +- " { 2 copy get exch 1 add exch } imagemask", ++ " { pdfImStr } imagemask", + " pop pop", + "} def", + "~23sn", +- "% Level 2 image operators", ++ "% Level 2/3 image operators", + "/pdfImBuf 100 string def", +- "/pdfIm {", +- " image", ++ "/pdfImStr {", ++ " 2 copy exch length lt {", ++ " 2 copy get exch 1 add exch", ++ " } {", ++ " ()", ++ " } ifelse", ++ "} def", ++ "/skipEOD {", + " { currentfile pdfImBuf readline", + " not { pop exit } if", + " (%-EOD-) eq { exit } if } loop", + "} def", ++ "/pdfIm { image skipEOD } def", ++ "~3sn", ++ "/pdfMask {", ++ " /ReusableStreamDecode filter", ++ " skipEOD", ++ " /maskStream exch def", ++ "} def", ++ "/pdfMaskEnd { maskStream closefile } def", ++ "/pdfMaskInit {", ++ " /maskArray exch def", ++ " /maskIdx 0 def", ++ "} def", ++ "/pdfMaskSrc {", ++ " maskIdx maskArray length lt {", ++ " maskArray maskIdx get", ++ " /maskIdx maskIdx 1 add def", ++ " } {", ++ " ()", ++ " } ifelse", ++ "} def", + "~23s", + "/pdfImSep {", + " findcmykcustomcolor exch", +@@ -523,17 +567,10 @@ + " 255 exch sub put", + " } for }", + " 6 5 roll customcolorimage", +- " { currentfile pdfImBuf readline", +- " not { pop exit } if", +- " (%-EOD-) eq { exit } if } loop", ++ " skipEOD", + "} def", + "~23sn", +- "/pdfImM {", +- " fCol imagemask", +- " { currentfile pdfImBuf readline", +- " not { pop exit } if", +- " (%-EOD-) eq { exit } if } loop", +- "} def", ++ "/pdfImM { fCol imagemask skipEOD } def", + "/pr { 2 index 2 index 3 2 roll putinterval 4 add } def", + "/pdfImClip {", + " gsave", +@@ -607,7 +644,7 @@ + " func n array astore", + "} def", + "/axialSH {", +- " dup 0 eq {", ++ " dup 2 lt {", + " true", + " } {", + " dup 8 eq {", +@@ -753,25 +790,12 @@ + double mWidth; // width of 'm' character + }; + +-static char *psFonts[] = { +- "Courier", +- "Courier-Bold", +- "Courier-Oblique", +- "Courier-BoldOblique", +- "Helvetica", +- "Helvetica-Bold", +- "Helvetica-Oblique", +- "Helvetica-BoldOblique", +- "Symbol", +- "Times-Roman", +- "Times-Bold", +- "Times-Italic", +- "Times-BoldItalic", +- "ZapfDingbats", +- NULL +-}; +- +-static PSSubstFont psSubstFonts[] = { ++// NB: must be in same order as base14SubstFonts in GfxFont.cc ++static PSSubstFont psBase14SubstFonts[14] = { ++ {"Courier", 0.600}, ++ {"Courier-Oblique", 0.600}, ++ {"Courier-Bold", 0.600}, ++ {"Courier-BoldOblique", 0.600}, + {"Helvetica", 0.833}, + {"Helvetica-Oblique", 0.833}, + {"Helvetica-Bold", 0.889}, +@@ -780,22 +804,28 @@ + {"Times-Italic", 0.722}, + {"Times-Bold", 0.833}, + {"Times-BoldItalic", 0.778}, +- {"Courier", 0.600}, +- {"Courier-Oblique", 0.600}, +- {"Courier-Bold", 0.600}, +- {"Courier-BoldOblique", 0.600} ++ // the last two are never used for substitution ++ {"Symbol", 0}, ++ {"ZapfDingbats", 0} ++}; ++ ++// Mapping from Type 1/1C font file to PS font name. ++struct PST1FontName { ++ Ref fontFileID; ++ GString *psName; // PostScript font name used for this ++ // embedded font file + }; + + // Encoding info for substitute 16-bit font + struct PSFont16Enc { + Ref fontID; +- GString *enc; ++ GString *enc; // NULL means font wasn't correctly substituted + }; + + //------------------------------------------------------------------------ +@@ -845,6 +875,13 @@ + }; + + //------------------------------------------------------------------------ ++ ++struct PSOutPaperSize { ++ PSOutPaperSize(int wA, int hA) { w = wA; h = hA; } ++ int w, h; ++}; ++ ++//------------------------------------------------------------------------ + // DeviceNRecoder + //------------------------------------------------------------------------ + +@@ -897,6 +934,9 @@ + if (imgStr) { + delete imgStr; + } ++ if (str->isEncoder()) { ++ delete str; ++ } + } + + void DeviceNRecoder::reset() { +@@ -942,10 +982,12 @@ + fwrite(data, 1, len, (FILE *)stream); + } + + PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA, + int firstPage, int lastPage, PSOutMode modeA, + int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, +- GBool manualCtrlA) { ++ GBool manualCtrlA, ++ PSOutCustomCodeCbk customCodeCbkA, ++ void *customCodeCbkDataA) { + FILE *f; + PSFileType fileTypeA; + +@@ -953,15 +995,18 @@ + underlayCbkData = NULL; + overlayCbk = NULL; + overlayCbkData = NULL; ++ customCodeCbk = customCodeCbkA; ++ customCodeCbkData = customCodeCbkDataA; + + fontIDs = NULL; +- fontFileIDs = NULL; +- fontFileNames = NULL; ++ fontNames = new GHash(gTrue); ++ t1FontNames = NULL; + font8Info = NULL; + font16Enc = NULL; + imgIDs = NULL; + formIDs = NULL; + xobjStack = NULL; ++ paperSizes = NULL; + embFontList = NULL; + customColors = NULL; + haveTextClip = gFalse; +@@ -978,71 +1023,82 @@ + signal(SIGPIPE, (SignalFunc)SIG_IGN); + #endif + if (!(f = popen(fileName + 1, "w"))) { + error(errIO, -1, "Couldn't run print command '{0:s}'", fileName); + ok = gFalse; + return; + } + #else + error(errIO, -1, "Print commands are not supported ('{0:s}')", fileName); + ok = gFalse; + return; + #endif + } else { + fileTypeA = psFile; + if (!(f = fopen(fileName, "w"))) { + error(errIO, -1, "Couldn't open PostScript file '{0:s}'", fileName); + ok = gFalse; + return; + } + } + + init(outputToFile, f, fileTypeA, +- xrefA, catalog, firstPage, lastPage, modeA, ++ docA, firstPage, lastPage, modeA, + imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA); + } + + PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, + PDFDoc *docA, + int firstPage, int lastPage, PSOutMode modeA, + int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, +- GBool manualCtrlA) { ++ GBool manualCtrlA, ++ PSOutCustomCodeCbk customCodeCbkA, ++ void *customCodeCbkDataA) { + underlayCbk = NULL; + underlayCbkData = NULL; + overlayCbk = NULL; + overlayCbkData = NULL; ++ customCodeCbk = customCodeCbkA; ++ customCodeCbkData = customCodeCbkDataA; + + fontIDs = NULL; +- fontFileIDs = NULL; +- fontFileNames = NULL; ++ fontNames = new GHash(gTrue); ++ t1FontNames = NULL; + font8Info = NULL; + font16Enc = NULL; + imgIDs = NULL; + formIDs = NULL; + xobjStack = NULL; ++ paperSizes = NULL; + embFontList = NULL; + customColors = NULL; + haveTextClip = gFalse; + t3String = NULL; + + init(outputFuncA, outputStreamA, psGeneric, + docA, firstPage, lastPage, modeA, + imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA); + } + + void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA, + PSFileType fileTypeA, PDFDoc *docA, + int firstPage, int lastPage, PSOutMode modeA, + int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, + GBool manualCtrlA) { + Catalog *catalog; + Page *page; + PDFRectangle *box; ++ PSOutPaperSize *size; ++ GList *names; ++ int pg, w, h, i; + + // initialize + ok = gTrue; + outputFunc = outputFuncA; + outputStream = outputStreamA; + fileType = fileTypeA; + doc = docA; + xref = doc->getXRef(); + catalog = doc->getCatalog(); + level = globalParams->getPSLevel(); + mode = modeA; + paperWidth = globalParams->getPSPaperWidth(); +@@ -1055,18 +1111,34 @@ + globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY); + } + if (paperWidth < 0 || paperHeight < 0) { +- // this check is needed in case the document has zero pages +- if (firstPage > 0 && firstPage <= catalog->getNumPages()) { +- page = catalog->getPage(firstPage); +- paperWidth = (int)ceil(page->getMediaWidth()); +- paperHeight = (int)ceil(page->getMediaHeight()); +- } else { +- paperWidth = 1; +- paperHeight = 1; +- } +- imgLLX = imgLLY = 0; +- imgURX = paperWidth; +- imgURY = paperHeight; ++ paperMatch = gTrue; ++ paperSizes = new GList(); ++ paperWidth = paperHeight = 1; // in case the document has zero pages ++ for (pg = (firstPage >= 1) ? firstPage : 1; ++ pg <= lastPage && pg <= catalog->getNumPages(); ++ ++pg) { ++ page = catalog->getPage(pg); ++ w = (int)ceil(page->getMediaWidth()); ++ h = (int)ceil(page->getMediaHeight()); ++ for (i = 0; i < paperSizes->getLength(); ++i) { ++ size = (PSOutPaperSize *)paperSizes->get(i); ++ if (size->w == w && size->h == h) { ++ break; ++ } ++ } ++ if (i == paperSizes->getLength()) { ++ paperSizes->append(new PSOutPaperSize(w, h)); ++ } ++ if (w > paperWidth) { ++ paperWidth = w; ++ } ++ if (h > paperHeight) { ++ paperHeight = h; ++ } ++ } ++ // NB: img{LLX,LLY,URX,URY} will be set by startPage() ++ } else { ++ paperMatch = gFalse; + } + preload = globalParams->getPSPreload(); + manualCtrl = manualCtrlA; +@@ -1088,17 +1160,21 @@ + clipLLX0 = clipLLY0 = 0; + clipURX0 = clipURY0 = -1; + +- // initialize fontIDs, fontFileIDs, and fontFileNames lists ++ // initialize fontIDs and fontNames lists + fontIDSize = 64; + fontIDLen = 0; + fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref)); +- fontFileIDSize = 64; +- fontFileIDLen = 0; +- fontFileIDs = (Ref *)gmallocn(fontFileIDSize, sizeof(Ref)); +- fontFileNameSize = 64; +- fontFileNameLen = 0; +- fontFileNames = (GString **)gmallocn(fontFileNameSize, sizeof(GString *)); +- nextTrueTypeNum = 0; ++ for (i = 0; i < 14; ++i) { ++ fontNames->add(new GString(psBase14SubstFonts[i].psName), 1); ++ } ++ names = globalParams->getPSResidentFonts(); ++ for (i = 0; i < names->getLength(); ++i) { ++ fontNames->add((GString *)names->get(i), 1); ++ } ++ delete names; ++ t1FontNameSize = 64; ++ t1FontNameLen = 0; ++ t1FontNames = (PST1FontName *)gmallocn(t1FontNameSize, sizeof(PST1FontName)); + font8InfoLen = 0; + font8InfoSize = 0; + font16EncLen = 0; +@@ -1173,20 +1249,21 @@ + } + #endif + } ++ if (paperSizes) { ++ deleteGList(paperSizes, PSOutPaperSize); ++ } + if (embFontList) { + delete embFontList; + } + if (fontIDs) { + gfree(fontIDs); + } +- if (fontFileIDs) { +- gfree(fontFileIDs); +- } +- if (fontFileNames) { +- for (i = 0; i < fontFileNameLen; ++i) { +- delete fontFileNames[i]; ++ delete fontNames; ++ if (t1FontNames) { ++ for (i = 0; i < t1FontNameLen; ++i) { ++ delete t1FontNames[i].psName; + } +- gfree(fontFileNames); ++ gfree(t1FontNames); + } + if (font8Info) { + for (i = 0; i < font8InfoLen; ++i) { +@@ -1196,7 +1273,9 @@ + } + if (font16Enc) { + for (i = 0; i < font16EncLen; ++i) { +- delete font16Enc[i].enc; ++ if (font16Enc[i].enc) { ++ delete font16Enc[i].enc; ++ } + } + gfree(font16Enc); + } +@@ -1216,7 +1295,9 @@ + PDFRectangle *mediaBox, PDFRectangle *cropBox, + int pageRotate) { + Object info, obj1; ++ PSOutPaperSize *size; + double x1, y1, x2, y2; ++ int i; + + switch (mode) { + case psModePS: +@@ -1230,7 +1311,7 @@ + break; + } + +- writePSFmt("% Produced by xpdf/pdftops {0:s}\n", xpdfVersion); ++ writePSFmt("%XpdfVersion: {0:s}\n", xpdfVersion); + xref->getDocInfo(&info); + if (info.isDict() && info.dictLookup("Creator", &obj1)->isString()) { + writePS("%%Creator: "); +@@ -1254,14 +1335,24 @@ + + switch (mode) { + case psModePS: +- writePSFmt("%%DocumentMedia: plain {0:d} {1:d} 0 () ()\n", +- paperWidth, paperHeight); ++ if (paperMatch) { ++ for (i = 0; i < paperSizes->getLength(); ++i) { ++ size = (PSOutPaperSize *)paperSizes->get(i); ++ writePSFmt("%%{0:s} {1:d}x{2:d} {1:d} {2:d} 0 () ()\n", ++ i==0 ? "DocumentMedia:" : "+", size->w, size->h); ++ } ++ } else { ++ writePSFmt("%%DocumentMedia: plain {0:d} {1:d} 0 () ()\n", ++ paperWidth, paperHeight); ++ } + writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight); + writePSFmt("%%Pages: {0:d}\n", lastPage - firstPage + 1); + writePS("%%EndComments\n"); +- writePS("%%BeginDefaults\n"); +- writePS("%%PageMedia: plain\n"); +- writePS("%%EndDefaults\n"); ++ if (!paperMatch) { ++ writePS("%%BeginDefaults\n"); ++ writePS("%%PageMedia: plain\n"); ++ writePS("%%EndDefaults\n"); ++ } + break; + case psModeEPS: + epsX1 = cropBox->x1; +@@ -1343,7 +1434,9 @@ + Page *page; + Dict *resDict; + Annots *annots; +- Object obj1, obj2; ++ Object *acroForm; ++ Object obj1, obj2, obj3; ++ GString *s; + int pg, i; + + if (mode == psModeForm) { +@@ -1371,11 +1464,31 @@ + } + delete annots; + } ++ if ((acroForm = catalog->getAcroForm()) && acroForm->isDict()) { ++ if (acroForm->dictLookup("DR", &obj1)->isDict()) { ++ setupResources(obj1.getDict()); ++ } ++ obj1.free(); ++ if (acroForm->dictLookup("Fields", &obj1)->isArray()) { ++ for (i = 0; i < obj1.arrayGetLength(); ++i) { ++ if (obj1.arrayGet(i, &obj2)->isDict()) { ++ if (obj2.dictLookup("DR", &obj3)->isDict()) { ++ setupResources(obj3.getDict()); ++ } ++ obj3.free(); ++ } ++ obj2.free(); ++ } ++ } ++ obj1.free(); ++ } + if (mode != psModeForm) { + if (mode != psModeEPS && !manualCtrl) { +- writePSFmt("{0:d} {1:d} {2:s} pdfSetup\n", +- paperWidth, paperHeight, ++ writePSFmt("{0:s} pdfSetup\n", + globalParams->getPSDuplex() ? "true" : "false"); ++ if (!paperMatch) { ++ writePSFmt("{0:d} {1:d} pdfSetupPaper\n", paperWidth, paperHeight); ++ } + } + #if OPI_SUPPORT + if (globalParams->getPSOPI()) { +@@ -1383,6 +1496,13 @@ + } + #endif + } ++ if (customCodeCbk) { ++ if ((s = (*customCodeCbk)(this, psOutCustomDocSetup, 0, ++ customCodeCbkData))) { ++ writePS(s->getCString()); ++ delete s; ++ } ++ } + } + + void PSOutputDev::writePageTrailer() { +@@ -1558,18 +1681,15 @@ + } + + void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) { +- Ref fontFileID; +- GString *name; +- PSFontParam *fontParam; ++ GfxFontLoc *fontLoc; + GString *psName; +- char buf[16]; + GBool subst; ++ char buf[16]; + UnicodeMap *uMap; + char *charName; + double xs, ys; + int code; + double w1, w2; +- double *fm; + int i, j; + + // check if font is already set up +@@ -1587,119 +1707,120 @@ + } + fontIDs[fontIDLen++] = *font->getID(); + ++ psName = NULL; + xs = ys = 1; + subst = gFalse; + +- // check for resident 8-bit font +- if (font->getName() && +- (fontParam = globalParams->getPSFont(font->getName()))) { +- psName = new GString(fontParam->psFontName->getCString()); +- +- // check for embedded Type 1 font +- } else if (globalParams->getPSEmbedType1() && +- font->getType() == fontType1 && +- font->getEmbeddedFontID(&fontFileID)) { +- psName = filterPSName(font->getEmbeddedFontName()); +- setupEmbeddedType1Font(&fontFileID, psName); +- +- // check for embedded Type 1C font +- } else if (globalParams->getPSEmbedType1() && +- font->getType() == fontType1C && +- font->getEmbeddedFontID(&fontFileID)) { +- // use the PDF font name because the embedded font name might +- // not include the subset prefix +- psName = filterPSName(font->getOrigName()); +- setupEmbeddedType1CFont(font, &fontFileID, psName); +- +- // check for embedded OpenType - Type 1C font +- } else if (globalParams->getPSEmbedType1() && +- font->getType() == fontType1COT && +- font->getEmbeddedFontID(&fontFileID)) { +- // use the PDF font name because the embedded font name might +- // not include the subset prefix +- psName = filterPSName(font->getOrigName()); +- setupEmbeddedOpenTypeT1CFont(font, &fontFileID, psName); +- +- // check for external Type 1 font file +- } else if (globalParams->getPSEmbedType1() && +- font->getType() == fontType1 && +- font->getExtFontFile()) { +- // this assumes that the PS font name matches the PDF font name +- psName = font->getName()->copy(); +- setupExternalType1Font(font->getExtFontFile(), psName); +- +- // check for embedded TrueType font +- } else if (globalParams->getPSEmbedTrueType() && +- (font->getType() == fontTrueType || +- font->getType() == fontTrueTypeOT) && +- font->getEmbeddedFontID(&fontFileID)) { +- psName = filterPSName(font->getEmbeddedFontName()); +- setupEmbeddedTrueTypeFont(font, &fontFileID, psName); +- +- // check for external TrueType font file +- } else if (globalParams->getPSEmbedTrueType() && +- font->getType() == fontTrueType && +- font->getExtFontFile()) { +- psName = filterPSName(font->getName()); +- setupExternalTrueTypeFont(font, psName); +- +- // check for embedded CID PostScript font +- } else if (globalParams->getPSEmbedCIDPostScript() && +- font->getType() == fontCIDType0C && +- font->getEmbeddedFontID(&fontFileID)) { +- psName = filterPSName(font->getEmbeddedFontName()); +- setupEmbeddedCIDType0Font(font, &fontFileID, psName); +- +- // check for embedded CID TrueType font +- } else if (globalParams->getPSEmbedCIDTrueType() && +- (font->getType() == fontCIDType2 || +- font->getType() == fontCIDType2OT) && +- font->getEmbeddedFontID(&fontFileID)) { +- psName = filterPSName(font->getEmbeddedFontName()); +- //~ should check to see if font actually uses vertical mode +- setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName, gTrue); +- +- // check for embedded OpenType - CID CFF font +- } else if (globalParams->getPSEmbedCIDPostScript() && +- font->getType() == fontCIDType0COT && +- font->getEmbeddedFontID(&fontFileID)) { +- psName = filterPSName(font->getEmbeddedFontName()); +- setupEmbeddedOpenTypeCFFFont(font, &fontFileID, psName); +- +- // check for Type 3 font +- } else if (font->getType() == fontType3) { ++ if (font->getType() == fontType3) { + psName = GString::format("T3_{0:d}_{1:d}", + font->getID()->num, font->getID()->gen); + setupType3Font(font, psName, parentResDict); +- +- // do 8-bit font substitution +- } else if (!font->isCIDFont()) { +- subst = gTrue; +- name = font->getName(); +- psName = NULL; +- if (name) { +- for (i = 0; psFonts[i]; ++i) { +- if (name->cmp(psFonts[i]) == 0) { +- psName = new GString(psFonts[i]); +- break; ++ } else { ++ fontLoc = font->locateFont(xref, gTrue); ++ switch (fontLoc->locType) { ++ case gfxFontLocEmbedded: ++ switch (fontLoc->fontType) { ++ case fontType1: ++ // this assumes that the PS font name matches the PDF font name ++ psName = font->getEmbeddedFontName()->copy(); ++ setupEmbeddedType1Font(&fontLoc->embFontID, psName); ++ break; ++ case fontType1C: ++ psName = makePSFontName(font, &fontLoc->embFontID); ++ setupEmbeddedType1CFont(font, &fontLoc->embFontID, psName); ++ break; ++ case fontType1COT: ++ psName = makePSFontName(font, &fontLoc->embFontID); ++ setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID, psName); ++ break; ++ case fontTrueType: ++ case fontTrueTypeOT: ++ psName = makePSFontName(font, font->getID()); ++ setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID, psName); ++ break; ++ case fontCIDType0C: ++ psName = makePSFontName(font, &fontLoc->embFontID); ++ setupEmbeddedCIDType0Font(font, &fontLoc->embFontID, psName); ++ break; ++ case fontCIDType2: ++ case fontCIDType2OT: ++ psName = makePSFontName(font, font->getID()); ++ //~ should check to see if font actually uses vertical mode ++ setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID, psName, gTrue); ++ break; ++ case fontCIDType0COT: ++ psName = makePSFontName(font, &fontLoc->embFontID); ++ setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID, psName); ++ break; ++ default: ++ break; ++ } ++ break; ++ case gfxFontLocExternal: ++ //~ add cases for external 16-bit fonts ++ switch (fontLoc->fontType) { ++ case fontType1: ++ if (font->getName()) { ++ // this assumes that the PS font name matches the PDF font name ++ psName = font->getName()->copy(); ++ } else { ++ //~ this won't work -- the PS font name won't match ++ psName = makePSFontName(font, font->getID()); + } ++ setupExternalType1Font(fontLoc->path, psName); ++ break; ++ case fontTrueType: ++ case fontTrueTypeOT: ++ psName = makePSFontName(font, font->getID()); ++ setupExternalTrueTypeFont(font, fontLoc->path, psName); ++ break; ++ case fontCIDType2: ++ case fontCIDType2OT: ++ psName = makePSFontName(font, font->getID()); ++ //~ should check to see if font actually uses vertical mode ++ setupExternalCIDTrueTypeFont(font, fontLoc->path, psName, gTrue); ++ break; ++ default: ++ break; + } ++ break; ++ case gfxFontLocResident: ++ psName = fontLoc->path->copy(); ++ break; + } ++ + if (!psName) { +- if (font->isFixedWidth()) { +- i = 8; +- } else if (font->isSerif()) { +- i = 4; ++ if (font->isCIDFont()) { ++ error(errSyntaxError, -1, ++ "Couldn't find a font to substitute for '{0:s}' ('{1:s}' character collection)", ++ font->getName() ? font->getName()->getCString() ++ : "(unnamed)", ++ ((GfxCIDFont *)font)->getCollection() ++ ? ((GfxCIDFont *)font)->getCollection()->getCString() ++ : "(unknown)"); ++ if (font16EncLen >= font16EncSize) { ++ font16EncSize += 16; ++ font16Enc = (PSFont16Enc *)greallocn(font16Enc, ++ font16EncSize, ++ sizeof(PSFont16Enc)); ++ } ++ font16Enc[font16EncLen].fontID = *font->getID(); ++ font16Enc[font16EncLen].enc = NULL; ++ ++font16EncLen; + } else { +- i = 0; +- } +- if (font->isBold()) { +- i += 2; +- } +- if (font->isItalic()) { +- i += 1; ++ error(errSyntaxError, -1, ++ "Couldn't find a font to substitute for '{0:s}'", ++ font->getName() ? font->getName()->getCString() ++ : "(unnamed)"); + } +- psName = new GString(psSubstFonts[i].psName); ++ delete fontLoc; ++ return; ++ } ++ ++ // scale substituted 8-bit fonts ++ if (fontLoc->locType == gfxFontLocResident && ++ fontLoc->substIdx >= 0) { ++ subst = gTrue; + for (code = 0; code < 256; ++code) { + if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) && + charName[0] == 'm' && charName[1] == '\0') { +@@ -1711,56 +1832,37 @@ + } else { + w1 = 0; + } +- w2 = psSubstFonts[i].mWidth; ++ w2 = psBase14SubstFonts[fontLoc->substIdx].mWidth; + xs = w1 / w2; + if (xs < 0.1) { + xs = 1; + } +- if (font->getType() == fontType3) { +- // This is a hack which makes it possible to substitute for some +- // Type 3 fonts. The problem is that it's impossible to know what +- // the base coordinate system used in the font is without actually +- // rendering the font. +- ys = xs; +- fm = font->getFontMatrix(); +- if (fm[0] != 0) { +- ys *= fm[3] / fm[0]; +- } +- } else { +- ys = 1; +- } + } + +- // do 16-bit font substitution +- } else if ((fontParam = globalParams-> +- getPSFont16(font->getName(), +- ((GfxCIDFont *)font)->getCollection(), +- font->getWMode()))) { +- subst = gTrue; +- psName = fontParam->psFontName->copy(); +- if (font16EncLen >= font16EncSize) { +- font16EncSize += 16; +- font16Enc = (PSFont16Enc *)greallocn(font16Enc, +- font16EncSize, sizeof(PSFont16Enc)); +- } +- font16Enc[font16EncLen].fontID = *font->getID(); +- font16Enc[font16EncLen].enc = fontParam->encoding->copy(); +- if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) { +- uMap->decRefCnt(); ++ // handle encodings for substituted CID fonts ++ if (fontLoc->locType == gfxFontLocResident && ++ fontLoc->fontType >= fontCIDType0) { ++ subst = gTrue; ++ if (font16EncLen >= font16EncSize) { ++ font16EncSize += 16; ++ font16Enc = (PSFont16Enc *)greallocn(font16Enc, ++ font16EncSize, ++ sizeof(PSFont16Enc)); ++ } ++ font16Enc[font16EncLen].fontID = *font->getID(); ++ if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) { ++ font16Enc[font16EncLen].enc = fontLoc->encoding->copy(); ++ uMap->decRefCnt(); ++ } else { ++ error(errSyntaxError, -1, ++ "Couldn't find Unicode map for 16-bit font encoding '{0:t}'", ++ fontLoc->encoding); ++ font16Enc[font16EncLen].enc = NULL; ++ } + ++font16EncLen; +- } else { +- error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'", +- font16Enc[font16EncLen].enc->getCString()); + } + +- // give up - can't do anything with this font +- } else { +- error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)", +- font->getName() ? font->getName()->getCString() : "(unnamed)", +- ((GfxCIDFont *)font)->getCollection() +- ? ((GfxCIDFont *)font)->getCollection()->getCString() +- : "(unknown)"); +- return; ++ delete fontLoc; + } + + // generate PostScript code to set up the font +@@ -1787,11 +1889,6 @@ + charName = buf; + } else { + charName = ((Gfx8BitFont *)font)->getCharName(i+j); +- // this is a kludge for broken PDF files that encode char 32 +- // as .notdef +- if (i+j == 32 && charName && !strcmp(charName, ".notdef")) { +- charName = "space"; +- } + } + writePS("/"); + writePSName(charName ? charName : (char *)".notdef"); +@@ -1821,36 +1918,30 @@ + int i; + + // check if font is already embedded +- for (i = 0; i < fontFileIDLen; ++i) { +- if (fontFileIDs[i].num == id->num && +- fontFileIDs[i].gen == id->gen) +- return; +- } +- +- // add entry to fontFileIDs list +- if (fontFileIDLen >= fontFileIDSize) { +- fontFileIDSize += 64; +- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); ++ if (fontNames->lookupInt(psName)) { ++ return; + } +- fontFileIDs[fontFileIDLen++] = *id; ++ fontNames->add(psName->copy(), 1); + + // get the font stream and info + refObj.initRef(id->num, id->gen); + refObj.fetch(xref, &strObj); + refObj.free(); + if (!strObj.isStream()) { + error(errSyntaxError, -1, "Embedded font file object is not a stream"); + goto err1; + } + if (!(dict = strObj.streamGetDict())) { + error(errSyntaxError, -1, + "Embedded font stream is missing its dictionary"); + goto err1; + } + dict->lookup("Length1", &obj1); + dict->lookup("Length2", &obj2); + dict->lookup("Length3", &obj3); + if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) { + error(errSyntaxError, -1, + "Missing length fields in embedded font stream dictionary"); + obj1.free(); + obj2.free(); + obj3.free(); +@@ -1947,22 +2039,12 @@ + void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) { + FILE *fontFile; + int c; +- int i; + + // check if font is already embedded +- for (i = 0; i < fontFileNameLen; ++i) { +- if (!fontFileNames[i]->cmp(fileName)) { +- return; +- } +- } +- +- // add entry to fontFileNames list +- if (fontFileNameLen >= fontFileNameSize) { +- fontFileNameSize += 64; +- fontFileNames = (GString **)greallocn(fontFileNames, +- fontFileNameSize, sizeof(GString *)); ++ if (fontNames->lookupInt(psName)) { ++ return; + } +- fontFileNames[fontFileNameLen++] = fileName->copy(); ++ fontNames->add(psName->copy(), 1); + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); +@@ -1992,18 +2074,22 @@ + int i; + + // check if font is already embedded +- for (i = 0; i < fontFileIDLen; ++i) { +- if (fontFileIDs[i].num == id->num && +- fontFileIDs[i].gen == id->gen) ++ for (i = 0; i < t1FontNameLen; ++i) { ++ if (t1FontNames[i].fontFileID.num == id->num && ++ t1FontNames[i].fontFileID.gen == id->gen) { ++ psName->clear(); ++ psName->insert(0, t1FontNames[i].psName); + return; ++ } + } +- +- // add entry to fontFileIDs list +- if (fontFileIDLen >= fontFileIDSize) { +- fontFileIDSize += 64; +- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); +- } +- fontFileIDs[fontFileIDLen++] = *id; ++ if (t1FontNameLen == t1FontNameSize) { ++ t1FontNameSize *= 2; ++ t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, ++ sizeof(PST1FontName)); ++ } ++ t1FontNames[t1FontNameLen].fontFileID = *id; ++ t1FontNames[t1FontNameLen].psName = psName->copy(); ++ ++t1FontNameLen; + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); +@@ -2012,13 +2098,14 @@ + embFontList->append("\n"); + + // convert it to a Type 1 font +- fontBuf = font->readEmbFontFile(xref, &fontLen); +- if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { +- ffT1C->convertToType1(psName->getCString(), NULL, gTrue, +- outputFunc, outputStream); +- delete ffT1C; ++ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { ++ if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { ++ ffT1C->convertToType1(psName->getCString(), NULL, gTrue, ++ outputFunc, outputStream); ++ delete ffT1C; ++ } ++ gfree(fontBuf); + } +- gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +@@ -2032,18 +2119,22 @@ + int i; + + // check if font is already embedded +- for (i = 0; i < fontFileIDLen; ++i) { +- if (fontFileIDs[i].num == id->num && +- fontFileIDs[i].gen == id->gen) ++ for (i = 0; i < t1FontNameLen; ++i) { ++ if (t1FontNames[i].fontFileID.num == id->num && ++ t1FontNames[i].fontFileID.gen == id->gen) { ++ psName->clear(); ++ psName->insert(0, t1FontNames[i].psName); + return; ++ } + } +- +- // add entry to fontFileIDs list +- if (fontFileIDLen >= fontFileIDSize) { +- fontFileIDSize += 64; +- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); +- } +- fontFileIDs[fontFileIDLen++] = *id; ++ if (t1FontNameLen == t1FontNameSize) { ++ t1FontNameSize *= 2; ++ t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, ++ sizeof(PST1FontName)); ++ } ++ t1FontNames[t1FontNameLen].fontFileID = *id; ++ t1FontNames[t1FontNameLen].psName = psName->copy(); ++ ++t1FontNameLen; + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); +@@ -2052,15 +2143,16 @@ + embFontList->append("\n"); + + // convert it to a Type 1 font +- fontBuf = font->readEmbFontFile(xref, &fontLen); +- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { +- if (ffTT->isOpenTypeCFF()) { +- ffTT->convertToType1(psName->getCString(), NULL, gTrue, +- outputFunc, outputStream); ++ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { ++ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { ++ if (ffTT->isOpenTypeCFF()) { ++ ffTT->convertToType1(psName->getCString(), NULL, gTrue, ++ outputFunc, outputStream); ++ } ++ delete ffTT; + } +- delete ffTT; ++ gfree(fontBuf); + } +- gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +@@ -2071,26 +2163,7 @@ + char *fontBuf; + int fontLen; + FoFiTrueType *ffTT; + int *codeToGID; +- int i; +- +- // check if font is already embedded +- for (i = 0; i < fontFileIDLen; ++i) { +- if (fontFileIDs[i].num == id->num && +- fontFileIDs[i].gen == id->gen) { +- psName->appendf("_{0:d}", nextTrueTypeNum++); +- break; +- } +- } +- +- // add entry to fontFileIDs list +- if (i == fontFileIDLen) { +- if (fontFileIDLen >= fontFileIDSize) { +- fontFileIDSize += 64; +- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); +- } +- fontFileIDs[fontFileIDLen++] = *id; +- } + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); +@@ -2099,60 +2172,38 @@ + embFontList->append("\n"); + + // convert it to a Type 42 font +- fontBuf = font->readEmbFontFile(xref, &fontLen); +- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { +- codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); +- ffTT->convertToType42(psName->getCString(), +- ((Gfx8BitFont *)font)->getHasEncoding() +- ? ((Gfx8BitFont *)font)->getEncoding() +- : (char **)NULL, +- codeToGID, outputFunc, outputStream); +- if (codeToGID) { +- if (font8InfoLen >= font8InfoSize) { +- font8InfoSize += 16; +- font8Info = (PSFont8Info *)greallocn(font8Info, +- font8InfoSize, +- sizeof(PSFont8Info)); ++ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { ++ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { ++ codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); ++ ffTT->convertToType42(psName->getCString(), ++ ((Gfx8BitFont *)font)->getHasEncoding() ++ ? ((Gfx8BitFont *)font)->getEncoding() ++ : (char **)NULL, ++ codeToGID, outputFunc, outputStream); ++ if (codeToGID) { ++ if (font8InfoLen >= font8InfoSize) { ++ font8InfoSize += 16; ++ font8Info = (PSFont8Info *)greallocn(font8Info, ++ font8InfoSize, ++ sizeof(PSFont8Info)); ++ } ++ font8Info[font8InfoLen].fontID = *font->getID(); ++ font8Info[font8InfoLen].codeToGID = codeToGID; ++ ++font8InfoLen; + } +- font8Info[font8InfoLen].fontID = *font->getID(); +- font8Info[font8InfoLen].codeToGID = codeToGID; +- ++font8InfoLen; ++ delete ffTT; + } +- delete ffTT; ++ gfree(fontBuf); + } +- gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); + } + +-void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *psName) { +- GString *fileName; +- char *fontBuf; +- int fontLen; ++void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *fileName, ++ GString *psName) { + FoFiTrueType *ffTT; + int *codeToGID; +- int i; +- +- // check if font is already embedded +- fileName = font->getExtFontFile(); +- for (i = 0; i < fontFileNameLen; ++i) { +- if (!fontFileNames[i]->cmp(fileName)) { +- psName->appendf("_{0:d}", nextTrueTypeNum++); +- break; +- } +- } +- +- // add entry to fontFileNames list +- if (i == fontFileNameLen) { +- if (fontFileNameLen >= fontFileNameSize) { +- fontFileNameSize += 64; +- fontFileNames = +- (GString **)greallocn(fontFileNames, +- fontFileNameSize, sizeof(GString *)); +- } +- fontFileNames[fontFileNameLen++] = fileName->copy(); +- } + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); +@@ -2161,8 +2212,7 @@ + embFontList->append("\n"); + + // convert it to a Type 42 font +- fontBuf = font->readExtFontFile(&fontLen); +- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { ++ if ((ffTT = FoFiTrueType::load(fileName->getCString()))) { + codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); + ffTT->convertToType42(psName->getCString(), + ((Gfx8BitFont *)font)->getHasEncoding() +@@ -2182,7 +2232,6 @@ + } + delete ffTT; + } +- gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +@@ -2196,18 +2245,22 @@ + int i; + + // check if font is already embedded +- for (i = 0; i < fontFileIDLen; ++i) { +- if (fontFileIDs[i].num == id->num && +- fontFileIDs[i].gen == id->gen) ++ for (i = 0; i < t1FontNameLen; ++i) { ++ if (t1FontNames[i].fontFileID.num == id->num && ++ t1FontNames[i].fontFileID.gen == id->gen) { ++ psName->clear(); ++ psName->insert(0, t1FontNames[i].psName); + return; ++ } + } +- +- // add entry to fontFileIDs list +- if (fontFileIDLen >= fontFileIDSize) { +- fontFileIDSize += 64; +- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); +- } +- fontFileIDs[fontFileIDLen++] = *id; ++ if (t1FontNameLen == t1FontNameSize) { ++ t1FontNameSize *= 2; ++ t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, ++ sizeof(PST1FontName)); ++ } ++ t1FontNames[t1FontNameLen].fontFileID = *id; ++ t1FontNames[t1FontNameLen].psName = psName->copy(); ++ ++t1FontNameLen; + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); +@@ -2216,18 +2269,21 @@ + embFontList->append("\n"); + + // convert it to a Type 0 font +- fontBuf = font->readEmbFontFile(xref, &fontLen); +- if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { +- if (globalParams->getPSLevel() >= psLevel3) { +- // Level 3: use a CID font +- ffT1C->convertToCIDType0(psName->getCString(), 0, NULL, +- outputFunc, outputStream); +- } else { +- // otherwise: use a non-CID composite font +- ffT1C->convertToType0(psName->getCString(), 0, NULL, +- outputFunc, outputStream); ++ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { ++ if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { ++ if (globalParams->getPSLevel() >= psLevel3) { ++ // Level 3: use a CID font ++ ffT1C->convertToCIDType0(psName->getCString(), NULL, 0, ++ outputFunc, outputStream); ++ } else { ++ // otherwise: use a non-CID composite font ++ ffT1C->convertToType0(psName->getCString(), NULL, 0, ++ outputFunc, outputStream); ++ } ++ delete ffT1C; + } +- delete ffT1C; ++ gfree(fontBuf); + } +- gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +@@ -2239,23 +2295,50 @@ + char *fontBuf; + int fontLen; + FoFiTrueType *ffTT; +- int i; + +- // check if font is already embedded +- for (i = 0; i < fontFileIDLen; ++i) { +- if (fontFileIDs[i].num == id->num && +- fontFileIDs[i].gen == id->gen) { +- psName->appendf("_{0:d}", nextTrueTypeNum++); +- break; ++ // beginning comment ++ writePSFmt("%%BeginResource: font {0:t}\n", psName); ++ embFontList->append("%%+ font "); ++ embFontList->append(psName->getCString()); ++ embFontList->append("\n"); ++ ++ // convert it to a Type 0 font ++ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { ++ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { ++ if (globalParams->getPSLevel() >= psLevel3) { ++ // Level 3: use a CID font ++ ffTT->convertToCIDType2(psName->getCString(), ++ ((GfxCIDFont *)font)->getCIDToGID(), ++ ((GfxCIDFont *)font)->getCIDToGIDLen(), ++ needVerticalMetrics, ++ outputFunc, outputStream); ++ } else { ++ // otherwise: use a non-CID composite font ++ ffTT->convertToType0(psName->getCString(), ++ ((GfxCIDFont *)font)->getCIDToGID(), ++ ((GfxCIDFont *)font)->getCIDToGIDLen(), ++ needVerticalMetrics, ++ outputFunc, outputStream); ++ } ++ delete ffTT; + } ++ gfree(fontBuf); + } + +- // add entry to fontFileIDs list +- if (fontFileIDLen >= fontFileIDSize) { +- fontFileIDSize += 64; +- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); +- } +- fontFileIDs[fontFileIDLen++] = *id; ++ // ending comment ++ writePS("%%EndResource\n"); ++} ++ ++void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font, ++ GString *fileName, ++ GString *psName, ++ GBool needVerticalMetrics) { ++ FoFiTrueType *ffTT; ++ int *codeToGID; ++ int codeToGIDLen; ++ CharCodeToUnicode *ctu; ++ Unicode uBuf[8]; ++ int cmap, code; + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); +@@ -2264,26 +2347,62 @@ + embFontList->append("\n"); + + // convert it to a Type 0 font +- fontBuf = font->readEmbFontFile(xref, &fontLen); +- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { +- if (globalParams->getPSLevel() >= psLevel3) { +- // Level 3: use a CID font +- ffTT->convertToCIDType2(psName->getCString(), +- ((GfxCIDFont *)font)->getCIDToGID(), +- ((GfxCIDFont *)font)->getCIDToGIDLen(), +- needVerticalMetrics, +- outputFunc, outputStream); ++ //~ this should use fontNum to load the correct font ++ if ((ffTT = FoFiTrueType::load(fileName->getCString()))) { ++ ++ // check for embedding permission ++ if (ffTT->getEmbeddingRights() >= 1) { ++ ++ // create a CID-to-GID mapping, via Unicode ++ if ((ctu = ((GfxCIDFont *)font)->getToUnicode())) { ++ // look for a Unicode cmap ++ for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) { ++ if ((ffTT->getCmapPlatform(cmap) == 3 && ++ ffTT->getCmapEncoding(cmap) == 1) || ++ ffTT->getCmapPlatform(cmap) == 0) { ++ break; ++ } ++ } ++ if (cmap < ffTT->getNumCmaps()) { ++ // map CID -> Unicode -> GID ++ codeToGIDLen = ctu->getLength(); ++ codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); ++ for (code = 0; code < codeToGIDLen; ++code) { ++ if (ctu->mapToUnicode(code, uBuf, 8) > 0) { ++ codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]); ++ } else { ++ codeToGID[code] = 0; ++ } ++ } ++ if (globalParams->getPSLevel() >= psLevel3) { ++ // Level 3: use a CID font ++ ffTT->convertToCIDType2(psName->getCString(), ++ codeToGID, codeToGIDLen, ++ needVerticalMetrics, ++ outputFunc, outputStream); ++ } else { ++ // otherwise: use a non-CID composite font ++ ffTT->convertToType0(psName->getCString(), ++ codeToGID, codeToGIDLen, ++ needVerticalMetrics, ++ outputFunc, outputStream); ++ } ++ gfree(codeToGID); ++ } ++ ctu->decRefCnt(); ++ } else { ++ error(errSyntaxError, -1, ++ "Couldn't find a mapping to Unicode for font '{0:s}'", ++ font->getName() ? font->getName()->getCString() : "(unnamed)"); ++ } + } else { +- // otherwise: use a non-CID composite font +- ffTT->convertToType0(psName->getCString(), +- ((GfxCIDFont *)font)->getCIDToGID(), +- ((GfxCIDFont *)font)->getCIDToGIDLen(), +- needVerticalMetrics, +- outputFunc, outputStream); ++ error(errSyntaxError, -1, ++ "TrueType font '%s' does not allow embedding", ++ font->getName() ? font->getName()->getCString() : "(unnamed)"); ++ + } + delete ffTT; + } +- gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +@@ -2297,18 +2416,22 @@ + int i; + + // check if font is already embedded +- for (i = 0; i < fontFileIDLen; ++i) { +- if (fontFileIDs[i].num == id->num && +- fontFileIDs[i].gen == id->gen) ++ for (i = 0; i < t1FontNameLen; ++i) { ++ if (t1FontNames[i].fontFileID.num == id->num && ++ t1FontNames[i].fontFileID.gen == id->gen) { ++ psName->clear(); ++ psName->insert(0, t1FontNames[i].psName); + return; ++ } + } +- +- // add entry to fontFileIDs list +- if (fontFileIDLen >= fontFileIDSize) { +- fontFileIDSize += 64; +- fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); +- } +- fontFileIDs[fontFileIDLen++] = *id; ++ if (t1FontNameLen == t1FontNameSize) { ++ t1FontNameSize *= 2; ++ t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, ++ sizeof(PST1FontName)); ++ } ++ t1FontNames[t1FontNameLen].fontFileID = *id; ++ t1FontNames[t1FontNameLen].psName = psName->copy(); ++ ++t1FontNameLen; + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); +@@ -2317,21 +2440,27 @@ + embFontList->append("\n"); + + // convert it to a Type 0 font +- fontBuf = font->readEmbFontFile(xref, &fontLen); +- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { +- if (ffTT->isOpenTypeCFF()) { +- if (globalParams->getPSLevel() >= psLevel3) { +- // Level 3: use a CID font +- ffTT->convertToCIDType0(psName->getCString(), +- ((GfxCIDFont *)font)->getCIDToGID(), +- ((GfxCIDFont *)font)->getCIDToGIDLen(), +- outputFunc, outputStream); +- } else { +- // otherwise: use a non-CID composite font +- ffTT->convertToType0(psName->getCString(), +- ((GfxCIDFont *)font)->getCIDToGID(), +- ((GfxCIDFont *)font)->getCIDToGIDLen(), +- outputFunc, outputStream); ++ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { ++ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { ++ if (ffTT->isOpenTypeCFF()) { ++ if (globalParams->getPSLevel() >= psLevel3) { ++ // Level 3: use a CID font ++ ffTT->convertToCIDType0(psName->getCString(), ++ ((GfxCIDFont *)font)->getCIDToGID(), ++ ((GfxCIDFont *)font)->getCIDToGIDLen(), ++ outputFunc, outputStream); ++ } else { ++ // otherwise: use a non-CID composite font ++ ffTT->convertToType0(psName->getCString(), ++ ((GfxCIDFont *)font)->getCIDToGID(), ++ ((GfxCIDFont *)font)->getCIDToGIDLen(), ++ outputFunc, outputStream); ++ } + } ++ delete ffTT; + } +- delete ffTT; ++ gfree(fontBuf); + } +- gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +@@ -2390,9 +2519,10 @@ + box.y1 = m[1]; + box.x2 = m[2]; + box.y2 = m[3]; + gfx = new Gfx(doc, this, resDict, &box, NULL); + inType3Char = gTrue; + for (i = 0; i < charProcs->getLength(); ++i) { ++ t3FillColorOnly = gFalse; + t3Cacheable = gFalse; + t3NeedsRestore = gFalse; + writePS("/"); +@@ -2430,9 +2560,45 @@ + writePS("%%EndResource\n"); + } + ++// Make a unique PS font name, based on the names given in the PDF ++// font object, and an object ID (font file object for ++GString *PSOutputDev::makePSFontName(GfxFont *font, Ref *id) { ++ GString *psName, *s; ++ ++ if ((s = font->getEmbeddedFontName())) { ++ psName = filterPSName(s); ++ if (!fontNames->lookupInt(psName)) { ++ fontNames->add(psName->copy(), 1); ++ return psName; ++ } ++ delete psName; ++ } ++ if ((s = font->getName())) { ++ psName = filterPSName(s); ++ if (!fontNames->lookupInt(psName)) { ++ fontNames->add(psName->copy(), 1); ++ return psName; ++ } ++ delete psName; ++ } ++ psName = GString::format("FF{0:d}_{1:d}", id->num, id->gen); ++ if ((s = font->getEmbeddedFontName())) { ++ s = filterPSName(s); ++ psName->append('_')->append(s); ++ delete s; ++ } else if ((s = font->getName())) { ++ s = filterPSName(s); ++ psName->append('_')->append(s); ++ delete s; ++ } ++ fontNames->add(psName->copy(), 1); ++ return psName; ++} ++ + void PSOutputDev::setupImages(Dict *resDict) { +- Object xObjDict, xObj, xObjRef, subtypeObj; +- int i; ++ Object xObjDict, xObj, xObjRef, subtypeObj, maskObj, maskRef; ++ Ref imgID; ++ int i, j; + + if (!(mode == psModeForm || inType3Char || preload)) { + return; +@@ -2447,9 +2613,32 @@ + xObj.streamGetDict()->lookup("Subtype", &subtypeObj); + if (subtypeObj.isName("Image")) { + if (xObjRef.isRef()) { +- setupImage(xObjRef.getRef(), xObj.getStream()); ++ imgID = xObjRef.getRef(); ++ for (j = 0; j < imgIDLen; ++j) { ++ if (imgIDs[j].num == imgID.num && imgIDs[j].gen == imgID.gen) { ++ break; ++ } ++ } ++ if (j == imgIDLen) { ++ if (imgIDLen >= imgIDSize) { ++ if (imgIDSize == 0) { ++ imgIDSize = 64; ++ } else { ++ imgIDSize *= 2; ++ } ++ imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref)); ++ } ++ imgIDs[imgIDLen++] = imgID; ++ setupImage(imgID, xObj.getStream(), gFalse); ++ if (level >= psLevel3 && ++ xObj.streamGetDict()->lookup("Mask", &maskObj)->isStream()) { ++ setupImage(imgID, maskObj.getStream(), gTrue); ++ } ++ maskObj.free(); ++ } + } else { + error(errSyntaxError, -1, + "Image in resource dict is not an indirect reference"); + } + } + subtypeObj.free(); +@@ -2461,30 +2650,12 @@ + xObjDict.free(); + } + +-void PSOutputDev::setupImage(Ref id, Stream *str) { ++void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) { + GBool useRLE, useCompressed, useASCIIHex; + GString *s; + int c; + int size, line, col, i; + +- // check if image is already setup +- for (i = 0; i < imgIDLen; ++i) { +- if (imgIDs[i].num == id.num && imgIDs[i].gen == id.gen) { +- return; +- } +- } +- +- // add entry to imgIDs list +- if (imgIDLen >= imgIDSize) { +- if (imgIDSize == 0) { +- imgIDSize = 64; +- } else { +- imgIDSize *= 2; +- } +- imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref)); +- } +- imgIDs[imgIDLen++] = id; +- + // filters + //~ this does not correctly handle the DeviceN color space + //~ -- need to use DeviceNRecoder +@@ -2493,17 +2664,21 @@ + useCompressed = gFalse; + useASCIIHex = gTrue; + } else { +- s = str->getPSFilter(level < psLevel3 ? 2 : 3, ""); +- if (s) { ++ if (globalParams->getPSUncompressPreloadedImages()) { + useRLE = gFalse; +- useCompressed = gTrue; +- delete s; +- } else { +- useRLE = gTrue; + useCompressed = gFalse; ++ } else { ++ s = str->getPSFilter(level < psLevel3 ? 2 : 3, ""); ++ if (s) { ++ useRLE = gFalse; ++ useCompressed = gTrue; ++ delete s; ++ } else { ++ useRLE = gTrue; ++ useCompressed = gFalse; ++ } + } +- useASCIIHex = level == psLevel1 || level == psLevel1Sep || +- globalParams->getPSASCIIHex(); ++ useASCIIHex = globalParams->getPSASCIIHex(); + } + if (useCompressed) { + str = str->getUndecodedStream(); +@@ -2552,8 +2727,8 @@ + if (useRLE) { + ++size; + } +- writePSFmt("{0:d} array dup /ImData_{1:d}_{2:d} exch def\n", +- size, id.num, id.gen); ++ writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n", ++ size, mask ? "Mask" : "Im", id.num, id.gen); + str->close(); + + // write the data into the array +@@ -2722,12 +2898,14 @@ + int rotateA, GBool useMediaBox, GBool crop, + int sliceX, int sliceY, + int sliceW, int sliceH, + GBool printing, + GBool (*abortCheckCbk)(void *data), + void *abortCheckCbkData) { +-#if HAVE_SPLASH + PreScanOutputDev *scan; + GBool rasterize; ++#if HAVE_SPLASH ++ GBool mono; ++ double dpi; + SplashOutputDev *splashOut; + SplashColor paperColor; + PDFRectangle box; +@@ -2737,43 +2915,33 @@ + Object obj; + Guchar *p; + Guchar col[4]; ++ double hDPI2, vDPI2; + double m0, m1, m2, m3, m4, m5; ++ int nStripes, stripeH, stripeY; + int c, w, h, x, y, comp, i; ++#endif + +- scan = new PreScanOutputDev(); +- page->displaySlice(scan, 72, 72, rotateA, useMediaBox, crop, +- sliceX, sliceY, sliceW, sliceH, +- printing, catalog, abortCheckCbk, abortCheckCbkData); +- rasterize = scan->usesTransparency(); +- delete scan; ++ if (globalParams->getPSAlwaysRasterize()) { ++ rasterize = gTrue; ++ } else { ++ scan = new PreScanOutputDev(); ++ page->displaySlice(scan, 72, 72, rotateA, useMediaBox, crop, ++ sliceX, sliceY, sliceW, sliceH, ++ printing, abortCheckCbk, abortCheckCbkData); ++ rasterize = scan->usesTransparency() || scan->usesPatternImageMask(); ++ delete scan; ++ } + if (!rasterize) { + return gTrue; + } + +- // rasterize the page +- if (level == psLevel1) { +- paperColor[0] = 0xff; +- splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, +- paperColor, gTrue, gFalse); +-#if SPLASH_CMYK +- } else if (level == psLevel1Sep) { +- paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0; +- splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse, +- paperColor, gTrue, gFalse); +-#endif +- } else { +- paperColor[0] = paperColor[1] = paperColor[2] = 0xff; +- splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, +- paperColor, gTrue, gFalse); +- } +- splashOut->startDoc(xref); +- page->displaySlice(splashOut, splashDPI, splashDPI, rotateA, +- useMediaBox, crop, +- sliceX, sliceY, sliceW, sliceH, +- printing, catalog, abortCheckCbk, abortCheckCbkData); ++#if HAVE_SPLASH ++ // get the rasterization parameters ++ dpi = globalParams->getPSRasterResolution(); ++ mono = globalParams->getPSRasterMono(); + + // start the PS page +- page->makeBox(splashDPI, splashDPI, rotateA, useMediaBox, gFalse, ++ page->makeBox(dpi, dpi, rotateA, useMediaBox, gFalse, + sliceX, sliceY, sliceW, sliceH, &box, &crop); + rotateA += page->getRotate(); + if (rotateA >= 360) { +@@ -2781,166 +2949,215 @@ + } else if (rotateA < 0) { + rotateA += 360; + } +- state = new GfxState(splashDPI, splashDPI, &box, rotateA, gFalse); +- startPage(page->getNum(), state); +- delete state; +- switch (rotateA) { +- case 0: +- default: // this should never happen ++ state = new GfxState(dpi, dpi, &box, rotateA, gFalse); ++ startPage(page->getNum(), state); ++ delete state; ++ ++ // set up the SplashOutputDev ++ if (mono || level == psLevel1) { ++ paperColor[0] = 0xff; ++ splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, ++ paperColor, gFalse, ++ globalParams->getAntialiasPrinting()); ++#if SPLASH_CMYK ++ } else if (level == psLevel1Sep) { ++ paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0; ++ splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse, ++ paperColor, gFalse, ++ globalParams->getAntialiasPrinting()); ++#endif ++ } else { ++ paperColor[0] = paperColor[1] = paperColor[2] = 0xff; ++ splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, ++ paperColor, gFalse, ++ globalParams->getAntialiasPrinting()); ++ } ++ splashOut->startDoc(xref); ++ ++ // break the page into stripes ++ hDPI2 = xScale * dpi; ++ vDPI2 = yScale * dpi; ++ if (sliceW < 0 || sliceH < 0) { ++ if (useMediaBox) { ++ box = *page->getMediaBox(); ++ } else { ++ box = *page->getCropBox(); ++ } ++ sliceX = sliceY = 0; ++ sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0); ++ sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0); ++ } ++ nStripes = (int)ceil((double)(sliceW * sliceH) / ++ (double)rasterizationSliceSize); ++ stripeH = (sliceH + nStripes - 1) / nStripes; ++ ++ // render the stripes ++ for (stripeY = sliceY; stripeY < sliceH; stripeY += stripeH) { ++ ++ // rasterize a stripe ++ page->makeBox(hDPI2, vDPI2, 0, useMediaBox, gFalse, ++ sliceX, stripeY, sliceW, stripeH, &box, &crop); + m0 = box.x2 - box.x1; + m1 = 0; + m2 = 0; + m3 = box.y2 - box.y1; + m4 = box.x1; + m5 = box.y1; +- break; +- case 90: +- m0 = 0; +- m1 = box.y2 - box.y1; +- m2 = -(box.x2 - box.x1); +- m3 = 0; +- m4 = box.x2; +- m5 = box.y1; +- break; +- case 180: +- m0 = -(box.x2 - box.x1); +- m1 = 0; +- m2 = 0; +- m3 = -(box.y2 - box.y1); +- m4 = box.x2; +- m5 = box.y2; +- break; +- case 270: +- m0 = 0; +- m1 = -(box.y2 - box.y1); +- m2 = box.x2 - box.x1; +- m3 = 0; +- m4 = box.x1; +- m5 = box.y2; +- break; +- } +- +- //~ need to add the process colors +- +- // draw the rasterized image +- bitmap = splashOut->getBitmap(); +- w = bitmap->getWidth(); +- h = bitmap->getHeight(); +- writePS("gsave\n"); +- writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] concat\n", +- m0, m1, m2, m3, m4, m5); +- switch (level) { +- case psLevel1: +- writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n", +- w, h, w, -h, h); +- p = bitmap->getDataPtr(); +- i = 0; +- for (y = 0; y < h; ++y) { +- for (x = 0; x < w; ++x) { +- writePSFmt("{0:02x}", *p++); +- if (++i == 32) { +- writePSChar('\n'); +- i = 0; +- } +- } +- } +- if (i != 0) { +- writePSChar('\n'); +- } +- break; +- case psLevel1Sep: +- writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n", +- w, h, w, -h, h); +- p = bitmap->getDataPtr(); +- i = 0; +- col[0] = col[1] = col[2] = col[3] = 0; +- for (y = 0; y < h; ++y) { +- for (comp = 0; comp < 4; ++comp) { ++ page->displaySlice(splashOut, hDPI2, vDPI2, ++ (360 - page->getRotate()) % 360, useMediaBox, crop, ++ sliceX, stripeY, sliceW, stripeH, ++ printing, abortCheckCbk, abortCheckCbkData); ++ ++ // draw the rasterized image ++ bitmap = splashOut->getBitmap(); ++ w = bitmap->getWidth(); ++ h = bitmap->getHeight(); ++ writePS("gsave\n"); ++ writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", ++ m0, m1, m2, m3, m4, m5); ++ switch (level) { ++ case psLevel1: ++ writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n", ++ w, h, w, -h, h); ++ p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); ++ i = 0; ++ for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { +- writePSFmt("{0:02x}", p[4*x + comp]); +- col[comp] |= p[4*x + comp]; ++ writePSFmt("{0:02x}", *p++); + if (++i == 32) { + writePSChar('\n'); + i = 0; + } + } + } +- p += bitmap->getRowSize(); +- } +- if (i != 0) { ++ if (i != 0) { ++ writePSChar('\n'); ++ } ++ break; ++ case psLevel1Sep: ++ writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n", ++ w, h, w, -h, h); ++ p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); ++ i = 0; ++ col[0] = col[1] = col[2] = col[3] = 0; ++ for (y = 0; y < h; ++y) { ++ for (comp = 0; comp < 4; ++comp) { ++ for (x = 0; x < w; ++x) { ++ writePSFmt("{0:02x}", p[4*x + comp]); ++ col[comp] |= p[4*x + comp]; ++ if (++i == 32) { ++ writePSChar('\n'); ++ i = 0; ++ } ++ } ++ } ++ p -= bitmap->getRowSize(); ++ } ++ if (i != 0) { ++ writePSChar('\n'); ++ } ++ if (col[0]) { ++ processColors |= psProcessCyan; ++ } ++ if (col[1]) { ++ processColors |= psProcessMagenta; ++ } ++ if (col[2]) { ++ processColors |= psProcessYellow; ++ } ++ if (col[3]) { ++ processColors |= psProcessBlack; ++ } ++ break; ++ case psLevel2: ++ case psLevel2Sep: ++ case psLevel3: ++ case psLevel3Sep: ++ if (mono) { ++ writePS("/DeviceGray setcolorspace\n"); ++ } else { ++ writePS("/DeviceRGB setcolorspace\n"); ++ } ++ writePS("<<\n /ImageType 1\n"); ++ writePSFmt(" /Width {0:d}\n", bitmap->getWidth()); ++ writePSFmt(" /Height {0:d}\n", bitmap->getHeight()); ++ writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h); ++ writePS(" /BitsPerComponent 8\n"); ++ if (mono) { ++ writePS(" /Decode [0 1]\n"); ++ } else { ++ writePS(" /Decode [0 1 0 1 0 1]\n"); ++ } ++ writePS(" /DataSource currentfile\n"); ++ if (globalParams->getPSASCIIHex()) { ++ writePS(" /ASCIIHexDecode filter\n"); ++ } else { ++ writePS(" /ASCII85Decode filter\n"); ++ } ++ writePS(" /RunLengthDecode filter\n"); ++ writePS(">>\n"); ++ writePS("image\n"); ++ obj.initNull(); ++ p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); ++ str0 = new MemStream((char *)p, 0, w * h * (mono ? 1 : 3), &obj); ++ str = new RunLengthEncoder(str0); ++ if (globalParams->getPSASCIIHex()) { ++ str = new ASCIIHexEncoder(str); ++ } else { ++ str = new ASCII85Encoder(str); ++ } ++ str->reset(); ++ while ((c = str->getChar()) != EOF) { ++ writePSChar(c); ++ } ++ str->close(); ++ delete str; ++ delete str0; + writePSChar('\n'); ++ processColors |= mono ? psProcessBlack : psProcessCMYK; ++ break; + } +- if (col[0]) { +- processColors |= psProcessCyan; +- } +- if (col[1]) { +- processColors |= psProcessMagenta; +- } +- if (col[2]) { +- processColors |= psProcessYellow; +- } +- if (col[3]) { +- processColors |= psProcessBlack; +- } +- break; +- case psLevel2: +- case psLevel2Sep: +- case psLevel3: +- case psLevel3Sep: +- writePS("/DeviceRGB setcolorspace\n"); +- writePS("<<\n /ImageType 1\n"); +- writePSFmt(" /Width {0:d}\n", bitmap->getWidth()); +- writePSFmt(" /Height {0:d}\n", bitmap->getHeight()); +- writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h); +- writePS(" /BitsPerComponent 8\n"); +- writePS(" /Decode [0 1 0 1 0 1]\n"); +- writePS(" /DataSource currentfile\n"); +- if (globalParams->getPSASCIIHex()) { +- writePS(" /ASCIIHexDecode filter\n"); +- } else { +- writePS(" /ASCII85Decode filter\n"); +- } +- writePS(" /RunLengthDecode filter\n"); +- writePS(">>\n"); +- writePS("image\n"); +- obj.initNull(); +- str0 = new MemStream((char *)bitmap->getDataPtr(), 0, w * h * 3, &obj); +- str = new RunLengthEncoder(str0); +- if (globalParams->getPSASCIIHex()) { +- str = new ASCIIHexEncoder(str); +- } else { +- str = new ASCII85Encoder(str); +- } +- str->reset(); +- while ((c = str->getChar()) != EOF) { +- writePSChar(c); +- } +- str->close(); +- delete str; +- delete str0; +- processColors |= psProcessCMYK; +- break; ++ writePS("grestore\n"); + } ++ + delete splashOut; +- writePS("grestore\n"); + + // finish the PS page + endPage(); + + return gFalse; +-#else ++ ++#else // HAVE_SPLASH ++ ++ error(errSyntaxWarning, -1, ++ "PDF page uses transparency and PSOutputDev was built without" ++ " the Splash rasterizer - output may not be correct"); + return gTrue; +-#endif ++#endif // HAVE_SPLASH + } + + void PSOutputDev::startPage(int pageNum, GfxState *state) { +- int x1, y1, x2, y2, width, height; ++ Page *page; ++ int x1, y1, x2, y2, width, height, t; + int imgWidth, imgHeight, imgWidth2, imgHeight2; + GBool landscape; +- ++ GString *s; + + if (mode == psModePS) { + writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage); ++ if (paperMatch) { ++ page = doc->getCatalog()->getPage(pageNum); ++ imgLLX = imgLLY = 0; ++ imgURX = (int)ceil(page->getMediaWidth()); ++ imgURY = (int)ceil(page->getMediaHeight()); ++ if (state->getRotate() == 90 || state->getRotate() == 270) { ++ t = imgURX; ++ imgURX = imgURY; ++ imgURY = t; ++ } ++ writePSFmt("%%PageMedia: {0:d}x{1:d}\n", imgURX, imgURY); ++ writePSFmt("%%PageBoundingBox: 0 0 {0:d} {1:d}\n", imgURX, imgURY); ++ } + writePS("%%BeginPageSetup\n"); + } + +@@ -2966,20 +3183,25 @@ + height = y2 - y1; + tx = ty = 0; + // rotation and portrait/landscape mode +- if (rotate0 >= 0) { ++ if (paperMatch) { ++ rotate = (360 - state->getRotate()) % 360; ++ landscape = gFalse; ++ } else if (rotate0 >= 0) { + rotate = (360 - rotate0) % 360; + landscape = gFalse; + } else { + rotate = (360 - state->getRotate()) % 360; + if (rotate == 0 || rotate == 180) { +- if (width > height && width > imgWidth) { ++ if ((width < height && imgWidth > imgHeight && height > imgHeight) || ++ (width > height && imgWidth < imgHeight && width > imgWidth)) { + rotate += 90; + landscape = gTrue; + } else { + landscape = gFalse; + } + } else { // rotate == 90 || rotate == 270 +- if (height > width && height > imgWidth) { ++ if ((height < width && imgWidth > imgHeight && width > imgHeight) || ++ (height > width && imgWidth < imgHeight && height > imgWidth)) { + rotate = 270 - rotate; + landscape = gTrue; + } else { +@@ -2989,6 +3211,9 @@ + } + writePSFmt("%%PageOrientation: {0:s}\n", + landscape ? "Landscape" : "Portrait"); ++ if (paperMatch) { ++ writePSFmt("{0:d} {1:d} pdfSetupPaper\n", imgURX, imgURY); ++ } + writePS("pdfStartPage\n"); + if (rotate == 0) { + imgWidth2 = imgWidth; +@@ -3015,9 +3240,9 @@ + xScale = xScale0; + yScale = yScale0; + } else if ((globalParams->getPSShrinkLarger() && +- (width > imgWidth2 || height > imgHeight2)) || +- (globalParams->getPSExpandSmaller() && +- (width < imgWidth2 && height < imgHeight2))) { ++ (width > imgWidth2 || height > imgHeight2)) || ++ (globalParams->getPSExpandSmaller() && ++ (width < imgWidth2 && height < imgHeight2))) { + xScale = (double)imgWidth2 / (double)width; + yScale = (double)imgHeight2 / (double)height; + if (yScale < xScale) { +@@ -3038,8 +3263,8 @@ + } + // center + if (tx0 >= 0 && ty0 >= 0) { +- tx += rotate == 0 ? tx0 : ty0; +- ty += rotate == 0 ? ty0 : -tx0; ++ tx += (rotate == 0 || rotate == 180) ? tx0 : ty0; ++ ty += (rotate == 0 || rotate == 180) ? ty0 : -tx0; + } else if (globalParams->getPSCenter()) { + if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { + tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2; +@@ -3049,22 +3274,21 @@ + ty += (imgHeight2 - yScale * height) / 2; + } + } +- tx += rotate == 0 ? imgLLX : imgLLY; +- ty += rotate == 0 ? imgLLY : -imgLLX; ++ tx += (rotate == 0 || rotate == 180) ? imgLLX : imgLLY; ++ ty += (rotate == 0 || rotate == 180) ? imgLLY : -imgLLX; + if (tx != 0 || ty != 0) { + writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); + } + if (xScale != 1 || yScale != 1) { + writePSFmt("{0:.6f} {1:.6f} scale\n", xScale, yScale); + } + if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { + writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re W\n", + clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0); + } else { + writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1); + } + +- writePS("%%EndPageSetup\n"); + ++seqPage; + break; + +@@ -3101,6 +3325,18 @@ + rotate = 0; + break; + } ++ ++ if (customCodeCbk) { ++ if ((s = (*customCodeCbk)(this, psOutCustomPageSetup, pageNum, ++ customCodeCbkData))) { ++ writePS(s->getCString()); ++ delete s; ++ } ++ } ++ ++ if (mode == psModePS) { ++ writePS("%%EndPageSetup\n"); ++ } + } + + void PSOutputDev::endPage() { +@@ -3458,25 +3693,33 @@ ++void PSOutputDev::saveTextPos(GfxState *state) { ++ writePS("currentpoint\n"); ++} ++ ++void PSOutputDev::restoreTextPos(GfxState *state) { ++ writePS("m\n"); ++} ++ + void PSOutputDev::stroke(GfxState *state) { + doPath(state->getPath()); +- if (t3String) { +- // if we're construct a cacheable Type 3 glyph, we need to do ++ if (inType3Char && t3FillColorOnly) { ++ // if we're constructing a cacheable Type 3 glyph, we need to do + // everything in the fill color + writePS("Sf\n"); + } else { +@@ -3494,19 +3737,19 @@ + writePS("f*\n"); + } + +@@ -3526,34 +3769,37 @@ + box.y1 = bbox[1]; + box.x2 = bbox[2]; + box.y2 = bbox[3]; + gfx = new Gfx(doc, this, resDict, &box, NULL); + writePS("/x {\n"); + if (paintType == 2) { + writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n", + xStep, bbox[0], bbox[1], bbox[2], bbox[3]); ++ t3FillColorOnly = gTrue; + } else { + if (x1 - 1 <= x0) { + writePS("1 0 setcharwidth\n"); + } else { + writePSFmt("{0:.6g} 0 setcharwidth\n", xStep); + } ++ t3FillColorOnly = gFalse; + } + inType3Char = gTrue; + ++numTilingPatterns; +- gfx->display(str); ++ gfx2->display(str); + --numTilingPatterns; + inType3Char = gFalse; + writePS("} def\n"); +- delete gfx; ++ delete gfx2; + writePS("end\n"); + writePS("currentdict end\n"); + writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns); + + // draw the tiles + writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns); ++ writePS("fCol\n"); + writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", + mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); + writePSFmt("{0:d} 1 {1:d} {{ {2:.6g} exch {3:.6g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n", + y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1); + writePS("grestore\n"); + } +@@ -3698,7 +3944,10 @@ + double xMin, yMin, xMax, yMax; + double x0, y0, r0, x1, y1, r1, t0, t1; + double xa, ya, ra; +- double sz, xz, yz, sMin, sMax, sa, ta; ++ double sz, sMin, sMax, h, ta; ++ double sLeft, sRight, sTop, sBottom, sZero, sDiag; ++ GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero; ++ GBool haveSMin, haveSMax; + double theta, alpha, a1, a2; + GBool enclosed; + int i; +@@ -3717,19 +3966,23 @@ + + // Compute the point at which r(s) = 0; check for the enclosed + // circles case; and compute the angles for the tangent lines. +- if (r0 == r1) { +- enclosed = x0 == x1 && y0 == y1; ++ h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); ++ if (h == 0) { ++ enclosed = gTrue; ++ theta = 0; // make gcc happy ++ sz = 0; // make gcc happy ++ } else if (r1 - r0 == 0) { ++ enclosed = gFalse; + theta = 0; + sz = 0; // make gcc happy ++ } else if (fabs(r1 - r0) >= h) { ++ enclosed = gTrue; ++ theta = 0; // make gcc happy ++ sz = 0; // make gcc happy + } else { ++ enclosed = gFalse; + sz = -r0 / (r1 - r0); +- xz = x0 + sz * (x1 - x0); +- yz = y0 + sz * (y1 - y0); +- enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0; +- theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz))); +- if (r0 > r1) { +- theta = -theta; +- } ++ theta = asin((r1 - r0) / h); + } + if (enclosed) { + a1 = 0; +@@ -3749,80 +4002,122 @@ + sMin = 0; + sMax = 1; + } else { +- sMin = 1; +- sMax = 0; +- // solve for x(s) + r(s) = xMin +- if ((x1 + r1) - (x0 + r0) != 0) { +- sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); +- if (sa < sMin) { +- sMin = sa; +- } else if (sa > sMax) { +- sMax = sa; +- } +- } +- // solve for x(s) - r(s) = xMax +- if ((x1 - r1) - (x0 - r0) != 0) { +- sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); +- if (sa < sMin) { +- sMin = sa; +- } else if (sa > sMax) { +- sMax = sa; +- } +- } +- // solve for y(s) + r(s) = yMin +- if ((y1 + r1) - (y0 + r0) != 0) { +- sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); +- if (sa < sMin) { +- sMin = sa; +- } else if (sa > sMax) { +- sMax = sa; +- } +- } +- // solve for y(s) - r(s) = yMax +- if ((y1 - r1) - (y0 - r0) != 0) { +- sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); +- if (sa < sMin) { +- sMin = sa; +- } else if (sa > sMax) { +- sMax = sa; +- } +- } +- // check against sz +- if (r0 < r1) { +- if (sMin < sz) { +- sMin = sz; +- } +- } else if (r0 > r1) { +- if (sMax > sz) { +- sMax = sz; +- } ++ // solve x(sLeft) + r(sLeft) = xMin ++ if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) { ++ sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); ++ } else { ++ sLeft = 0; // make gcc happy ++ } ++ // solve x(sRight) - r(sRight) = xMax ++ if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) { ++ sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); ++ } else { ++ sRight = 0; // make gcc happy ++ } ++ // solve y(sBottom) + r(sBottom) = yMin ++ if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) { ++ sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); ++ } else { ++ sBottom = 0; // make gcc happy ++ } ++ // solve y(sTop) - r(sTop) = yMax ++ if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) { ++ sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); ++ } else { ++ sTop = 0; // make gcc happy ++ } ++ // solve r(sZero) = 0 ++ if ((haveSZero = fabs(r1 - r0) > 0.000001)) { ++ sZero = -r0 / (r1 - r0); ++ } else { ++ sZero = 0; // make gcc happy ++ } ++ // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2) ++ if (haveSZero) { ++ sDiag = (sqrt((xMax - xMin) * (xMax - xMin) + ++ (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); ++ } else { ++ sDiag = 0; // make gcc happy + } +- // check the 'extend' flags +- if (!shading->getExtend0() && sMin < 0) { ++ // compute sMin ++ if (shading->getExtend0()) { ++ sMin = 0; ++ haveSMin = gFalse; ++ if (x0 < x1 && haveSLeft && sLeft < 0) { ++ sMin = sLeft; ++ haveSMin = gTrue; ++ } else if (x0 > x1 && haveSRight && sRight < 0) { ++ sMin = sRight; ++ haveSMin = gTrue; ++ } ++ if (y0 < y1 && haveSBottom && sBottom < 0) { ++ if (!haveSMin || sBottom > sMin) { ++ sMin = sBottom; ++ haveSMin = gTrue; ++ } ++ } else if (y0 > y1 && haveSTop && sTop < 0) { ++ if (!haveSMin || sTop > sMin) { ++ sMin = sTop; ++ haveSMin = gTrue; ++ } ++ } ++ if (haveSZero && sZero < 0) { ++ if (!haveSMin || sZero > sMin) { ++ sMin = sZero; ++ } ++ } ++ } else { + sMin = 0; + } +- if (!shading->getExtend1() && sMax > 1) { ++ // compute sMax ++ if (shading->getExtend1()) { ++ sMax = 1; ++ haveSMax = gFalse; ++ if (x1 < x0 && haveSLeft && sLeft > 1) { ++ sMax = sLeft; ++ haveSMax = gTrue; ++ } else if (x1 > x0 && haveSRight && sRight > 1) { ++ sMax = sRight; ++ haveSMax = gTrue; ++ } ++ if (y1 < y0 && haveSBottom && sBottom > 1) { ++ if (!haveSMax || sBottom < sMax) { ++ sMax = sBottom; ++ haveSMax = gTrue; ++ } ++ } else if (y1 > y0 && haveSTop && sTop > 1) { ++ if (!haveSMax || sTop < sMax) { ++ sMax = sTop; ++ haveSMax = gTrue; ++ } ++ } ++ if (haveSZero && sDiag > 1) { ++ if (!haveSMax || sDiag < sMax) { ++ sMax = sDiag; ++ } ++ } ++ } else { + sMax = 1; + } + } + + // generate the PS code +@@ -3970,15 +4265,16 @@ + GString *s2; +- double dx, dy, dx2, dy2, originX, originY; ++ double dx, dy, originX, originY; + char *p; + UnicodeMap *uMap; + CharCode code; + Unicode u[8]; + char buf[8]; +- int len, nChars, uLen, n, m, i, j; ++ double *dxdy; ++ int dxdySize, len, nChars, uLen, n, m, i, j; + + // check for invisible text -- this is used by Acrobat Capture + if (state->getRender() == 3) { +@@ -4003,6 +4299,10 @@ + for (i = 0; i < font16EncLen; ++i) { + if (font->getID()->num == font16Enc[i].fontID.num && + font->getID()->gen == font16Enc[i].fontID.gen) { ++ if (!font16Enc[i].enc) { ++ // font substitution failed, so don't output any text ++ return; ++ } + uMap = globalParams->getUnicodeMap(font16Enc[i].enc); + break; + } +@@ -4019,63 +4319,89 @@ + } + } + +- // compute width of chars in string, ignoring char spacing and word +- // spacing -- the Tj operator will adjust for the metrics of the +- // font that's actually used +- dx = dy = 0; ++ // compute the positioning (dx, dy) for each char in the string + nChars = 0; + p = s->getCString(); + len = s->getLength(); + s2 = new GString(); ++ dxdySize = font->isCIDFont() ? 8 : s->getLength(); ++ dxdy = (double *)gmallocn(2 * dxdySize, sizeof(double)); + while (len > 0) { + n = font->getNextChar(p, len, &code, + u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, +- &dx2, &dy2, &originX, &originY); ++ &dx, &dy, &originX, &originY); ++ dx *= state->getFontSize(); ++ dy *= state->getFontSize(); ++ if (wMode) { ++ dy += state->getCharSpace(); ++ if (n == 1 && *p == ' ') { ++ dy += state->getWordSpace(); ++ } ++ } else { ++ dx += state->getCharSpace(); ++ if (n == 1 && *p == ' ') { ++ dx += state->getWordSpace(); ++ } ++ } ++ dx *= state->getHorizScaling(); + if (font->isCIDFont()) { + if (uMap) { ++ if (nChars + uLen > dxdySize) { ++ do { ++ dxdySize *= 2; ++ } while (nChars + uLen > dxdySize); ++ dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); ++ } + for (i = 0; i < uLen; ++i) { + m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf)); + for (j = 0; j < m; ++j) { + s2->append(buf[j]); + } ++ //~ this really needs to get the number of chars in the target ++ //~ encoding - which may be more than the number of Unicode ++ //~ chars ++ dxdy[2 * nChars] = dx; ++ dxdy[2 * nChars + 1] = dy; ++ ++nChars; + } +- //~ this really needs to get the number of chars in the target +- //~ encoding - which may be more than the number of Unicode +- //~ chars +- nChars += uLen; + } else { ++ if (nChars + 1 > dxdySize) { ++ dxdySize *= 2; ++ dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); ++ } + s2->append((char)((code >> 8) & 0xff)); + s2->append((char)(code & 0xff)); ++ dxdy[2 * nChars] = dx; ++ dxdy[2 * nChars + 1] = dy; + ++nChars; + } + } else { + if (!codeToGID || codeToGID[code] >= 0) { + s2->append((char)code); ++ dxdy[2 * nChars] = dx; ++ dxdy[2 * nChars + 1] = dy; ++ ++nChars; + } + } +- dx += dx2; +- dy += dy2; + p += n; + len -= n; + } +- dx *= state->getFontSize() * state->getHorizScaling(); +- dy *= state->getFontSize(); + if (uMap) { + uMap->decRefCnt(); + } + +- if (s2->getLength() > 0) { ++ if (nChars > 0) { + writePSString(s2); +- if (font->isCIDFont()) { +- if (wMode) { +- writePSFmt(" {0:d} {1:.4g} Tj16V\n", nChars, dy); +- } else { +- writePSFmt(" {0:d} {1:.4g} Tj16\n", nChars, dx); ++ writePS("\n["); ++ for (i = 0; i < 2 * nChars; ++i) { ++ if (i > 0) { ++ writePS("\n"); + } +- } else { +- writePSFmt(" {0:.4g} Tj\n", dx); ++ writePSFmt("{0:.6g}", dxdy[i]); + } ++ writePS("] Tj\n"); + } ++ gfree(dxdy); + delete s2; + + if (state->getRender() & 4) { +@@ -4730,24 +5056,32 @@ + + // data source + if (mode == psModeForm || inType3Char || preload) { +- writePS(" /DataSource { 2 copy get exch 1 add exch }\n"); ++ writePS(" /DataSource { pdfImStr }\n"); + } else { + writePS(" /DataSource currentfile\n"); + } + + // filters +- s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, +- " "); +- if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || +- inlineImg || !s) { +- useRLE = gTrue; +- useASCII = !(mode == psModeForm || inType3Char || preload); ++ if ((mode == psModeForm || inType3Char || preload) && ++ globalParams->getPSUncompressPreloadedImages()) { ++ s = NULL; ++ useRLE = gFalse; + useCompressed = gFalse; ++ useASCII = gFalse; + } else { +- useRLE = gFalse; +- useASCII = str->isBinary() && +- !(mode == psModeForm || inType3Char || preload); +- useCompressed = gTrue; ++ s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, ++ " "); ++ if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || ++ inlineImg || !s) { ++ useRLE = gTrue; ++ useASCII = !(mode == psModeForm || inType3Char || preload); ++ useCompressed = gFalse; ++ } else { ++ useRLE = gFalse; ++ useASCII = str->isBinary() && ++ !(mode == psModeForm || inType3Char || preload); ++ useCompressed = gTrue; ++ } + } + if (useASCII) { + writePSFmt(" /ASCII{0:s}Decode filter\n", +@@ -4872,6 +5206,7 @@ + int n, numComps; + GBool useRLE, useASCII, useASCIIHex, useCompressed; + GBool maskUseRLE, maskUseASCII, maskUseCompressed; ++ GString *maskFilters; + GfxSeparationColorSpace *sepCS; + GfxColor color; + GfxCMYK cmyk; +@@ -4881,6 +5216,83 @@ + useASCIIHex = globalParams->getPSASCIIHex(); + useRLE = useASCII = useCompressed = gFalse; // make gcc happy + maskUseRLE = maskUseASCII = maskUseCompressed = gFalse; // make gcc happy ++ maskFilters = NULL; // make gcc happy ++ ++ // explicit masking ++ if (maskStr) { ++ ++ // mask data source ++ if ((mode == psModeForm || inType3Char || preload) && ++ globalParams->getPSUncompressPreloadedImages()) { ++ s = NULL; ++ maskUseRLE = gFalse; ++ maskUseCompressed = gFalse; ++ maskUseASCII = gFalse; ++ } else { ++ s = maskStr->getPSFilter(3, " "); ++ if (!s) { ++ maskUseRLE = gTrue; ++ maskUseASCII = !(mode == psModeForm || inType3Char || preload); ++ maskUseCompressed = gFalse; ++ } else { ++ maskUseRLE = gFalse; ++ maskUseASCII = maskStr->isBinary() && ++ !(mode == psModeForm || inType3Char || preload); ++ maskUseCompressed = gTrue; ++ } ++ } ++ maskFilters = new GString(); ++ if (maskUseASCII) { ++ maskFilters->appendf(" /ASCII{0:s}Decode filter\n", ++ useASCIIHex ? "Hex" : "85"); ++ } ++ if (maskUseRLE) { ++ maskFilters->append(" /RunLengthDecode filter\n"); ++ } ++ if (maskUseCompressed) { ++ maskFilters->append(s); ++ } ++ if (s) { ++ delete s; ++ } ++ if (mode == psModeForm || inType3Char || preload) { ++ writePSFmt("MaskData_{0:d}_{1:d} pdfMaskInit\n", ++ ref->getRefNum(), ref->getRefGen()); ++ } else { ++ writePS("currentfile\n"); ++ writePS(maskFilters->getCString()); ++ writePS("pdfMask\n"); ++ ++ // add RunLengthEncode and ASCIIHex/85 encode filters ++ if (maskUseCompressed) { ++ maskStr = maskStr->getUndecodedStream(); ++ } ++ if (maskUseRLE) { ++ maskStr = new RunLengthEncoder(maskStr); ++ } ++ if (maskUseASCII) { ++ if (useASCIIHex) { ++ maskStr = new ASCIIHexEncoder(maskStr); ++ } else { ++ maskStr = new ASCII85Encoder(maskStr); ++ } ++ } ++ ++ // copy the stream data ++ maskStr->reset(); ++ while ((c = maskStr->getChar()) != EOF) { ++ writePSChar(c); ++ } ++ maskStr->close(); ++ writePSChar('\n'); ++ writePS("%-EOD-\n"); ++ ++ // delete encoders ++ if (maskUseRLE || maskUseASCII) { ++ delete maskStr; ++ } ++ } ++ } + + // color space + if (colorMap) { +@@ -5015,24 +5427,32 @@ + + // data source + if (mode == psModeForm || inType3Char || preload) { +- writePS(" /DataSource { 2 copy get exch 1 add exch }\n"); ++ writePS(" /DataSource { pdfImStr }\n"); + } else { + writePS(" /DataSource currentfile\n"); + } + + // filters +- s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, +- " "); +- if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || +- inlineImg || !s) { +- useRLE = gTrue; +- useASCII = !(mode == psModeForm || inType3Char || preload); ++ if ((mode == psModeForm || inType3Char || preload) && ++ globalParams->getPSUncompressPreloadedImages()) { ++ s = NULL; ++ useRLE = gFalse; + useCompressed = gFalse; ++ useASCII = gFalse; + } else { +- useRLE = gFalse; +- useASCII = str->isBinary() && +- !(mode == psModeForm || inType3Char || preload); +- useCompressed = gTrue; ++ s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, ++ " "); ++ if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || ++ inlineImg || !s) { ++ useRLE = gTrue; ++ useASCII = !(mode == psModeForm || inType3Char || preload); ++ useCompressed = gFalse; ++ } else { ++ useRLE = gFalse; ++ useASCII = str->isBinary() && ++ !(mode == psModeForm || inType3Char || preload); ++ useCompressed = gTrue; ++ } + } + if (useASCII) { + writePSFmt(" /ASCII{0:s}Decode filter\n", +@@ -5065,30 +5485,13 @@ + maskInvert ? 1 : 0, maskInvert ? 0 : 1); + + // mask data source +- writePS(" /DataSource currentfile\n"); +- s = maskStr->getPSFilter(3, " "); +- if (!s) { +- maskUseRLE = gTrue; +- maskUseASCII = gTrue; +- maskUseCompressed = gFalse; ++ if (mode == psModeForm || inType3Char || preload) { ++ writePS(" /DataSource {pdfMaskSrc}\n"); ++ writePS(maskFilters->getCString()); + } else { +- maskUseRLE = gFalse; +- maskUseASCII = maskStr->isBinary(); +- maskUseCompressed = gTrue; +- } +- if (maskUseASCII) { +- writePSFmt(" /ASCII{0:s}Decode filter\n", +- useASCIIHex ? "Hex" : "85"); +- } +- if (maskUseRLE) { +- writePS(" /RunLengthDecode filter\n"); +- } +- if (maskUseCompressed) { +- writePS(s->getCString()); +- } +- if (s) { +- delete s; ++ writePS(" /DataSource maskStream\n"); + } ++ delete maskFilters; + + writePS(">>\n"); + writePS(">>\n"); +@@ -5116,39 +5519,6 @@ + + } + +- // explicit masking +- if (maskStr) { +- +- if (maskUseCompressed) { +- maskStr = maskStr->getUndecodedStream(); +- } +- +- // add RunLengthEncode and ASCIIHex/85 encode filters +- if (maskUseRLE) { +- maskStr = new RunLengthEncoder(maskStr); +- } +- if (maskUseASCII) { +- if (useASCIIHex) { +- maskStr = new ASCIIHexEncoder(maskStr); +- } else { +- maskStr = new ASCII85Encoder(maskStr); +- } +- } +- +- // copy the stream data +- maskStr->reset(); +- while ((c = maskStr->getChar()) != EOF) { +- writePSChar(c); +- } +- maskStr->close(); +- writePSChar('\n'); +- +- // delete encoders +- if (maskUseRLE || maskUseASCII) { +- delete maskStr; +- } +- } +- + // get rid of the array and index + if (mode == psModeForm || inType3Char || preload) { + writePS("pop pop\n"); +@@ -5196,6 +5566,13 @@ + delete str; + } + } ++ ++ // close the mask stream ++ if (maskStr) { ++ if (!(mode == psModeForm || inType3Char || preload)) { ++ writePS("pdfMaskEnd\n"); ++ } ++ } + } + + void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace, +@@ -5898,6 +6275,7 @@ + t3URY = ury; + t3String = new GString(); + writePS("q\n"); ++ t3FillColorOnly = gTrue; + t3Cacheable = gTrue; + t3NeedsRestore = gTrue; + } +diff -ru xpdf-3.02/xpdf/PSOutputDev.h xpdf-3.03/xpdf/PSOutputDev.h +--- xpdf-3.02/xpdf/PSOutputDev.h 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/PSOutputDev.h 2011-08-15 23:08:53.000000000 +0200 +@@ -21,15 +21,20 @@ + #include "GlobalParams.h" + #include "OutputDev.h" + ++class GHash; ++class PDFDoc; ++class XRef; + class Function; + class GfxPath; + class GfxFont; + class GfxColorSpace; + class GfxSeparationColorSpace; + class PDFRectangle; ++struct PST1FontName; + struct PSFont8Info; + struct PSFont16Enc; + class PSOutCustomColor; ++class PSOutputDev; + + //------------------------------------------------------------------------ + // PSOutputDev +@@ -48,25 +53,38 @@ + psGeneric // write to a generic stream + }; + +-typedef void (*PSOutputFunc)(void *stream, const char *data, int len); ++enum PSOutCustomCodeLocation { ++ psOutCustomDocSetup, ++ psOutCustomPageSetup ++}; ++ ++typedef void (*PSOutputFunc)(void *stream, const char *data, int len); ++ ++typedef GString *(*PSOutCustomCodeCbk)(PSOutputDev *psOut, ++ PSOutCustomCodeLocation loc, int n, ++ void *data); + + class PSOutputDev: public OutputDev { + public: + + // Open a PostScript output file, and write the prolog. + PSOutputDev(char *fileName, PDFDoc *docA, + int firstPage, int lastPage, PSOutMode modeA, + int imgLLXA = 0, int imgLLYA = 0, + int imgURXA = 0, int imgURYA = 0, +- GBool manualCtrlA = gFalse); ++ GBool manualCtrlA = gFalse, ++ PSOutCustomCodeCbk customCodeCbkA = NULL, ++ void *customCodeCbkDataA = NULL); + + // Open a PSOutputDev that will write to a generic stream. + PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, + PDFDoc *docA, + int firstPage, int lastPage, PSOutMode modeA, + int imgLLXA = 0, int imgLLYA = 0, + int imgURXA = 0, int imgURYA = 0, +- GBool manualCtrlA = gFalse); ++ GBool manualCtrlA = gFalse, ++ PSOutCustomCodeCbk customCodeCbkA = NULL, ++ void *customCodeCbkDataA = NULL); + + // Destructor -- writes the trailer and closes the file. + virtual ~PSOutputDev(); +@@ -171,12 +189,14 @@ + virtual void updateHorizScaling(GfxState *state); + virtual void updateTextPos(GfxState *state); + virtual void updateTextShift(GfxState *state, double shift); ++ virtual void saveTextPos(GfxState *state); ++ virtual void restoreTextPos(GfxState *state); + + //----- path painting + virtual void stroke(GfxState *state); + virtual void fill(GfxState *state); + virtual void eoFill(GfxState *state); +@@ -244,10 +264,10 @@ + private: + + void init(PSOutputFunc outputFuncA, void *outputStreamA, + PSFileType fileTypeA, PDFDoc *docA, + int firstPage, int lastPage, PSOutMode modeA, + int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, + GBool manualCtrlA); +@@ -256,14 +282,20 @@ + void setupEmbeddedType1CFont(GfxFont *font, Ref *id, GString *psName); + void setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GString *psName); + void setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GString *psName); +- void setupExternalTrueTypeFont(GfxFont *font, GString *psName); ++ void setupExternalTrueTypeFont(GfxFont *font, GString *fileName, ++ GString *psName); + void setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, GString *psName); + void setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GString *psName, + GBool needVerticalMetrics); ++ void setupExternalCIDTrueTypeFont(GfxFont *font, ++ GString *fileName, ++ GString *psName, ++ GBool needVerticalMetrics); + void setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GString *psName); + void setupType3Font(GfxFont *font, GString *psName, Dict *parentResDict); ++ GString *makePSFontName(GfxFont *font, Ref *id); + void setupImages(Dict *resDict); +- void setupImage(Ref id, Stream *str); ++ void setupImage(Ref id, Stream *str, GBool mask); + void setupForms(Dict *resDict); + void setupForm(Ref id, Object *strObj); + void addProcessColor(double c, double m, double y, double k); +@@ -308,6 +335,7 @@ + PSOutMode mode; // PostScript mode (PS, EPS, form) + int paperWidth; // width of paper, in pts + int paperHeight; // height of paper, in pts ++ GBool paperMatch; // true if paper size is set to match each page + int imgLLX, imgLLY, // imageable area, in pts + imgURX, imgURY; + GBool preload; // load all images into memory, and +@@ -322,20 +350,21 @@ + void *underlayCbkData; + void (*overlayCbk)(PSOutputDev *psOut, void *data); + void *overlayCbkData; ++ GString *(*customCodeCbk)(PSOutputDev *psOut, ++ PSOutCustomCodeLocation loc, int n, ++ void *data); ++ void *customCodeCbkData; + + PDFDoc *doc; + XRef *xref; // the xref table for this PDF file + + Ref *fontIDs; // list of object IDs of all used fonts + int fontIDLen; // number of entries in fontIDs array + int fontIDSize; // size of fontIDs array +- Ref *fontFileIDs; // list of object IDs of all embedded fonts +- int fontFileIDLen; // number of entries in fontFileIDs array +- int fontFileIDSize; // size of fontFileIDs array +- GString **fontFileNames; // list of names of all embedded external fonts +- int fontFileNameLen; // number of entries in fontFileNames array +- int fontFileNameSize; // size of fontFileNames array +- int nextTrueTypeNum; // next unique number to append to a TrueType +- // font name ++ GHash *fontNames; // all used font names ++ PST1FontName *t1FontNames; // font names for Type 1/1C fonts ++ int t1FontNameLen; // number of entries in t1FontNames array ++ int t1FontNameSize; // size of t1FontNames array + PSFont8Info *font8Info; // info for 8-bit fonts + int font8InfoLen; // number of entries in font8Info array + int font8InfoSize; // size of font8Info array +@@ -354,6 +383,8 @@ + int numTilingPatterns; // current number of nested tiling patterns + int nextFunc; // next unique number to use for a function + ++ GList *paperSizes; // list of used paper sizes, if paperMatch ++ // is true [PSOutPaperSize] + double tx0, ty0; // global translation + double xScale0, yScale0; // global scaling + int rotate0; // rotation angle (0, 90, 180, 270) +@@ -378,6 +409,7 @@ + GString *t3String; // Type 3 content string + double t3WX, t3WY, // Type 3 character parameters + t3LLX, t3LLY, t3URX, t3URY; ++ GBool t3FillColorOnly; // operators should only use the fill color + GBool t3Cacheable; // cleared if char is not cacheable + GBool t3NeedsRestore; // set if a 'q' operator was issued + +@@ -388,7 +420,6 @@ + + GBool ok; // set up ok? + +- + friend class WinPDFPrinter; + }; + +diff -ru xpdf-3.02/xpdf/SplashOutputDev.cc xpdf-3.03/xpdf/SplashOutputDev..cc +--- xpdf-3.02/xpdf/SplashOutputDev.cc 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/SplashOutputDev.cc 2011-08-15 23:08:53.000000000 +0200 +@@ -14,14 +14,18 @@ + + #include + #include ++#include + #include "gfile.h" + #include "GlobalParams.h" + #include "Error.h" + #include "Object.h" ++#include "Gfx.h" + #include "GfxFont.h" + #include "Link.h" + #include "CharCodeToUnicode.h" + #include "FontEncodingTables.h" ++#include "BuiltinFont.h" ++#include "BuiltinFontTables.h" + #include "FoFiTrueType.h" + #include "SplashBitmap.h" + #include "SplashGlyphBitmap.h" +@@ -45,6 +49,13 @@ + + //------------------------------------------------------------------------ + ++// Type 3 font cache size parameters ++#define type3FontCacheAssoc 8 ++#define type3FontCacheMaxSets 8 ++#define type3FontCacheSize (128*1024) ++ ++//------------------------------------------------------------------------ ++ + // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. + static inline Guchar div255(int x) { + return (Guchar)((x + (x >> 8) + 0x80) >> 8); +@@ -180,64 +191,100 @@ + } + } + +-static void cvtRGBToHSV(Guchar r, Guchar g, Guchar b, int *h, int *s, int *v) { +- int cmax, cmid, cmin, x; ++static int getLum(int r, int g, int b) { ++ return (int)(0.3 * r + 0.59 * g + 0.11 * b); ++} + +- if (r >= g) { +- if (g >= b) { x = 0; cmax = r; cmid = g; cmin = b; } +- else if (b >= r) { x = 4; cmax = b; cmid = r; cmin = g; } +- else { x = 5; cmax = r; cmid = b; cmin = g; } +- } else { +- if (r >= b) { x = 1; cmax = g; cmid = r; cmin = b; } +- else if (g >= b) { x = 2; cmax = g; cmid = b; cmin = r; } +- else { x = 3; cmax = b; cmid = g; cmin = r; } +- } +- if (cmax == cmin) { +- *h = *s = 0; ++static int getSat(int r, int g, int b) { ++ int rgbMin, rgbMax; ++ ++ rgbMin = rgbMax = r; ++ if (g < rgbMin) { ++ rgbMin = g; ++ } else if (g > rgbMax) { ++ rgbMax = g; ++ } ++ if (b < rgbMin) { ++ rgbMin = b; ++ } else if (b > rgbMax) { ++ rgbMax = b; ++ } ++ return rgbMax - rgbMin; ++} ++ ++static void clipColor(int rIn, int gIn, int bIn, ++ Guchar *rOut, Guchar *gOut, Guchar *bOut) { ++ int lum, rgbMin, rgbMax; ++ ++ lum = getLum(rIn, gIn, bIn); ++ rgbMin = rgbMax = rIn; ++ if (gIn < rgbMin) { ++ rgbMin = gIn; ++ } else if (gIn > rgbMax) { ++ rgbMax = gIn; ++ } ++ if (bIn < rgbMin) { ++ rgbMin = bIn; ++ } else if (bIn > rgbMax) { ++ rgbMax = bIn; ++ } ++ if (rgbMin < 0) { ++ *rOut = (Guchar)(lum + ((rIn - lum) * lum) / (lum - rgbMin)); ++ *gOut = (Guchar)(lum + ((gIn - lum) * lum) / (lum - rgbMin)); ++ *bOut = (Guchar)(lum + ((bIn - lum) * lum) / (lum - rgbMin)); ++ } else if (rgbMax > 255) { ++ *rOut = (Guchar)(lum + ((rIn - lum) * (255 - lum)) / (rgbMax - lum)); ++ *gOut = (Guchar)(lum + ((gIn - lum) * (255 - lum)) / (rgbMax - lum)); ++ *bOut = (Guchar)(lum + ((bIn - lum) * (255 - lum)) / (rgbMax - lum)); + } else { +- *h = x * 60; +- if (x & 1) { +- *h += ((cmax - cmid) * 60) / (cmax - cmin); +- } else { +- *h += ((cmid - cmin) * 60) / (cmax - cmin); +- } +- *s = (255 * (cmax - cmin)) / cmax; ++ *rOut = rIn; ++ *gOut = gIn; ++ *bOut = bIn; + } +- *v = cmax; + } + +-static void cvtHSVToRGB(int h, int s, int v, Guchar *r, Guchar *g, Guchar *b) { +- int x, f, cmax, cmid, cmin; ++static void setLum(Guchar rIn, Guchar gIn, Guchar bIn, int lum, ++ Guchar *rOut, Guchar *gOut, Guchar *bOut) { ++ int d; ++ ++ d = lum - getLum(rIn, gIn, bIn); ++ clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut); ++} ++ ++static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat, ++ Guchar *rOut, Guchar *gOut, Guchar *bOut) { ++ int rgbMin, rgbMid, rgbMax; ++ Guchar *minOut, *midOut, *maxOut; + +- if (s == 0) { +- *r = *g = *b = v; ++ if (rIn < gIn) { ++ rgbMin = rIn; minOut = rOut; ++ rgbMid = gIn; midOut = gOut; + } else { +- x = h / 60; +- f = h % 60; +- cmax = v; +- if (x & 1) { +- cmid = div255(v * 255 - ((s * f) / 60)); +- } else { +- cmid = div255(v * (255 - ((s * (60 - f)) / 60))); +- } +- cmin = div255(v * (255 - s)); +- switch (x) { +- case 0: *r = cmax; *g = cmid; *b = cmin; break; +- case 1: *g = cmax; *r = cmid; *b = cmin; break; +- case 2: *g = cmax; *b = cmid; *r = cmin; break; +- case 3: *b = cmax; *g = cmid; *r = cmin; break; +- case 4: *b = cmax; *r = cmid; *g = cmin; break; +- case 5: *r = cmax; *b = cmid; *g = cmin; break; +- } ++ rgbMin = gIn; minOut = gOut; ++ rgbMid = rIn; midOut = rOut; + } ++ if (bIn > rgbMid) { ++ rgbMax = bIn; maxOut = bOut; ++ } else if (bIn > rgbMin) { ++ rgbMax = rgbMid; maxOut = midOut; ++ rgbMid = bIn; midOut = bOut; ++ } else { ++ rgbMax = rgbMid; maxOut = midOut; ++ rgbMid = rgbMin; midOut = minOut; ++ rgbMin = bIn; minOut = bOut; ++ } ++ if (rgbMax > rgbMin) { ++ *midOut = (Guchar)((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin); ++ *maxOut = (Guchar)sat; ++ } else { ++ *midOut = *maxOut = 0; ++ } ++ *minOut = 0; + } + + static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { +- int hs, ss, vs, hd, sd, vd; +-#if SPLASH_CMYK +- Guchar r, g, b; +-#endif ++ Guchar r0, g0, b0, r1, g1, b1; + + switch (cm) { + case splashModeMono1: +@@ -246,25 +293,22 @@ + break; + case splashModeRGB8: + case splashModeBGR8: +- cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs); +- cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd); +- cvtHSVToRGB(hs, sd, vd, &blend[0], &blend[1], &blend[2]); ++ setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]), ++ &r0, &g0, &b0); ++ setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), ++ &blend[0], &blend[1], &blend[2]); + break; + #if SPLASH_CMYK + case splashModeCMYK8: +- //~ (0xff - ...) should be clipped +- cvtRGBToHSV(0xff - (src[0] + src[3]), +- 0xff - (src[1] + src[3]), +- 0xff - (src[2] + src[3]), &hs, &ss, &vs); +- cvtRGBToHSV(0xff - (dest[0] + dest[3]), +- 0xff - (dest[1] + dest[3]), +- 0xff - (dest[2] + dest[3]), &hd, &sd, &vd); +- cvtHSVToRGB(hs, sd, vd, &r, &g, &b); +- //~ should do black generation +- blend[0] = 0xff - r; +- blend[1] = 0xff - g; +- blend[2] = 0xff - b; +- blend[3] = 0; ++ // NB: inputs have already been converted to additive mode ++ setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]), ++ &r0, &g0, &b0); ++ setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), ++ &r1, &g1, &b1); ++ blend[0] = r1; ++ blend[1] = g1; ++ blend[2] = b1; ++ blend[3] = dest[3]; + break; + #endif + } +@@ -273,10 +317,7 @@ + static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, + SplashColorMode cm) { +- int hs, ss, vs, hd, sd, vd; +-#if SPLASH_CMYK +- Guchar r, g, b; +-#endif ++ Guchar r0, g0, b0, r1, g1, b1; + + switch (cm) { + case splashModeMono1: +@@ -285,25 +326,22 @@ + break; + case splashModeRGB8: + case splashModeBGR8: +- cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs); +- cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd); +- cvtHSVToRGB(hd, ss, vd, &blend[0], &blend[1], &blend[2]); ++ setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]), ++ &r0, &g0, &b0); ++ setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), ++ &blend[0], &blend[1], &blend[2]); + break; + #if SPLASH_CMYK + case splashModeCMYK8: +- //~ (0xff - ...) should be clipped +- cvtRGBToHSV(0xff - (src[0] + src[3]), +- 0xff - (src[1] + src[3]), +- 0xff - (src[2] + src[3]), &hs, &ss, &vs); +- cvtRGBToHSV(0xff - (dest[0] + dest[3]), +- 0xff - (dest[1] + dest[3]), +- 0xff - (dest[2] + dest[3]), &hd, &sd, &vd); +- cvtHSVToRGB(hd, ss, vd, &r, &g, &b); +- //~ should do black generation +- blend[0] = 0xff - r; +- blend[1] = 0xff - g; +- blend[2] = 0xff - b; +- blend[3] = 0; ++ // NB: inputs have already been converted to additive mode ++ setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]), ++ &r0, &g0, &b0); ++ setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), ++ &r1, &g1, &b1); ++ blend[0] = r1; ++ blend[1] = g1; ++ blend[2] = b1; ++ blend[3] = dest[3]; + break; + #endif + } +@@ -311,7 +349,6 @@ + + static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { +- int hs, ss, vs, hd, sd, vd; + #if SPLASH_CMYK + Guchar r, g, b; + #endif +@@ -323,25 +360,18 @@ + break; + case splashModeRGB8: + case splashModeBGR8: +- cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs); +- cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd); +- cvtHSVToRGB(hs, ss, vd, &blend[0], &blend[1], &blend[2]); ++ setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]), ++ &blend[0], &blend[1], &blend[2]); + break; + #if SPLASH_CMYK + case splashModeCMYK8: +- //~ (0xff - ...) should be clipped +- cvtRGBToHSV(0xff - (src[0] + src[3]), +- 0xff - (src[1] + src[3]), +- 0xff - (src[2] + src[3]), &hs, &ss, &vs); +- cvtRGBToHSV(0xff - (dest[0] + dest[3]), +- 0xff - (dest[1] + dest[3]), +- 0xff - (dest[2] + dest[3]), &hd, &sd, &vd); +- cvtHSVToRGB(hs, ss, vd, &r, &g, &b); +- //~ should do black generation +- blend[0] = 0xff - r; +- blend[1] = 0xff - g; +- blend[2] = 0xff - b; +- blend[3] = 0; ++ // NB: inputs have already been converted to additive mode ++ setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]), ++ &r, &g, &b); ++ blend[0] = r; ++ blend[1] = g; ++ blend[2] = b; ++ blend[3] = dest[3]; + break; + #endif + } +@@ -350,7 +380,6 @@ + static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, + SplashColorMode cm) { +- int hs, ss, vs, hd, sd, vd; + #if SPLASH_CMYK + Guchar r, g, b; + #endif +@@ -362,25 +391,18 @@ + break; + case splashModeRGB8: + case splashModeBGR8: +- cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs); +- cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd); +- cvtHSVToRGB(hd, sd, vs, &blend[0], &blend[1], &blend[2]); ++ setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]), ++ &blend[0], &blend[1], &blend[2]); + break; + #if SPLASH_CMYK + case splashModeCMYK8: +- //~ (0xff - ...) should be clipped +- cvtRGBToHSV(0xff - (src[0] + src[3]), +- 0xff - (src[1] + src[3]), +- 0xff - (src[2] + src[3]), &hs, &ss, &vs); +- cvtRGBToHSV(0xff - (dest[0] + dest[3]), +- 0xff - (dest[1] + dest[3]), +- 0xff - (dest[2] + dest[3]), &hd, &sd, &vd); +- cvtHSVToRGB(hd, sd, vs, &r, &g, &b); +- //~ should do black generation +- blend[0] = 0xff - r; +- blend[1] = 0xff - g; +- blend[2] = 0xff - b; +- blend[3] = 0; ++ // NB: inputs have already been converted to additive mode ++ setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]), ++ &r, &g, &b); ++ blend[0] = r; ++ blend[1] = g; ++ blend[2] = b; ++ blend[3] = src[3]; + break; + #endif + } +@@ -510,21 +503,23 @@ + glyphW = glyphWA; + glyphH = glyphHA; + validBBox = validBBoxA; ++ // sanity check for excessively large glyphs (which most likely ++ // indicate an incorrect BBox) ++ i = glyphW * glyphH; ++ if (i > 100000 || glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0) { ++ glyphW = glyphH = 100; ++ validBBox = gFalse; ++ } + if (aa) { + glyphSize = glyphW * glyphH; + } else { + glyphSize = ((glyphW + 7) >> 3) * glyphH; + } +- cacheAssoc = 8; +- if (glyphSize <= 256) { +- cacheSets = 8; +- } else if (glyphSize <= 512) { +- cacheSets = 4; +- } else if (glyphSize <= 1024) { +- cacheSets = 2; +- } else { +- cacheSets = 1; +- } ++ cacheAssoc = type3FontCacheAssoc; ++ for (cacheSets = type3FontCacheMaxSets; ++ cacheSets > 1 && ++ cacheSets * cacheAssoc * glyphSize > type3FontCacheSize; ++ cacheSets >>= 1) ; + cacheData = (Guchar *)gmallocn(cacheSets * cacheAssoc, glyphSize); + cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc, + sizeof(T3FontCacheTag)); +@@ -584,6 +579,7 @@ + colorMode = colorModeA; + bitmapRowPad = bitmapRowPadA; + bitmapTopDown = bitmapTopDownA; ++ bitmapUpsideDown = gFalse; + allowAntialias = allowAntialiasA; + vectorAntialias = allowAntialias && + globalParams->getVectorAntialias() && +@@ -591,12 +587,15 @@ + setupScreenParams(72.0, 72.0); + reverseVideo = reverseVideoA; + splashColorCopy(paperColor, paperColorA); ++ skipHorizText = gFalse; ++ skipRotatedText = gFalse; + + xref = NULL; + + bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, + colorMode != splashModeMono1, bitmapTopDown); + splash = new Splash(bitmap, vectorAntialias, &screenParams); ++ splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->clear(paperColor, 0); + + fontEngine = NULL; +@@ -609,6 +608,8 @@ + textClipPath = NULL; + + transpGroupStack = NULL; ++ ++ nestCount = 0; + } + + void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) { +@@ -723,15 +726,18 @@ + bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, + colorMode != splashModeMono1, bitmapTopDown); + } + splash = new Splash(bitmap, vectorAntialias, &screenParams); ++ splash->setMinLineWidth(globalParams->getMinLineWidth()); + if (state) { + ctm = state->getCTM(); + mat[0] = (SplashCoord)ctm[0]; +@@ -835,7 +841,10 @@ + } + + void SplashOutputDev::updateFlatness(GfxState *state) { ++#if 0 // Acrobat ignores the flatness setting, and always renders curves ++ // with a fairly small flatness value + splash->setFlatness(state->getFlatness()); ++#endif + } + + void SplashOutputDev::updateLineJoin(GfxState *state) { +@@ -868,14 +877,24 @@ + GfxCMYK cmyk; + #endif + +- state->getFillGray(&gray); +- state->getFillRGB(&rgb); ++ switch (colorMode) { ++ case splashModeMono1: ++ case splashModeMono8: ++ state->getFillGray(&gray); ++ splash->setFillPattern(getColor(gray)); ++ break; ++ case splashModeRGB8: ++ case splashModeBGR8: ++ state->getFillRGB(&rgb); ++ splash->setFillPattern(getColor(&rgb)); ++ break; + #if SPLASH_CMYK +- state->getFillCMYK(&cmyk); +- splash->setFillPattern(getColor(gray, &rgb, &cmyk)); +-#else +- splash->setFillPattern(getColor(gray, &rgb)); ++ case splashModeCMYK8: ++ state->getFillCMYK(&cmyk); ++ splash->setFillPattern(getColor(&cmyk)); ++ break; + #endif ++ } + } + + void SplashOutputDev::updateStrokeColor(GfxState *state) { +@@ -885,28 +904,41 @@ + GfxCMYK cmyk; + #endif + +- state->getStrokeGray(&gray); +- state->getStrokeRGB(&rgb); ++ switch (colorMode) { ++ case splashModeMono1: ++ case splashModeMono8: ++ state->getStrokeGray(&gray); ++ splash->setStrokePattern(getColor(gray)); ++ break; ++ case splashModeRGB8: ++ case splashModeBGR8: ++ state->getStrokeRGB(&rgb); ++ splash->setStrokePattern(getColor(&rgb)); ++ break; + #if SPLASH_CMYK +- state->getStrokeCMYK(&cmyk); +- splash->setStrokePattern(getColor(gray, &rgb, &cmyk)); +-#else +- splash->setStrokePattern(getColor(gray, &rgb)); ++ case splashModeCMYK8: ++ state->getStrokeCMYK(&cmyk); ++ splash->setStrokePattern(getColor(&cmyk)); ++ break; + #endif ++ } + } + +-#if SPLASH_CMYK +-SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb, +- GfxCMYK *cmyk) { +-#else +-SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb) { +-#endif +- SplashPattern *pattern; ++SplashPattern *SplashOutputDev::getColor(GfxGray gray) { + SplashColor color; +- GfxColorComp r, g, b; + + if (reverseVideo) { + gray = gfxColorComp1 - gray; ++ } ++ color[0] = colToByte(gray); ++ return new SplashSolidColor(color); ++} ++ ++SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) { ++ GfxColorComp r, g, b; ++ SplashColor color; ++ ++ if (reverseVideo) { + r = gfxColorComp1 - rgb->r; + g = gfxColorComp1 - rgb->g; + b = gfxColorComp1 - rgb->b; +@@ -915,33 +947,58 @@ + g = rgb->g; + b = rgb->b; + } ++ color[0] = colToByte(r); ++ color[1] = colToByte(g); ++ color[2] = colToByte(b); ++ return new SplashSolidColor(color); ++} + +- pattern = NULL; // make gcc happy +- switch (colorMode) { +- case splashModeMono1: +- case splashModeMono8: +- color[0] = colToByte(gray); +- pattern = new SplashSolidColor(color); +- break; +- case splashModeRGB8: +- case splashModeBGR8: +- color[0] = colToByte(r); +- color[1] = colToByte(g); +- color[2] = colToByte(b); +- pattern = new SplashSolidColor(color); +- break; + #if SPLASH_CMYK +- case splashModeCMYK8: +- color[0] = colToByte(cmyk->c); +- color[1] = colToByte(cmyk->m); +- color[2] = colToByte(cmyk->y); +- color[3] = colToByte(cmyk->k); +- pattern = new SplashSolidColor(color); +- break; ++SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) { ++ SplashColor color; ++ ++ color[0] = colToByte(cmyk->c); ++ color[1] = colToByte(cmyk->m); ++ color[2] = colToByte(cmyk->y); ++ color[3] = colToByte(cmyk->k); ++ return new SplashSolidColor(color); ++} + #endif +- } + +- return pattern; + } + + void SplashOutputDev::updateBlendMode(GfxState *state) { +@@ -956,35 +1013,82 @@ + splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity()); + } + + void SplashOutputDev::updateFont(GfxState *state) { + needFontUpdate = gTrue; + } + + void SplashOutputDev::doUpdateFont(GfxState *state) { + GfxFont *gfxFont; ++ GfxFontLoc *fontLoc; + GfxFontType fontType; + SplashOutFontFileID *id; + SplashFontFile *fontFile; ++ int fontNum; + FoFiTrueType *ff; + Ref embRef; + Object refObj, strObj; +- GString *tmpFileName, *fileName, *substName; ++ GString *tmpFileName, *fileName; + FILE *tmpFile; + int *codeToGID; +- DisplayFontParam *dfp; + CharCodeToUnicode *ctu; + double *textMat; +- double m11, m12, m21, m22, w1, w2, fontSize; ++ double m11, m12, m21, m22, fontSize; ++ double w, fontScaleMin, fontScaleAvg, fontScale; ++ Gushort ww; + SplashCoord mat[4]; + char *name; + Unicode uBuf[8]; +- int c, substIdx, n, code, cmap; ++ int c, substIdx, n, code, cmap, i; + + needFontUpdate = gFalse; + font = NULL; + tmpFileName = NULL; + substIdx = -1; +- dfp = NULL; + + if (!(gfxFont = state->getFont())) { + goto err1; +@@ -994,6 +1098,13 @@ + goto err1; + } + ++ // sanity-check the font size - skip anything larger than 10 inches ++ // (this avoids problems allocating memory for the font cache) ++ if (state->getTransformedFontSize() ++ > 10 * (state->getHDPI() + state->getVDPI())) { ++ goto err1; ++ } ++ + // check the font file cache + id = new SplashOutFontFileID(gfxFont->getID()); + if ((fontFile = fontEngine->getFontFile(id))) { +@@ -1001,19 +1112,32 @@ + + } else { + +- // if there is an embedded font, write it to disk +- if (gfxFont->getEmbeddedFontID(&embRef)) { ++ fileName = NULL; ++ fontNum = 0; ++ ++ if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) { ++ error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", ++ gfxFont->getName() ? gfxFont->getName()->getCString() ++ : "(unnamed)"); ++ goto err2; ++ } ++ ++ // embedded font ++ if (fontLoc->locType == gfxFontLocEmbedded) { ++ gfxFont->getEmbeddedFontID(&embRef); + if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) { +- error(-1, "Couldn't create temporary font file"); ++ error(errIO, -1, "Couldn't create temporary font file"); ++ delete fontLoc; + goto err2; + } + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + if (!strObj.isStream()) { +- error(-1, "Embedded font object is wrong type"); ++ error(errSyntaxError, -1, "Embedded font object is wrong type"); + strObj.free(); + fclose(tmpFile); ++ delete fontLoc; + goto err2; + } + strObj.streamReset(); +@@ -1025,94 +1149,53 @@ + fclose(tmpFile); + fileName = tmpFileName; + +- // if there is an external font file, use it +- } else if (!(fileName = gfxFont->getExtFontFile())) { +- +- // look for a display font mapping or a substitute font +- if (gfxFont->isCIDFont()) { +- if (((GfxCIDFont *)gfxFont)->getCollection()) { +- dfp = globalParams-> +- getDisplayCIDFont(gfxFont->getName(), +- ((GfxCIDFont *)gfxFont)->getCollection()); +- } +- } else { +- if (gfxFont->getName()) { +- dfp = globalParams->getDisplayFont(gfxFont->getName()); +- } +- if (!dfp) { +- // 8-bit font substitution +- if (gfxFont->isFixedWidth()) { +- substIdx = 8; +- } else if (gfxFont->isSerif()) { +- substIdx = 4; +- } else { +- substIdx = 0; +- } +- if (gfxFont->isBold()) { +- substIdx += 2; +- } +- if (gfxFont->isItalic()) { +- substIdx += 1; +- } +- substName = new GString(splashOutSubstFonts[substIdx].name); +- dfp = globalParams->getDisplayFont(substName); +- delete substName; +- id->setSubstIdx(substIdx); +- } +- } +- if (!dfp) { +- error(-1, "Couldn't find a font for '%s'", +- gfxFont->getName() ? gfxFont->getName()->getCString() +- : "(unnamed)"); +- goto err2; +- } +- switch (dfp->kind) { +- case displayFontT1: +- fileName = dfp->t1.fileName; +- fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1; +- break; +- case displayFontTT: +- fileName = dfp->tt.fileName; +- fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType; +- break; ++ // external font ++ } else { // gfxFontLocExternal ++ fileName = fontLoc->path; ++ fontNum = fontLoc->fontNum; ++ if (fontLoc->substIdx >= 0) { ++ id->setSubstIdx(fontLoc->substIdx); + } + } + + // load the font file +- switch (fontType) { ++ switch (fontLoc->fontType) { + case fontType1: + if (!(fontFile = fontEngine->loadType1Font( +- id, +- fileName->getCString(), +- fileName == tmpFileName, +- (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { +- error(-1, "Couldn't create a font for '%s'", ++ id, ++ fileName->getCString(), ++ fileName == tmpFileName, ++ (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { ++ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); ++ delete fontLoc; + goto err2; + } + break; + case fontType1C: + if (!(fontFile = fontEngine->loadType1CFont( +- id, +- fileName->getCString(), +- fileName == tmpFileName, +- (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { +- error(-1, "Couldn't create a font for '%s'", ++ id, ++ fileName->getCString(), ++ fileName == tmpFileName, ++ (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { ++ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); ++ delete fontLoc; + goto err2; + } + break; + case fontType1COT: + if (!(fontFile = fontEngine->loadOpenTypeT1CFont( +- id, +- fileName->getCString(), +- fileName == tmpFileName, +- (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { +- error(-1, "Couldn't create a font for '%s'", ++ id, ++ fileName->getCString(), ++ fileName == tmpFileName, ++ (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { ++ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); ++ delete fontLoc; + goto err2; + } + break; +@@ -1122,18 +1205,33 @@ + codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff); + n = 256; + delete ff; ++ // if we're substituting for a non-TrueType font, we need to mark ++ // all notdef codes as "do not draw" (rather than drawing TrueType ++ // notdef glyphs) ++ if (gfxFont->getType() != fontTrueType && ++ gfxFont->getType() != fontTrueTypeOT) { ++ for (i = 0; i < 256; ++i) { ++ if (codeToGID[i] == 0) { ++ codeToGID[i] = -1; ++ } ++ } ++ } + } else { + codeToGID = NULL; + n = 0; + } + if (!(fontFile = fontEngine->loadTrueTypeFont( + id, +- fileName->getCString(), ++ fileName->getCString(), fontNum, + fileName == tmpFileName, +- codeToGID, n))) { +- error(-1, "Couldn't create a font for '%s'", ++ codeToGID, n, ++ gfxFont->getEmbeddedFontName() ++ ? gfxFont->getEmbeddedFontName()->getCString() ++ : (char *)NULL))) { ++ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); ++ delete fontLoc; + goto err2; + } + break; +@@ -1143,20 +1241,32 @@ + id, + fileName->getCString(), + fileName == tmpFileName))) { +- error(-1, "Couldn't create a font for '%s'", ++ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); ++ delete fontLoc; + goto err2; + } + break; + case fontCIDType0COT: + if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { + n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); + codeToGID = (int *)gmallocn(n, sizeof(int)); + memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), + n * sizeof(int)); + } else { + codeToGID = NULL; + n = 0; + } + if (!(fontFile = fontEngine->loadOpenTypeCFFFont( + id, + codeToGID, n))) { + error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); ++ delete fontLoc; + goto err2; + } + break; +@@ -1164,9 +1274,17 @@ + case fontCIDType2OT: + codeToGID = NULL; + n = 0; +- if (dfp) { ++ if (fontLoc->locType == gfxFontLocEmbedded) { ++ if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { ++ n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); ++ codeToGID = (int *)gmallocn(n, sizeof(int)); ++ memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), ++ n * sizeof(int)); ++ } ++ } else { + // create a CID-to-GID mapping, via Unicode + if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) { ++ //~ this should use fontNum to load the correct font + if ((ff = FoFiTrueType::load(fileName->getCString()))) { + // look for a Unicode cmap + for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) { +@@ -1179,12 +1297,12 @@ + if (cmap < ff->getNumCmaps()) { + // map CID -> Unicode -> GID + n = ctu->getLength(); +- codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort)); ++ codeToGID = (int *)gmallocn(n, sizeof(int)); + for (code = 0; code < n; ++code) { + if (ctu->mapToUnicode(code, uBuf, 8) > 0) { + codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]); + } else { +- codeToGID[code] = 0; ++ codeToGID[code] = -1; + } + } + } +@@ -1192,26 +1310,24 @@ + } + ctu->decRefCnt(); + } else { +- error(-1, "Couldn't find a mapping to Unicode for font '%s'", ++ error(errSyntaxError, -1, ++ "Couldn't find a mapping to Unicode for font '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + } +- } else { +- if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { +- n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); +- codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort)); +- memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), +- n * sizeof(Gushort)); +- } + } + if (!(fontFile = fontEngine->loadTrueTypeFont( + id, +- fileName->getCString(), ++ fileName->getCString(), fontNum, + fileName == tmpFileName, +- codeToGID, n))) { +- error(-1, "Couldn't create a font for '%s'", ++ codeToGID, n, ++ gfxFont->getEmbeddedFontName() ++ ? gfxFont->getEmbeddedFontName()->getCString() ++ : (char *)NULL))) { ++ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); ++ delete fontLoc; + goto err2; + } + break; +@@ -1219,6 +1335,8 @@ + // this shouldn't happen + goto err2; + } ++ ++ delete fontLoc; + } + + // get the font matrix +@@ -1230,26 +1348,42 @@ + m22 = textMat[3] * fontSize; + + // for substituted fonts: adjust the font matrix -- compare the +- // width of 'm' in the original font and the substituted font ++ // widths of letters and digits (A-Z, a-z, 0-9) in the original font ++ // and the substituted font + substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx(); +- if (substIdx >= 0) { ++ if (substIdx >= 0 && substIdx < 12) { ++ fontScaleMin = 1; ++ fontScaleAvg = 0; ++ n = 0; + for (code = 0; code < 256; ++code) { + if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) && +- name[0] == 'm' && name[1] == '\0') { +- break; ++ name[0] && !name[1] && ++ ((name[0] >= 'A' && name[0] <= 'Z') || ++ (name[0] >= 'a' && name[0] <= 'z') || ++ (name[0] >= '0' && name[0] <= '9'))) { ++ w = ((Gfx8BitFont *)gfxFont)->getWidth(code); ++ builtinFontSubst[substIdx]->widths->getWidth(name, &ww); ++ if (w > 0.01 && ww > 10) { ++ w /= ww * 0.001; ++ if (w < fontScaleMin) { ++ fontScaleMin = w; ++ } ++ fontScaleAvg += w; ++ ++n; ++ } + } + } +- if (code < 256) { +- w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code); +- w2 = splashOutSubstFonts[substIdx].mWidth; +- if (!gfxFont->isSymbolic()) { +- // if real font is substantially narrower than substituted +- // font, reduce the font size accordingly +- if (w1 > 0.01 && w1 < 0.9 * w2) { +- w1 /= w2; +- m11 *= w1; +- m21 *= w1; +- } ++ // if real font is narrower than substituted font, reduce the font ++ // size accordingly -- this currently uses a scale factor halfway ++ // between the minimum and average computed scale factors, which ++ // is a bit of a kludge, but seems to produce mostly decent ++ // results ++ if (n) { ++ fontScaleAvg /= n; ++ if (fontScaleAvg < 1) { ++ fontScale = 0.5 * (fontScaleMin + fontScaleAvg); ++ m11 *= fontScale; ++ m12 *= fontScale; + } + } + } +@@ -1375,6 +1655,18 @@ + Unicode *u, int uLen) { + SplashPath *path; + int render; ++ GBool doFill, doStroke, doClip, strokeAdjust; ++ double m[4]; ++ GBool horiz; ++ ++ if (skipHorizText || skipRotatedText) { ++ state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); ++ horiz = m[0] > 0 && fabs(m[1]) < 0.001 && ++ fabs(m[2]) < 0.001 && m[3] < 0; ++ if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { ++ return; ++ } ++ } + + // check for invisible text -- this is used by Acrobat Capture + render = state->getRender(); +@@ -1392,36 +1684,76 @@ + x -= originX; + y -= originY; + +- // fill +- if (!(render & 1)) { +- if (!state->getFillColorSpace()->isNonMarking()) { +- splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font); ++ doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking(); ++ doStroke = ((render & 3) == 1 || (render & 3) == 2) && ++ !state->getStrokeColorSpace()->isNonMarking(); ++ doClip = render & 4; ++ ++ path = NULL; ++ if (doStroke || doClip) { ++ if ((path = font->getGlyphPath(code))) { ++ path->offset((SplashCoord)x, (SplashCoord)y); + } + } + ++ // don't use stroke adjustment when stroking text -- the results ++ // tend to be ugly (because characters with horizontal upper or ++ // lower edges get misaligned relative to the other characters) ++ strokeAdjust = gFalse; // make gcc happy ++ if (doStroke) { ++ strokeAdjust = splash->getStrokeAdjust(); ++ splash->setStrokeAdjust(gFalse); ++ } ++ ++ // fill and stroke ++ if (doFill && doStroke) { ++ if (path) { ++ setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), ++ state->getOverprintMode(), state->getFillColor()); ++ splash->fill(path, gFalse); ++ setOverprintMask(state->getStrokeColorSpace(), ++ state->getStrokeOverprint(), ++ state->getOverprintMode(), ++ state->getStrokeColor()); ++ splash->stroke(path); ++ } ++ ++ // fill ++ } else if (doFill) { ++ setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), ++ state->getOverprintMode(), state->getFillColor()); ++ splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font); ++ + // stroke +- if ((render & 3) == 1 || (render & 3) == 2) { +- if (!state->getStrokeColorSpace()->isNonMarking()) { +- if ((path = font->getGlyphPath(code))) { +- path->offset((SplashCoord)x, (SplashCoord)y); +- splash->stroke(path); +- delete path; +- } ++ } else if (doStroke) { ++ if (path) { ++ setOverprintMask(state->getStrokeColorSpace(), ++ state->getStrokeOverprint(), ++ state->getOverprintMode(), ++ state->getStrokeColor()); ++ splash->stroke(path); + } + } + + // clip +- if (render & 4) { +- if ((path = font->getGlyphPath(code))) { +- path->offset((SplashCoord)x, (SplashCoord)y); ++ if (doClip) { ++ if (path) { + if (textClipPath) { + textClipPath->append(path); +- delete path; + } else { + textClipPath = path; ++ path = NULL; + } + } + } ++ ++ if (doStroke) { ++ splash->setStrokeAdjust(strokeAdjust); ++ } ++ ++ if (path) { ++ delete path; ++ } + } + + GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, +@@ -1433,9 +1765,20 @@ + T3FontCache *t3Font; + T3GlyphStack *t3gs; + GBool validBBox; ++ double m[4]; ++ GBool horiz; + double x1, y1, xMin, yMin, xMax, yMax, xt, yt; + int i, j; + ++ if (skipHorizText || skipRotatedText) { ++ state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); ++ horiz = m[0] > 0 && fabs(m[1]) < 0.001 && ++ fabs(m[2]) < 0.001 && m[3] < 0; ++ if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { ++ return gTrue; ++ } ++ } ++ + if (!(gfxFont = state->getFont())) { + return gFalse; + } +@@ -1517,10 +1860,10 @@ + validBBox = gTrue; + } + t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3], +- (int)floor(xMin - xt), +- (int)floor(yMin - yt), +- (int)ceil(xMax) - (int)floor(xMin) + 3, +- (int)ceil(yMax) - (int)floor(yMin) + 3, ++ (int)floor(xMin - xt) - 2, ++ (int)floor(yMin - yt) - 2, ++ (int)ceil(xMax) - (int)floor(xMin) + 4, ++ (int)ceil(yMax) - (int)floor(yMin) + 4, + validBBox, + colorMode != splashModeMono1); + } +@@ -1532,7 +1875,7 @@ + for (j = 0; j < t3Font->cacheAssoc; ++j) { + if ((t3Font->cacheTags[i+j].mru & 0x8000) && + t3Font->cacheTags[i+j].code == code) { +- drawType3Glyph(t3Font, &t3Font->cacheTags[i+j], ++ drawType3Glyph(state, t3Font, &t3Font->cacheTags[i+j], + t3Font->cacheData + (i+j) * t3Font->glyphSize); + return gTrue; + } +@@ -1547,6 +1890,8 @@ + t3GlyphStack->cacheTag = NULL; + t3GlyphStack->cacheData = NULL; + ++ haveT3Dx = gFalse; ++ + return gFalse; + } + +@@ -1555,6 +1900,7 @@ + double *ctm; + + if (t3GlyphStack->cacheTag) { ++ --nestCount; + memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(), + t3GlyphStack->cache->glyphSize); + delete bitmap; +@@ -1565,7 +1911,7 @@ + state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], + t3GlyphStack->origCTM4, t3GlyphStack->origCTM5); + updateCTM(state, 0, 0, 0, 0, 0, 0); +- drawType3Glyph(t3GlyphStack->cache, ++ drawType3Glyph(state, t3GlyphStack->cache, + t3GlyphStack->cacheTag, t3GlyphStack->cacheData); + } + t3gs = t3GlyphStack; +@@ -1574,6 +1920,7 @@ + } + + void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) { ++ haveT3Dx = gTrue; + } + + void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, +@@ -1584,6 +1931,12 @@ + double xt, yt, xMin, xMax, yMin, yMax, x1, y1; + int i, j; + ++ // ignore multiple d0/d1 operators ++ if (haveT3Dx) { ++ return; ++ } ++ haveT3Dx = gTrue; ++ + t3Font = t3GlyphStack->cache; + + // check for a valid bbox +@@ -1662,7 +2015,7 @@ + t3GlyphStack->origSplash->getScreen()); + color[0] = 0; + splash->clear(color); +- color[0] = 1; ++ color[0] = 0xff; + } else { + bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, + splashModeMono8, gFalse); +@@ -1672,18 +2025,23 @@ + splash->clear(color); + color[0] = 0xff; + } ++ splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setFillPattern(new SplashSolidColor(color)); + splash->setStrokePattern(new SplashSolidColor(color)); + //~ this should copy other state from t3GlyphStack->origSplash? ++ //~ [this is likely the same situation as in beginTransparencyGroup()] + state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], + -t3Font->glyphX, -t3Font->glyphY); + updateCTM(state, 0, 0, 0, 0, 0, 0); ++ ++nestCount; + } + +-void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font, ++void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font, + T3FontCacheTag *tag, Guchar *data) { + SplashGlyphBitmap glyph; + ++ setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), ++ state->getOverprintMode(), state->getFillColor()); + glyph.x = -t3Font->glyphX; + glyph.y = -t3Font->glyphY; + glyph.w = t3Font->glyphW; +@@ -1717,9 +2075,10 @@ + if (imgMaskData->y == imgMaskData->height) { + return gFalse; + } +- for (x = 0, p = imgMaskData->imgStr->getLine(), q = line; +- x < imgMaskData->width; +- ++x) { ++ if (!(p = imgMaskData->imgStr->getLine())) { ++ return gFalse; ++ } ++ for (x = 0, q = line; x < imgMaskData->width; ++x) { + *q++ = *p++ ^ imgMaskData->invert; + } + ++imgMaskData->y; +@@ -1765,6 +2126,46 @@ + str->close(); + } + ++void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, ++ Object *ref, Stream *str, ++ int width, int height, ++ GBool invert, ++ GBool inlineImg) { ++ double *ctm; ++ SplashCoord mat[6]; ++ SplashOutImageMaskData imgMaskData; ++ SplashBitmap *maskBitmap; ++ Splash *maskSplash; ++ SplashColor maskColor; ++ ++ ctm = state->getCTM(); ++ mat[0] = ctm[0]; ++ mat[1] = ctm[1]; ++ mat[2] = -ctm[2]; ++ mat[3] = -ctm[3]; ++ mat[4] = ctm[2] + ctm[4]; ++ mat[5] = ctm[3] + ctm[5]; ++ imgMaskData.imgStr = new ImageStream(str, width, 1, 1); ++ imgMaskData.imgStr->reset(); ++ imgMaskData.invert = invert ? 0 : 1; ++ imgMaskData.width = width; ++ imgMaskData.height = height; ++ imgMaskData.y = 0; ++ maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), ++ 1, splashModeMono8, gFalse); ++ maskSplash = new Splash(maskBitmap, gTrue); ++ maskColor[0] = 0; ++ maskSplash->clear(maskColor); ++ maskColor[0] = 0xff; ++ maskSplash->setFillPattern(new SplashSolidColor(maskColor)); ++ maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, ++ width, height, mat, gFalse); ++ delete imgMaskData.imgStr; ++ str->close(); ++ delete maskSplash; ++ splash->setSoftMask(maskBitmap); ++} ++ + struct SplashOutImageData { + ImageStream *imgStr; + GfxImageColorMap *colorMap; +@@ -1789,6 +2190,9 @@ + if (imgData->y == imgData->height) { + return gFalse; + } ++ if (!(p = imgData->imgStr->getLine())) { ++ return gFalse; ++ } + + nComps = imgData->colorMap->getNumPixelComps(); + +@@ -1796,17 +2200,13 @@ + switch (imgData->colorMode) { + case splashModeMono1: + case splashModeMono8: +- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine; +- x < imgData->width; +- ++x, ++p) { ++ for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { + *q++ = imgData->lookup[*p]; + } + break; + case splashModeRGB8: + case splashModeBGR8: +- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine; +- x < imgData->width; +- ++x, ++p) { ++ for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { + col = &imgData->lookup[3 * *p]; + *q++ = col[0]; + *q++ = col[1]; +@@ -1815,9 +2215,7 @@ + break; + #if SPLASH_CMYK + case splashModeCMYK8: +- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine; +- x < imgData->width; +- ++x, ++p) { ++ for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { + col = &imgData->lookup[4 * *p]; + *q++ = col[0]; + *q++ = col[1]; +@@ -1831,18 +2229,14 @@ + switch (imgData->colorMode) { + case splashModeMono1: + case splashModeMono8: +- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine; +- x < imgData->width; +- ++x, p += nComps) { ++ for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { + imgData->colorMap->getGray(p, &gray); + *q++ = colToByte(gray); + } + break; + case splashModeRGB8: + case splashModeBGR8: +- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine; +- x < imgData->width; +- ++x, p += nComps) { ++ for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { + imgData->colorMap->getRGB(p, &rgb); + *q++ = colToByte(rgb.r); + *q++ = colToByte(rgb.g); +@@ -1851,9 +2245,7 @@ + break; + #if SPLASH_CMYK + case splashModeCMYK8: +- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine; +- x < imgData->width; +- ++x, p += nComps) { ++ for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { + imgData->colorMap->getCMYK(p, &cmyk); + *q++ = colToByte(cmyk.c); + *q++ = colToByte(cmyk.m); +@@ -1885,10 +2277,13 @@ + if (imgData->y == imgData->height) { + return gFalse; + } ++ if (!(p = imgData->imgStr->getLine())) { ++ return gFalse; ++ } + + nComps = imgData->colorMap->getNumPixelComps(); + +- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, aq = alphaLine; ++ for (x = 0, q = colorLine, aq = alphaLine; + x < imgData->width; + ++x, p += nComps) { + alpha = 0; +@@ -1904,7 +2299,6 @@ + case splashModeMono1: + case splashModeMono8: + *q++ = imgData->lookup[*p]; +- *aq++ = alpha; + break; + case splashModeRGB8: + case splashModeBGR8: +@@ -1912,7 +2306,6 @@ + *q++ = col[0]; + *q++ = col[1]; + *q++ = col[2]; +- *aq++ = alpha; + break; + #if SPLASH_CMYK + case splashModeCMYK8: +@@ -1921,17 +2314,16 @@ + *q++ = col[1]; + *q++ = col[2]; + *q++ = col[3]; +- *aq++ = alpha; + break; + #endif + } ++ *aq++ = alpha; + } else { + switch (imgData->colorMode) { + case splashModeMono1: + case splashModeMono8: + imgData->colorMap->getGray(p, &gray); + *q++ = colToByte(gray); +- *aq++ = alpha; + break; + case splashModeRGB8: + case splashModeBGR8: +@@ -1939,7 +2331,6 @@ + *q++ = colToByte(rgb.r); + *q++ = colToByte(rgb.g); + *q++ = colToByte(rgb.b); +- *aq++ = alpha; + break; + #if SPLASH_CMYK + case splashModeCMYK8: +@@ -1948,10 +2339,10 @@ + *q++ = colToByte(cmyk.m); + *q++ = colToByte(cmyk.y); + *q++ = colToByte(cmyk.k); +- *aq++ = alpha; + break; + #endif + } ++ *aq++ = alpha; + } + } + +@@ -2012,7 +2406,7 @@ + break; + case splashModeRGB8: + case splashModeBGR8: +- imgData.lookup = (SplashColorPtr)gmalloc(3 * n); ++ imgData.lookup = (SplashColorPtr)gmallocn(n, 3); + for (i = 0; i < n; ++i) { + pix = (Guchar)i; + colorMap->getRGB(&pix, &rgb); +@@ -2023,7 +2417,7 @@ + break; + #if SPLASH_CMYK + case splashModeCMYK8: +- imgData.lookup = (SplashColorPtr)gmalloc(4 * n); ++ imgData.lookup = (SplashColorPtr)gmallocn(n, 4); + for (i = 0; i < n; ++i) { + pix = (Guchar)i; + colorMap->getCMYK(&pix, &cmyk); +@@ -2034,7 +2428,6 @@ + } + break; + #endif +- break; + } + } + +@@ -2071,7 +2464,6 @@ + Guchar *alphaLine) { + SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data; + Guchar *p, *aq; +- SplashColor maskColor; + SplashColorPtr q, col; + GfxRGB rgb; + GfxGray gray; +@@ -2079,25 +2471,35 @@ + GfxCMYK cmyk; + #endif + Guchar alpha; ++ Guchar *maskPtr; ++ int maskBit; + int nComps, x; + + if (imgData->y == imgData->height) { + return gFalse; + } ++ if (!(p = imgData->imgStr->getLine())) { ++ return gFalse; ++ } + + nComps = imgData->colorMap->getNumPixelComps(); + +- for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, aq = alphaLine; ++ maskPtr = imgData->mask->getDataPtr() + ++ imgData->y * imgData->mask->getRowSize(); ++ maskBit = 0x80; ++ for (x = 0, q = colorLine, aq = alphaLine; + x < imgData->width; + ++x, p += nComps) { +- imgData->mask->getPixel(x, imgData->y, maskColor); +- alpha = maskColor[0] ? 0xff : 0x00; ++ alpha = (*maskPtr & maskBit) ? 0xff : 0x00; ++ if (!(maskBit >>= 1)) { ++ ++maskPtr; ++ maskBit = 0x80; ++ } + if (imgData->lookup) { + switch (imgData->colorMode) { + case splashModeMono1: + case splashModeMono8: + *q++ = imgData->lookup[*p]; +- *aq++ = alpha; + break; + case splashModeRGB8: + case splashModeBGR8: +@@ -2105,7 +2507,6 @@ + *q++ = col[0]; + *q++ = col[1]; + *q++ = col[2]; +- *aq++ = alpha; + break; + #if SPLASH_CMYK + case splashModeCMYK8: +@@ -2114,17 +2515,16 @@ + *q++ = col[1]; + *q++ = col[2]; + *q++ = col[3]; +- *aq++ = alpha; + break; + #endif + } ++ *aq++ = alpha; + } else { + switch (imgData->colorMode) { + case splashModeMono1: + case splashModeMono8: + imgData->colorMap->getGray(p, &gray); + *q++ = colToByte(gray); +- *aq++ = alpha; + break; + case splashModeRGB8: + case splashModeBGR8: +@@ -2132,7 +2532,6 @@ + *q++ = colToByte(rgb.r); + *q++ = colToByte(rgb.g); + *q++ = colToByte(rgb.b); +- *aq++ = alpha; + break; + #if SPLASH_CMYK + case splashModeCMYK8: +@@ -2141,10 +2540,10 @@ + *q++ = colToByte(cmyk.m); + *q++ = colToByte(cmyk.y); + *q++ = colToByte(cmyk.k); +- *aq++ = alpha; + break; + #endif + } ++ *aq++ = alpha; + } + } + +@@ -2433,7 +2838,7 @@ + SplashTransparencyGroup *transpGroup; + SplashColor color; + double xMin, yMin, xMax, yMax, x, y; +- int tx, ty, w, h; ++ int tx, ty, w, h, i; + + // transform the bbox + state->transform(bbox[0], bbox[1], &x, &y); +@@ -2475,14 +2880,14 @@ + tx = (int)floor(xMin); + if (tx < 0) { + tx = 0; +- } else if (tx > bitmap->getWidth()) { +- tx = bitmap->getWidth(); ++ } else if (tx >= bitmap->getWidth()) { ++ tx = bitmap->getWidth() - 1; + } + ty = (int)floor(yMin); + if (ty < 0) { + ty = 0; +- } else if (ty > bitmap->getHeight()) { +- ty = bitmap->getHeight(); ++ } else if (ty >= bitmap->getHeight()) { ++ ty = bitmap->getHeight() - 1; + } + w = (int)ceil(xMax) - tx + 1; + if (tx + w > bitmap->getWidth()) { +@@ -2512,31 +2917,47 @@ + transpGroup->origBitmap = bitmap; + transpGroup->origSplash = splash; + +- //~ this ignores the blendingColorSpace arg ++ //~ this handles the blendingColorSpace arg for soft masks, but ++ //~ not yet for transparency groups ++ ++ // switch to the blending color space ++ if (forSoftMask && isolated && blendingColorSpace) { ++ if (blendingColorSpace->getMode() == csDeviceGray || ++ blendingColorSpace->getMode() == csCalGray || ++ (blendingColorSpace->getMode() == csICCBased && ++ blendingColorSpace->getNComps() == 1)) { ++ colorMode = splashModeMono8; ++ } else if (blendingColorSpace->getMode() == csDeviceRGB || ++ blendingColorSpace->getMode() == csCalRGB || ++ (blendingColorSpace->getMode() == csICCBased && ++ blendingColorSpace->getNComps() == 3)) { ++ //~ does this need to use BGR8? ++ colorMode = splashModeRGB8; ++#if SPLASH_CMYK ++ } else if (blendingColorSpace->getMode() == csDeviceCMYK || ++ (blendingColorSpace->getMode() == csICCBased && ++ blendingColorSpace->getNComps() == 4)) { ++ colorMode = splashModeCMYK8; ++#endif ++ } ++ } + + // create the temporary bitmap + bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue, + bitmapTopDown); + splash = new Splash(bitmap, vectorAntialias, + transpGroup->origSplash->getScreen()); ++ splash->setMinLineWidth(globalParams->getMinLineWidth()); ++ //~ Acrobat apparently copies at least the fill and stroke colors, and ++ //~ maybe other state(?) -- but not the clipping path (and not sure ++ //~ what else) ++ //~ [this is likely the same situation as in type3D1()] ++ splash->setFillPattern(transpGroup->origSplash->getFillPattern()->copy()); ++ splash->setStrokePattern( ++ transpGroup->origSplash->getStrokePattern()->copy()); + if (isolated) { +- switch (colorMode) { +- case splashModeMono1: +- case splashModeMono8: +- color[0] = 0; +- break; +- case splashModeRGB8: +- case splashModeBGR8: +- color[0] = color[1] = color[2] = 0; +- break; +-#if SPLASH_CMYK +- case splashModeCMYK8: +- color[0] = color[1] = color[2] = color[3] = 0; +- break; +-#endif +- default: +- // make gcc happy +- break; ++ for (i = 0; i < splashMaxColorComps; ++i) { ++ color[i] = 0; + } + splash->clear(color, 0); + } else { +@@ -2546,16 +2967,16 @@ + transpGroup->tBitmap = bitmap; + state->shiftCTM(-tx, -ty); + updateCTM(state, 0, 0, 0, 0, 0, 0); ++ ++nestCount; + } + + void SplashOutputDev::endTransparencyGroup(GfxState *state) { +- double *ctm; +- + // restore state ++ --nestCount; + delete splash; + bitmap = transpGroupStack->origBitmap; ++ colorMode = bitmap->getMode(); + splash = transpGroupStack->origSplash; +- ctm = state->getCTM(); + state->shiftCTM(transpGroupStack->tx, transpGroupStack->ty); + updateCTM(state, 0, 0, 0, 0, 0, 0); + } +@@ -2573,9 +2994,12 @@ + + // paint the transparency group onto the parent bitmap + // - the clip path was set in the parent's state) +- splash->composite(tBitmap, 0, 0, tx, ty, +- tBitmap->getWidth(), tBitmap->getHeight(), +- gFalse, !isolated); ++ if (tx < bitmap->getWidth() && ty < bitmap->getHeight()) { ++ splash->setOverprintMask(0xffffffff); ++ splash->composite(tBitmap, 0, 0, tx, ty, ++ tBitmap->getWidth(), tBitmap->getHeight(), ++ gFalse, !isolated); ++ } + + // pop the stack + transpGroup = transpGroupStack; +@@ -2606,13 +3030,13 @@ + tBitmap = transpGroupStack->tBitmap; + + // composite with backdrop color +- if (!alpha && colorMode != splashModeMono1) { ++ if (!alpha && tBitmap->getMode() != splashModeMono1) { + //~ need to correctly handle the case where no blending color + //~ space is given + tSplash = new Splash(tBitmap, vectorAntialias, + transpGroupStack->origSplash->getScreen()); + if (transpGroupStack->blendingColorSpace) { +- switch (colorMode) { ++ switch (tBitmap->getMode()) { + case splashModeMono1: + // transparency is not supported in mono1 mode + break; +@@ -2648,36 +3072,38 @@ + 1, splashModeMono8, gFalse); + memset(softMask->getDataPtr(), 0, + softMask->getRowSize() * softMask->getHeight()); +- p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx; +- for (y = 0; y < tBitmap->getHeight(); ++y) { +- for (x = 0; x < tBitmap->getWidth(); ++x) { +- tBitmap->getPixel(x, y, color); +- if (alpha) { +- //~ unimplemented +- } else { +- // convert to luminosity +- switch (colorMode) { +- case splashModeMono1: +- case splashModeMono8: +- lum = color[0] / 255.0; +- break; +- case splashModeRGB8: +- case splashModeBGR8: +- lum = (0.3 / 255.0) * color[0] + +- (0.59 / 255.0) * color[1] + +- (0.11 / 255.0) * color[2]; +- break; +-#if SPLASH_CMYK +- case splashModeCMYK8: +- lum = (1 - color[4] / 255.0) +- - (0.3 / 255.0) * color[0] +- - (0.59 / 255.0) * color[1] +- - (0.11 / 255.0) * color[2]; +- if (lum < 0) { +- lum = 0; +- } +- break; ++ if (tx < softMask->getWidth() && ty < softMask->getHeight()) { ++ p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx; ++ for (y = 0; y < tBitmap->getHeight(); ++y) { ++ for (x = 0; x < tBitmap->getWidth(); ++x) { ++ if (alpha) { ++ lum = tBitmap->getAlpha(x, y) / 255.0; ++ } else { ++ tBitmap->getPixel(x, y, color); ++ // convert to luminosity ++ switch (tBitmap->getMode()) { ++ case splashModeMono1: ++ case splashModeMono8: ++ lum = color[0] / 255.0; ++ break; ++ case splashModeRGB8: ++ case splashModeBGR8: ++ lum = (0.3 / 255.0) * color[0] + ++ (0.59 / 255.0) * color[1] + ++ (0.11 / 255.0) * color[2]; ++ break; ++#if SPLASH_CMYK ++ case splashModeCMYK8: ++ lum = (1 - color[3] / 255.0) ++ - (0.3 / 255.0) * color[0] ++ - (0.59 / 255.0) * color[1] ++ - (0.11 / 255.0) * color[2]; ++ if (lum < 0) { ++ lum = 0; ++ } ++ break; + #endif ++ } + } + if (transferFunc) { + transferFunc->transform(&lum, &lum2); +@@ -2686,8 +3112,8 @@ + } + p[x] = (int)(lum2 * 255.0 + 0.5); + } ++ p += softMask->getRowSize(); + } +- p += softMask->getRowSize(); + } + splash->setSoftMask(softMask); + +@@ -2743,39 +3169,49 @@ + rgb.r = byteToCol(r); + rgb.g = byteToCol(g); + rgb.b = byteToCol(b); +- gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5); +- if (gray > gfxColorComp1) { +- gray = gfxColorComp1; +- } ++ switch (colorMode) { ++ case splashModeMono1: ++ case splashModeMono8: ++ gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5); ++ if (gray > gfxColorComp1) { ++ gray = gfxColorComp1; ++ } ++ splash->setFillPattern(getColor(gray)); ++ break; ++ case splashModeRGB8: ++ case splashModeBGR8: ++ splash->setFillPattern(getColor(&rgb)); ++ break; + #if SPLASH_CMYK +- cmyk.c = gfxColorComp1 - rgb.r; +- cmyk.m = gfxColorComp1 - rgb.g; +- cmyk.y = gfxColorComp1 - rgb.b; +- cmyk.k = 0; +- splash->setFillPattern(getColor(gray, &rgb, &cmyk)); +-#else +- splash->setFillPattern(getColor(gray, &rgb)); ++ case splashModeCMYK8: ++ cmyk.c = gfxColorComp1 - rgb.r; ++ cmyk.m = gfxColorComp1 - rgb.g; ++ cmyk.y = gfxColorComp1 - rgb.b; ++ cmyk.k = 0; ++ splash->setFillPattern(getColor(&cmyk)); ++ break; + #endif ++ } + } + +-SplashFont *SplashOutputDev::getFont(GString *name, double *textMatA) { +- DisplayFontParam *dfp; ++SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) { + Ref ref; + SplashOutFontFileID *id; ++ GfxFontLoc *fontLoc; + SplashFontFile *fontFile; + SplashFont *fontObj; + FoFiTrueType *ff; +- Gushort *codeToGID; ++ int *codeToGID; + Unicode u; + SplashCoord textMat[4]; + int cmap, i; + +- for (i = 0; i < 16; ++i) { +- if (!name->cmp(splashOutSubstFonts[i].name)) { ++ for (i = 0; i < nBuiltinFonts; ++i) { ++ if (!name->cmp(builtinFonts[i].name)) { + break; + } + } +- if (i == 16) { ++ if (i == nBuiltinFonts) { + return NULL; + } + ref.num = i; +@@ -2788,12 +3224,16 @@ + + // load the font file + } else { +- dfp = globalParams->getDisplayFont(name); +- if (dfp && dfp->kind == displayFontT1) { +- fontFile = fontEngine->loadType1Font(id, dfp->t1.fileName->getCString(), ++ if (!(fontLoc = GfxFont::locateBase14Font(name))) { ++ return NULL; ++ } ++ if (fontLoc->fontType == fontType1) { ++ fontFile = fontEngine->loadType1Font(id, fontLoc->path->getCString(), + gFalse, winAnsiEncoding); +- } else if (dfp && dfp->kind == displayFontTT) { +- if (!(ff = FoFiTrueType::load(dfp->tt.fileName->getCString()))) { ++ } else if (fontLoc->fontType == fontTrueType) { ++ if (!(ff = FoFiTrueType::load(fontLoc->path->getCString()))) { ++ delete fontLoc; ++ delete id; + return NULL; + } + for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) { +@@ -2805,9 +3245,11 @@ + } + if (cmap == ff->getNumCmaps()) { + delete ff; ++ delete fontLoc; ++ delete id; + return NULL; + } +- codeToGID = (Gushort *)gmallocn(256, sizeof(Gushort)); ++ codeToGID = (int *)gmallocn(256, sizeof(int)); + for (i = 0; i < 256; ++i) { + codeToGID[i] = 0; + if (winAnsiEncoding[i] && +@@ -2817,11 +3259,18 @@ + } + delete ff; + fontFile = fontEngine->loadTrueTypeFont(id, +- dfp->tt.fileName->getCString(), +- gFalse, codeToGID, 256); ++ fontLoc->path->getCString(), ++ fontLoc->fontNum, ++ gFalse, codeToGID, 256, NULL); + } else { ++ delete fontLoc; ++ delete id; + return NULL; + } ++ delete fontLoc; ++ } ++ if (!fontFile) { ++ return NULL; + } + + // create the scaled font +@@ -2835,11 +3284,7 @@ + } + + #if 1 //~tmp: turn off anti-aliasing temporarily +-GBool SplashOutputDev::getVectorAntialias() { +- return splash->getVectorAntialias(); +-} +- +-void SplashOutputDev::setVectorAntialias(GBool vaa) { +- splash->setVectorAntialias(vaa); ++void SplashOutputDev::setInShading(GBool sh) { ++ splash->setInShading(sh); + } + #endif +diff -ru xpdf-3.02/xpdf/SplashOutputDev.h xpdf-3.03/xpdf/SplashOutputDev.h +--- xpdf-3.02/xpdf/SplashOutputDev.h 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/SplashOutputDev.h 2011-08-15 23:08:53.000000000 +0200 +@@ -58,15 +58,21 @@ + + // Does this device use upside-down coordinates? + // (Upside-down means (0,0) is the top left corner of the page.) +- virtual GBool upsideDown() { return gTrue; } ++ virtual GBool upsideDown() { return bitmapTopDown ^ bitmapUpsideDown; } + + // Does this device use drawChar() or drawString()? + virtual GBool useDrawChar() { return gTrue; } + +@@ -124,6 +136,10 @@ + virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, + int width, int height, GBool invert, + GBool inlineImg); ++ virtual void setSoftMaskFromImageMask(GfxState *state, ++ Object *ref, Stream *str, ++ int width, int height, GBool invert, ++ GBool inlineImg); + virtual void drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, GfxImageColorMap *colorMap, + int *maskColors, GBool inlineImg); +@@ -174,6 +190,10 @@ + // caller. + SplashBitmap *takeBitmap(); + ++ // Set this flag to true to generate an upside-down bitmap (useful ++ // for Windows BMP files). ++ void setBitmapUpsideDown(GBool f) { bitmapUpsideDown = f; } ++ + // Get the Splash object. + Splash *getSplash() { return splash; } + +@@ -187,26 +207,36 @@ + void setFillColor(int r, int g, int b); + + // Get a font object for a Base-14 font, using the Latin-1 encoding. +- SplashFont *getFont(GString *name, double *textMatA); ++ SplashFont *getFont(GString *name, SplashCoord *textMatA); + + SplashFont *getCurrentFont() { return font; } + ++ // If is true, don't draw horizontal text. ++ // If is true, don't draw rotated (non-horizontal) text. ++ void setSkipText(GBool skipHorizTextA, GBool skipRotatedTextA) ++ { skipHorizText = skipHorizTextA; skipRotatedText = skipRotatedTextA; } ++ ++ int getNestCount() { return nestCount; } ++ ++ + #if 1 //~tmp: turn off anti-aliasing temporarily +- virtual GBool getVectorAntialias(); +- virtual void setVectorAntialias(GBool vaa); ++ virtual void setInShading(GBool sh); + #endif + + private: + + void setupScreenParams(double hDPI, double vDPI); ++ SplashPattern *getColor(GfxGray gray); ++ SplashPattern *getColor(GfxRGB *rgb); + #if SPLASH_CMYK +- SplashPattern *getColor(GfxGray gray, GfxRGB *rgb, GfxCMYK *cmyk); +-#else +- SplashPattern *getColor(GfxGray gray, GfxRGB *rgb); ++ SplashPattern *getColor(GfxCMYK *cmyk); + #endif +@@ -219,11 +249,14 @@ + SplashColorMode colorMode; + int bitmapRowPad; + GBool bitmapTopDown; ++ GBool bitmapUpsideDown; + GBool allowAntialias; + GBool vectorAntialias; + GBool reverseVideo; // reverse video mode + SplashColor paperColor; // paper color + SplashScreenParams screenParams; ++ GBool skipHorizText; ++ GBool skipRotatedText; + + XRef *xref; // xref table for current document + +@@ -235,6 +268,7 @@ + t3FontCache[splashOutT3FontCacheSize]; + int nT3Fonts; // number of valid entries in t3FontCache + T3GlyphStack *t3GlyphStack; // Type 3 glyph context stack ++ GBool haveT3Dx; // set after seeing a d0/d1 operator + + SplashFont *font; // current font + GBool needFontUpdate; // set when the font needs to be updated +@@ -242,6 +276,8 @@ + + SplashTransparencyGroup * // transparency group stack + transpGroupStack; ++ ++ int nestCount; + }; + + #endif +diff -ru xpdf-3.02/xpdf/TextOutputDev.cc xpdf-3.03/xpdf/TextOutputDev.cc +--- xpdf-3.02/xpdf/TextOutputDev.cc 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/TextOutputDev.cc 2011-08-15 23:08:53.000000000 +0200 +@@ -545,7 +618,7 @@ + + // insert the new word + if (cursor && wordBaseIdx == cursorBaseIdx && +- word->primaryCmp(cursor) > 0) { ++ word->primaryCmp(cursor) >= 0) { + w0 = cursor; + w1 = cursor->next; + } else { +@@ -928,7 +1001,7 @@ + xMax = blk->xMin + d1 * (blk->xMax - blk->xMin); + yMin = blk->yMin + d2 * (blk->yMax - blk->yMin); + yMax = blk->yMin + d3 * (blk->yMax - blk->yMin); +- base = blk->yMin + base * (blk->yMax - blk->yMin); ++ base = blk->yMin + d4 * (blk->yMax - blk->yMin); + break; + case 1: + xMin = blk->xMax - d3 * (blk->xMax - blk->xMin); +@@ -1150,15 +1223,15 @@ + } + } + +-void TextBlock::coalesce(UnicodeMap *uMap) { ++void TextBlock::coalesce(UnicodeMap *uMap, double fixedPitch) { + TextWord *word0, *word1, *word2, *bestWord0, *bestWord1, *lastWord; + TextLine *line, *line0, *line1; + int poolMinBaseIdx, startBaseIdx, minBaseIdx, maxBaseIdx; + int baseIdx, bestWordBaseIdx, idx0, idx1; + double minBase, maxBase; +- double fontSize, delta, priDelta, secDelta; ++ double fontSize, wordSpacing, delta, priDelta, secDelta; + TextLine **lineArray; +- GBool found; ++ GBool found, overlap; + int col1, col2; + int i, j, k; + +@@ -1168,11 +1241,7 @@ + while (word0) { + priDelta = dupMaxPriDelta * word0->fontSize; + secDelta = dupMaxSecDelta * word0->fontSize; +- if (rot == 0 || rot == 3) { +- maxBaseIdx = pool->getBaseIdx(word0->base + secDelta); +- } else { +- maxBaseIdx = pool->getBaseIdx(word0->base - secDelta); +- } ++ maxBaseIdx = pool->getBaseIdx(word0->base + secDelta); + found = gFalse; + word1 = word2 = NULL; // make gcc happy + for (idx1 = idx0; idx1 <= maxBaseIdx; ++idx1) { +@@ -1269,6 +1338,7 @@ + maxBase = word0->base + maxIntraLineDelta * fontSize; + minBaseIdx = pool->getBaseIdx(minBase); + maxBaseIdx = pool->getBaseIdx(maxBase); ++ wordSpacing = fixedPitch ? fixedPitch : maxWordSpacing * fontSize; + + // find the rest of the words in this line + while (1) { +@@ -1277,25 +1347,32 @@ + // this line + bestWordBaseIdx = 0; + bestWord0 = bestWord1 = NULL; +- for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) { ++ overlap = gFalse; ++ for (baseIdx = minBaseIdx; ++ !overlap && baseIdx <= maxBaseIdx; ++ ++baseIdx) { + for (word0 = NULL, word1 = pool->getPool(baseIdx); + word1; + word0 = word1, word1 = word1->next) { + if (word1->base >= minBase && +- word1->base <= maxBase && +- (delta = lastWord->primaryDelta(word1)) >= +- minCharSpacing * fontSize) { +- if (delta < maxWordSpacing * fontSize && +- (!bestWord1 || word1->primaryCmp(bestWord1) < 0)) { +- bestWordBaseIdx = baseIdx; +- bestWord0 = word0; +- bestWord1 = word1; ++ word1->base <= maxBase) { ++ delta = lastWord->primaryDelta(word1); ++ if (delta < minCharSpacing * fontSize) { ++ overlap = gTrue; ++ break; ++ } else { ++ if (delta < wordSpacing && ++ (!bestWord1 || word1->primaryCmp(bestWord1) < 0)) { ++ bestWordBaseIdx = baseIdx; ++ bestWord0 = word0; ++ bestWord1 = word1; ++ } ++ break; + } +- break; + } + } + } +- if (!bestWord1) { ++ if (overlap || !bestWord1) { + break; + } + +@@ -1342,52 +1419,79 @@ + + // column assignment + nColumns = 0; +- for (i = 0; i < nLines; ++i) { +- line0 = lineArray[i]; +- col1 = 0; +- for (j = 0; j < i; ++j) { +- line1 = lineArray[j]; +- if (line1->primaryDelta(line0) >= 0) { +- col2 = line1->col[line1->len] + 1; +- } else { +- k = 0; // make gcc happy +- switch (rot) { +- case 0: +- for (k = 0; +- k < line1->len && +- line0->xMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]); +- ++k) ; +- break; +- case 1: +- for (k = 0; +- k < line1->len && +- line0->yMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]); +- ++k) ; +- break; +- case 2: +- for (k = 0; +- k < line1->len && +- line0->xMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]); +- ++k) ; +- break; +- case 3: +- for (k = 0; +- k < line1->len && +- line0->yMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]); +- ++k) ; +- break; +- } +- col2 = line1->col[k]; ++ if (fixedPitch) { ++ for (i = 0; i < nLines; ++i) { ++ line0 = lineArray[i]; ++ col1 = 0; // make gcc happy ++ switch (rot) { ++ case 0: ++ col1 = (int)((line0->xMin - xMin) / fixedPitch + 0.5); ++ break; ++ case 1: ++ col1 = (int)((line0->yMin - yMin) / fixedPitch + 0.5); ++ break; ++ case 2: ++ col1 = (int)((xMax - line0->xMax) / fixedPitch + 0.5); ++ break; ++ case 3: ++ col1 = (int)((yMax - line0->yMax) / fixedPitch + 0.5); ++ break; + } +- if (col2 > col1) { +- col1 = col2; ++ for (k = 0; k <= line0->len; ++k) { ++ line0->col[k] += col1; ++ } ++ if (line0->col[line0->len] > nColumns) { ++ nColumns = line0->col[line0->len]; + } + } +- for (k = 0; k <= line0->len; ++k) { +- line0->col[k] += col1; +- } +- if (line0->col[line0->len] > nColumns) { +- nColumns = line0->col[line0->len]; ++ } else { ++ for (i = 0; i < nLines; ++i) { ++ line0 = lineArray[i]; ++ col1 = 0; ++ for (j = 0; j < i; ++j) { ++ line1 = lineArray[j]; ++ if (line1->primaryDelta(line0) >= 0) { ++ col2 = line1->col[line1->len] + 1; ++ } else { ++ k = 0; // make gcc happy ++ switch (rot) { ++ case 0: ++ for (k = 0; ++ k < line1->len && ++ line0->xMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]); ++ ++k) ; ++ break; ++ case 1: ++ for (k = 0; ++ k < line1->len && ++ line0->yMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]); ++ ++k) ; ++ break; ++ case 2: ++ for (k = 0; ++ k < line1->len && ++ line0->xMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]); ++ ++k) ; ++ break; ++ case 3: ++ for (k = 0; ++ k < line1->len && ++ line0->yMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]); ++ ++k) ; ++ break; ++ } ++ col2 = line1->col[k]; ++ } ++ if (col2 > col1) { ++ col1 = col2; ++ } ++ } ++ for (k = 0; k <= line0->len; ++k) { ++ line0->col[k] += col1; ++ } ++ if (line0->col[line0->len] > nColumns) { ++ nColumns = line0->col[line0->len]; ++ } + } + } + gfree(lineArray); +@@ -1744,6 +1848,9 @@ + nest = 0; + nTinyChars = 0; + lastCharOverlap = gFalse; ++ actualText = NULL; ++ actualTextLen = 0; ++ actualTextNBytes = 0; + if (!rawOrder) { + for (rot = 0; rot < 4; ++rot) { + pools[rot] = new TextPool(); +@@ -1799,6 +1906,7 @@ + delete curWord; + curWord = NULL; + } ++ gfree(actualText); + if (rawOrder) { + while (rawWords) { + word = rawWords; +@@ -1817,6 +1925,8 @@ + gfree(blocks); + } + deleteGList(fonts, TextFontInfo); ++ deleteGList(underlines, TextUnderline); ++ deleteGList(links, TextLink); + + curWord = NULL; + charPos = 0; +@@ -1824,6 +1934,9 @@ + curFontSize = 0; + nest = 0; + nTinyChars = 0; ++ actualText = NULL; ++ actualTextLen = 0; ++ actualTextNBytes = 0; + if (!rawOrder) { + for (rot = 0; rot < 4; ++rot) { + pools[rot] = new TextPool(); +@@ -1834,6 +1947,8 @@ + rawWords = NULL; + rawLastWord = NULL; + fonts = new GList(); ++ underlines = new GList(); ++ links = new GList(); + } + + void TextPage::updateFont(GfxState *state) { +@@ -1993,7 +2124,7 @@ + // (2) this character overlaps the previous one (duplicated text), or + // (3) the previous character was an overlap (we want each duplicated + // character to be in a word by itself at this stage), +- // (4) the font size has changed ++ // (4) the font or font size has changed + if (curWord && curWord->len > 0) { + base = sp = delta = 0; // make gcc happy + switch (curWord->rot) { +@@ -2024,6 +2155,7 @@ + sp < -minDupBreakOverlap * curWord->fontSize || + sp > minWordBreakSpace * curWord->fontSize || + fabs(base - curWord->base) > 0.5 || ++ curFont != curWord->font || + curFontSize != curWord->fontSize) { + endWord(); + } +@@ -2057,15 +2189,46 @@ + } + } + charPos += nBytes; + } + ++void TextPage::incCharCount(int nChars) { ++ charPos += nChars; ++} ++ +@@ -2109,7 +2272,7 @@ + links->append(new TextLink(xMin, yMin, xMax, yMax, link)); + } + +-void TextPage::coalesce(GBool physLayout, GBool doHTML) { ++void TextPage::coalesce(GBool physLayout, double fixedPitch, GBool doHTML) { + UnicodeMap *uMap; + TextPool *pool; + TextWord *word0, *word1, *word2; +@@ -2139,7 +2302,7 @@ + blkList = NULL; + lastBlk = NULL; + nBlocks = 0; +- primaryRot = -1; ++ primaryRot = 0; + + #if 0 // for debugging + printf("*** initial words ***\n"); +@@ -2603,7 +2766,7 @@ + //~ addition to primary rotation + + // coalesce the block, and add it to the list +- blk->coalesce(uMap); ++ blk->coalesce(uMap, fixedPitch); + if (lastBlk) { + lastBlk->next = blk; + } else { +@@ -2611,11 +2774,12 @@ + } + lastBlk = blk; + count[rot] += blk->charCount; +- if (primaryRot < 0 || count[rot] > count[primaryRot]) { +- primaryRot = rot; +- } + ++nBlocks; + } ++ ++ if (count[rot] > count[primaryRot]) { ++ primaryRot = rot; ++ } + } + + #if 0 // for debugging +@@ -2674,76 +2838,108 @@ + + //----- column assignment + +- // sort blocks into xy order for column assignment +- blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *)); +- for (blk = blkList, i = 0; blk; blk = blk->next, ++i) { +- blocks[i] = blk; +- } +- qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpXYPrimaryRot); ++ if (physLayout && fixedPitch) { + +- // column assignment +- for (i = 0; i < nBlocks; ++i) { +- blk0 = blocks[i]; +- col1 = 0; +- for (j = 0; j < i; ++j) { +- blk1 = blocks[j]; +- col2 = 0; // make gcc happy ++ blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *)); ++ for (blk = blkList, i = 0; blk; blk = blk->next, ++i) { ++ blocks[i] = blk; ++ col1 = 0; // make gcc happy + switch (primaryRot) { + case 0: +- if (blk0->xMin > blk1->xMax) { +- col2 = blk1->col + blk1->nColumns + 3; +- } else if (blk1->xMax == blk1->xMin) { +- col2 = blk1->col; +- } else { +- col2 = blk1->col + (int)(((blk0->xMin - blk1->xMin) / +- (blk1->xMax - blk1->xMin)) * +- blk1->nColumns); +- } ++ col1 = (int)(blk->xMin / fixedPitch + 0.5); + break; + case 1: +- if (blk0->yMin > blk1->yMax) { +- col2 = blk1->col + blk1->nColumns + 3; +- } else if (blk1->yMax == blk1->yMin) { +- col2 = blk1->col; +- } else { +- col2 = blk1->col + (int)(((blk0->yMin - blk1->yMin) / +- (blk1->yMax - blk1->yMin)) * +- blk1->nColumns); +- } ++ col1 = (int)(blk->yMin / fixedPitch + 0.5); + break; + case 2: +- if (blk0->xMax < blk1->xMin) { +- col2 = blk1->col + blk1->nColumns + 3; +- } else if (blk1->xMin == blk1->xMax) { +- col2 = blk1->col; +- } else { +- col2 = blk1->col + (int)(((blk0->xMax - blk1->xMax) / +- (blk1->xMin - blk1->xMax)) * +- blk1->nColumns); +- } ++ col1 = (int)((pageWidth - blk->xMax) / fixedPitch + 0.5); + break; + case 3: +- if (blk0->yMax < blk1->yMin) { +- col2 = blk1->col + blk1->nColumns + 3; +- } else if (blk1->yMin == blk1->yMax) { +- col2 = blk1->col; +- } else { +- col2 = blk1->col + (int)(((blk0->yMax - blk1->yMax) / +- (blk1->yMin - blk1->yMax)) * +- blk1->nColumns); +- } ++ col1 = (int)((pageHeight - blk->yMax) / fixedPitch + 0.5); + break; + } +- if (col2 > col1) { +- col1 = col2; ++ blk->col = col1; ++ for (line = blk->lines; line; line = line->next) { ++ for (j = 0; j <= line->len; ++j) { ++ line->col[j] += col1; ++ } + } + } +- blk0->col = col1; +- for (line = blk0->lines; line; line = line->next) { +- for (j = 0; j <= line->len; ++j) { +- line->col[j] += col1; ++ ++ } else { ++ ++ // sort blocks into xy order for column assignment ++ blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *)); ++ for (blk = blkList, i = 0; blk; blk = blk->next, ++i) { ++ blocks[i] = blk; ++ } ++ qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpXYPrimaryRot); ++ ++ // column assignment ++ for (i = 0; i < nBlocks; ++i) { ++ blk0 = blocks[i]; ++ col1 = 0; ++ for (j = 0; j < i; ++j) { ++ blk1 = blocks[j]; ++ col2 = 0; // make gcc happy ++ switch (primaryRot) { ++ case 0: ++ if (blk0->xMin > blk1->xMax) { ++ col2 = blk1->col + blk1->nColumns + 3; ++ } else if (blk1->xMax == blk1->xMin) { ++ col2 = blk1->col; ++ } else { ++ col2 = blk1->col + (int)(((blk0->xMin - blk1->xMin) / ++ (blk1->xMax - blk1->xMin)) * ++ blk1->nColumns); ++ } ++ break; ++ case 1: ++ if (blk0->yMin > blk1->yMax) { ++ col2 = blk1->col + blk1->nColumns + 3; ++ } else if (blk1->yMax == blk1->yMin) { ++ col2 = blk1->col; ++ } else { ++ col2 = blk1->col + (int)(((blk0->yMin - blk1->yMin) / ++ (blk1->yMax - blk1->yMin)) * ++ blk1->nColumns); ++ } ++ break; ++ case 2: ++ if (blk0->xMax < blk1->xMin) { ++ col2 = blk1->col + blk1->nColumns + 3; ++ } else if (blk1->xMin == blk1->xMax) { ++ col2 = blk1->col; ++ } else { ++ col2 = blk1->col + (int)(((blk0->xMax - blk1->xMax) / ++ (blk1->xMin - blk1->xMax)) * ++ blk1->nColumns); ++ } ++ break; ++ case 3: ++ if (blk0->yMax < blk1->yMin) { ++ col2 = blk1->col + blk1->nColumns + 3; ++ } else if (blk1->yMin == blk1->yMax) { ++ col2 = blk1->col; ++ } else { ++ col2 = blk1->col + (int)(((blk0->yMax - blk1->yMax) / ++ (blk1->yMin - blk1->yMax)) * ++ blk1->nColumns); ++ } ++ break; ++ } ++ if (col2 > col1) { ++ col1 = col2; ++ } ++ } ++ blk0->col = col1; ++ for (line = blk0->lines; line; line = line->next) { ++ for (j = 0; j <= line->len; ++j) { ++ line->col[j] += col1; ++ } + } + } ++ + } + + #if 0 // for debugging +@@ -2753,7 +2949,7 @@ + blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax, blk->col, + blk->nColumns); + for (line = blk->lines; line; line = line->next) { +- printf(" line:\n"); ++ printf(" line: col[0]=%d\n", line->col[0]); + for (word0 = line->words; word0; word0 = word0->next) { + printf(" word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '", + word0->xMin, word0->xMax, word0->yMin, word0->yMax, +@@ -2932,6 +3128,7 @@ + GBool startAtTop, GBool stopAtBottom, + GBool startAtLast, GBool stopAtLast, + GBool caseSensitive, GBool backward, ++ GBool wholeWord, + double *xMin, double *yMin, + double *xMax, double *yMax) { + TextBlock *blk; +@@ -2989,25 +3186,35 @@ + blk = blocks[i]; + + // check: is the block above the top limit? +- if (!startAtTop && (backward ? blk->yMin > yStart : blk->yMax < yStart)) { ++ // (this only works if the page's primary rotation is zero -- ++ // otherwise the blocks won't be sorted in the useful order) ++ if (!startAtTop && primaryRot == 0 && ++ (backward ? blk->yMin > yStart : blk->yMax < yStart)) { + continue; + } + + // check: is the block below the bottom limit? +- if (!stopAtBottom && (backward ? blk->yMax < yStop : blk->yMin > yStop)) { ++ // (this only works if the page's primary rotation is zero -- ++ // otherwise the blocks won't be sorted in the useful order) ++ if (!stopAtBottom && primaryRot == 0 && ++ (backward ? blk->yMax < yStop : blk->yMin > yStop)) { + break; + } + + for (line = blk->lines; line; line = line->next) { + + // check: is the line above the top limit? +- if (!startAtTop && ++ // (this only works if the page's primary rotation is zero -- ++ // otherwise the lines won't be sorted in the useful order) ++ if (!startAtTop && primaryRot == 0 && + (backward ? line->yMin > yStart : line->yMin < yStart)) { + continue; + } + + // check: is the line below the bottom limit? +- if (!stopAtBottom && ++ // (this only works if the page's primary rotation is zero -- ++ // otherwise the lines won't be sorted in the useful order) ++ if (!stopAtBottom && primaryRot == 0 && + (backward ? line->yMin < yStop : line->yMin > yStop)) { + continue; + } +@@ -3030,68 +3237,72 @@ + j = backward ? m - len : 0; + p = txt + j; + while (backward ? j >= 0 : j <= m - len) { +- +- // compare the strings +- for (k = 0; k < len; ++k) { +- if (p[k] != s2[k]) { +- break; ++ if (!wholeWord || ++ ((j == 0 || !unicodeTypeAlphaNum(txt[j - 1])) && ++ (j + len == m || !unicodeTypeAlphaNum(txt[j + len])))) { ++ ++ // compare the strings ++ for (k = 0; k < len; ++k) { ++ if (p[k] != s2[k]) { ++ break; ++ } + } +- } + +- // found it +- if (k == len) { +- switch (line->rot) { +- case 0: +- xMin1 = line->edge[j]; +- xMax1 = line->edge[j + len]; +- yMin1 = line->yMin; +- yMax1 = line->yMax; +- break; +- case 1: +- xMin1 = line->xMin; +- xMax1 = line->xMax; +- yMin1 = line->edge[j]; +- yMax1 = line->edge[j + len]; +- break; +- case 2: +- xMin1 = line->edge[j + len]; +- xMax1 = line->edge[j]; +- yMin1 = line->yMin; +- yMax1 = line->yMax; +- break; +- case 3: +- xMin1 = line->xMin; +- xMax1 = line->xMax; +- yMin1 = line->edge[j + len]; +- yMax1 = line->edge[j]; +- break; +- } +- if (backward) { +- if ((startAtTop || +- yMin1 < yStart || (yMin1 == yStart && xMin1 < xStart)) && +- (stopAtBottom || +- yMin1 > yStop || (yMin1 == yStop && xMin1 > xStop))) { +- if (!found || +- yMin1 > yMin0 || (yMin1 == yMin0 && xMin1 > xMin0)) { +- xMin0 = xMin1; +- xMax0 = xMax1; +- yMin0 = yMin1; +- yMax0 = yMax1; +- found = gTrue; +- } ++ // found it ++ if (k == len) { ++ switch (line->rot) { ++ case 0: ++ xMin1 = line->edge[j]; ++ xMax1 = line->edge[j + len]; ++ yMin1 = line->yMin; ++ yMax1 = line->yMax; ++ break; ++ case 1: ++ xMin1 = line->xMin; ++ xMax1 = line->xMax; ++ yMin1 = line->edge[j]; ++ yMax1 = line->edge[j + len]; ++ break; ++ case 2: ++ xMin1 = line->edge[j + len]; ++ xMax1 = line->edge[j]; ++ yMin1 = line->yMin; ++ yMax1 = line->yMax; ++ break; ++ case 3: ++ xMin1 = line->xMin; ++ xMax1 = line->xMax; ++ yMin1 = line->edge[j + len]; ++ yMax1 = line->edge[j]; ++ break; + } +- } else { +- if ((startAtTop || +- yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) && +- (stopAtBottom || +- yMin1 < yStop || (yMin1 == yStop && xMin1 < xStop))) { +- if (!found || +- yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) { +- xMin0 = xMin1; +- xMax0 = xMax1; +- yMin0 = yMin1; +- yMax0 = yMax1; +- found = gTrue; ++ if (backward) { ++ if ((startAtTop || ++ yMin1 < yStart || (yMin1 == yStart && xMin1 < xStart)) && ++ (stopAtBottom || ++ yMin1 > yStop || (yMin1 == yStop && xMin1 > xStop))) { ++ if (!found || ++ yMin1 > yMin0 || (yMin1 == yMin0 && xMin1 > xMin0)) { ++ xMin0 = xMin1; ++ xMax0 = xMax1; ++ yMin0 = yMin1; ++ yMax0 = yMax1; ++ found = gTrue; ++ } ++ } ++ } else { ++ if ((startAtTop || ++ yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) && ++ (stopAtBottom || ++ yMin1 < yStop || (yMin1 == yStop && xMin1 < xStop))) { ++ if (!found || ++ yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) { ++ xMin0 = xMin1; ++ xMax0 = xMax1; ++ yMin0 = yMin1; ++ yMax0 = yMax1; ++ found = gTrue; ++ } + } + } + } +@@ -3820,10 +4038,20 @@ + fwrite(text, 1, len, (FILE *)stream); + } + + TextOutputDev::TextOutputDev(char *fileName, GBool physLayoutA, +- GBool rawOrderA, GBool append) { ++ double fixedPitchA, GBool rawOrderA, ++ GBool append) { + text = NULL; + physLayout = physLayoutA; ++ fixedPitch = physLayout ? fixedPitchA : 0; + rawOrder = rawOrderA; + doHTML = gFalse; + ok = gTrue; +@@ -3854,11 +4074,13 @@ + } + + TextOutputDev::TextOutputDev(TextOutputFunc func, void *stream, +- GBool physLayoutA, GBool rawOrderA) { ++ GBool physLayoutA, double fixedPitchA, ++ GBool rawOrderA) { + outputFunc = func; + outputStream = stream; + needClose = gFalse; + physLayout = physLayoutA; ++ fixedPitch = physLayout ? fixedPitchA : 0; + rawOrder = rawOrderA; + doHTML = gFalse; + text = new TextPage(rawOrderA); +@@ -3883,12 +4105,16 @@ + + void TextOutputDev::endPage() { + text->endPage(); +- text->coalesce(physLayout, doHTML); ++ text->coalesce(physLayout, fixedPitch, doHTML); + if (outputStream) { + text->dump(outputStream, outputFunc, physLayout); + } + } + ++void TextOutputDev::restoreState(GfxState *state) { ++ text->updateFont(state); ++} ++ + void TextOutputDev::updateFont(GfxState *state) { + text->updateFont(state); + } +@@ -3903,7 +4129,19 @@ + double dx, double dy, + double originX, double originY, + CharCode c, int nBytes, Unicode *u, int uLen) { +- text->addChar(state, x, y, dx, dy, c, nBytes, u, uLen); ++ text->addChar(state, x - originX, y - originY, dx, dy, c, nBytes, u, uLen); ++} ++ ++void TextOutputDev::incCharCount(int nChars) { ++ text->incCharCount(nChars); ++} ++ +@@ -4057,10 +4295,12 @@ + GBool startAtTop, GBool stopAtBottom, + GBool startAtLast, GBool stopAtLast, + GBool caseSensitive, GBool backward, ++ GBool wholeWord, + double *xMin, double *yMin, + double *xMax, double *yMax) { + return text->findText(s, len, startAtTop, stopAtBottom, +- startAtLast, stopAtLast, caseSensitive, backward, ++ startAtLast, stopAtLast, ++ caseSensitive, backward, wholeWord, + xMin, yMin, xMax, yMax); + } + +diff -ru xpdf-3.02/xpdf/TextOutputDev.h xpdf-3.03/xpdf/TextOutputDev.h +--- xpdf-3.02/xpdf/TextOutputDev.h 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/TextOutputDev.h 2011-08-15 23:08:53.000000000 +0200 +@@ -280,7 +281,7 @@ + + void addWord(TextWord *word); + +- void coalesce(UnicodeMap *uMap); ++ void coalesce(UnicodeMap *uMap, double fixedPitch); + + // Update this block's priMin and priMax values, looking at . + void updatePriMinMax(TextBlock *blk); +@@ -429,6 +430,15 @@ + double dx, double dy, + CharCode c, int nBytes, Unicode *u, int uLen); + ++ // Add invisible characters. ++ void incCharCount(int nChars); ++ +@@ -442,7 +452,7 @@ + void addLink(int xMin, int yMin, int xMax, int yMax, Link *link); + + // Coalesce strings that look like parts of the same line. +- void coalesce(GBool physLayout, GBool doHTML); ++ void coalesce(GBool physLayout, double fixedPitch, GBool doHTML); + + // Find a string. If is true, starts looking at the + // top of the page; else if is true, starts looking +@@ -455,6 +465,7 @@ + GBool startAtTop, GBool stopAtBottom, + GBool startAtLast, GBool stopAtLast, + GBool caseSensitive, GBool backward, ++ GBool wholeWord, + double *xMin, double *yMin, + double *xMax, double *yMax); + +@@ -502,6 +513,13 @@ + int nTinyChars; // number of "tiny" chars seen so far + GBool lastCharOverlap; // set if the last added char overlapped the + // previous char +@@ -544,14 +562,16 @@ + // is maintained. If is true, the text is kept in + // content stream order. + TextOutputDev(char *fileName, GBool physLayoutA, +- GBool rawOrderA, GBool append); ++ double fixedPitchA, GBool rawOrderA, ++ GBool append); + + // Create a TextOutputDev which will write to a generic stream. If + // is true, the original physical layout of the text + // is maintained. If is true, the text is kept in + // content stream order. + TextOutputDev(TextOutputFunc func, void *stream, +- GBool physLayoutA, GBool rawOrderA); ++ GBool physLayoutA, double fixedPitchA, ++ GBool rawOrderA); + + // Destructor. + virtual ~TextOutputDev(); +@@ -575,6 +595,10 @@ + // Does this device need non-text content? + virtual GBool needNonText() { return gFalse; } + ++ // Does this device require incCharCount to be called for text on ++ // non-shown layers? ++ virtual GBool needCharCount() { return gTrue; } ++ + //----- initialization and control + + // Start a page. +@@ -583,6 +607,9 @@ + // End a page. + virtual void endPage(); + ++ //----- save/restore graphics state ++ virtual void restoreState(GfxState *state); ++ + //----- update text state + virtual void updateFont(GfxState *state); + +@@ -593,6 +620,9 @@ + double dx, double dy, + double originX, double originY, + CharCode c, int nBytes, Unicode *u, int uLen); ++ virtual void incCharCount(int nChars); +@@ -615,6 +645,7 @@ + GBool startAtTop, GBool stopAtBottom, + GBool startAtLast, GBool stopAtLast, + GBool caseSensitive, GBool backward, ++ GBool wholeWord, + double *xMin, double *yMin, + double *xMax, double *yMax); + +@@ -653,6 +684,9 @@ + TextPage *text; // text for the current page + GBool physLayout; // maintain original physical layout when + // dumping text ++ double fixedPitch; // if physLayout is true and this is non-zero, ++ // assume fixed-pitch characters with this ++ // width + GBool rawOrder; // keep text in content stream order + GBool doHTML; // extra processing for HTML conversion + GBool ok; // set up ok? +diff -ru xpdf-3.02/xpdf/XRef.cc xpdf-3.03/xpdf/XRef.cc +--- xpdf-3.02/xpdf/XRef.cc 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/XRef.cc 2011-08-15 23:08:53.000000000 +0200 +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include "gmem.h" + #include "Object.h" + #include "Stream.h" +@@ -190,13 +202,14 @@ + // XRef + //------------------------------------------------------------------------ + +-XRef::XRef(BaseStream *strA) { ++XRef::XRef(BaseStream *strA, GBool repair) { + Guint pos; + Object obj; + + ok = gTrue; + errCode = errNone; + size = 0; ++ last = -1; + entries = NULL; + streamEnds = NULL; + streamEndsLen = 0; +@@ -206,30 +219,32 @@ + permFlags = defPermFlags; + ownerPasswordOk = gFalse; + +- // read the trailer + str = strA; + start = str->getStart(); +- pos = getStartXref(); + +- // if there was a problem with the 'startxref' position, try to +- // reconstruct the xref table +- if (pos == 0) { ++ // if the 'repair' flag is set, try to reconstruct the xref table ++ if (repair) { + if (!(ok = constructXRef())) { + errCode = errDamaged; + return; + } + +- // read the xref table ++ // if the 'repair' flag is not set, read the xref table + } else { +- while (readXRef(&pos)) ; + +- // if there was a problem with the xref table, +- // try to reconstruct it ++ // read the trailer ++ pos = getStartXref(); ++ if (pos == 0) { ++ errCode = errDamaged; ++ ok = gFalse; ++ return; ++ } ++ ++ // read the xref table ++ while (readXRef(&pos)) ; + if (!ok) { +- if (!(ok = constructXRef())) { +- errCode = errDamaged; +- return; +- } ++ errCode = errDamaged; ++ return; + } + } + +@@ -288,7 +303,7 @@ + if (i < 0) { + return 0; + } +- for (p = &buf[i+9]; isspace(*p); ++p) ; ++ for (p = &buf[i+9]; isspace(*p & 0xff); ++p) ; + lastXRefPos = strToUnsigned(p); + + return lastXRefPos; +@@ -307,7 +322,7 @@ + new Lexer(NULL, + str->makeSubStream(start + *pos, gFalse, 0, &obj)), + gTrue); +- parser->getObj(&obj); ++ parser->getObj(&obj, gTrue); + + // parse an old-style xref table + if (obj.isCmd("xref")) { +@@ -317,11 +332,11 @@ + // parse an xref stream + } else if (obj.isInt()) { + obj.free(); +- if (!parser->getObj(&obj)->isInt()) { ++ if (!parser->getObj(&obj, gTrue)->isInt()) { + goto err1; + } + obj.free(); +- if (!parser->getObj(&obj)->isCmd("obj")) { ++ if (!parser->getObj(&obj, gTrue)->isCmd("obj")) { + goto err1; + } + obj.free(); +@@ -353,7 +368,7 @@ + int first, n, newSize, i; + + while (1) { +- parser->getObj(&obj); ++ parser->getObj(&obj, gTrue); + if (obj.isCmd("trailer")) { + obj.free(); + break; +@@ -363,7 +378,7 @@ + } + first = obj.getInt(); + obj.free(); +- if (!parser->getObj(&obj)->isInt()) { ++ if (!parser->getObj(&obj, gTrue)->isInt()) { + goto err1; + } + n = obj.getInt(); +@@ -386,17 +401,17 @@ + size = newSize; + } + for (i = first; i < first + n; ++i) { +- if (!parser->getObj(&obj)->isInt()) { ++ if (!parser->getObj(&obj, gTrue)->isInt()) { + goto err1; + } + entry.offset = (Guint)obj.getInt(); + obj.free(); +- if (!parser->getObj(&obj)->isInt()) { ++ if (!parser->getObj(&obj, gTrue)->isInt()) { + goto err1; + } + entry.gen = obj.getInt(); + obj.free(); +- parser->getObj(&obj); ++ parser->getObj(&obj, gTrue); + if (obj.isCmd("n")) { + entry.type = xrefEntryUncompressed; + } else if (obj.isCmd("f")) { +@@ -417,6 +432,9 @@ + entries[0] = entries[1]; + entries[1].offset = 0xffffffff; + } ++ if (i > last) { ++ last = i; ++ } + } + } + } +@@ -429,13 +447,25 @@ + // get the 'Prev' pointer + obj.getDict()->lookupNF("Prev", &obj2); + if (obj2.isInt()) { +- *pos = (Guint)obj2.getInt(); +- more = gTrue; ++ pos2 = (Guint)obj2.getInt(); ++ if (pos2 != *pos) { ++ *pos = pos2; ++ more = gTrue; ++ } else { ++ error(errSyntaxWarning, -1, "Infinite loop in xref table"); ++ more = gFalse; ++ } + } else if (obj2.isRef()) { + // certain buggy PDF generators generate "/Prev NNN 0 R" instead + // of "/Prev NNN" +- *pos = (Guint)obj2.getRefNum(); +- more = gTrue; ++ pos2 = (Guint)obj2.getRefNum(); ++ if (pos2 != *pos) { ++ *pos = pos2; ++ more = gTrue; ++ } else { ++ error(errSyntaxWarning, -1, "Infinite loop in xref table"); ++ more = gFalse; ++ } + } else { + more = gFalse; + } +@@ -624,6 +654,9 @@ + default: + return gFalse; + } ++ if (i > last) { ++ last = i; ++ } + } + } + +@@ -647,7 +680,6 @@ + size = 0; + entries = NULL; + +- error(-1, "PDF file is damaged - attempting to reconstruct xref table...."); + gotRoot = gFalse; + streamEndsLen = streamEndsSize = 0; + +@@ -687,30 +719,30 @@ + delete parser; + + // look for object +- } else if (isdigit(*p)) { ++ } else if (isdigit(*p & 0xff)) { + num = atoi(p); + if (num > 0) { + do { + ++p; +- } while (*p && isdigit(*p)); +- if (isspace(*p)) { ++ } while (*p && isdigit(*p & 0xff)); ++ if (isspace(*p & 0xff)) { + do { + ++p; +- } while (*p && isspace(*p)); +- if (isdigit(*p)) { ++ } while (*p && isspace(*p & 0xff)); ++ if (isdigit(*p & 0xff)) { + gen = atoi(p); + do { + ++p; +- } while (*p && isdigit(*p)); +- if (isspace(*p)) { ++ } while (*p && isdigit(*p & 0xff)); ++ if (isspace(*p & 0xff)) { + do { + ++p; +- } while (*p && isspace(*p)); ++ } while (*p && isspace(*p & 0xff)); + if (!strncmp(p, "obj", 3)) { + if (num >= size) { + newSize = (num + 1 + 255) & ~255; + if (newSize < 0) { +- error(-1, "Bad object number"); ++ error(errSyntaxError, -1, "Bad object number"); + return gFalse; + } + entries = (XRefEntry *) +@@ -726,6 +758,9 @@ + entries[num].offset = pos - start; + entries[num].gen = gen; + entries[num].type = xrefEntryUncompressed; ++ if (num > last) { ++ last = num; ++ } + } + } + } +diff -ru xpdf-3.02/xpdf/XRef.h xpdf-3.03/xpdf/XRef.h +--- xpdf-3.02/xpdf/XRef.h 2007-02-27 23:05:52.000000000 +0100 ++++ xpdf-3.03/xpdf/XRef.h 2011-08-15 23:08:53.000000000 +0200 +@@ -43,7 +43,7 @@ + public: + + // Constructor. Read xref table from stream. +- XRef(BaseStream *strA); ++ XRef(BaseStream *strA, GBool repair); + + // Destructor. + ~XRef(); +@@ -67,19 +67,20 @@ + // Get catalog object. + Object *getCatalog(Object *obj) { return fetch(rootNum, rootGen, obj); } + + // Fetch an indirect reference. +- Object *fetch(int num, int gen, Object *obj); ++ Object *fetch(int num, int gen, Object *obj, int recursion = 0); + + // Return the document's Info dictionary (if any). + Object *getDocInfo(Object *obj); + Object *getDocInfoNF(Object *obj); + + // Return the number of objects in the xref table. +- int getNumObjects() { return size; } ++ int getNumObjects() { return last + 1; } + + // Return the offset of the last xref table. + Guint getLastXRefPos() { return lastXRefPos; } +@@ -104,6 +105,7 @@ + // at beginning of file) + XRefEntry *entries; // xref entries + int size; // size of array ++ int last; // last used index in + int rootNum, rootGen; // catalog dict + GBool ok; // true if xref table is valid + int errCode; // error code (if is false) \ No newline at end of file -- cgit v1.2.3