home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Enigma Amiga Life 110
/
EnigmaAmiga110CD.iso
/
indispensabili
/
utility
/
apdf
/
xpdf-0.80
/
xpdf
/
gfx.cc
< prev
next >
Wrap
C/C++ Source or Header
|
1999-04-27
|
38KB
|
1,574 lines
//========================================================================
//
// Gfx.cc
//
// Copyright 1996 Derek B. Noonburg
//
//========================================================================
#ifdef __GNUC__
#pragma implementation
#endif
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include "gmem.h"
#include "Object.h"
#include "Array.h"
#include "Dict.h"
#include "Stream.h"
#include "Lexer.h"
#include "Parser.h"
#include "GfxFont.h"
#include "GfxState.h"
#include "OutputDev.h"
#include "Params.h"
#include "Error.h"
#include "Gfx.h"
//------------------------------------------------------------------------
// Operator table
//------------------------------------------------------------------------
Operator Gfx::opTab[] = {
{"\"", 3, {tchkNum, tchkNum, tchkString},
&Gfx::opMoveSetShowText},
{"'", 1, {tchkString},
&Gfx::opMoveShowText},
{"B", 0, {tchkNone},
&Gfx::opFillStroke},
{"B*", 0, {tchkNone},
&Gfx::opEOFillStroke},
{"BDC", 2, {tchkName, tchkProps},
&Gfx::opBeginMarkedContent},
{"BI", 0, {tchkNone},
&Gfx::opBeginImage},
{"BMC", 1, {tchkName},
&Gfx::opBeginMarkedContent},
{"BT", 0, {tchkNone},
&Gfx::opBeginText},
{"BX", 0, {tchkNone},
&Gfx::opBeginIgnoreUndef},
{"CS", 1, {tchkName},
&Gfx::opSetStrokeColorSpace},
{"DP", 2, {tchkName, tchkProps},
&Gfx::opMarkPoint},
{"Do", 1, {tchkName},
&Gfx::opXObject},
{"EI", 0, {tchkNone},
&Gfx::opEndImage},
{"EMC", 0, {tchkNone},
&Gfx::opEndMarkedContent},
{"ET", 0, {tchkNone},
&Gfx::opEndText},
{"EX", 0, {tchkNone},
&Gfx::opEndIgnoreUndef},
{"F", 0, {tchkNone},
&Gfx::opFill},
{"G", 1, {tchkNum},
&Gfx::opSetStrokeGray},
{"ID", 0, {tchkNone},
&Gfx::opImageData},
{"J", 1, {tchkInt},
&Gfx::opSetLineCap},
{"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
&Gfx::opSetStrokeCMYKColor},
{"M", 1, {tchkNum},
&Gfx::opSetMiterLimit},
{"MP", 1, {tchkName},
&Gfx::opMarkPoint},
{"Q", 0, {tchkNone},
&Gfx::opRestore},
{"RG", 3, {tchkNum, tchkNum, tchkNum},
&Gfx::opSetStrokeRGBColor},
{"S", 0, {tchkNone},
&Gfx::opStroke},
{"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
&Gfx::opSetStrokeColor},
{"SCN", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
tchkSCN},
&Gfx::opSetStrokeColorN},
{"T*", 0, {tchkNone},
&Gfx::opTextNextLine},
{"TD", 2, {tchkNum, tchkNum},
&Gfx::opTextMoveSet},
{"TJ", 1, {tchkArray},
&Gfx::opShowSpaceText},
{"TL", 1, {tchkNum},
&Gfx::opSetTextLeading},
{"Tc", 1, {tchkNum},
&Gfx::opSetCharSpacing},
{"Td", 2, {tchkNum, tchkNum},
&Gfx::opTextMove},
{"Tf", 2, {tchkName, tchkNum},
&Gfx::opSetFont},
{"Tj", 1, {tchkString},
&Gfx::opShowText},
{"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
tchkNum, tchkNum},
&Gfx::opSetTextMatrix},
{"Tr", 1, {tchkInt},
&Gfx::opSetTextRender},
{"Ts", 1, {tchkNum},
&Gfx::opSetTextRise},
{"Tw", 1, {tchkNum},
&Gfx::opSetWordSpacing},
{"Tz", 1, {tchkNum},
&Gfx::opSetHorizScaling},
{"W", 0, {tchkNone},
&Gfx::opClip},
{"W*", 0, {tchkNone},
&Gfx::opEOClip},
{"b", 0, {tchkNone},
&Gfx::opCloseFillStroke},
{"b*", 0, {tchkNone},
&Gfx::opCloseEOFillStroke},
{"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
tchkNum, tchkNum},
&Gfx::opCurveTo},
{"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
tchkNum, tchkNum},
&Gfx::opConcat},
{"cs", 1, {tchkName},
&Gfx::opSetFillColorSpace},
{"d", 2, {tchkArray, tchkNum},
&Gfx::opSetDash},
{"d0", 2, {tchkNum, tchkNum},
&Gfx::opSetCharWidth},
{"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
tchkNum, tchkNum},
&Gfx::opSetCacheDevice},
{"f", 0, {tchkNone},
&Gfx::opFill},
{"f*", 0, {tchkNone},
&Gfx::opEOFill},
{"g", 1, {tchkNum},
&Gfx::opSetFillGray},
{"gs", 1, {tchkName},
&Gfx::opSetExtGState},
{"h", 0, {tchkNone},
&Gfx::opClosePath},
{"i", 1, {tchkNum},
&Gfx::opSetFlat},
{"j", 1, {tchkInt},
&Gfx::opSetLineJoin},
{"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
&Gfx::opSetFillCMYKColor},
{"l", 2, {tchkNum, tchkNum},
&Gfx::opLineTo},
{"m", 2, {tchkNum, tchkNum},
&Gfx::opMoveTo},
{"n", 0, {tchkNone},
&Gfx::opEndPath},
{"q", 0, {tchkNone},
&Gfx::opSave},
{"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
&Gfx::opRectangle},
{"rg", 3, {tchkNum, tchkNum, tchkNum},
&Gfx::opSetFillRGBColor},
{"s", 0, {tchkNone},
&Gfx::opCloseStroke},
{"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
&Gfx::opSetFillColor},
{"scn", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
tchkSCN},
&Gfx::opSetFillColorN},
{"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
&Gfx::opCurveTo1},
{"w", 1, {tchkNum},
&Gfx::opSetLineWidth},
{"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
&Gfx::opCurveTo2},
};
#define numOps (sizeof(opTab) / sizeof(Operator))
//------------------------------------------------------------------------
// Gfx
//------------------------------------------------------------------------
Gfx::Gfx(OutputDev *out1, int pageNum, Dict *resDict,
int dpi, double x1, double y1, double x2, double y2, GBool crop,
double cropX1, double cropY1, double cropX2, double cropY2,
int rotate) {
Object obj1;
// start the resource stack
res = new GfxResources(NULL);
// build font dictionary
res->fonts = NULL;
if (resDict) {
resDict->lookup("Font", &obj1);
if (obj1.isDict())
res->fonts = new GfxFontDict(obj1.getDict());
obj1.free();
}
// get XObject dictionary
if (resDict)
resDict->lookup("XObject", &res->xObjDict);
else
res->xObjDict.initNull();
// get colorspace dictionary
if (resDict)
resDict->lookup("ColorSpace", &res->colorSpaceDict);
else
res->colorSpaceDict.initNull();
// initialize
out = out1;
state = new GfxState(dpi, x1, y1, x2, y2, rotate, out->upsideDown());
fontChanged = gFalse;
clip = clipNone;
ignoreUndef = 0;
out->startPage(pageNum, state);
out->setDefaultCTM(state->getCTM());
out->updateAll(state);
// set crop box
if (crop) {
state->moveTo(cropX1, cropY1);
state->lineTo(cropX2, cropY1);
state->lineTo(cropX2, cropY2);
state->lineTo(cropX1, cropY2);
state->closePath();
out->clip(state);
state->clearPath();
}
}
Gfx::~Gfx() {
GfxResources *resPtr;
while (state->hasSaves()) {
state = state->restore();
out->restoreState(state);
}
out->endPage();
while (res) {
resPtr = res->next;
delete res;
res = resPtr;
}
if (state)
delete state;
}
GfxResources::~GfxResources() {
if (fonts)
delete fonts;
xObjDict.free();
colorSpaceDict.free();
}
void Gfx::display(Object *obj) {
Object obj2;
int i;
if (obj->isArray()) {
for (i = 0; i < obj->arrayGetLength(); ++i) {
obj->arrayGet(i, &obj2);
if (!obj2.isStream()) {
error(-1, "Weird page contents");
obj2.free();
return;
}
obj2.free();
}
} else if (!obj->isStream()) {
error(-1, "Weird page contents");
return;
}
parser = new Parser(new Lexer(obj));
go();
}
void Gfx::go() {
Object obj;
Object args[maxArgs];
int numCmds, numArgs;
int i;
// scan a sequence of objects
numCmds = 0;
numArgs = 0;
parser->getObj(&obj);
while (!obj.isEOF()) {
// got a command - execute it
if (obj.isCmd()) {
if (printCommands) {
obj.print(stdout);
for (i = 0; i < numArgs; ++i) {
printf(" ");
args[i].print(stdout);
}
printf("\n");
}
execOp(&obj, args, numArgs);
obj.free();
for (i = 0; i < numArgs; ++i)
args[i].free();
numArgs = 0;
// periodically update display
if (++numCmds == 200) {
out->dump();
numCmds = 0;
}
// got an argument - save it
} else if (numArgs < maxArgs) {
args[numArgs++] = obj;
// too many arguments - something is wrong
} else {
error(getPos(), "Too many args in content stream");
if (printCommands) {
printf("throwing away arg: ");
obj.print(stdout);
printf("\n");
}
obj.free();
}
// grab the next object
parser->getObj(&obj);
}
obj.free();
// args at end with no command
if (numArgs > 0) {
error(getPos(), "Leftover args in content stream");
if (printCommands) {
printf("%d leftovers:", numArgs);
for (i = 0; i < numArgs; ++i) {
printf(" ");
args[i].print(stdout);
}
printf("\n");
}
for (i = 0; i < numArgs; ++i)
args[i].free();
}
// update display
if (numCmds > 0)
out->dump();
// clean up
if (parser)
delete parser;
if (printCommands)
fflush(stdout);
}
void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
Operator *op;
char *name;
int i;
// find operator
name = cmd->getName();
if (!(op = findOp(name))) {
if (ignoreUndef == 0)
error(getPos(), "Unknown operator '%s'", name);
return;
}
// type check args
if (op->numArgs >= 0) {
if (numArgs != op->numArgs) {
error(getPos(), "Wrong number (%d) of args to '%s' operator",
numArgs, name);
return;
}
} else {
if (numArgs > -op->numArgs) {
error(getPos(), "Too many (%d) args to '%s' operator",
numArgs, name);
return;
}
}
for (i = 0; i < numArgs; ++i) {
if (!checkArg(&args[i], op->tchk[i])) {
error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
i, name, args[i].getTypeName());
return;
}
}
// do it
(this->*op->func)(args, numArgs);
}
Operator *Gfx::findOp(char *name) {
int a, b, m, cmp;
a = -1;
b = numOps;
// invariant: opTab[a] < name < opTab[b]
while (b - a > 1) {
m = (a + b) / 2;
cmp = strcmp(opTab[m].name, name);
if (cmp < 0)
a = m;
else if (cmp > 0)
b = m;
else
a = b = m;
}
if (cmp != 0)
return NULL;
return &opTab[a];
}
GBool Gfx::checkArg(Object *arg, TchkType type) {
switch (type) {
case tchkBool: return arg->isBool();
case tchkInt: return arg->isInt();
case tchkNum: return arg->isNum();
case tchkString: return arg->isString();
case tchkName: return arg->isName();
case tchkArray: return arg->isArray();
case tchkProps: return arg->isDict() || arg->isName();
case tchkSCN: return arg->isNum() || arg->isName();
case tchkNone: return gFalse;
}
return gFalse;
}
int Gfx::getPos() {
return parser->getPos();
}
GfxFont *Gfx::lookupFont(char *name) {
GfxFont *font;
GfxResources *resPtr;
for (resPtr = res; resPtr; resPtr = resPtr->next) {
if (resPtr->fonts) {
if ((font = resPtr->fonts->lookup(name)))
return font;
}
}
error(getPos(), "unknown font tag '%s'", name);
return NULL;
}
GBool Gfx::lookupXObject(char *name, Object *obj) {
GfxResources *resPtr;
for (resPtr = res; resPtr; resPtr = resPtr->next) {
if (resPtr->xObjDict.isDict()) {
if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
return gTrue;
obj->free();
}
}
error(getPos(), "XObject '%s' is unknown", name);
return gFalse;
}
void Gfx::lookupColorSpace(char *name, Object *obj) {
GfxResources *resPtr;
for (resPtr = res; resPtr; resPtr = resPtr->next) {
if (resPtr->colorSpaceDict.isDict()) {
if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull())
return;
obj->free();
}
}
obj->initNull();
}
//------------------------------------------------------------------------
// graphics state operators
//------------------------------------------------------------------------
void Gfx::opSave(Object args[], int numArgs) {
out->saveState(state);
state = state->save();
}
void Gfx::opRestore(Object args[], int numArgs) {
state = state->restore();
out->restoreState(state);
}
void Gfx::opConcat(Object args[], int numArgs) {
state->concatCTM(args[0].getNum(), args[1].getNum(),
args[2].getNum(), args[3].getNum(),
args[4].getNum(), args[5].getNum());
out->updateCTM(state, args[0].getNum(), args[1].getNum(),
args[2].getNum(), args[3].getNum(),
args[4].getNum(), args[5].getNum());
fontChanged = gTrue;
}
void Gfx::opSetDash(Object args[], int numArgs) {
Array *a;
int length;
Object obj;
double *dash;
int i;
a = args[0].getArray();
length = a->getLength();
if (length == 0) {
dash = NULL;
} else {
dash = (double *)gmalloc(length * sizeof(double));
for (i = 0; i < length; ++i) {
dash[i] = a->get(i, &obj)->getNum();
obj.free();
}
}
state->setLineDash(dash, length, args[1].getNum());
out->updateLineDash(state);
}
void Gfx::opSetFlat(Object args[], int numArgs) {
state->setFlatness((int)args[0].getNum());
out->updateFlatness(state);
}
void Gfx::opSetLineJoin(Object args[], int numArgs) {
state->setLineJoin(args[0].getInt());
out->updateLineJoin(state);
}
void Gfx::opSetLineCap(Object args[], int numArgs) {
state->setLineCap(args[0].getInt());
out->updateLineCap(state);
}
void Gfx::opSetMiterLimit(Object args[], int numArgs) {
state->setMiterLimit(args[0].getNum());
out->updateMiterLimit(state);
}
void Gfx::opSetLineWidth(Object args[], int numArgs) {
state->setLineWidth(args[0].getNum());
out->updateLineWidth(state);
}
void Gfx::opSetExtGState(Object args[], int numArgs) {
}
//------------------------------------------------------------------------
// color operators
//------------------------------------------------------------------------
void Gfx::opSetFillGray(Object args[], int numArgs) {
state->setFillColorSpace(new GfxColorSpace(colorGray));
state->setFillGray(args[0].getNum());
out->updateFillColor(state);
}
void Gfx::opSetStrokeGray(Object args[], int numArgs) {
state->setStrokeColorSpace(new GfxColorSpace(colorGray));
state->setStrokeGray(args[0].getNum());
out->updateStrokeColor(state);
}
void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
state->setFillColorSpace(new GfxColorSpace(colorCMYK));
state->setFillCMYK(args[0].getNum(), args[1].getNum(),
args[2].getNum(), args[3].getNum());
out->updateFillColor(state);
}
void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
state->setStrokeColorSpace(new GfxColorSpace(colorCMYK));
state->setStrokeCMYK(args[0].getNum(), args[1].getNum(),
args[2].getNum(), args[3].getNum());
out->updateStrokeColor(state);
}
void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
state->setFillColorSpace(new GfxColorSpace(colorRGB));
state->setFillRGB(args[0].getNum(), args[1].getNum(), args[2].getNum());
out->updateFillColor(state);
}
void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
state->setStrokeColorSpace(new GfxColorSpace(colorRGB));
state->setStrokeRGB(args[0].getNum(), args[1].getNum(), args[2].getNum());
out->updateStrokeColor(state);
}
void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
Object obj;
GfxColorSpace *colorSpace;
double x[4];
lookupColorSpace(args[0].getName(), &obj);
if (obj.isNull())
colorSpace = new GfxColorSpace(&args[0]);
else
colorSpace = new GfxColorSpace(&obj);
obj.free();
if (colorSpace->isOk()) {
state->setFillColorSpace(colorSpace);
} else {
delete colorSpace;
error(getPos(), "Bad colorspace");
}
x[0] = x[1] = x[2] = x[3] = 0;
state->setFillColor(x);
out->updateFillColor(state);
}
void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
Object obj;
GfxColorSpace *colorSpace;
double x[4];
lookupColorSpace(args[0].getName(), &obj);
if (obj.isNull())
colorSpace = new GfxColorSpace(&args[0]);
else
colorSpace = new GfxColorSpace(&obj);
obj.free();
if (colorSpace->isOk()) {
state->setStrokeColorSpace(colorSpace);
} else {
delete colorSpace;
error(getPos(), "Bad colorspace");
}
x[0] = x[1] = x[2] = x[3] = 0;
state->setStrokeColor(x);
out->updateStrokeColor(state);
}
void Gfx::opSetFillColor(Object args[], int numArgs) {
double x[4];
int i;
x[0] = x[1] = x[2] = x[3] = 0;
for (i = 0; i < numArgs; ++i)
x[i] = args[i].getNum();
state->setFillColor(x);
out->updateFillColor(state);
}
void Gfx::opSetStrokeColor(Object args[], int numArgs) {
double x[4];
int i;
x[0] = x[1] = x[2] = x[3] = 0;
for (i = 0; i < numArgs; ++i)
x[i] = args[i].getNum();
state->setStrokeColor(x);
out->updateStrokeColor(state);
}
void Gfx::opSetFillColorN(Object args[], int numArgs) {
double x[4];
int i;
x[0] = x[1] = x[2] = x[3] = 0;
for (i = 0; i < numArgs && i < 4; ++i) {
if (args[i].isNum())
x[i] = args[i].getNum();
else
break;
}
state->setFillColor(x);
out->updateFillColor(state);
}
void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
double x[4];
int i;
x[0] = x[1] = x[2] = x[3] = 0;
for (i = 0; i < numArgs && i < 4; ++i) {
if (args[i].isNum())
x[i] = args[i].getNum();
else
break;
}
state->setStrokeColor(x);
out->updateStrokeColor(state);
}
//------------------------------------------------------------------------
// path segment operators
//------------------------------------------------------------------------
void Gfx::opMoveTo(Object args[], int numArgs) {
state->moveTo(args[0].getNum(), args[1].getNum());
}
void Gfx::opLineTo(Object args[], int numArgs) {
if (!state->isCurPt()) {
error(getPos(), "No current point in lineto");
return;
}
state->lineTo(args[0].getNum(), args[1].getNum());
}
void Gfx::opCurveTo(Object args[], int numArgs) {
double x1, y1, x2, y2, x3, y3;
if (!state->isCurPt()) {
error(getPos(), "No current point in curveto");
return;
}
x1 = args[0].getNum();
y1 = args[1].getNum();
x2 = args[2].getNum();
y2 = args[3].getNum();
x3 = args[4].getNum();
y3 = args[5].getNum();
state->curveTo(x1, y1, x2, y2, x3, y3);
}
void Gfx::opCurveTo1(Object args[], int numArgs) {
double x1, y1, x2, y2, x3, y3;
if (!state->isCurPt()) {
error(getPos(), "No current point in curveto1");
return;
}
x1 = state->getCurX();
y1 = state->getCurY();
x2 = args[0].getNum();
y2 = args[1].getNum();
x3 = args[2].getNum();
y3 = args[3].getNum();
state->curveTo(x1, y1, x2, y2, x3, y3);
}
void Gfx::opCurveTo2(Object args[], int numArgs) {
double x1, y1, x2, y2, x3, y3;
if (!state->isCurPt()) {
error(getPos(), "No current point in curveto2");
return;
}
x1 = args[0].getNum();
y1 = args[1].getNum();
x2 = args[2].getNum();
y2 = args[3].getNum();
x3 = x2;
y3 = y2;
state->curveTo(x1, y1, x2, y2, x3, y3);
}
void Gfx::opRectangle(Object args[], int numArgs) {
double x, y, w, h;
x = args[0].getNum();
y = args[1].getNum();
w = args[2].getNum();
h = args[3].getNum();
state->moveTo(x, y);
state->lineTo(x + w, y);
state->lineTo(x + w, y + h);
state->lineTo(x, y + h);
state->closePath();
}
void Gfx::opClosePath(Object args[], int numArgs) {
if (!state->isPath()) {
error(getPos(), "No current point in closepath");
return;
}
state->closePath();
}
//------------------------------------------------------------------------
// path painting operators
//------------------------------------------------------------------------
void Gfx::opEndPath(Object args[], int numArgs) {
doEndPath();
}
void Gfx::opStroke(Object args[], int numArgs) {
if (!state->isCurPt()) {
//error(getPos(), "No path in stroke");
return;
}
if (state->isPath())
out->stroke(state);
doEndPath();
}
void Gfx::opCloseStroke(Object args[], int numArgs) {
if (!state->isCurPt()) {
//error(getPos(), "No path in closepath/stroke");
return;
}
if (state->isPath()) {
state->closePath();
out->stroke(state);
}
doEndPath();
}
void Gfx::opFill(Object args[], int numArgs) {
if (!state->isCurPt()) {
//error(getPos(), "No path in fill");
return;
}
if (state->isPath())
out->fill(state);
doEndPath();
}
void Gfx::opEOFill(Object args[], int numArgs) {
if (!state->isCurPt()) {
//error(getPos(), "No path in eofill");
return;
}
if (state->isPath())
out->eoFill(state);
doEndPath();
}
void Gfx::opFillStroke(Object args[], int numArgs) {
if (!state->isCurPt()) {
//error(getPos(), "No path in fill/stroke");
return;
}
if (state->isPath()) {
out->fill(state);
out->stroke(state);
}
doEndPath();
}
void Gfx::opCloseFillStroke(Object args[], int numArgs) {
if (!state->isCurPt()) {
//error(getPos(), "No path in closepath/fill/stroke");
return;
}
if (state->isPath()) {
state->closePath();
out->fill(state);
out->stroke(state);
}
doEndPath();
}
void Gfx::opEOFillStroke(Object args[], int numArgs) {
if (!state->isCurPt()) {
//error(getPos(), "No path in eofill/stroke");
return;
}
if (state->isPath()) {
out->eoFill(state);
out->stroke(state);
}
doEndPath();
}
void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
if (!state->isCurPt()) {
//error(getPos(), "No path in closepath/eofill/stroke");
return;
}
if (state->isPath()) {
state->closePath();
out->eoFill(state);
out->stroke(state);
}
doEndPath();
}
void Gfx::doEndPath() {
if (state->isPath()) {
if (clip == clipNormal)
out->clip(state);
else if (clip == clipEO)
out->eoClip(state);
}
clip = clipNone;
state->clearPath();
}
//------------------------------------------------------------------------
// path clipping operators
//------------------------------------------------------------------------
void Gfx::opClip(Object args[], int numArgs) {
clip = clipNormal;
}
void Gfx::opEOClip(Object args[], int numArgs) {
clip = clipEO;
}
//------------------------------------------------------------------------
// text object operators
//------------------------------------------------------------------------
void Gfx::opBeginText(Object args[], int numArgs) {
state->setTextMat(1, 0, 0, 1, 0, 0);
state->textMoveTo(0, 0);
out->updateTextMat(state);
out->updateTextPos(state);
fontChanged = gTrue;
}
void Gfx::opEndText(Object args[], int numArgs) {
}
//------------------------------------------------------------------------
// text state operators
//------------------------------------------------------------------------
void Gfx::opSetCharSpacing(Object args[], int numArgs) {
state->setCharSpace(args[0].getNum());
out->updateCharSpace(state);
}
void Gfx::opSetFont(Object args[], int numArgs) {
GfxFont *font;
if (!(font = lookupFont(args[0].getName())))
return;
if (printCommands) {
printf(" font: '%s' %g\n",
font->getName() ? font->getName()->getCString() : "???",
args[1].getNum());
}
state->setFont(font, args[1].getNum());
fontChanged = gTrue;
}
void Gfx::opSetTextLeading(Object args[], int numArgs) {
state->setLeading(args[0].getNum());
}
void Gfx::opSetTextRender(Object args[], int numArgs) {
state->setRender(args[0].getInt());
out->updateRender(state);
}
void Gfx::opSetTextRise(Object args[], int numArgs) {
state->setRise(args[0].getNum());
out->updateRise(state);
}
void Gfx::opSetWordSpacing(Object args[], int numArgs) {
state->setWordSpace(args[0].getNum());
out->updateWordSpace(state);
}
void Gfx::opSetHorizScaling(Object args[], int numArgs) {
state->setHorizScaling(args[0].getNum());
out->updateHorizScaling(state);
}
//------------------------------------------------------------------------
// text positioning operators
//------------------------------------------------------------------------
void Gfx::opTextMove(Object args[], int numArgs) {
double tx, ty;
tx = state->getLineX() + args[0].getNum();
ty = state->getLineY() + args[1].getNum();
state->textMoveTo(tx, ty);
out->updateTextPos(state);
}
void Gfx::opTextMoveSet(Object args[], int numArgs) {
double tx, ty;
tx = state->getLineX() + args[0].getNum();
ty = args[1].getNum();
state->setLeading(-ty);
ty += state->getLineY();
state->textMoveTo(tx, ty);
out->updateTextPos(state);
}
void Gfx::opSetTextMatrix(Object args[], int numArgs) {
state->setTextMat(args[0].getNum(), args[1].getNum(),
args[2].getNum(), args[3].getNum(),
args[4].getNum(), args[5].getNum());
state->textMoveTo(0, 0);
out->updateTextMat(state);
out->updateTextPos(state);
fontChanged = gTrue;
}
void Gfx::opTextNextLine(Object args[], int numArgs) {
double tx, ty;
tx = state->getLineX();
ty = state->getLineY() - state->getLeading();
state->textMoveTo(tx, ty);
out->updateTextPos(state);
}
//------------------------------------------------------------------------
// text string operators
//------------------------------------------------------------------------
void Gfx::opShowText(Object args[], int numArgs) {
if (!state->getFont()) {
error(getPos(), "No font in show");
return;
}
doShowText(args[0].getString());
}
void Gfx::opMoveShowText(Object args[], int numArgs) {
double tx, ty;
if (!state->getFont()) {
error(getPos(), "No font in move/show");
return;
}
tx = state->getLineX();
ty = state->getLineY() - state->getLeading();
state->textMoveTo(tx, ty);
out->updateTextPos(state);
doShowText(args[0].getString());
}
void Gfx::opMoveSetShowText(Object args[], int numArgs) {
double tx, ty;
if (!state->getFont()) {
error(getPos(), "No font in move/set/show");
return;
}
state->setWordSpace(args[0].getNum());
state->setCharSpace(args[1].getNum());
tx = state->getLineX();
ty = state->getLineY() - state->getLeading();
state->textMoveTo(tx, ty);
out->updateWordSpace(state);
out->updateCharSpace(state);
out->updateTextPos(state);
doShowText(args[2].getString());
}
void Gfx::opShowSpaceText(Object args[], int numArgs) {
Array *a;
Object obj;
int i;
if (!state->getFont()) {
error(getPos(), "No font in show/space");
return;
}
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());
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();
}
}
void Gfx::doShowText(GString *s) {
GfxFont *font;
GfxFontEncoding16 *enc;
Guchar *p;
Guchar c8;
int c16;
GString *s16;
int m, n;
double dx, dy, width, w, h;
if (fontChanged) {
out->updateFont(state);
fontChanged = gFalse;
}
font = state->getFont();
//----- 16-bit font
if (font->is16Bit()) {
enc = font->getEncoding16();
if (out->useDrawChar()) {
out->beginString(state, s);
s16 = NULL;
} else {
s16 = new GString(" ");
}
state->textTransformDelta(0, state->getRise(), &dx, &dy);
p = (Guchar *)s->getCString();
n = s->getLength();
while (n > 0) {
m = getNextChar16(enc, p, &c16);
width = state->getFontSize() * state->getHorizScaling() *
font->getWidth16(c16) +
state->getCharSpace();
if (c16 == ' ')
width += state->getWordSpace();
state->textTransformDelta(width, 0, &w, &h);
if (out->useDrawChar()) {
out->drawChar16(state, state->getCurX() + dx, state->getCurY() + dy,
w, h, c16);
} else {
s16->setChar(0, (char)(c16 >> 8));
s16->setChar(1, (char)c16);
out->drawString16(state, s16);
}
state->textShift(width);
n -= m;
p += m;
}
if (out->useDrawChar())
out->endString(state);
else
delete s16;
//----- 8-bit font
} else {
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);
}
out->endString(state);
} 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);
}
}
}
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]);
}
return n;
}
//------------------------------------------------------------------------
// XObject operators
//------------------------------------------------------------------------
void Gfx::opXObject(Object args[], int numArgs) {
Object obj1, obj2;
if (!lookupXObject(args[0].getName(), &obj1))
return;
if (!obj1.isStream("XObject")) {
error(getPos(), "XObject '%s' is wrong type", args[0].getName());
obj1.free();
return;
}
obj1.streamGetDict()->lookup("Subtype", &obj2);
if (obj2.isName("Image"))
doImage(obj1.getStream(), gFalse);
else if (obj2.isName("Form"))
doForm(&obj1);
else if (obj2.isName())
error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
else
error(getPos(), "XObject subtype is missing or wrong type");
obj2.free();
obj1.free();
}
void Gfx::doImage(Stream *str, GBool inlineImg) {
Dict *dict;
Object obj1, obj2;
int width, height;
int bits;
GBool mask;
GfxColorSpace *colorSpace;
GfxImageColorMap *colorMap;
GBool invert;
// get stream dict
dict = str->getDict();
// get size
dict->lookup("Width", &obj1);
if (obj1.isNull()) {
obj1.free();
dict->lookup("W", &obj1);
}
if (!obj1.isInt())
goto err2;
width = obj1.getInt();
obj1.free();
dict->lookup("Height", &obj1);
if (obj1.isNull()) {
obj1.free();
dict->lookup("H", &obj1);
}
if (!obj1.isInt())
goto err2;
height = obj1.getInt();
obj1.free();
// image or mask?
dict->lookup("ImageMask", &obj1);
if (obj1.isNull()) {
obj1.free();
dict->lookup("IM", &obj1);
}
mask = gFalse;
if (obj1.isBool())
mask = obj1.getBool();
else if (!obj1.isNull())
goto err2;
obj1.free();
// bit depth
dict->lookup("BitsPerComponent", &obj1);
if (obj1.isNull()) {
obj1.free();
dict->lookup("BPC", &obj1);
}
if (!obj1.isInt())
goto err2;
bits = obj1.getInt();
obj1.free();
// display a mask
if (mask) {
// check for inverted mask
if (bits != 1)
goto err1;
invert = gFalse;
dict->lookup("Decode", &obj1);
if (obj1.isNull()) {
obj1.free();
dict->lookup("D", &obj1);
}
if (obj1.isArray()) {
obj1.arrayGet(0, &obj2);
if (obj2.isInt() && obj2.getInt() == 1)
invert = gTrue;
obj2.free();
} else if (!obj1.isNull()) {
goto err2;
}
obj1.free();
// draw it
out->drawImageMask(state, str, width, height, invert, inlineImg);
} else {
// get color space and color map
dict->lookup("ColorSpace", &obj1);
if (obj1.isNull()) {
obj1.free();
dict->lookup("CS", &obj1);
}
if (obj1.isName()) {
lookupColorSpace(obj1.getName(), &obj2);
if (!obj2.isNull()) {
obj1.free();
obj1 = obj2;
} else {
obj2.free();
}
}
colorSpace = new GfxColorSpace(&obj1);
obj1.free();
if (!colorSpace->isOk()) {
delete colorSpace;
goto err1;
}
dict->lookup("Decode", &obj1);
if (obj1.isNull()) {
obj1.free();
dict->lookup("D", &obj1);
}
colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
obj1.free();
if (!colorMap->isOk()) {
delete colorSpace;
goto err1;
}
// draw it
out->drawImage(state, str, width, height, colorMap, inlineImg);
delete colorMap;
}
return;
err2:
obj1.free();
err1:
error(getPos(), "Bad image parameters");
}
void Gfx::doForm(Object *str) {
Parser *oldParser;
GfxResources *resPtr;
Dict *dict;
Dict *resDict;
Object matrixObj, bboxObj;
double m[6];
Object obj1, obj2;
int i;
// get stream dict
dict = str->streamGetDict();
// check form type
dict->lookup("FormType", &obj1);
if (!(obj1.isInt() && obj1.getInt() == 1)) {
obj1.free();
error(getPos(), "Unknown form type");
return;
}
obj1.free();
// get matrix and bounding box
dict->lookup("Matrix", &matrixObj);
if (!matrixObj.isArray()) {
matrixObj.free();
error(getPos(), "Bad form matrix");
return;
}
dict->lookup("BBox", &bboxObj);
if (!bboxObj.isArray()) {
matrixObj.free();
bboxObj.free();
error(getPos(), "Bad form bounding box");
return;
}
// push new resources on stack
dict->lookup("Resources", &obj1);
if (obj1.isDict()) {
resDict = obj1.getDict();
res = new GfxResources(res);
res->fonts = NULL;
resDict->lookup("Font", &obj2);
if (obj2.isDict())
res->fonts = new GfxFontDict(obj2.getDict());
obj2.free();
resDict->lookup("XObject", &res->xObjDict);
resDict->lookup("ColorSpace", &res->colorSpaceDict);
obj1.free();
}
// save current graphics state
out->saveState(state);
state = state->save();
// save current parser
oldParser = parser;
// set form transformation matrix
for (i = 0; i < 6; ++i) {
matrixObj.arrayGet(i, &obj1);
m[i] = obj1.getNum();
obj1.free();
}
state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
// set form bounding box
for (i = 0; i < 4; ++i) {
bboxObj.arrayGet(i, &obj1);
m[i] = obj1.getNum();
obj1.free();
}
state->moveTo(m[0], m[1]);
state->lineTo(m[0]+m[2], m[1]);
state->lineTo(m[0]+m[2], m[1]+m[3]);
state->lineTo(m[0], m[1]+m[3]);
state->closePath();
out->clip(state);
state->clearPath();
// draw the form
display(str);
// free matrix and bounding box
matrixObj.free();
bboxObj.free();
// restore parser
parser = oldParser;
// restore graphics state
state = state->restore();
out->restoreState(state);
// pop resource stack
resPtr = res->next;
delete res;
res = resPtr;
return;
}
//------------------------------------------------------------------------
// in-line image operators
//------------------------------------------------------------------------
void Gfx::opBeginImage(Object args[], int numArgs) {
Stream *str;
int c1, c2;
// build dict/stream
str = buildImageStream();
// display the image
if (str) {
doImage(str, gTrue);
// skip 'EI' tag
c1 = str->getBaseStream()->getChar();
c2 = str->getBaseStream()->getChar();
while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
c1 = c2;
c2 = str->getBaseStream()->getChar();
}
delete str;
}
}
Stream *Gfx::buildImageStream() {
Object dict;
Object obj;
char *key;
Stream *str;
// build dictionary
dict.initDict();
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())
break;
dict.dictAdd(key, &obj);
}
parser->getObj(&obj);
}
if (obj.isEOF())
error(getPos(), "End of file in inline image");
obj.free();
// make stream
str = new SubStream(parser->getStream(), &dict);
str = str->addFilters(&dict);
return str;
}
void Gfx::opImageData(Object args[], int numArgs) {
error(getPos(), "Internal: got 'ID' operator");
}
void Gfx::opEndImage(Object args[], int numArgs) {
error(getPos(), "Internal: got 'EI' operator");
}
//------------------------------------------------------------------------
// type 3 font operators
//------------------------------------------------------------------------
void Gfx::opSetCharWidth(Object args[], int numArgs) {
error(getPos(), "Encountered 'd0' operator in content stream");
}
void Gfx::opSetCacheDevice(Object args[], int numArgs) {
error(getPos(), "Encountered 'd1' operator in content stream");
}
//------------------------------------------------------------------------
// compatibility operators
//------------------------------------------------------------------------
void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
++ignoreUndef;
}
void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
if (ignoreUndef > 0)
--ignoreUndef;
}
//------------------------------------------------------------------------
// marked content operators
//------------------------------------------------------------------------
void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
if (printCommands) {
printf(" marked content: %s ", args[0].getName());
if (numArgs == 2)
args[2].print(stdout);
printf("\n");
}
}
void Gfx::opEndMarkedContent(Object args[], int numArgs) {
}
void Gfx::opMarkPoint(Object args[], int numArgs) {
if (printCommands) {
printf(" mark point: %s ", args[0].getName());
if (numArgs == 2)
args[2].print(stdout);
printf("\n");
}
}