fixed transparent background of poly2bitmap files
[swftools.git] / lib / pdf / VectorGraphicOutputDev.cc
blob1b7616afdcab852205ccbc582b4b6fd4001663e9
1 /* VectorGraphicOutputDev.cc
2 implements a pdf output device (OutputDev).
4 This file is part of swftools.
6 Swftools is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 Swftools is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with swftools; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <stddef.h>
24 #include <string.h>
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #include <math.h>
30 #include "../../config.h"
32 //xpdf header files
33 #include "popplercompat.h"
34 #include "VectorGraphicOutputDev.h"
36 // swftools header files
37 #include "../os.h"
38 #include "../log.h"
39 #include "../mem.h"
40 #include "../utf8.h"
41 #include "../gfxdevice.h"
42 #include "../gfxtools.h"
43 #include "../gfxfont.h"
44 #include "../gfxpoly.h"
45 #include "../devices/record.h"
46 #include "../devices/ops.h"
47 #include "../devices/polyops.h"
48 #include "../devices/render.h"
50 #include "../png.h"
52 /* config */
53 static int verbose = 0;
54 static int dbgindent = 1;
55 static void dbg(const char*format, ...)
57 char buf[1024];
58 int l;
59 va_list arglist;
60 if(!verbose)
61 return;
62 va_start(arglist, format);
63 vsnprintf(buf, sizeof(buf)-1, format, arglist);
64 va_end(arglist);
65 l = strlen(buf);
66 while(l && buf[l-1]=='\n') {
67 buf[l-1] = 0;
68 l--;
70 printf("(pdf) ");
71 int indent = dbgindent;
72 while(indent) {
73 printf(" ");
74 indent--;
76 printf("%s\n", buf);
77 fflush(stdout);
80 GFXOutputState::GFXOutputState() {
81 this->clipping = 0;
82 this->createsoftmask = 0;
83 this->transparencygroup = 0;
84 this->softmask = 0;
85 this->grouprecording = 0;
86 this->isolated = 0;
89 VectorGraphicOutputDev::VectorGraphicOutputDev(InfoOutputDev*info, PDFDoc*doc, int*page2page, int num_pages, int x, int y, int x1, int y1, int x2, int y2)
90 :CommonOutputDev(info, doc, page2page, num_pages, x, y, x1, y1, x2, y2)
92 this->type3active = 0;
93 this->statepos = 0;
94 this->xref = 0;
95 this->current_gfxfont = 0;
96 this->current_fontinfo = 0;
97 this->current_text_stroke = 0;
98 this->current_text_clip = 0;
99 this->outer_clip_box = 0;
100 this->config_convertgradients=1;
101 this->config_transparent=0;
102 this->config_disable_polygon_conversion = 0;
103 this->config_multiply = 1;
104 this->config_textonly = 0;
106 /* for processing drawChar events */
107 this->charDev = new CharOutputDev(info, doc, page2page, num_pages, x, y, x1, y1, x2, y2);
108 memset(&this->char_output_dev, 0, sizeof(gfxdevice_t));
109 this->char_output_dev.internal = this;
110 this->char_output_dev.drawchar = drawchar_callback;
111 this->char_output_dev.addfont = addfont_callback;
113 memset(states, 0, sizeof(states));
116 void VectorGraphicOutputDev::setParameter(const char*key, const char*value)
118 if(!strcmp(key,"transparent")) {
119 this->config_transparent = atoi(value);
120 } else if(!strcmp(key,"convertgradients")) {
121 this->config_convertgradients = atoi(value);
122 } else if(!strcmp(key,"textonly")) {
123 this->config_textonly = atoi(value);
124 } else if(!strcmp(key,"multiply")) {
125 this->config_multiply = atoi(value);
126 if(this->config_multiply<1)
127 this->config_multiply=1;
128 } else if(!strcmp(key,"disable_polygon_conversion")) {
129 this->config_disable_polygon_conversion = atoi(value);
130 } else if(!strcmp(key,"disable_tiling_pattern_fills")) {
131 this->config_disable_tiling_pattern_fills = atoi(value);
133 this->charDev->setParameter(key, value);
136 void VectorGraphicOutputDev::setDevice(gfxdevice_t*dev)
138 this->device = dev;
139 charDev->setDevice(dev);
142 //void VectorGraphicOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) {printf("void VectorGraphicOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) \n");}
143 //void VectorGraphicOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) {printf("void VectorGraphicOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) \n");}
145 void dump_outline(gfxline_t*line)
147 /*gfxbbox_t*r = gfxline_isrectangle(line);
148 if(!r)
149 printf("is not a rectangle\n");
150 else
151 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
154 while(line) {
155 if(line->type == gfx_moveTo) {
156 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
157 } else if(line->type == gfx_lineTo) {
158 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
159 } else if(line->type == gfx_splineTo) {
160 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
162 line = line->next;
166 void gfxPath_dump(GfxPath*path)
168 int num = path->getNumSubpaths();
169 int t;
170 int cpos=0;
171 for(t = 0; t < num; t++) {
172 GfxSubpath *subpath = path->getSubpath(t);
173 int subnum = subpath->getNumPoints();
174 int s;
175 for(s=0;s<subnum;s++) {
176 double x=subpath->getX(s);
177 double y=subpath->getY(s);
178 if(s==0 && !subpath->getCurve(s)) {
179 printf("M %f %f\n", x, y);
180 } else if(s==0 && subpath->getCurve(s)) {
181 printf("E %f %f\n", x, y);
182 } else if(subpath->getCurve(s)) {
183 printf("C %f %f\n", x, y);
184 } else {
185 printf("T %f %f\n", x, y);
191 gfxline_t* VectorGraphicOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed)
193 int num = path->getNumSubpaths();
194 int s,t;
195 int cpos = 0;
196 double lastx=0,lasty=0,posx=0,posy=0;
197 int needsfix=0;
198 if(!num) {
199 msg("<warning> empty path");
200 return 0;
202 gfxdrawer_t draw;
203 gfxdrawer_target_gfxline(&draw);
205 for(t = 0; t < num; t++) {
206 GfxSubpath *subpath = path->getSubpath(t);
207 int subnum = subpath->getNumPoints();
208 double bx=0,by=0,cx=0,cy=0;
210 for(s=0;s<subnum;s++) {
211 double x,y;
213 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
215 if(s==0) {
216 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
217 draw.lineTo(&draw, lastx, lasty);
219 draw.moveTo(&draw, x,y);
220 posx = lastx = x;
221 posy = lasty = y;
222 cpos = 0;
223 needsfix = 0;
224 } else if(subpath->getCurve(s) && cpos==0) {
225 bx = x;
226 by = y;
227 cpos = 1;
228 } else if(subpath->getCurve(s) && cpos==1) {
229 cx = x;
230 cy = y;
231 cpos = 2;
232 } else {
233 posx = x;
234 posy = y;
235 if(cpos==0) {
236 draw.lineTo(&draw, x,y);
237 } else {
238 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
240 needsfix = 1;
241 cpos = 0;
245 /* fix non-closed lines */
246 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
247 draw.lineTo(&draw, lastx, lasty);
249 gfxline_t*result = (gfxline_t*)draw.result(&draw);
251 gfxline_optimize(result);
253 return result;
256 GBool VectorGraphicOutputDev::useTilingPatternFill()
258 infofeature("tiled patterns");
259 // if(config_convertgradients)
260 // return gTrue;
261 if(config_disable_tiling_pattern_fills)
262 return gTrue;
263 return gFalse;
265 GBool VectorGraphicOutputDev::useShadedFills()
267 infofeature("shaded fills");
268 if(config_convertgradients)
269 return gTrue;
270 return gFalse;
273 POPPLER_TILING_PATERN_RETURN VectorGraphicOutputDev::tilingPatternFill(GfxState *state,
274 POPPLER_TILING_PATERN_GFX
275 Object *str,
276 int paintType, Dict *resDict,
277 double *mat, double *bbox,
278 int x0, int y0, int x1, int y1,
279 double xStep, double yStep)
281 msg("<debug> tilingPatternFill");
282 infofeature("tiling pattern fills");
283 #ifdef HAVE_POPPLER
284 // since we don't implement this method yet,
285 // reduce it to a series of other drawing operations.
286 return gFalse;
287 #endif
290 GBool VectorGraphicOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
292 msg("<error> functionShadedFill not supported yet");
293 infofeature("function shaded fills");
294 return gFalse;
296 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
298 gfxcolor_t c;
299 GfxRGB rgb;
300 colspace->getRGB(col, &rgb);
301 c.r = colToByte(rgb.r);
302 c.g = colToByte(rgb.g);
303 c.b = colToByte(rgb.b);
304 c.a = 255;
305 return c;
308 GBool VectorGraphicOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
310 if(config_textonly) {return gTrue;}
312 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
313 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
314 x1=x0+r1;y1=y0;
315 x2=x0; y2=y0+r1;
316 this->transformXY(state, x0,y0, &x0,&y0);
317 this->transformXY(state, x1,y1, &x1,&y1);
318 this->transformXY(state, x2,y2, &x2,&y2);
320 GfxColor color0;
321 GfxColor color1;
322 GfxColor color2;
323 shading->getColor(0.0, &color0);
324 shading->getColor(0.5, &color1);
325 shading->getColor(1.0, &color2);
327 GfxColorSpace* colspace = shading->getColorSpace();
329 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
330 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
331 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
332 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
333 infofeature("radial shaded fills");
335 gfxgradient_t gr[3];
336 gfxgradient_t*g = &gr[0];
337 g[0].next = &g[1];
338 g[1].next = &g[2];
339 g[2].next = 0;
340 g[0].color = col2col(colspace, &color0);
341 g[1].color = col2col(colspace, &color1);
342 g[2].color = col2col(colspace, &color2);
343 g[0].pos = 0.0;
344 g[1].pos = 0.5;
345 g[2].pos = 1.0;
347 gfxbbox_t b = states[statepos].clipbbox;
348 gfxline_t p1,p2,p3,p4,p5;
349 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
350 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
351 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
352 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
353 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
355 gfxmatrix_t m;
356 //m.m00 = (x3-x0); m.m10 = (x1-x0);
357 //m.m01 = (y3-y0); m.m11 = (y1-y0);
358 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
359 m.m00 = (x1-x0); m.m10 = (x2-x0);
360 m.m01 = (y1-y0); m.m11 = (y2-y0);
361 m.tx = x0 - 0.5;
362 m.ty = y0 - 0.5;
364 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
365 return gTrue;
368 GBool VectorGraphicOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
370 if(config_textonly) {return gTrue;}
372 double x0,y0,x1,y1;
373 shading->getCoords(&x0,&y0,&x1,&y1);
374 this->transformXY(state, x0,y0,&x0,&y0);
375 this->transformXY(state, x1,y1,&x1,&y1);
377 GfxColor color0;
378 GfxColor color1;
379 GfxColor color2;
380 shading->getColor(0.0, &color0);
381 shading->getColor(0.5, &color1);
382 shading->getColor(1.0, &color2);
384 GfxColorSpace* colspace = shading->getColorSpace();
386 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
387 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
388 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
389 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
391 infofeature("axial shaded fills");
393 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
394 g[0].next = &g[1];
395 g[1].next = &g[2];
396 g[2].next = 0;
397 g[0].color = col2col(colspace, &color0);
398 g[1].color = col2col(colspace, &color1);
399 g[2].color = col2col(colspace, &color2);
400 g[0].pos = 0.0;
401 g[1].pos = 0.5;
402 g[2].pos = 1.0;
404 double xMin,yMin,xMax,yMax;
405 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
406 this->transformXY(state, xMin, yMin, &xMin, &yMin);
407 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
409 xMin = 0; yMin = 0;
410 xMin = 1024; yMin = 1024;
412 gfxbbox_t b = states[statepos].clipbbox;
413 gfxline_t p1,p2,p3,p4,p5;
414 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
415 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
416 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
417 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
418 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
420 /* the gradient starts at (-1.0,0.0), so move (0,0) to
421 the middle of the two control points */
422 gfxmatrix_t m;
423 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
424 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
425 m.tx = (x0 + x1)/2 - 0.5;
426 m.ty = (y0 + y1)/2 - 0.5;
428 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
430 free(g);
431 return gTrue;
434 GBool VectorGraphicOutputDev::useDrawForm()
436 infofeature("forms");
437 return gFalse;
439 void VectorGraphicOutputDev::drawForm(Ref id)
441 msg("<error> drawForm not implemented");
443 GBool VectorGraphicOutputDev::needNonText()
445 return gTrue;
447 void VectorGraphicOutputDev::endPage()
449 msg("<verbose> endPage (VectorGraphicOutputDev)");
450 charDev->endPage(); // link postprocessing
451 if(outer_clip_box) {
452 device->endclip(device);
453 outer_clip_box = 0;
456 void VectorGraphicOutputDev::setDefaultCTM(double *ctm)
458 charDev->setDefaultCTM(ctm);
459 OutputDev::setDefaultCTM(ctm);
462 static inline double sqr(double x) {return x*x;}
464 #define STROKE_FILL 1
465 #define STROKE_CLIP 2
466 void VectorGraphicOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
468 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
469 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
470 double miterLimit = state->getMiterLimit();
471 double width = state->getTransformedLineWidth();
473 GfxRGB rgb;
474 double opaq = state->getStrokeOpacity();
475 if(type3active)
476 state->getFillRGB(&rgb);
477 else
478 state->getStrokeRGB(&rgb);
479 gfxcolor_t col;
480 col.r = colToByte(rgb.r);
481 col.g = colToByte(rgb.g);
482 col.b = colToByte(rgb.b);
483 col.a = (unsigned char)(opaq*255);
485 gfx_capType capType = gfx_capRound;
486 if(lineCap == 0) capType = gfx_capButt;
487 else if(lineCap == 1) capType = gfx_capRound;
488 else if(lineCap == 2) capType = gfx_capSquare;
489 else msg("<error> Invalid line cap type");
491 gfx_joinType joinType = gfx_joinRound;
492 if(lineJoin == 0) joinType = gfx_joinMiter;
493 else if(lineJoin == 1) joinType = gfx_joinRound;
494 else if(lineJoin == 2) joinType = gfx_joinBevel;
495 else msg("<error> Invalid line join type");
497 gfxline_t*line2 = 0;
499 int dashLength = states[statepos].dashLength;
500 double*dashPattern = states[statepos].dashPattern;
501 double dashStart = states[statepos].dashStart;
502 if(dashLength && dashPattern) {
503 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
504 int t;
506 /* try to find out how much the transformation matrix would
507 stretch the dashes, and factor that into the dash lengths.
508 This is not the entirely correct approach- it would be
509 better to first convert the path to an unscaled version,
510 then apply dashing, and then transform the path using
511 the current transformation matrix. However there are few
512 PDFs which actually stretch a dashed path in a non-orthonormal
513 way */
514 double tx1, ty1, tx2, ty2, tx3, ty3;
515 this->transformXY(state, 0, 0, &tx1, &ty1);
516 this->transformXY(state, 0, 1, &tx2, &ty2);
517 this->transformXY(state, 1, 0, &tx3, &ty3);
518 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
519 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
520 if(fabs(d1-d2)>0.5)
521 warnfeature("non-ortogonally dashed strokes", 0);
522 double f = (d1+d2)/2;
524 if(!dashStart && dashLength==1 && !dashPattern[0]) {
525 // zero phase and zero dashlength make the line invisible
526 return;
529 msg("<trace> %d dashes", dashLength);
530 msg("<trace> | phase: %f", dashStart);
531 for(t=0;t<dashLength;t++) {
532 dash[t] = (float)dashPattern[t] * f;
533 if(!dash[t]) {
534 dash[t] = 1e-37;
536 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
538 dash[dashLength] = -1;
539 if(getLogLevel() >= LOGLEVEL_TRACE) {
540 dump_outline(line);
543 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
544 line = line2;
546 free(dash);
547 msg("<trace> After dashing:");
550 if(getLogLevel() >= LOGLEVEL_TRACE) {
551 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
552 width,
553 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
554 lineCap==0?"butt": (lineCap==1?"round":"square"),
555 dashLength,
556 col.r,col.g,col.b,col.a
558 dump_outline(line);
561 if(flags&STROKE_FILL) {
562 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
563 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
564 if(getLogLevel() >= LOGLEVEL_TRACE) {
565 dump_outline(gfxline);
567 if(!gfxline) {
568 msg("<warning> Empty polygon (resulting from stroked line)");
570 if(flags&STROKE_CLIP) {
571 device->startclip(device, gfxline);
572 states[statepos].clipping++;
573 } else {
574 device->fill(device, gfxline, &col);
576 gfxline_free(gfxline);
577 gfxpoly_destroy(poly);
578 } else {
579 if(flags&STROKE_CLIP)
580 msg("<error> Stroke&clip not supported at the same time");
581 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
584 if(line2)
585 gfxline_free(line2);
588 void VectorGraphicOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
590 gfxcolor_t col = gfxstate_getfillcolor(state);
592 if(getLogLevel() >= LOGLEVEL_TRACE) {
593 msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
594 dump_outline(line);
596 device->fill(device, line, &col);
599 void VectorGraphicOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
601 if(getLogLevel() >= LOGLEVEL_TRACE) {
602 msg("<trace> %sclip", evenodd?"eo":"");
603 dump_outline(line);
605 gfxbbox_t bbox = gfxline_getbbox(line);
606 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
608 device->startclip(device, line);
609 states[statepos].clipping++;
612 void VectorGraphicOutputDev::clip(GfxState *state)
614 GfxPath * path = state->getPath();
615 msg("<trace> clip");
616 gfxline_t*line = gfxPath_to_gfxline(state, path, 1);
617 if(!config_disable_polygon_conversion) {
618 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
619 gfxline_free(line);
620 line = line2;
622 clipToGfxLine(state, line, 0);
623 gfxline_free(line);
626 void VectorGraphicOutputDev::eoClip(GfxState *state)
628 GfxPath * path = state->getPath();
629 gfxline_t*line = gfxPath_to_gfxline(state, path, 1);
630 clipToGfxLine(state, line, 1);
631 gfxline_free(line);
633 void VectorGraphicOutputDev::clipToStrokePath(GfxState *state)
635 GfxPath * path = state->getPath();
636 gfxline_t*line= gfxPath_to_gfxline(state, path, 0);
638 if(getLogLevel() >= LOGLEVEL_TRACE) {
639 double width = state->getTransformedLineWidth();
640 msg("<trace> cliptostrokepath width=%f", width);
641 dump_outline(line);
644 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
645 gfxline_free(line);
648 void VectorGraphicOutputDev::finish()
650 if(outer_clip_box) {
651 if(device) {
652 device->endclip(device);
654 outer_clip_box = 0;
658 VectorGraphicOutputDev::~VectorGraphicOutputDev()
660 finish();
661 delete charDev;charDev=0;
663 GBool VectorGraphicOutputDev::upsideDown()
665 return gTrue;
667 GBool VectorGraphicOutputDev::useDrawChar()
669 return gTrue;
672 void VectorGraphicOutputDev::updateFontMatrix(GfxState*state)
674 this->current_font_matrix = gfxmatrix_from_state(state);
675 charDev->updateTextMat(state);
678 void VectorGraphicOutputDev::updateFont(GfxState*state)
680 charDev->updateFont(state);
683 void VectorGraphicOutputDev::processLink(Link *link, Catalog *catalog)
685 charDev->processLink(link, catalog);
688 void VectorGraphicOutputDev::beginString(GfxState *state, GString *s)
690 int render = state->getRender();
691 if(current_text_stroke) {
692 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
694 charDev->beginString(state, s);
697 static gfxline_t* mkEmptyGfxShape(double x, double y)
699 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
700 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
701 return line;
704 void addfont_callback(gfxdevice_t*dev, gfxfont_t*font)
706 VectorGraphicOutputDev*self = (VectorGraphicOutputDev*)dev->internal;
707 self->device->addfont(self->device, font);
709 void drawchar_callback(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
711 VectorGraphicOutputDev*self = (VectorGraphicOutputDev*)dev->internal;
712 self->gfxfont_from_callback = font;
713 self->glyphnr_from_callback = glyph;
714 memcpy(&self->textcolor_from_callback, color, sizeof(gfxcolor_t));
715 memcpy(&self->textmatrix_from_callback, matrix, sizeof(gfxmatrix_t));
718 void VectorGraphicOutputDev::drawChar(GfxState *state, double x, double y,
719 double dx, double dy,
720 double originX, double originY,
721 CharCode charid, int nBytes, Unicode *_u, int uLen)
723 int render = state->getRender();
724 if(((render == RENDER_FILL) ||
725 (render == RENDER_FILLSTROKE && state->getTransformedLineWidth()<1.0) ||
726 (render == RENDER_INVISIBLE))) {
727 charDev->drawChar(state, x, y, dx, dy, originX, originY, charid, nBytes, _u, uLen);
728 return;
731 msg("<debug> Drawing glyph %d as shape", charid);
732 infofeature("text rendered as shape");
734 charDev->setDevice(&char_output_dev);
735 this->gfxfont_from_callback = 0;
736 this->glyphnr_from_callback = 0;
737 charDev->drawChar(state, x, y, dx, dy, originX, originY, charid, nBytes, _u, uLen);
738 charDev->setDevice(device);
740 if(!gfxfont_from_callback) {
741 // some chars are ignored by CharOutputDev
742 return;
744 gfxline_t*glyph = gfxfont_from_callback->glyphs[glyphnr_from_callback].line;
746 gfxline_t*tglyph = gfxline_clone(glyph);
747 gfxline_transform(tglyph, &textmatrix_from_callback);
748 if((render&3) != RENDER_INVISIBLE) {
749 gfxline_t*add = gfxline_clone(tglyph);
750 current_text_stroke = gfxline_append(current_text_stroke, add);
752 if(render&RENDER_CLIP) {
753 gfxline_t*add = gfxline_clone(tglyph);
754 current_text_clip = gfxline_append(current_text_clip, add);
755 if(!current_text_clip) {
756 current_text_clip = mkEmptyGfxShape(textmatrix_from_callback.tx, textmatrix_from_callback.ty);
759 gfxline_free(tglyph);
762 void VectorGraphicOutputDev::endString(GfxState *state)
764 int render = state->getRender();
765 msg("<trace> endString() render=%d textstroke=%p", render, current_text_stroke);
767 if(current_text_stroke) {
768 /* fillstroke and stroke text rendering objects we can process right
769 now (as there may be texts of other rendering modes in this
770 text object)- clipping objects have to wait until endTextObject,
771 however */
772 device->setparameter(device, "mark","TXT");
773 if((render&3) == RENDER_FILL) {
774 fillGfxLine(state, current_text_stroke, 0);
775 gfxline_free(current_text_stroke);
776 current_text_stroke = 0;
777 } else if((render&3) == RENDER_FILLSTROKE) {
778 fillGfxLine(state, current_text_stroke, 0);
779 strokeGfxline(state, current_text_stroke,0);
780 gfxline_free(current_text_stroke);
781 current_text_stroke = 0;
782 } else if((render&3) == RENDER_STROKE) {
783 strokeGfxline(state, current_text_stroke,0);
784 gfxline_free(current_text_stroke);
785 current_text_stroke = 0;
787 device->setparameter(device, "mark","");
791 void VectorGraphicOutputDev::endTextObject(GfxState *state)
793 int render = state->getRender();
794 msg("<trace> endTextObject() render=%d textstroke=%p clipstroke=%p", render, current_text_stroke, current_text_clip);
796 if(current_text_clip) {
797 device->setparameter(device, "mark","TXT");
798 clipToGfxLine(state, current_text_clip, 0);
799 device->setparameter(device, "mark","");
800 gfxline_free(current_text_clip);
801 current_text_clip = 0;
805 /* the logic seems to be as following:
806 first, beginType3Char is called, with the charcode and the coordinates.
807 if this function returns true, it already knew about the char and has now drawn it.
808 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
809 called with some parameters.
810 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
811 at the position first passed to beginType3Char). the char ends with endType3Char.
813 The drawing operations between beginType3Char and endType3Char are somewhat different to
814 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
815 color determines the color of a font)
818 GBool VectorGraphicOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
820 return charDev->beginType3Char(state, x, y, dx, dy, charid, u, uLen);
823 void VectorGraphicOutputDev::type3D0(GfxState *state, double wx, double wy)
825 charDev->type3D0(state, wx, wy);
827 void VectorGraphicOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury)
829 charDev->type3D1(state, wx, wy, llx, lly, urx, ury);
832 void VectorGraphicOutputDev::endType3Char(GfxState *state)
834 charDev->endType3Char(state);
837 GBool VectorGraphicOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
838 int rotate, GBool useMediaBox, GBool crop,
839 int sliceX, int sliceY, int sliceW, int sliceH,
840 GBool printing, Catalog *catalog,
841 GBool (*abortCheckCbk)(void *data),
842 void *abortCheckCbkData)
844 this->setPage(page);
845 charDev->setPage(page);
846 return gTrue;
850 void VectorGraphicOutputDev::beginPage(GfxState *state, int pageNum)
852 this->currentpage = pageNum;
853 int rot = doc->getPageRotate(1);
854 gfxcolor_t white = {255,255,255,255};
855 gfxcolor_t black = {255,0,0,0};
856 laststate = state;
857 gfxline_t clippath[5];
859 msg("<notice> processing PDF page %d (%dx%d:%d:%d)", pageNum, this->width, this->height, -this->movex, -this->movey);
860 if(rot!=0)
861 msg("<verbose> page is rotated %d degrees", rot);
863 clippath[0].type = gfx_moveTo;clippath[0].x = 0; clippath[0].y = 0; clippath[0].next = &clippath[1];
864 clippath[1].type = gfx_lineTo;clippath[1].x = width; clippath[1].y = 0; clippath[1].next = &clippath[2];
865 clippath[2].type = gfx_lineTo;clippath[2].x = width; clippath[2].y = height; clippath[2].next = &clippath[3];
866 clippath[3].type = gfx_lineTo;clippath[3].x = 0; clippath[3].y = height; clippath[3].next = &clippath[4];
867 clippath[4].type = gfx_lineTo;clippath[4].x = 0; clippath[4].y = 0; clippath[4].next = 0;
869 device->startclip(device, clippath); outer_clip_box = 1;
870 if(!config_transparent) {
871 device->fill(device, clippath, &white);
874 states[statepos].clipbbox.xmin = 0;
875 states[statepos].clipbbox.ymin = 0;
876 states[statepos].clipbbox.xmax = this->width;
877 states[statepos].clipbbox.ymax = this->height;
879 states[statepos].dashPattern = 0;
880 states[statepos].dashLength = 0;
881 states[statepos].dashStart = 0;
883 charDev->startPage(pageNum, state);
886 void VectorGraphicOutputDev::saveState(GfxState *state) {
887 dbg("saveState %p", state); dbgindent+=2;
889 msg("<trace> saveState %p", state);
890 updateAll(state);
891 if(statepos>=64) {
892 msg("<fatal> Too many nested states in pdf.");
893 exit(1);
895 statepos ++;
896 states[statepos].state = state;
897 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
898 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
899 states[statepos].clipping = 0;
900 states[statepos].olddevice = 0;
901 states[statepos].clipbbox = states[statepos-1].clipbbox;
903 states[statepos].dashPattern = states[statepos-1].dashPattern;
904 states[statepos].dashStart = states[statepos-1].dashStart;
905 states[statepos].dashLength = states[statepos-1].dashLength;
908 void VectorGraphicOutputDev::restoreState(GfxState *state) {
909 dbgindent-=2; dbg("restoreState %p", state);
911 if(statepos==0) {
912 msg("<fatal> Invalid restoreState");
913 exit(1);
915 msg("<trace> restoreState %p%s%s", state,
916 states[statepos].softmask?" (end softmask)":"",
917 states[statepos].clipping?" (end clipping)":"");
918 if(states[statepos].softmask) {
919 clearSoftMask(state);
922 if(states[statepos].dashPattern) {
923 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
924 free(states[statepos].dashPattern);
925 states[statepos].dashPattern = 0;
929 updateAll(state);
931 while(states[statepos].clipping) {
932 device->endclip(device);
933 states[statepos].clipping--;
935 if(states[statepos].state!=state) {
936 msg("<fatal> bad state nesting");
937 if(verbose) {
938 int t;
939 for(t=0;t<=statepos;t++) {
940 printf("%p ", states[t].state);
942 printf("\n");
944 exit(1);
946 states[statepos].state=0;
947 statepos--;
950 void VectorGraphicOutputDev::updateLineDash(GfxState *state)
952 if(states[statepos].dashPattern &&
953 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
954 free(states[statepos].dashPattern);
955 states[statepos].dashPattern = 0;
957 double *pattern = 0;
958 int dashLength;
959 double dashStart;
960 state->getLineDash(&pattern, &dashLength, &dashStart);
961 msg("<debug> updateLineDash, %d dashes", dashLength);
962 if(!dashLength) {
963 states[statepos].dashPattern = 0;
964 states[statepos].dashLength = 0;
965 } else {
966 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
967 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
968 states[statepos].dashPattern = p;
969 states[statepos].dashLength = dashLength;
970 states[statepos].dashStart = dashStart;
974 #define SQR(x) ((x)*(x))
975 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
977 if((newwidth<1 || newheight<1) ||
978 (width<=newwidth || height<=newheight))
979 return 0;
980 unsigned char*newdata;
981 int x,y;
982 newdata= (unsigned char*)malloc(newwidth*newheight);
983 double fx = ((double)width)/newwidth;
984 double fy = ((double)height)/newheight;
985 double px = 0;
986 int blocksize = (int)(8192/(fx*fy));
987 int r = 8192*256/palettesize;
988 for(x=0;x<newwidth;x++) {
989 double ex = px + fx;
990 int fromx = (int)px;
991 int tox = (int)ex;
992 int xweight1 = (int)((1-(px-fromx))*256);
993 int xweight2 = (int)((ex-tox)*256);
994 double py =0;
995 for(y=0;y<newheight;y++) {
996 double ey = py + fy;
997 int fromy = (int)py;
998 int toy = (int)ey;
999 int yweight1 = (int)((1-(py-fromy))*256);
1000 int yweight2 = (int)((ey-toy)*256);
1001 int a = 0;
1002 int xx,yy;
1003 if(tox>=width)
1004 tox = width-1;
1005 if(toy>=height)
1006 toy = height-1;
1007 for(xx=fromx;xx<=tox;xx++)
1008 for(yy=fromy;yy<=toy;yy++) {
1009 int b = 1-data[width*yy+xx];
1010 int weight=256;
1011 if(xx==fromx) weight = (weight*xweight1)/256;
1012 if(xx==tox) weight = (weight*xweight2)/256;
1013 if(yy==fromy) weight = (weight*yweight1)/256;
1014 if(yy==toy) weight = (weight*yweight2)/256;
1015 a+=b*weight;
1017 //if(a) a=(palettesize-1)*r/blocksize;
1018 newdata[y*newwidth+x] = (a*blocksize)/r;
1019 py = ey;
1021 px = ex;
1023 return newdata;
1026 #define IMAGE_TYPE_JPEG 0
1027 #define IMAGE_TYPE_LOSSLESS 1
1029 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1030 double x1,double y1,
1031 double x2,double y2,
1032 double x3,double y3,
1033 double x4,double y4, int type, int multiply)
1035 gfxcolor_t*newpic=0;
1037 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1038 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1040 gfxline_t p1,p2,p3,p4,p5;
1041 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1042 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1043 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1044 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1045 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1047 {p1.x = (int)(p1.x*20)/20.0;
1048 p1.y = (int)(p1.y*20)/20.0;
1049 p2.x = (int)(p2.x*20)/20.0;
1050 p2.y = (int)(p2.y*20)/20.0;
1051 p3.x = (int)(p3.x*20)/20.0;
1052 p3.y = (int)(p3.y*20)/20.0;
1053 p4.x = (int)(p4.x*20)/20.0;
1054 p4.y = (int)(p4.y*20)/20.0;
1055 p5.x = (int)(p5.x*20)/20.0;
1056 p5.y = (int)(p5.y*20)/20.0;
1059 gfxmatrix_t m;
1060 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1061 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1063 m.tx = p1.x - 0.5*multiply;
1064 m.ty = p1.y - 0.5*multiply;
1066 gfximage_t img;
1067 img.data = (gfxcolor_t*)data;
1068 img.width = sizex;
1069 img.height = sizey;
1071 if(type == IMAGE_TYPE_JPEG)
1072 /* TODO: pass image_dpi to device instead */
1073 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1075 dump_outline(&p1);
1076 dev->fillbitmap(dev, &p1, &img, &m, 0);
1079 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1080 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
1082 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
1085 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1086 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
1088 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
1092 void VectorGraphicOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1093 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1094 GBool inlineImg, int mask, int*maskColors,
1095 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1097 /* the code in this function is *old*. It's not pretty, but it works. */
1099 double x1,y1,x2,y2,x3,y3,x4,y4;
1100 ImageStream *imgStr;
1101 Guchar pixBuf[4];
1102 GfxRGB rgb;
1103 int ncomps = 1;
1104 int bits = 1;
1105 unsigned char* maskbitmap = 0;
1107 if(colorMap) {
1108 ncomps = colorMap->getNumPixelComps();
1109 bits = colorMap->getBits();
1112 if(maskStr) {
1113 int x,y;
1114 unsigned char buf[8];
1115 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1116 if(maskColorMap) {
1117 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1118 imgMaskStr->reset();
1119 unsigned char pal[256];
1120 int n = 1 << colorMap->getBits();
1121 int t;
1122 for(t=0;t<n;t++) {
1123 GfxGray gray;
1124 pixBuf[0] = t;
1125 maskColorMap->getGray(pixBuf, &gray);
1126 pal[t] = colToByte(gray);
1128 for (y = 0; y < maskHeight; y++) {
1129 for (x = 0; x < maskWidth; x++) {
1130 imgMaskStr->getPixel(buf);
1131 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1134 delete imgMaskStr;
1135 } else {
1136 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1137 imgMaskStr->reset();
1138 for (y = 0; y < maskHeight; y++) {
1139 for (x = 0; x < maskWidth; x++) {
1140 imgMaskStr->getPixel(buf);
1141 buf[0]^=maskInvert;
1142 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1145 delete imgMaskStr;
1147 maskStr->close();
1150 imgStr = new ImageStream(str, width, ncomps,bits);
1151 imgStr->reset();
1153 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
1155 msg("<verbose> Ignoring %d by %d image", width, height);
1156 unsigned char buf[8];
1157 int x,y;
1158 for (y = 0; y < height; ++y)
1159 for (x = 0; x < width; ++x) {
1160 imgStr->getPixel(buf);
1162 delete imgStr;
1163 if(maskbitmap)
1164 free(maskbitmap);
1165 return;
1168 this->transformXY(state, 0, 1, &x1, &y1);
1169 this->transformXY(state, 0, 0, &x2, &y2);
1170 this->transformXY(state, 1, 0, &x3, &y3);
1171 this->transformXY(state, 1, 1, &x4, &y4);
1173 if(type3active) {
1174 /* as type 3 bitmaps are antialized, we need to place them
1175 at integer coordinates, otherwise flash player's antializing
1176 will kick in and make everything blurry */
1177 x1 = (int)(x1);y1 = (int)(y1);
1178 x2 = (int)(x2);y2 = (int)(y2);
1179 x3 = (int)(x3);y3 = (int)(y3);
1180 x4 = (int)(x4);y4 = (int)(y4);
1183 if(!(str->getKind()==strDCT)) {
1184 if(!type3active) {
1185 if(mask) infofeature("masked pbm pictures");
1186 else infofeature("pbm pictures");
1188 if(mask)
1189 msg("<verbose> drawing %d by %d masked picture", width, height);
1191 if(str->getKind()==strDCT) {
1192 infofeature("jpeg pictures");
1195 if(mask) {
1196 unsigned char buf[8];
1197 int x,y;
1198 unsigned char*pic = new unsigned char[width*height];
1199 gfxcolor_t pal[256];
1200 GfxRGB rgb;
1201 state->getFillRGB(&rgb);
1203 memset(pal,255,sizeof(pal));
1204 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1205 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1206 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1207 pal[0].a = 255; pal[1].a = 0;
1209 int numpalette = 2;
1210 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1211 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1212 for (y = 0; y < height; ++y)
1213 for (x = 0; x < width; ++x)
1215 imgStr->getPixel(buf);
1216 if(invert)
1217 buf[0]=1-buf[0];
1218 pic[width*y+x] = buf[0];
1221 if(type3active) {
1222 unsigned char*pic2 = 0;
1223 numpalette = 16;
1225 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1227 if(!pic2) {
1228 delete[] pic;
1229 delete imgStr;
1230 return;
1233 width = realwidth;
1234 height = realheight;
1235 delete[] pic;
1236 pic = pic2;
1238 /* make a black/white palette */
1240 float r = 255./(float)(numpalette-1);
1241 int t;
1242 for(t=0;t<numpalette;t++) {
1243 pal[t].r = colToByte(rgb.r);
1244 pal[t].g = colToByte(rgb.g);
1245 pal[t].b = colToByte(rgb.b);
1246 pal[t].a = (unsigned char)(t*r);
1251 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1252 for (y = 0; y < height; ++y) {
1253 for (x = 0; x < width; ++x) {
1254 pic2[width*y+x] = pal[pic[y*width+x]];
1257 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
1258 delete[] pic2;
1259 delete[] pic;
1260 delete imgStr;
1261 if(maskbitmap) free(maskbitmap);
1262 return;
1265 int x,y;
1267 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1268 gfxcolor_t*pic=new gfxcolor_t[width*height];
1269 for (y = 0; y < height; ++y) {
1270 for (x = 0; x < width; ++x) {
1271 imgStr->getPixel(pixBuf);
1272 colorMap->getRGB(pixBuf, &rgb);
1273 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1274 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1275 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1276 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1277 if(maskbitmap) {
1278 int x1 = x*maskWidth/width;
1279 int y1 = y*maskHeight/height;
1280 int x2 = (x+1)*maskWidth/width;
1281 int y2 = (y+1)*maskHeight/height;
1282 int xx,yy;
1283 unsigned int alpha=0;
1284 unsigned int count=0;
1285 for(xx=x1;xx<x2;xx++)
1286 for(yy=y1;yy<y2;yy++) {
1287 alpha += maskbitmap[yy*maskWidth+xx];
1288 count ++;
1290 if(count) {
1291 pic[width*y+x].a = alpha / count;
1292 } else {
1293 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
1298 if(str->getKind()==strDCT)
1299 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
1300 else
1301 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
1302 delete[] pic;
1303 delete imgStr;
1304 if(maskbitmap) free(maskbitmap);
1305 return;
1306 } else {
1307 gfxcolor_t*pic=new gfxcolor_t[width*height];
1308 gfxcolor_t pal[256];
1309 int n = 1 << colorMap->getBits();
1310 int t;
1311 for(t=0;t<256;t++) {
1312 pixBuf[0] = t;
1313 colorMap->getRGB(pixBuf, &rgb);
1314 pal[t].r = (unsigned char)(colToByte(rgb.r));
1315 pal[t].g = (unsigned char)(colToByte(rgb.g));
1316 pal[t].b = (unsigned char)(colToByte(rgb.b));
1317 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1319 for (y = 0; y < height; ++y) {
1320 for (x = 0; x < width; ++x) {
1321 imgStr->getPixel(pixBuf);
1322 pic[width*y+x] = pal[pixBuf[0]];
1323 if(maskColors && *maskColors==pixBuf[0]) {
1324 pic[width*y+x].a = 0;
1328 if(maskbitmap) {
1329 if(maskWidth < width && maskHeight < height) {
1330 for(y = 0; y < height; y++) {
1331 for (x = 0; x < width; x++) {
1332 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1335 } else {
1336 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
1337 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
1338 double dx = width / (double)maskWidth;
1339 double dy = height / (double)maskHeight;
1340 double yy = 0;
1341 for(y = 0; y < maskHeight; y++) {
1342 double xx = 0;
1343 for (x = 0; x < maskWidth; x++) {
1344 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
1345 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
1346 xx += dx;
1348 yy += dy;
1350 delete[] pic;
1351 pic = newpic;
1352 width = maskWidth;
1353 height = maskHeight;
1356 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
1358 delete[] pic;
1359 delete imgStr;
1360 if(maskbitmap) free(maskbitmap);
1361 return;
1365 void VectorGraphicOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1366 int width, int height, GBool invert,
1367 GBool inlineImg)
1369 if(config_textonly) {
1370 OutputDev::drawImageMask(state,ref,str,width,height,invert,inlineImg);
1371 return;
1373 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1374 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1375 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1378 void VectorGraphicOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1379 int width, int height, GfxImageColorMap *colorMap,
1380 int *maskColors, GBool inlineImg)
1382 if(config_textonly) {
1383 OutputDev::drawImage(state,ref,str,width,height,colorMap,maskColors,inlineImg);
1384 return;
1386 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1387 colorMap?"colorMap":"no colorMap",
1388 maskColors?"maskColors":"no maskColors",
1389 inlineImg);
1390 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1391 colorMap?"colorMap":"no colorMap",
1392 maskColors?"maskColors":"no maskColors",
1393 inlineImg);
1394 if(colorMap)
1395 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
1396 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1397 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1400 void VectorGraphicOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1401 int width, int height,
1402 GfxImageColorMap *colorMap,
1403 Stream *maskStr, int maskWidth, int maskHeight,
1404 GBool maskInvert)
1406 if(config_textonly) {
1407 OutputDev::drawMaskedImage(state,ref,str,width,height,colorMap,maskStr,maskWidth,maskHeight,maskInvert);
1408 return;
1410 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1411 colorMap?"colorMap":"no colorMap",
1412 maskWidth, maskHeight);
1413 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1414 colorMap?"colorMap":"no colorMap",
1415 maskWidth, maskHeight);
1416 if(colorMap)
1417 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
1418 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1419 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1422 void VectorGraphicOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1423 int width, int height,
1424 GfxImageColorMap *colorMap,
1425 Stream *maskStr,
1426 int maskWidth, int maskHeight,
1427 GfxImageColorMap *maskColorMap)
1429 if(config_textonly) {
1430 OutputDev::drawSoftMaskedImage(state,ref,str,width,height,colorMap,maskStr,maskWidth,maskHeight,maskColorMap);
1431 return;
1433 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1434 colorMap?"colorMap":"no colorMap",
1435 maskWidth, maskHeight);
1436 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1437 colorMap?"colorMap":"no colorMap",
1438 maskWidth, maskHeight);
1439 if(colorMap)
1440 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
1441 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1442 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1446 void VectorGraphicOutputDev::stroke(GfxState *state)
1448 if(config_textonly) {return;}
1450 dbg("stroke");
1452 GfxPath * path = state->getPath();
1453 gfxline_t*line= gfxPath_to_gfxline(state, path, 0);
1454 strokeGfxline(state, line, 0);
1455 gfxline_free(line);
1458 void VectorGraphicOutputDev::fill(GfxState *state)
1460 if(config_textonly) {return;}
1462 gfxcolor_t col = gfxstate_getfillcolor(state);
1463 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1465 GfxPath * path = state->getPath();
1466 gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
1467 if(!config_disable_polygon_conversion) {
1468 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1469 gfxline_free(line);
1470 line = line2;
1472 fillGfxLine(state, line, 0);
1473 gfxline_free(line);
1476 void VectorGraphicOutputDev::eoFill(GfxState *state)
1478 if(config_textonly) {return;}
1480 gfxcolor_t col = gfxstate_getfillcolor(state);
1481 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1483 GfxPath * path = state->getPath();
1484 gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
1485 fillGfxLine(state, line, 1);
1486 gfxline_free(line);
1490 void VectorGraphicOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
1491 GfxColorSpace *blendingColorSpace,
1492 GBool isolated, GBool knockout,
1493 GBool forSoftMask)
1495 const char*colormodename = "";
1497 if(blendingColorSpace) {
1498 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
1500 dbg("beginTransparencyGroup device=%p %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", device, bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
1501 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);
1503 //states[statepos].createsoftmask |= forSoftMask;
1504 states[statepos].createsoftmask = forSoftMask;
1505 states[statepos].transparencygroup = !forSoftMask;
1506 states[statepos].isolated = isolated;
1508 states[statepos].olddevice = this->device;
1509 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
1510 dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
1512 gfxdevice_record_init(this->device, 0);
1514 /*if(!forSoftMask) { ////???
1515 state->setFillOpacity(0.0);
1517 dbgindent+=2;
1520 void VectorGraphicOutputDev::endTransparencyGroup(GfxState *state)
1522 dbgindent-=2;
1523 gfxdevice_t*r = this->device;
1525 dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
1527 this->device = states[statepos].olddevice;
1528 if(!this->device) {
1529 msg("<error> Invalid state nesting");
1531 states[statepos].olddevice = 0;
1533 gfxresult_t*recording = r->finish(r);
1535 dbg(" forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
1536 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
1538 if(states[statepos].createsoftmask) {
1539 states[statepos-1].softmaskrecording = recording;
1540 } else {
1541 states[statepos-1].grouprecording = recording;
1544 states[statepos].createsoftmask = 0;
1545 states[statepos].transparencygroup = 0;
1546 free(r);
1549 void VectorGraphicOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
1551 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
1552 "colordodge","colorburn","hardlight","softlight","difference",
1553 "exclusion","hue","saturation","color","luminosity"};
1555 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
1556 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
1558 if(state->getBlendMode() == gfxBlendNormal)
1559 infofeature("transparency groups");
1560 else {
1561 char buffer[80];
1562 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
1563 warnfeature(buffer, 0);
1566 gfxresult_t*grouprecording = states[statepos].grouprecording;
1568 int blendmode = state->getBlendMode();
1569 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
1570 int alpha = (int)(state->getFillOpacity()*255);
1571 if(blendmode == gfxBlendMultiply && alpha>200)
1572 alpha = 128;
1573 gfxdevice_t ops;
1574 dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
1575 gfxdevice_ops_init(&ops, this->device, alpha);
1576 gfxresult_record_replay(grouprecording, &ops, 0);
1577 ops.finish(&ops);
1579 grouprecording->destroy(grouprecording);
1581 states[statepos].grouprecording = 0;
1584 void VectorGraphicOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
1586 if(states[statepos].softmask) {
1587 /* shouldn't happen, but *does* happen */
1588 clearSoftMask(state);
1591 /* alpha = 1: retrieve mask values from alpha layer
1592 alpha = 0: retrieve mask values from luminance */
1594 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
1595 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
1596 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
1597 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
1598 if(!alpha)
1599 infofeature("soft masks");
1600 else
1601 warnfeature("soft masks from alpha channel",0);
1603 if(states[statepos].olddevice) {
1604 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
1605 exit(1);
1607 states[statepos].olddevice = this->device;
1608 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
1609 gfxdevice_record_init(this->device, 0);
1611 dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
1613 states[statepos].softmask = 1;
1614 states[statepos].softmask_alpha = alpha;
1617 static inline Guchar div255(int x) {
1618 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
1621 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
1623 if(c < min) c = min;
1624 if(c > max) c = max;
1625 return c;
1628 void VectorGraphicOutputDev::clearSoftMask(GfxState *state)
1630 if(!states[statepos].softmask)
1631 return;
1632 states[statepos].softmask = 0;
1633 dbg("clearSoftMask statepos=%d", statepos);
1634 msg("<verbose> clearSoftMask statepos=%d", statepos);
1636 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
1637 msg("<error> Error in softmask/tgroup ordering");
1638 return;
1641 gfxresult_t*mask = states[statepos].softmaskrecording;
1642 gfxresult_t*below = this->device->finish(this->device);free(this->device);
1643 this->device = states[statepos].olddevice;
1645 /* get outline of all objects below the soft mask */
1646 gfxdevice_t uniondev;
1647 gfxdevice_union_init(&uniondev, 0);
1648 gfxresult_record_replay(below, &uniondev, 0);
1649 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
1650 uniondev.finish(&uniondev);
1651 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
1652 gfxline_free(belowoutline);belowoutline=0;
1653 #if 0
1654 this->device->startclip(this->device, belowoutline);
1655 gfxresult_record_replay(below, this->device, 0);
1656 gfxresult_record_replay(mask, this->device, 0);
1657 this->device->endclip(this->device);
1658 #endif
1660 int width = (int)bbox.xmax,height = (int)bbox.ymax;
1661 if(width<=0 || height<=0)
1662 return;
1664 gfxdevice_t belowrender;
1665 gfxdevice_render_init(&belowrender);
1666 if(states[statepos+1].isolated) {
1667 belowrender.setparameter(&belowrender, "fillwhite", "1");
1669 belowrender.setparameter(&belowrender, "antialize", "2");
1670 belowrender.startpage(&belowrender, width, height);
1671 gfxresult_record_replay(below, &belowrender, 0);
1672 belowrender.endpage(&belowrender);
1673 gfxresult_t* belowresult = belowrender.finish(&belowrender);
1674 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
1675 //png_write("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
1677 gfxdevice_t maskrender;
1678 gfxdevice_render_init(&maskrender);
1679 maskrender.startpage(&maskrender, width, height);
1680 gfxresult_record_replay(mask, &maskrender, 0);
1681 maskrender.endpage(&maskrender);
1682 gfxresult_t* maskresult = maskrender.finish(&maskrender);
1683 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
1685 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
1686 msg("<fatal> Internal error in mask drawing");
1687 return;
1690 int y,x;
1691 for(y=0;y<height;y++) {
1692 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
1693 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
1694 for(x=0;x<width;x++) {
1695 int alpha;
1696 if(states[statepos].softmask_alpha) {
1697 alpha = l1->a;
1698 } else {
1699 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
1702 l2->a = div255(alpha*l2->a);
1704 /* DON'T premultiply alpha- this is done by fillbitmap,
1705 depending on the output device */
1706 //l2->r = div255(alpha*l2->r);
1707 //l2->g = div255(alpha*l2->g);
1708 //l2->b = div255(alpha*l2->b);
1710 l1++;
1711 l2++;
1714 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
1716 gfxmatrix_t matrix;
1717 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
1718 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
1720 if(!config_textonly) {
1721 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
1724 mask->destroy(mask);
1725 below->destroy(below);
1726 maskresult->destroy(maskresult);
1727 belowresult->destroy(belowresult);
1728 states[statepos].softmaskrecording = 0;
1731 //class MemCheck
1733 // public: ~MemCheck()
1734 // {
1735 // delete globalParams;globalParams=0;
1736 // Object::memCheck(stderr);
1737 // gMemReport(stderr);
1738 // }
1739 //} myMemCheck;