Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/pdf/xpdf/Gfx.cc
diff options
context:
space:
mode:
authorMartin Kretzschmar <mkretzschmar@src.gnome.org>2002-09-18 22:20:42 (GMT)
committer Martin Kretzschmar <mkretzschmar@src.gnome.org>2002-09-18 22:20:42 (GMT)
commit2a393c134fe3fe8eb85bf818cb7ad6ae4396322a (patch)
treeeba8b0dcaba42d799ed8313faee15fb74a5a0cd2 /pdf/xpdf/Gfx.cc
parent7aac8dc8533347e21311b15186e0af82f1b22fd6 (diff)
Synched with Xpdf 1.01
Diffstat (limited to 'pdf/xpdf/Gfx.cc')
-rw-r--r--pdf/xpdf/Gfx.cc1217
1 files changed, 935 insertions, 282 deletions
diff --git a/pdf/xpdf/Gfx.cc b/pdf/xpdf/Gfx.cc
index 0096d4b..0b00f91 100644
--- a/pdf/xpdf/Gfx.cc
+++ b/pdf/xpdf/Gfx.cc
@@ -2,7 +2,7 @@
//
// Gfx.cc
//
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2002 Glyph & Cog, LLC
//
//========================================================================
@@ -10,11 +10,13 @@
#pragma implementation
#endif
+#include <aconf.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <math.h>
#include "gmem.h"
+#include "CharTypes.h"
#include "Object.h"
#include "Array.h"
#include "Dict.h"
@@ -24,10 +26,31 @@
#include "GfxFont.h"
#include "GfxState.h"
#include "OutputDev.h"
-#include "Params.h"
+#include "Page.h"
#include "Error.h"
#include "Gfx.h"
+// the MSVC math.h doesn't define this
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+//------------------------------------------------------------------------
+// constants
+//------------------------------------------------------------------------
+
+// Max number of splits along the t axis for an axial shading fill.
+#define axialMaxSplits 256
+
+// Max delta allowed in any color component for an axial shading fill.
+#define axialColorDelta (1 / 256.0)
+
+// Max number of splits along the t axis for a radial shading fill.
+#define radialMaxSplits 256
+
+// Max delta allowed in any color component for a radial shading fill.
+#define radialColorDelta (1 / 256.0)
+
//------------------------------------------------------------------------
// Operator table
//------------------------------------------------------------------------
@@ -190,14 +213,10 @@ Operator Gfx::opTab[] = {
#define numOps (sizeof(opTab) / sizeof(Operator))
//------------------------------------------------------------------------
-
-GBool printCommands = gFalse;
-
-//------------------------------------------------------------------------
// GfxResources
//------------------------------------------------------------------------
-GfxResources::GfxResources(Dict *resDict, GfxResources *next) {
+GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
Object obj1;
if (resDict) {
@@ -206,7 +225,7 @@ GfxResources::GfxResources(Dict *resDict, GfxResources *next) {
fonts = NULL;
resDict->lookup("Font", &obj1);
if (obj1.isDict()) {
- fonts = new GfxFontDict(obj1.getDict());
+ fonts = new GfxFontDict(xref, obj1.getDict());
}
obj1.free();
@@ -219,6 +238,9 @@ GfxResources::GfxResources(Dict *resDict, GfxResources *next) {
// get pattern dictionary
resDict->lookup("Pattern", &patternDict);
+ // get shading dictionary
+ resDict->lookup("Shading", &shadingDict);
+
// get graphics state parameter dictionary
resDict->lookup("ExtGState", &gStateDict);
@@ -230,7 +252,7 @@ GfxResources::GfxResources(Dict *resDict, GfxResources *next) {
gStateDict.initNull();
}
- this->next = next;
+ next = nextA;
}
GfxResources::~GfxResources() {
@@ -240,6 +262,7 @@ GfxResources::~GfxResources() {
xObjDict.free();
colorSpaceDict.free();
patternDict.free();
+ shadingDict.free();
gStateDict.free();
}
@@ -318,6 +341,25 @@ GfxPattern *GfxResources::lookupPattern(char *name) {
return NULL;
}
+GfxShading *GfxResources::lookupShading(char *name) {
+ GfxResources *resPtr;
+ GfxShading *shading;
+ Object obj;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->shadingDict.isDict()) {
+ if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
+ shading = GfxShading::parse(&obj);
+ obj.free();
+ return shading;
+ }
+ obj.free();
+ }
+ }
+ error(-1, "Unknown shading '%s'", name);
+ return NULL;
+}
+
GBool GfxResources::lookupGState(char *name, Object *obj) {
GfxResources *resPtr;
@@ -337,18 +379,21 @@ GBool GfxResources::lookupGState(char *name, Object *obj) {
// Gfx
//------------------------------------------------------------------------
-Gfx::Gfx(OutputDev *out1, int pageNum, Dict *resDict,
- double dpi, double x1, double y1, double x2, double y2, GBool crop,
- double cropX1, double cropY1, double cropX2, double cropY2,
- int rotate) {
+Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
+ PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
+ GBool printCommandsA) {
int i;
+ xref = xrefA;
+ subPage = gFalse;
+ printCommands = printCommandsA;
+
// start the resource stack
- res = new GfxResources(resDict, NULL);
+ res = new GfxResources(xref, resDict, NULL);
// initialize
- out = out1;
- state = new GfxState(dpi, x1, y1, x2, y2, rotate, out->upsideDown());
+ out = outA;
+ state = new GfxState(dpi, box, rotate, out->upsideDown());
fontChanged = gFalse;
clip = clipNone;
ignoreUndef = 0;
@@ -361,31 +406,65 @@ Gfx::Gfx(OutputDev *out1, int pageNum, Dict *resDict,
// set crop box
if (crop) {
- state->moveTo(cropX1, cropY1);
- state->lineTo(cropX2, cropY1);
- state->lineTo(cropX2, cropY2);
- state->lineTo(cropX1, cropY2);
+ state->moveTo(cropBox->x1, cropBox->y1);
+ state->lineTo(cropBox->x2, cropBox->y1);
+ state->lineTo(cropBox->x2, cropBox->y2);
+ state->lineTo(cropBox->x1, cropBox->y2);
state->closePath();
+ state->clip();
out->clip(state);
state->clearPath();
}
}
-Gfx::~Gfx() {
- GfxResources *resPtr;
+Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
+ PDFRectangle *box, GBool crop, PDFRectangle *cropBox) {
+ int i;
+
+ xref = xrefA;
+ subPage = gTrue;
+ printCommands = gFalse;
+
+ // start the resource stack
+ res = new GfxResources(xref, resDict, NULL);
+ // initialize
+ out = outA;
+ state = new GfxState(72, box, 0, gFalse);
+ fontChanged = gFalse;
+ clip = clipNone;
+ ignoreUndef = 0;
+ for (i = 0; i < 6; ++i) {
+ baseMatrix[i] = state->getCTM()[i];
+ }
+
+ // set crop box
+ if (crop) {
+ state->moveTo(cropBox->x1, cropBox->y1);
+ state->lineTo(cropBox->x2, cropBox->y1);
+ state->lineTo(cropBox->x2, cropBox->y2);
+ state->lineTo(cropBox->x1, cropBox->y2);
+ state->closePath();
+ state->clip();
+ out->clip(state);
+ state->clearPath();
+ }
+}
+
+Gfx::~Gfx() {
while (state->hasSaves()) {
state = state->restore();
out->restoreState(state);
}
- out->endPage();
+ if (!subPage) {
+ out->endPage();
+ }
while (res) {
- resPtr = res->getNext();
- delete res;
- res = resPtr;
+ popResources();
}
- if (state)
+ if (state) {
delete state;
+ }
}
void Gfx::display(Object *obj, GBool topLevel) {
@@ -406,7 +485,7 @@ void Gfx::display(Object *obj, GBool topLevel) {
error(-1, "Weird page contents");
return;
}
- parser = new Parser(new Lexer(obj));
+ parser = new Parser(xref, new Lexer(xref, obj));
go(topLevel);
delete parser;
parser = NULL;
@@ -415,11 +494,11 @@ void Gfx::display(Object *obj, GBool topLevel) {
void Gfx::go(GBool topLevel) {
Object obj;
Object args[maxArgs];
- int numCmds, numArgs;
+ int numArgs;
int i;
// scan a sequence of objects
- numCmds = 0;
+ updateLevel = 0;
numArgs = 0;
parser->getObj(&obj);
while (!obj.isEOF()) {
@@ -433,6 +512,7 @@ void Gfx::go(GBool topLevel) {
args[i].print(stdout);
}
printf("\n");
+ fflush(stdout);
}
execOp(&obj, args, numArgs);
obj.free();
@@ -441,9 +521,9 @@ void Gfx::go(GBool topLevel) {
numArgs = 0;
// periodically update display
- if (++numCmds == 200) {
+ if (++updateLevel >= 20000) {
out->dump();
- numCmds = 0;
+ updateLevel = 0;
}
// got an argument - save it
@@ -457,6 +537,7 @@ void Gfx::go(GBool topLevel) {
printf("throwing away arg: ");
obj.print(stdout);
printf("\n");
+ fflush(stdout);
}
obj.free();
}
@@ -476,20 +557,16 @@ void Gfx::go(GBool topLevel) {
args[i].print(stdout);
}
printf("\n");
+ fflush(stdout);
}
for (i = 0; i < numArgs; ++i)
args[i].free();
}
// update display
- if (topLevel && numCmds > 0) {
+ if (topLevel && updateLevel > 0) {
out->dump();
}
-
- // clean up
- if (printCommands) {
- fflush(stdout);
- }
}
void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
@@ -583,12 +660,6 @@ void Gfx::opSave(Object args[], int numArgs) {
void Gfx::opRestore(Object args[], int numArgs) {
state = state->restore();
out->restoreState(state);
-
- // Some PDF producers (Macromedia FreeHand) generate a save (q) and
- // restore (Q) inside a path sequence. The PDF spec seems to imply
- // that this is illegal. Calling clearPath() here implements the
- // behavior apparently expected by this software.
- state->clearPath();
}
void Gfx::opConcat(Object args[], int numArgs) {
@@ -768,7 +839,7 @@ void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
if (colorSpace) {
state->setFillColorSpace(colorSpace);
} else {
- error(getPos(), "Bad color space");
+ error(getPos(), "Bad color space (fill)");
}
for (i = 0; i < gfxColorMaxComps; ++i) {
color.c[i] = 0;
@@ -794,7 +865,7 @@ void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
if (colorSpace) {
state->setStrokeColorSpace(colorSpace);
} else {
- error(getPos(), "Bad color space");
+ error(getPos(), "Bad color space (stroke)");
}
for (i = 0; i < gfxColorMaxComps; ++i) {
color.c[i] = 0;
@@ -970,7 +1041,7 @@ void Gfx::opRectangle(Object args[], int numArgs) {
}
void Gfx::opClosePath(Object args[], int numArgs) {
- if (!state->isPath()) {
+ if (!state->isCurPt()) {
error(getPos(), "No current point in closepath");
return;
}
@@ -1103,23 +1174,26 @@ void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
doEndPath();
}
-void Gfx::opShFill(Object args[], int numArgs) {
-}
-
void Gfx::doPatternFill(GBool eoFill) {
GfxPatternColorSpace *patCS;
GfxPattern *pattern;
GfxTilingPattern *tPat;
GfxColorSpace *cs;
- GfxPath *path;
- GfxSubpath *subpath;
double xMin, yMin, xMax, yMax, x, y, x1, y1;
+ double cxMin, cyMin, cxMax, cyMax;
int xi0, yi0, xi1, yi1, xi, yi;
double *ctm, *btm, *ptm;
- double m[6], ictm[6], m1[6], im[6];
+ double m[6], ictm[6], m1[6], imb[6];
double det;
double xstep, ystep;
- int i, j;
+ int i;
+
+ // 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;
+ }
// get color space
patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
@@ -1160,42 +1234,14 @@ void Gfx::doPatternFill(GBool eoFill) {
m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
- // construct a (current space) -> (pattern space) transform matrix
- det = 1 / (m[0] * m[3] - m[1] * m[2]);
- im[0] = m[3] * det;
- im[1] = -m[1] * det;
- im[2] = -m[2] * det;
- im[3] = m[0] * det;
- im[4] = (m[2] * m[5] - m[3] * m[4]) * det;
- im[5] = (m[1] * m[4] - m[0] * m[5]) * det;
-
- // compute bounding box of current path, in pattern space
- xMin = xMax = yMin = yMax = 0; // make gcc happy
- path = state->getPath();
- for (i = 0; i < path->getNumSubpaths(); ++i) {
- subpath = path->getSubpath(i);
- for (j = 0; j < subpath->getNumPoints(); ++j) {
- x = subpath->getX(j);
- y = subpath->getY(j);
- x1 = x * im[0] + y * im[2] + im[4];
- y1 = x * im[1] + y * im[3] + im[5];
- if (i == 0 && j == 0) {
- xMin = xMax = x1;
- yMin = yMax = y1;
- } else {
- if (x1 < xMin) {
- xMin = x1;
- } else if (x1 > xMax) {
- xMax = x1;
- }
- if (y1 < yMin) {
- yMin = y1;
- } else if (y1 > yMax) {
- yMax = y1;
- }
- }
- }
- }
+ // construct a (base space) -> (pattern space) transform matrix
+ det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
+ imb[0] = m1[3] * det;
+ imb[1] = -m1[1] * det;
+ imb[2] = -m1[2] * det;
+ imb[3] = m1[0] * det;
+ imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
+ imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
// save current graphics state
out->saveState(state);
@@ -1211,6 +1257,7 @@ void Gfx::doPatternFill(GBool eoFill) {
out->updateFillColor(state);
// clip to current path
+ state->clip();
if (eoFill) {
out->eoClip(state);
} else {
@@ -1218,6 +1265,47 @@ void Gfx::doPatternFill(GBool eoFill) {
}
state->clearPath();
+ // transform clip region bbox to pattern space
+ state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
+ xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
+ yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
+ x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
+ y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+ x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
+ y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+ x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
+ y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+
// draw the pattern
//~ this should treat negative steps differently -- start at right/top
//~ edge instead of left/bottom (?)
@@ -1246,12 +1334,485 @@ void Gfx::doPatternFill(GBool eoFill) {
out->restoreState(state);
}
+void Gfx::opShFill(Object args[], int numArgs) {
+ GfxShading *shading;
+ double xMin, yMin, xMax, yMax;
+
+ if (!(shading = res->lookupShading(args[0].getName()))) {
+ return;
+ }
+
+ // save current graphics state
+ out->saveState(state);
+ state = state->save();
+
+ // 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());
+
+ // do shading type-specific operations
+ switch (shading->getType()) {
+ case 2:
+ doAxialShFill((GfxAxialShading *)shading);
+ break;
+ case 3:
+ doRadialShFill((GfxRadialShading *)shading);
+ break;
+ }
+
+ // restore graphics state
+ state = state->restore();
+ out->restoreState(state);
+
+ delete shading;
+}
+
+void Gfx::doAxialShFill(GfxAxialShading *shading) {
+ double xMin, yMin, xMax, yMax;
+ double x0, y0, x1, y1;
+ double dx, dy, mul;
+ double tMin, tMax, t, tx, ty;
+ double s[4], 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;
+
+ // get the clip region bbox
+ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+
+ // compute min and max t values, based on the four corners of the
+ // clip region bbox
+ shading->getCoords(&x0, &y0, &x1, &y1);
+ dx = x1 - x0;
+ dy = y1 - y0;
+ mul = 1 / (dx * dx + dy * dy);
+ tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
+ t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
+ if (t < tMin) {
+ tMin = t;
+ } else if (t > tMax) {
+ tMax = t;
+ }
+ t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
+ if (t < tMin) {
+ tMin = t;
+ } else if (t > tMax) {
+ tMax = t;
+ }
+ t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
+ if (t < tMin) {
+ tMin = t;
+ } else if (t > tMax) {
+ tMax = t;
+ }
+ if (tMin < 0 && !shading->getExtend0()) {
+ tMin = 0;
+ }
+ if (tMax > 1 && !shading->getExtend1()) {
+ tMax = 1;
+ }
+
+ // get the function domain
+ t0 = shading->getDomain0();
+ t1 = shading->getDomain1();
+
+ // Traverse the t axis and do the shading.
+ //
+ // For each point (tx, ty) on the t axis, consider a line through
+ // that point perpendicular to the t axis:
+ //
+ // x(s) = tx + s * -dy --> s = (x - tx) / -dy
+ // y(s) = ty + s * dx --> s = (y - ty) / dx
+ //
+ // Then look at the intersection of this line with the bounding box
+ // (xMin, yMin, xMax, yMax). In the general case, there are four
+ // intersection points:
+ //
+ // s0 = (xMin - tx) / -dy
+ // s1 = (xMax - tx) / -dy
+ // s2 = (yMin - ty) / dx
+ // s3 = (yMax - ty) / dx
+ //
+ // and we want the middle two s values.
+ //
+ // In the case where dx = 0, take s0 and s1; in the case where dy =
+ // 0, take s2 and s3.
+ //
+ // Each filled polygon is bounded by two of these line segments
+ // perpdendicular to the t axis.
+ //
+ // The t axis is bisected into smaller regions until the color
+ // difference across a region is small enough, and then the region
+ // is painted with a single color.
+
+ // set up
+ nComps = shading->getColorSpace()->getNComps();
+ ta[0] = tMin;
+ ta[axialMaxSplits] = tMax;
+ next[0] = axialMaxSplits;
+
+ // compute the color at t = tMin
+ if (tMin < 0) {
+ tt = t0;
+ } else if (tMin > 1) {
+ tt = t1;
+ } else {
+ tt = t0 + (t1 - t0) * tMin;
+ }
+ shading->getColor(tt, &color0);
+
+ // compute the coordinates of the point on the t axis at t = tMin;
+ // then compute the intersection of the perpendicular line with the
+ // bounding box
+ tx = x0 + tMin * dx;
+ ty = y0 + tMin * dy;
+ if (dx == 0 && dy == 0) {
+ sMin = sMax = 0;
+ } if (dx == 0) {
+ sMin = (xMin - tx) / -dy;
+ sMax = (xMax - tx) / -dy;
+ if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+ } else if (dy == 0) {
+ sMin = (yMin - ty) / dx;
+ sMax = (yMax - ty) / dx;
+ if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+ } else {
+ s[0] = (yMin - ty) / dx;
+ s[1] = (yMax - ty) / dx;
+ s[2] = (xMin - tx) / -dy;
+ s[3] = (xMax - tx) / -dy;
+ for (j = 0; j < 3; ++j) {
+ kk = j;
+ for (k = j + 1; k < 4; ++k) {
+ if (s[k] < s[kk]) {
+ kk = k;
+ }
+ }
+ tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
+ }
+ sMin = s[1];
+ sMax = s[2];
+ }
+ ux0 = tx - sMin * dy;
+ uy0 = ty + sMin * dx;
+ vx0 = tx - sMax * dy;
+ vy0 = ty + sMax * dx;
+
+ i = 0;
+ while (i < axialMaxSplits) {
+
+ // bisect until color difference is small enough or we hit the
+ // bisection limit
+ j = next[i];
+ while (j > i + 1) {
+ if (ta[j] < 0) {
+ tt = t0;
+ } else if (ta[j] > 1) {
+ tt = t1;
+ } else {
+ tt = t0 + (t1 - t0) * ta[j];
+ }
+ shading->getColor(tt, &color1);
+ for (k = 0; k < nComps; ++k) {
+ if (fabs(color1.c[k] - color0.c[k]) > axialColorDelta) {
+ break;
+ }
+ }
+ if (k == nComps) {
+ break;
+ }
+ k = (i + j) / 2;
+ ta[k] = 0.5 * (ta[i] + ta[j]);
+ next[i] = k;
+ next[k] = j;
+ j = k;
+ }
+
+ // use the average of the colors of the two sides of the region
+ for (k = 0; k < nComps; ++k) {
+ color0.c[k] = 0.5 * (color0.c[k] + color1.c[k]);
+ }
+
+ // compute the coordinates of the point on the t axis; then
+ // compute the intersection of the perpendicular line with the
+ // bounding box
+ tx = x0 + ta[j] * dx;
+ ty = y0 + ta[j] * dy;
+ if (dx == 0 && dy == 0) {
+ sMin = sMax = 0;
+ } if (dx == 0) {
+ sMin = (xMin - tx) / -dy;
+ sMax = (xMax - tx) / -dy;
+ if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+ } else if (dy == 0) {
+ sMin = (yMin - ty) / dx;
+ sMax = (yMax - ty) / dx;
+ if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+ } else {
+ s[0] = (yMin - ty) / dx;
+ s[1] = (yMax - ty) / dx;
+ s[2] = (xMin - tx) / -dy;
+ s[3] = (xMax - tx) / -dy;
+ for (j = 0; j < 3; ++j) {
+ kk = j;
+ for (k = j + 1; k < 4; ++k) {
+ if (s[k] < s[kk]) {
+ kk = k;
+ }
+ }
+ tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
+ }
+ sMin = s[1];
+ sMax = s[2];
+ }
+ ux1 = tx - sMin * dy;
+ uy1 = ty + sMin * dx;
+ vx1 = tx - sMax * dy;
+ vy1 = ty + sMax * dx;
+
+ // set the color
+ state->setFillColor(&color0);
+ out->updateFillColor(state);
+
+ // fill the region
+ state->moveTo(ux0, uy0);
+ state->lineTo(vx0, vy0);
+ state->lineTo(vx1, vy1);
+ state->lineTo(ux1, uy1);
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+
+ // set up for next region
+ ux0 = ux1;
+ uy0 = uy1;
+ vx0 = vx1;
+ vy0 = vy1;
+ color0 = color1;
+ i = next[i];
+ }
+}
+
+void Gfx::doRadialShFill(GfxRadialShading *shading) {
+ double sMin, sMax, xMin, yMin, xMax, yMax;
+ double x0, y0, r0, x1, y1, r1, t0, t1;
+ int nComps;
+ GfxColor colorA, colorB;
+ double xa, ya, xb, yb, ra, rb;
+ double ta, tb, sa, sb;
+ int ia, ib, k, n;
+ double *ctm;
+ double angle, t;
+
+ // get the shading info
+ shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
+ t0 = shading->getDomain0();
+ t1 = shading->getDomain1();
+ nComps = shading->getColorSpace()->getNComps();
+
+ // compute the (possibly extended) s range
+ sMin = 0;
+ sMax = 1;
+ if (shading->getExtend0()) {
+ if (r0 < r1) {
+ // extend the smaller end
+ sMin = -r0 / (r1 - r0);
+ } else {
+ // extend the larger end
+ //~ this computes the diagonal of the bounding box -- we should
+ //~ really compute the intersection of the moving/expanding
+ //~ circles with each of the four corners and look for the max
+ //~ radius
+ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+ sMin = (sqrt((xMax - xMin) * (xMax - xMin) +
+ (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
+ if (sMin > 0) {
+ sMin = 0;
+ } else if (sMin < -20) {
+ // sanity check
+ sMin = -20;
+ }
+ }
+ }
+ if (shading->getExtend1()) {
+ if (r1 < r0) {
+ // extend the smaller end
+ sMax = -r0 / (r1 - r0);
+ } else if (r1 > r0) {
+ // extend the larger end
+ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+ sMax = (sqrt((xMax - xMin) * (xMax - xMin) +
+ (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
+ if (sMax < 1) {
+ sMin = 1;
+ } else if (sMax > 20) {
+ // sanity check
+ sMax = 20;
+ }
+ }
+ }
+
+ // compute the number of steps into which circles must be divided to
+ // achieve a curve flatness of 0.1 pixel in device space for the
+ // largest circle (note that "device space" is 72 dpi when generating
+ // PostScript, hence the relatively small 0.1 pixel accuracy)
+ ctm = state->getCTM();
+ t = fabs(ctm[0]);
+ if (fabs(ctm[1]) > t) {
+ t = fabs(ctm[1]);
+ }
+ if (fabs(ctm[2]) > t) {
+ t = fabs(ctm[2]);
+ }
+ if (fabs(ctm[3]) > t) {
+ t = fabs(ctm[3]);
+ }
+ if (r0 > r1) {
+ t *= r0;
+ } else {
+ t *= r1;
+ }
+ if (t < 1) {
+ n = 3;
+ } else {
+ n = (int)(M_PI / acos(1 - 0.1 / t));
+ if (n < 3) {
+ n = 3;
+ } else if (n > 200) {
+ n = 200;
+ }
+ }
+
+ // Traverse the t axis and do the shading.
+ //
+ // This generates and fills a series of rings. Each ring is defined
+ // by two circles:
+ // sa, ta, xa, ya, ra, colorA
+ // sb, tb, xb, yb, rb, colorB
+ //
+ // The s/t axis is divided into radialMaxSplits parts; these parts
+ // are combined as much as possible while respecting the
+ // radialColorDelta parameter.
+
+ // setup for the start circle
+ ia = 0;
+ sa = sMin;
+ ta = t0 + sa * (t1 - t0);
+ xa = x0 + sa * (x1 - x0);
+ ya = y0 + sa * (y1 - y0);
+ ra = r0 + sa * (r1 - r0);
+ if (ta < t0) {
+ shading->getColor(t0, &colorA);
+ } else if (ta > t1) {
+ shading->getColor(t1, &colorA);
+ } else {
+ shading->getColor(ta, &colorA);
+ }
+
+ while (ia < radialMaxSplits) {
+
+ // go as far along the t axis (toward t1) as we can, such that the
+ // color difference is within the tolerance (radialColorDelta) --
+ // this uses bisection (between the current value, t, and t1),
+ // limited to radialMaxSplits points along the t axis
+ ib = radialMaxSplits;
+ sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
+ tb = t0 + sb * (t1 - t0);
+ if (tb < t0) {
+ shading->getColor(t0, &colorB);
+ } else if (tb > t1) {
+ shading->getColor(t1, &colorB);
+ } else {
+ shading->getColor(tb, &colorB);
+ }
+ while (ib - ia > 1) {
+ for (k = 0; k < nComps; ++k) {
+ if (fabs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
+ break;
+ }
+ }
+ if (k == nComps) {
+ break;
+ }
+ ib = (ia + ib) / 2;
+ sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
+ tb = t0 + sb * (t1 - t0);
+ if (tb < t0) {
+ shading->getColor(t0, &colorB);
+ } else if (tb > t1) {
+ shading->getColor(t1, &colorB);
+ } else {
+ shading->getColor(tb, &colorB);
+ }
+ }
+
+ // compute center and radius of the circle
+ xb = x0 + sb * (x1 - x0);
+ yb = y0 + sb * (y1 - y0);
+ rb = r0 + sb * (r1 - r0);
+
+ // use the average of the colors at the two circles
+ for (k = 0; k < nComps; ++k) {
+ colorA.c[k] = 0.5 * (colorA.c[k] + colorB.c[k]);
+ }
+ state->setFillColor(&colorA);
+ out->updateFillColor(state);
+
+ // construct path for first circle
+ state->moveTo(xa + ra, ya);
+ for (k = 1; k < n; ++k) {
+ angle = ((double)k / (double)n) * 2 * M_PI;
+ state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
+ }
+ state->closePath();
+
+ // construct and append path for second circle
+ state->moveTo(xb + rb, yb);
+ for (k = 1; k < n; ++k) {
+ angle = ((double)k / (double)n) * 2 * M_PI;
+ state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
+ }
+ state->closePath();
+
+ // fill the ring
+ out->eoFill(state);
+ state->clearPath();
+
+ // step to the next value of t
+ ia = ib;
+ sa = sb;
+ ta = tb;
+ xa = xb;
+ ya = yb;
+ ra = rb;
+ colorA = colorB;
+ }
+}
+
void Gfx::doEndPath() {
- if (state->isPath()) {
- if (clip == clipNormal)
+ if (state->isPath() && clip != clipNone) {
+ state->clip();
+ if (clip == clipNormal) {
out->clip(state);
- else if (clip == clipEO)
+ } else {
out->eoClip(state);
+ }
}
clip = clipNone;
state->clearPath();
@@ -1300,9 +1861,11 @@ void Gfx::opSetFont(Object args[], int numArgs) {
return;
}
if (printCommands) {
- printf(" font: '%s' %g\n",
+ printf(" font: tag=%s name='%s' %g\n",
+ font->getTag()->getCString(),
font->getName() ? font->getName()->getCString() : "???",
args[1].getNum());
+ fflush(stdout);
}
state->setFont(font, args[1].getNum());
fontChanged = gTrue;
@@ -1330,6 +1893,7 @@ void Gfx::opSetWordSpacing(Object args[], int numArgs) {
void Gfx::opSetHorizScaling(Object args[], int numArgs) {
state->setHorizScaling(args[0].getNum());
out->updateHorizScaling(state);
+ fontChanged = gTrue;
}
//------------------------------------------------------------------------
@@ -1422,17 +1986,23 @@ void Gfx::opMoveSetShowText(Object args[], int numArgs) {
void Gfx::opShowSpaceText(Object args[], int numArgs) {
Array *a;
Object obj;
+ int wMode;
int i;
if (!state->getFont()) {
error(getPos(), "No font in show/space");
return;
}
+ wMode = state->getFont()->getWMode();
a = args[0].getArray();
for (i = 0; i < a->getLength(); ++i) {
a->get(i, &obj);
if (obj.isNum()) {
- state->textShift(-obj.getNum() * 0.001 * state->getFontSize());
+ if (wMode) {
+ state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize());
+ } else {
+ state->textShift(-obj.getNum() * 0.001 * state->getFontSize(), 0);
+ }
out->updateTextShift(state, obj.getNum());
} else if (obj.isString()) {
doShowText(obj.getString());
@@ -1445,188 +2015,174 @@ void Gfx::opShowSpaceText(Object args[], int numArgs) {
void Gfx::doShowText(GString *s) {
GfxFont *font;
- GfxFontEncoding16 *enc;
- Guchar *p;
- Guchar c8;
- int c16;
- GString *s16;
- char s16a[2];
- int m, n;
-#if 0 //~type3
- double dx, dy, width, height, w, h, x, y;
+ int wMode;
+ double riseX, riseY;
+ CharCode code;
+ Unicode u[8];
+ double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy;
+ double originX, originY, tOriginX, tOriginY;
double oldCTM[6], newCTM[6];
double *mat;
Object charProc;
+ Dict *resDict;
Parser *oldParser;
- int i;
-#else
- double dx, dy, width, height, w, h, sWidth, sHeight;
-#endif
+ char *p;
+ int len, n, uLen, nChars, nSpaces, i;
if (fontChanged) {
out->updateFont(state);
fontChanged = gFalse;
}
font = state->getFont();
+ wMode = font->getWMode();
- //----- 16-bit font
- if (font->is16Bit()) {
- enc = font->getEncoding16();
- if (out->useDrawChar()) {
- out->beginString(state, s);
- s16 = NULL;
- } else {
- s16 = new GString();
- }
- sWidth = sHeight = 0;
- state->textTransformDelta(0, state->getRise(), &dx, &dy);
- p = (Guchar *)s->getCString();
- n = s->getLength();
- while (n > 0) {
- m = getNextChar16(enc, p, &c16);
- if (enc->wMode == 0) {
- width = state->getFontSize() * state->getHorizScaling() *
- font->getWidth16(c16) +
- state->getCharSpace();
- if (c16 == ' ') {
- width += state->getWordSpace();
- }
- height = 0;
- } else {
- width = 0;
- height = state->getFontSize() * font->getHeight16(c16);
- }
- state->textTransformDelta(width, height, &w, &h);
- if (out->useDrawChar()) {
- out->drawChar16(state, state->getCurX() + dx, state->getCurY() + dy,
- w, h, c16);
- state->textShift(width, height);
- } else {
- s16a[0] = (char)(c16 >> 8);
- s16a[1] = (char)c16;
- s16->append(s16a, 2);
- sWidth += w;
- sHeight += h;
- }
- n -= m;
- p += m;
- }
- if (out->useDrawChar()) {
- out->endString(state);
- } else {
- out->drawString16(state, s16);
- delete s16;
- state->textShift(sWidth, sHeight);
- }
+ if (out->useDrawChar()) {
+ out->beginString(state, s);
+ }
- //----- 8-bit font
- } else {
-#if 0 //~type3
- //~ also check out->renderType3()
- if (font->getType() == fontType3) {
- out->beginString(state, s);
- mat = state->getCTM();
- for (i = 0; i < 6; ++i) {
- oldCTM[i] = mat[i];
+ // handle a Type 3 char
+ if (font->getType() == fontType3 && out->interpretType3Chars()) {
+ mat = state->getCTM();
+ for (i = 0; i < 6; ++i) {
+ oldCTM[i] = mat[i];
+ }
+ mat = state->getTextMat();
+ newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
+ newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
+ newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
+ newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
+ mat = font->getFontMatrix();
+ newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
+ newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
+ newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
+ newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
+ newCTM[0] *= state->getFontSize();
+ 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();
+ oldParser = parser;
+ p = s->getCString();
+ len = s->getLength();
+ while (len > 0) {
+ n = font->getNextChar(p, len, &code,
+ u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
+ &dx, &dy, &originX, &originY);
+ dx = dx * state->getFontSize() + state->getCharSpace();
+ if (n == 1 && *p == ' ') {
+ dx += state->getWordSpace();
}
- mat = state->getTextMat();
- newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
- newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
- newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
- newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
- mat = font->getFontMatrix();
- newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
- newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
- newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
- newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
- newCTM[0] *= state->getFontSize();
- newCTM[3] *= state->getFontSize();
- newCTM[0] *= state->getHorizScaling();
- newCTM[2] *= state->getHorizScaling();
- state->textTransformDelta(0, state->getRise(), &dx, &dy);
- oldParser = parser;
- for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
- c8 = *p;
- font->getCharProc(c8, &charProc);
- state->transform(state->getCurX() + dx, state->getCurY() + dy, &x, &y);
- state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
- //~ out->updateCTM(???)
+ dx *= state->getHorizScaling();
+ dy *= state->getFontSize();
+ state->textTransformDelta(dx, dy, &tdx, &tdy);
+ state->transform(curX + riseX, curY + riseY, &x, &y);
+ out->saveState(state);
+ state = state->save();
+ state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
+ //~ out->updateCTM(???)
+ if (!out->beginType3Char(state, code, u, uLen)) {
+ ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
+ if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
+ pushResources(resDict);
+ }
if (charProc.isStream()) {
display(&charProc, gFalse);
} else {
error(getPos(), "Missing or bad Type3 CharProc entry");
}
- state->setCTM(oldCTM[0], oldCTM[1], oldCTM[2],
- oldCTM[3], oldCTM[4], oldCTM[5]);
- //~ out->updateCTM(???) - use gsave/grestore instead?
+ out->endType3Char(state);
+ if (resDict) {
+ popResources();
+ }
charProc.free();
- width = state->getFontSize() * state->getHorizScaling() *
- font->getWidth(c8) +
- state->getCharSpace();
- if (c8 == ' ') {
- width += state->getWordSpace();
+ }
+ state = state->restore();
+ out->restoreState(state);
+ // GfxState::restore() does *not* restore the current position,
+ // so we track it here with (curX, curY)
+ curX += tdx;
+ curY += tdy;
+ state->moveTo(curX, curY);
+ 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) {
+ n = font->getNextChar(p, len, &code,
+ u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
+ &dx, &dy, &originX, &originY);
+ if (wMode) {
+ dx *= state->getFontSize();
+ dy = dy * state->getFontSize() + state->getCharSpace();
+ if (n == 1 && *p == ' ') {
+ dy += state->getWordSpace();
+ }
+ } else {
+ dx = dx * state->getFontSize() + state->getCharSpace();
+ if (n == 1 && *p == ' ') {
+ dx += state->getWordSpace();
}
- state->textShift(width);
+ dx *= state->getHorizScaling();
+ dy *= state->getFontSize();
}
- parser = oldParser;
- out->endString(state);
- } else
-#endif
- if (out->useDrawChar()) {
- out->beginString(state, s);
- state->textTransformDelta(0, state->getRise(), &dx, &dy);
- for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
- c8 = *p;
- width = state->getFontSize() * state->getHorizScaling() *
- font->getWidth(c8) +
- state->getCharSpace();
- if (c8 == ' ')
- width += state->getWordSpace();
- state->textTransformDelta(width, 0, &w, &h);
- out->drawChar(state, state->getCurX() + dx, state->getCurY() + dy,
- w, h, c8);
- state->textShift(width);
+ state->textTransformDelta(dx, dy, &tdx, &tdy);
+ originX *= state->getFontSize();
+ originY *= state->getFontSize();
+ state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
+ out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
+ tdx, tdy, tOriginX, tOriginY, code, u, uLen);
+ state->shift(tdx, tdy);
+ p += n;
+ len -= n;
+ }
+
+ } else {
+ dx = dy = 0;
+ p = s->getCString();
+ len = s->getLength();
+ nChars = nSpaces = 0;
+ while (len > 0) {
+ n = font->getNextChar(p, len, &code,
+ u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
+ &dx2, &dy2, &originX, &originY);
+ dx += dx2;
+ dy += dy2;
+ if (n == 1 && *p == ' ') {
+ ++nSpaces;
}
- out->endString(state);
+ ++nChars;
+ p += n;
+ len -= n;
+ }
+ if (wMode) {
+ dx *= state->getFontSize();
+ dy = dy * state->getFontSize()
+ + nChars * state->getCharSpace()
+ + nSpaces * state->getWordSpace();
} else {
- out->drawString(state, s);
- width = state->getFontSize() * state->getHorizScaling() *
- font->getWidth(s) +
- s->getLength() * state->getCharSpace();
- for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
- if (*p == ' ')
- width += state->getWordSpace();
- }
- state->textShift(width);
+ dx = dx * state->getFontSize()
+ + nChars * state->getCharSpace()
+ + nSpaces * state->getWordSpace();
+ dx *= state->getHorizScaling();
+ dy *= state->getFontSize();
}
+ state->textTransformDelta(dx, dy, &tdx, &tdy);
+ out->drawString(state, s);
+ state->shift(tdx, tdy);
}
-}
-int Gfx::getNextChar16(GfxFontEncoding16 *enc, Guchar *p, int *c16) {
- int n;
- int code;
- int a, b, m;
-
- n = enc->codeLen[*p];
- if (n == 1) {
- *c16 = enc->map1[*p];
- } else {
- code = (p[0] << 8) + p[1];
- a = 0;
- b = enc->map2Len;
- // invariant: map2[2*a] <= code < map2[2*b]
- while (b - a > 1) {
- m = (a + b) / 2;
- if (enc->map2[2*m] <= code)
- a = m;
- else if (enc->map2[2*m] > code)
- b = m;
- else
- break;
- }
- *c16 = enc->map2[2*a+1] + (code - enc->map2[2*a]);
+ if (out->useDrawChar()) {
+ out->endString(state);
}
- return n;
+
+ updateLevel += 10 * s->getLength();
}
//------------------------------------------------------------------------
@@ -1634,7 +2190,7 @@ int Gfx::getNextChar16(GfxFontEncoding16 *enc, Guchar *p, int *c16) {
//------------------------------------------------------------------------
void Gfx::opXObject(Object args[], int numArgs) {
- Object obj1, obj2, refObj;
+ Object obj1, obj2, obj3, refObj;
#if OPI_SUPPORT
Object opiDict;
#endif
@@ -1660,6 +2216,10 @@ void Gfx::opXObject(Object args[], int numArgs) {
refObj.free();
} else if (obj2.isName("Form")) {
doForm(&obj1);
+ } else if (obj2.isName("PS")) {
+ obj1.streamGetDict()->lookup("Level1", &obj3);
+ out->psXObject(obj1.getStream(),
+ obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
} else if (obj2.isName()) {
error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
} else {
@@ -1677,13 +2237,17 @@ void Gfx::opXObject(Object args[], int numArgs) {
void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
Dict *dict;
- Object obj1, obj2;
int width, height;
int bits;
GBool mask;
+ GBool invert;
GfxColorSpace *colorSpace;
GfxImageColorMap *colorMap;
- GBool invert;
+ Object maskObj;
+ GBool haveMask;
+ int maskColors[2*gfxColorMaxComps];
+ Object obj1, obj2;
+ int i;
// get stream dict
dict = str->getDict();
@@ -1791,11 +2355,30 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
goto err1;
}
+ // get the mask
+ haveMask = gFalse;
+ dict->lookup("Mask", &maskObj);
+ if (maskObj.isArray()) {
+ for (i = 0; i < maskObj.arrayGetLength(); ++i) {
+ maskObj.arrayGet(i, &obj1);
+ maskColors[i] = obj1.getInt();
+ obj1.free();
+ }
+ haveMask = gTrue;
+ }
+
// draw it
- out->drawImage(state, ref, str, width, height, colorMap, inlineImg);
+ out->drawImage(state, ref, str, width, height, colorMap,
+ haveMask ? maskColors : (int *)NULL, inlineImg);
delete colorMap;
- str->close();
+
+ maskObj.free();
+ }
+
+ if ((i = width * height) > 1000) {
+ i = 1000;
}
+ updateLevel += i;
return;
@@ -1856,7 +2439,7 @@ void Gfx::doForm(Object *str) {
// get resources
dict->lookup("Resources", &resObj);
- resDict = resObj.isDict() ? resObj.getDict() : NULL;
+ resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
// draw it
doForm1(str, resDict, m, bbox);
@@ -1864,19 +2447,22 @@ void Gfx::doForm(Object *str) {
resObj.free();
}
-void Gfx::doWidgetForm(Object *str, double xMin, double yMin,
- double xMax, double yMax) {
+void Gfx::doAnnot(Object *str, double xMin, double yMin,
+ double xMax, double yMax) {
Dict *dict, *resDict;
Object matrixObj, bboxObj, resObj;
Object obj1;
- double m[6], bbox[6];
- double sx, sy;
+ double m[6], bbox[6], ictm[6];
+ double *ctm;
+ double formX0, formY0, formX1, formY1;
+ double annotX0, annotY0, annotX1, annotY1;
+ double det, x, y, sx, sy;
int i;
// get stream dict
dict = str->streamGetDict();
- // get bounding box
+ // get the form bounding box
dict->lookup("BBox", &bboxObj);
if (!bboxObj.isArray()) {
bboxObj.free();
@@ -1890,7 +2476,7 @@ void Gfx::doWidgetForm(Object *str, double xMin, double yMin,
}
bboxObj.free();
- // get matrix
+ // get the form matrix
dict->lookup("Matrix", &matrixObj);
if (matrixObj.isArray()) {
for (i = 0; i < 6; ++i) {
@@ -1905,20 +2491,68 @@ void Gfx::doWidgetForm(Object *str, double xMin, double yMin,
}
matrixObj.free();
- // scale form bbox to widget rectangle
- sx = fabs((xMax - xMin) / (bbox[2] - bbox[0]));
- sy = fabs((yMax - yMin) / (bbox[3] - bbox[1]));
- m[0] *= sx; m[1] *= sy;
- m[2] *= sx; m[3] *= sy;
- m[4] *= sx; m[5] *= sy;
+ // transform the form bbox from form space to user space
+ formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
+ formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
+ formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
+ formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
+
+ // transform the annotation bbox from default user space to user
+ // space: (bbox * baseMatrix) * iCTM
+ ctm = state->getCTM();
+ det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+ ictm[0] = ctm[3] * det;
+ ictm[1] = -ctm[1] * det;
+ ictm[2] = -ctm[2] * det;
+ ictm[3] = ctm[0] * det;
+ ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
+ ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+ x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
+ y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
+ annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
+ annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
+ x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
+ y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
+ annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
+ annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
+
+ // swap min/max coords
+ if (formX0 > formX1) {
+ x = formX0; formX0 = formX1; formX1 = x;
+ }
+ if (formY0 > formY1) {
+ y = formY0; formY0 = formY1; formY1 = y;
+ }
+ if (annotX0 > annotX1) {
+ x = annotX0; annotX0 = annotX1; annotX1 = x;
+ }
+ if (annotY0 > annotY1) {
+ y = annotY0; annotY0 = annotY1; annotY1 = y;
+ }
- // translate to widget rectangle
- m[4] += xMin;
- m[5] += yMin;
+ // scale the form to fit the annotation bbox
+ if (formX1 == formX0) {
+ // this shouldn't happen
+ sx = 1;
+ } else {
+ sx = (annotX1 - annotX0) / (formX1 - formX0);
+ }
+ if (formY1 == formY0) {
+ // this shouldn't happen
+ sy = 1;
+ } else {
+ sy = (annotY1 - annotY0) / (formY1 - formY0);
+ }
+ m[0] *= sx;
+ m[2] *= sx;
+ m[4] = (m[4] - formX0) * sx + annotX0;
+ m[1] *= sy;
+ m[3] *= sy;
+ m[5] = (m[5] - formY0) * sy + annotY0;
// get resources
dict->lookup("Resources", &resObj);
- resDict = resObj.isDict() ? resObj.getDict() : NULL;
+ resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
// draw it
doForm1(str, resDict, m, bbox);
@@ -1930,11 +2564,10 @@ void Gfx::doWidgetForm(Object *str, double xMin, double yMin,
void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
Parser *oldParser;
double oldBaseMatrix[6];
- GfxResources *resPtr;
int i;
// push new resources on stack
- res = new GfxResources(resDict, res);
+ pushResources(resDict);
// save current graphics state
out->saveState(state);
@@ -1961,6 +2594,7 @@ void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
state->lineTo(bbox[2], bbox[3]);
state->lineTo(bbox[0], bbox[3]);
state->closePath();
+ state->clip();
out->clip(state);
state->clearPath();
@@ -1980,11 +2614,21 @@ void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
out->restoreState(state);
// pop resource stack
+ popResources();
+
+ return;
+}
+
+void Gfx::pushResources(Dict *resDict) {
+ res = new GfxResources(xref, resDict, res);
+}
+
+void Gfx::popResources() {
+ GfxResources *resPtr;
+
resPtr = res->getNext();
delete res;
res = resPtr;
-
- return;
}
//------------------------------------------------------------------------
@@ -2020,25 +2664,30 @@ Stream *Gfx::buildImageStream() {
Stream *str;
// build dictionary
- dict.initDict();
+ dict.initDict(xref);
parser->getObj(&obj);
while (!obj.isCmd("ID") && !obj.isEOF()) {
if (!obj.isName()) {
error(getPos(), "Inline image dictionary key must be a name object");
obj.free();
- parser->getObj(&obj);
} else {
key = copyString(obj.getName());
obj.free();
parser->getObj(&obj);
- if (obj.isEOF() || obj.isError())
+ if (obj.isEOF() || obj.isError()) {
+ gfree(key);
break;
+ }
dict.dictAdd(key, &obj);
}
parser->getObj(&obj);
}
- if (obj.isEOF())
+ if (obj.isEOF()) {
error(getPos(), "End of file in inline image");
+ obj.free();
+ dict.free();
+ return NULL;
+ }
obj.free();
// make stream
@@ -2061,11 +2710,13 @@ void Gfx::opEndImage(Object args[], int numArgs) {
//------------------------------------------------------------------------
void Gfx::opSetCharWidth(Object args[], int numArgs) {
- error(getPos(), "Encountered 'd0' operator in content stream");
+ out->type3D0(state, args[0].getNum(), args[1].getNum());
}
void Gfx::opSetCacheDevice(Object args[], int numArgs) {
- error(getPos(), "Encountered 'd1' operator in content stream");
+ out->type3D1(state, args[0].getNum(), args[1].getNum(),
+ args[2].getNum(), args[3].getNum(),
+ args[4].getNum(), args[5].getNum());
}
//------------------------------------------------------------------------
@@ -2091,6 +2742,7 @@ void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
if (numArgs == 2)
args[2].print(stdout);
printf("\n");
+ fflush(stdout);
}
}
@@ -2103,5 +2755,6 @@ void Gfx::opMarkPoint(Object args[], int numArgs) {
if (numArgs == 2)
args[2].print(stdout);
printf("\n");
+ fflush(stdout);
}
}