#include <stdio.h>
#include <stdlib.h>
+#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include "../gfxdevice.h"
#include "../gfxtools.h"
#include "../gfxfont.h"
+#include "../devices/record.h"
+#include "../devices/ops.h"
+#include "../devices/arts.h"
+#include "../devices/render.h"
+#include "../png.h"
#include <math.h>
{"Symbol", "s050000l"},
{"ZapfDingbats", "d050000l"}};
+static int verbose = 1;
+static int dbgindent = 0;
+static void dbg(char*format, ...)
+{
+ char buf[1024];
+ int l;
+ va_list arglist;
+ if(!verbose)
+ return;
+ va_start(arglist, format);
+ vsprintf(buf, format, arglist);
+ va_end(arglist);
+ l = strlen(buf);
+ while(l && buf[l-1]=='\n') {
+ buf[l-1] = 0;
+ l--;
+ }
+ printf("(pdf) ");
+ int indent = dbgindent;
+ while(indent) {
+ printf(" ");
+ indent--;
+ }
+ printf("%s\n", buf);
+ fflush(stdout);
+}
+
+
typedef struct _feature
{
char*string;
this->textRender = 0;
this->createsoftmask = 0;
this->transparencygroup = 0;
+ this->softmask = 0;
+ this->grouprecording = 0;
}
GBool GFXOutputDev::interpretType3Chars()
draw.lineTo(&draw, lastx, lasty);
}
gfxline_t*result = (gfxline_t*)draw.result(&draw);
+
+ gfxline_optimize(result);
+
return result;
}
msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
dump_outline(line);
}
- if(states[statepos].transparencygroup && col.a != 255)
- return;
-
device->fill(device, line, &col);
}
void GFXOutputDev::clip(GfxState *state)
{
- if(states[statepos].createsoftmask)
- return;
-
GfxPath * path = state->getPath();
gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
clipToGfxLine(state, line);
double originX, double originY,
CharCode c, int nBytes, Unicode *_u, int uLen)
{
- if(states[statepos].createsoftmask)
- return;
-
int render = state->getRender();
// check for invisible text -- this is used by Acrobat Capture
if (render == 3) {
}
device->startpage(device, width, height);
+ this->width = width;
+ this->height = height;
}
void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
device->fill(device, clippath, &white);
}
-void GFXOutputDev::drawLink(Link *link, Catalog *catalog)
+void GFXOutputDev::processLink(Link *link, Catalog *catalog)
{
double x1, y1, x2, y2, w;
gfxline_t points[5];
}
void GFXOutputDev::saveState(GfxState *state) {
- msg("<trace> saveState\n");
- updateAll(state);
- if(statepos>=64) {
- msg("<error> Too many nested states in pdf.");
- return;
- }
- statepos ++;
- states[statepos].textRender = states[statepos-1].textRender;
- states[statepos].createsoftmask = states[statepos-1].createsoftmask;
- states[statepos].transparencygroup = states[statepos-1].transparencygroup;
- states[statepos].clipping = 0;
+ dbg("saveState");dbgindent+=2;
+
+ msg("<trace> saveState\n");
+ updateAll(state);
+ if(statepos>=64) {
+ msg("<error> Too many nested states in pdf.");
+ return;
+ }
+ statepos ++;
+ states[statepos].textRender = states[statepos-1].textRender;
+ states[statepos].createsoftmask = states[statepos-1].createsoftmask;
+ states[statepos].transparencygroup = states[statepos-1].transparencygroup;
+ states[statepos].clipping = 0;
};
void GFXOutputDev::restoreState(GfxState *state) {
+ dbgindent-=2; dbg("restoreState");
+
if(statepos==0) {
msg("<error> Invalid restoreState");
return;
}
msg("<trace> restoreState");
+ if(states[statepos].softmask) {
+ clearSoftMask(state);
+ }
updateAll(state);
while(states[statepos].clipping) {
device->endclip(device);
GfxRGB rgb;
double opaq = state->getFillOpacity();
state->getFillRGB(&rgb);
+ dbg("update fillopaq %f", opaq);
}
+void GFXOutputDev::updateStrokeOpacity(GfxState *state)
+{
+ double opaq = state->getFillOpacity();
+ dbg("update strokeopaq %f", opaq);
+}
+void GFXOutputDev::updateFillOverprint(GfxState *state)
+{
+ double opaq = state->getFillOverprint();
+ dbg("update filloverprint %f", opaq);
+}
+void GFXOutputDev::updateStrokeOverprint(GfxState *state)
+{
+ double opaq = state->getStrokeOverprint();
+ dbg("update strokeoverprint %f", opaq);
+}
+void GFXOutputDev::updateTransfer(GfxState *state)
+{
+ dbg("update transfer");
+}
+
void GFXOutputDev::updateStrokeColor(GfxState *state)
{
/* TODO: pass image_dpi to device instead */
dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
+ gfxline_show(&p1,stdout);
+
+ printf("%.2f %.2f %.2f\n", m.m00, m.m10, m.tx);
+ printf("%.2f %.2f %.2f\n", m.m01, m.m11, m.ty);
dev->fillbitmap(dev, &p1, &img, &m, 0);
}
state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
-
if(!pbminfo && !(str->getKind()==strDCT)) {
if(!type3active) {
msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
int width, int height, GBool invert,
GBool inlineImg)
{
- if(states[statepos].createsoftmask)
- return;
+ dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
}
int width, int height, GfxImageColorMap *colorMap,
int *maskColors, GBool inlineImg)
{
- if(states[statepos].createsoftmask)
- return;
+ dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
+ colorMap?"colorMap":"no colorMap",
+ maskColors?"maskColors":"no maskColors",
+ inlineImg);
msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
colorMap?"colorMap":"no colorMap",
maskColors?"maskColors":"no maskColors",
Stream *maskStr, int maskWidth, int maskHeight,
GBool maskInvert)
{
- if(states[statepos].createsoftmask)
- return;
+ dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
+ colorMap?"colorMap":"no colorMap",
+ maskWidth, maskHeight);
msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
colorMap?"colorMap":"no colorMap",
maskWidth, maskHeight);
int maskWidth, int maskHeight,
GfxImageColorMap *maskColorMap)
{
- if(states[statepos].createsoftmask)
- return;
+ dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
+ colorMap?"colorMap":"no colorMap",
+ maskWidth, maskHeight);
msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
colorMap?"colorMap":"no colorMap",
maskWidth, maskHeight);
void GFXOutputDev::stroke(GfxState *state)
{
- if(states[statepos].createsoftmask)
- return;
+ dbg("stroke");
GfxPath * path = state->getPath();
gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
void GFXOutputDev::fill(GfxState *state)
{
- if(states[statepos].createsoftmask)
- return;
+ dbg("fill");
GfxPath * path = state->getPath();
gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
void GFXOutputDev::eoFill(GfxState *state)
{
- if(states[statepos].createsoftmask)
- return;
+ dbg("eofill");
GfxPath * path = state->getPath();
gfxcolor_t col = getFillColor(state);
if(pdfpage>this->pagepos)
this->pagepos = pdfpage;
}
-
+
+struct BBox
+{
+ double posx,posy;
+ double width,height;
+};
+
+BBox mkBBox(GfxState*state, double*bbox, double width, double height)
+{
+ double xMin, yMin, xMax, yMax, x, y;
+ double tx, ty, w, h;
+ // transform the bbox
+ state->transform(bbox[0], bbox[1], &x, &y);
+ xMin = xMax = x;
+ yMin = yMax = y;
+ state->transform(bbox[0], bbox[3], &x, &y);
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ state->transform(bbox[2], bbox[1], &x, &y);
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ state->transform(bbox[2], bbox[3], &x, &y);
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ tx = (int)floor(xMin);
+ if (tx < 0) {
+ tx = 0;
+ } else if (tx > width) {
+ tx = width;
+ }
+ ty = (int)floor(yMin);
+ if (ty < 0) {
+ ty = 0;
+ } else if (ty > height) {
+ ty = height;
+ }
+ w = (int)ceil(xMax) - tx + 1;
+ if (tx + w > width) {
+ w = width - tx;
+ }
+ if (w < 1) {
+ w = 1;
+ }
+ h = (int)ceil(yMax) - ty + 1;
+ if (ty + h > height) {
+ h = height - ty;
+ }
+ if (h < 1) {
+ h = 1;
+ }
+ BBox nbbox;
+ nbbox.posx = xMin;
+ nbbox.posx = yMin;
+ nbbox.width = w;
+ nbbox.height = h;
+ return nbbox;
+}
+
#if xpdfUpdateVersion >= 16
void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
GfxColorSpace *blendingColorSpace,
GBool forSoftMask)
{
char*colormodename = "";
+ BBox rect = mkBBox(state, bbox, this->width, this->height);
+
if(blendingColorSpace) {
colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
}
+ dbg("beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
+ dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
msg("<verbose> beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
- states[statepos].createsoftmask = forSoftMask;
+ states[statepos].createsoftmask |= forSoftMask;
states[statepos].transparencygroup = !forSoftMask;
+ states[statepos].olddevice = this->device;
+
+ this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
+
+ gfxdevice_record_init(this->device);
- if(!forSoftMask) {
+ /*if(!forSoftMask) { ////???
state->setFillOpacity(0.0);
- }
+ }*/
warnfeature("transparency groups",1);
+ dbgindent+=2;
}
void GFXOutputDev::endTransparencyGroup(GfxState *state)
{
+ dbgindent-=2;
+ dbg("endTransparencyGroup");
msg("<verbose> endTransparencyGroup");
+
+ gfxdevice_t*r = this->device;
+
+ this->device = states[statepos].olddevice;
+
+ if(states[statepos].createsoftmask) {
+ states[statepos-1].softmaskrecording = r->finish(r);
+ } else {
+ states[statepos-1].grouprecording = r->finish(r);
+ }
+
states[statepos].createsoftmask = 0;
states[statepos].transparencygroup = 0;
+ free(r);
}
void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
{
- msg("<verbose> paintTransparencyGroup");
+ char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
+ "colordodge","colorburn","hardlight","softlight","difference",
+ "exclusion","hue","saturation","color","luminosity"};
+
+ dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
+ msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
+
+ gfxresult_t*grouprecording = states[statepos].grouprecording;
+
+ if(state->getBlendMode() == gfxBlendNormal) {
+ gfxdevice_t ops;
+ gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
+ gfxresult_record_replay(grouprecording, &ops);
+ ops.finish(&ops);
+ }
+ grouprecording->destroy(grouprecording);
+
+ states[statepos].grouprecording = 0;
}
void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
{
+ /* alpha = 1: retrieve mask values from alpha layer
+ alpha = 0: retrieve mask values from luminance */
+ dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
+ bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
warnfeature("soft masks",0);
+
+ states[statepos].olddevice = this->device;
+ this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
+ gfxdevice_record_init(this->device);
+
+ dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
+
+ states[statepos].softmask = 1;
}
void GFXOutputDev::clearSoftMask(GfxState *state)
{
+ if(!states[statepos].softmask)
+ return;
+ states[statepos].softmask = 0;
+ dbg("clearSoftMask statepos=%d", statepos);
msg("<verbose> clearSoftMask");
+
+ if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
+ msg("<error> Error in softmask/tgroup ordering");
+ return;
+ }
+
+ gfxresult_t*mask = states[statepos].softmaskrecording;
+ gfxresult_t*below = this->device->finish(this->device);
+ this->device = states[statepos].olddevice;
+
+ /* get outline of all objects below the soft mask */
+ gfxdevice_t uniondev;
+ gfxdevice_union_init(&uniondev, 0);
+ gfxresult_record_replay(below, &uniondev);
+ gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
+ uniondev.finish(&uniondev);
+
+ gfxbbox_t bbox = gfxline_getbbox(belowoutline);
+#if 0
+ this->device->startclip(this->device, belowoutline);
+ gfxresult_record_replay(below, this->device);
+ gfxresult_record_replay(mask, this->device);
+ this->device->endclip(this->device);
+ gfxline_free(belowoutline);
+#endif
+
+ int width = (int)bbox.xmax,height = (int)bbox.ymax;
+
+ gfxdevice_t belowrender;
+ gfxdevice_render_init(&belowrender);
+ belowrender.setparameter(&belowrender, "antialize", "2");
+ belowrender.startpage(&belowrender, width, height);
+ gfxresult_record_replay(below, &belowrender);
+ belowrender.endpage(&belowrender);
+ gfxresult_t* belowresult = belowrender.finish(&belowrender);
+ gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
+
+ gfxdevice_t maskrender;
+ gfxdevice_render_init(&maskrender);
+ maskrender.startpage(&maskrender, width, height);
+ gfxresult_record_replay(mask, &maskrender);
+ maskrender.endpage(&maskrender);
+ gfxresult_t* maskresult = maskrender.finish(&maskrender);
+ gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
+
+ if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
+ msg("<fatal> Internal error in mask drawing");
+ return;
+ }
+
+ int y,x;
+ for(y=0;y<height;y++) {
+ gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
+ gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
+ for(x=0;x<width;x++) {
+ l1->a = 255;
+ l2->a = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
+
+ /* premultiply alpha... do we need this? (depends on output device)
+ l2->r = (l2->a*l2->r) >> 8;
+ l2->g = (l2->a*l2->g) >> 8;
+ l2->b = (l2->a*l2->b) >> 8;
+ */
+
+ l1++;
+ l2++;
+ }
+ }
+ gfxline_t*line = gfxline_makerectangle(0,0,width,height);
+
+ gfxmatrix_t matrix;
+ matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
+ matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
+
+ this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
+
+ mask->destroy(mask);
+ below->destroy(below);
+ maskresult->destroy(maskresult);
+ belowresult->destroy(belowresult);
+ states[statepos].softmaskrecording = 0;
}
#endif