new option disable_tiling_pattern_fills
[swftools.git] / lib / pdf / VectorGraphicOutputDev.cc
blob9194a0263c5f401e57c1082d5a6bc081c6d6b590
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);
873 states[statepos].clipbbox.xmin = 0;
874 states[statepos].clipbbox.ymin = 0;
875 states[statepos].clipbbox.xmax = this->width;
876 states[statepos].clipbbox.ymax = this->height;
878 states[statepos].dashPattern = 0;
879 states[statepos].dashLength = 0;
880 states[statepos].dashStart = 0;
882 charDev->startPage(pageNum, state);
885 void VectorGraphicOutputDev::saveState(GfxState *state) {
886 dbg("saveState %p", state); dbgindent+=2;
888 msg("<trace> saveState %p", state);
889 updateAll(state);
890 if(statepos>=64) {
891 msg("<fatal> Too many nested states in pdf.");
892 exit(1);
894 statepos ++;
895 states[statepos].state = state;
896 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
897 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
898 states[statepos].clipping = 0;
899 states[statepos].olddevice = 0;
900 states[statepos].clipbbox = states[statepos-1].clipbbox;
902 states[statepos].dashPattern = states[statepos-1].dashPattern;
903 states[statepos].dashStart = states[statepos-1].dashStart;
904 states[statepos].dashLength = states[statepos-1].dashLength;
907 void VectorGraphicOutputDev::restoreState(GfxState *state) {
908 dbgindent-=2; dbg("restoreState %p", state);
910 if(statepos==0) {
911 msg("<fatal> Invalid restoreState");
912 exit(1);
914 msg("<trace> restoreState %p%s%s", state,
915 states[statepos].softmask?" (end softmask)":"",
916 states[statepos].clipping?" (end clipping)":"");
917 if(states[statepos].softmask) {
918 clearSoftMask(state);
921 if(states[statepos].dashPattern) {
922 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
923 free(states[statepos].dashPattern);
924 states[statepos].dashPattern = 0;
928 updateAll(state);
930 while(states[statepos].clipping) {
931 device->endclip(device);
932 states[statepos].clipping--;
934 if(states[statepos].state!=state) {
935 msg("<fatal> bad state nesting");
936 if(verbose) {
937 int t;
938 for(t=0;t<=statepos;t++) {
939 printf("%p ", states[t].state);
941 printf("\n");
943 exit(1);
945 states[statepos].state=0;
946 statepos--;
949 void VectorGraphicOutputDev::updateLineDash(GfxState *state)
951 if(states[statepos].dashPattern &&
952 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
953 free(states[statepos].dashPattern);
954 states[statepos].dashPattern = 0;
956 double *pattern = 0;
957 int dashLength;
958 double dashStart;
959 state->getLineDash(&pattern, &dashLength, &dashStart);
960 msg("<debug> updateLineDash, %d dashes", dashLength);
961 if(!dashLength) {
962 states[statepos].dashPattern = 0;
963 states[statepos].dashLength = 0;
964 } else {
965 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
966 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
967 states[statepos].dashPattern = p;
968 states[statepos].dashLength = dashLength;
969 states[statepos].dashStart = dashStart;
973 #define SQR(x) ((x)*(x))
974 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
976 if((newwidth<1 || newheight<1) ||
977 (width<=newwidth || height<=newheight))
978 return 0;
979 unsigned char*newdata;
980 int x,y;
981 newdata= (unsigned char*)malloc(newwidth*newheight);
982 double fx = ((double)width)/newwidth;
983 double fy = ((double)height)/newheight;
984 double px = 0;
985 int blocksize = (int)(8192/(fx*fy));
986 int r = 8192*256/palettesize;
987 for(x=0;x<newwidth;x++) {
988 double ex = px + fx;
989 int fromx = (int)px;
990 int tox = (int)ex;
991 int xweight1 = (int)((1-(px-fromx))*256);
992 int xweight2 = (int)((ex-tox)*256);
993 double py =0;
994 for(y=0;y<newheight;y++) {
995 double ey = py + fy;
996 int fromy = (int)py;
997 int toy = (int)ey;
998 int yweight1 = (int)((1-(py-fromy))*256);
999 int yweight2 = (int)((ey-toy)*256);
1000 int a = 0;
1001 int xx,yy;
1002 if(tox>=width)
1003 tox = width-1;
1004 if(toy>=height)
1005 toy = height-1;
1006 for(xx=fromx;xx<=tox;xx++)
1007 for(yy=fromy;yy<=toy;yy++) {
1008 int b = 1-data[width*yy+xx];
1009 int weight=256;
1010 if(xx==fromx) weight = (weight*xweight1)/256;
1011 if(xx==tox) weight = (weight*xweight2)/256;
1012 if(yy==fromy) weight = (weight*yweight1)/256;
1013 if(yy==toy) weight = (weight*yweight2)/256;
1014 a+=b*weight;
1016 //if(a) a=(palettesize-1)*r/blocksize;
1017 newdata[y*newwidth+x] = (a*blocksize)/r;
1018 py = ey;
1020 px = ex;
1022 return newdata;
1025 #define IMAGE_TYPE_JPEG 0
1026 #define IMAGE_TYPE_LOSSLESS 1
1028 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1029 double x1,double y1,
1030 double x2,double y2,
1031 double x3,double y3,
1032 double x4,double y4, int type, int multiply)
1034 gfxcolor_t*newpic=0;
1036 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1037 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1039 gfxline_t p1,p2,p3,p4,p5;
1040 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1041 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1042 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1043 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1044 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1046 {p1.x = (int)(p1.x*20)/20.0;
1047 p1.y = (int)(p1.y*20)/20.0;
1048 p2.x = (int)(p2.x*20)/20.0;
1049 p2.y = (int)(p2.y*20)/20.0;
1050 p3.x = (int)(p3.x*20)/20.0;
1051 p3.y = (int)(p3.y*20)/20.0;
1052 p4.x = (int)(p4.x*20)/20.0;
1053 p4.y = (int)(p4.y*20)/20.0;
1054 p5.x = (int)(p5.x*20)/20.0;
1055 p5.y = (int)(p5.y*20)/20.0;
1058 gfxmatrix_t m;
1059 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1060 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1062 m.tx = p1.x - 0.5*multiply;
1063 m.ty = p1.y - 0.5*multiply;
1065 gfximage_t img;
1066 img.data = (gfxcolor_t*)data;
1067 img.width = sizex;
1068 img.height = sizey;
1070 if(type == IMAGE_TYPE_JPEG)
1071 /* TODO: pass image_dpi to device instead */
1072 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1074 dump_outline(&p1);
1075 dev->fillbitmap(dev, &p1, &img, &m, 0);
1078 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1079 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
1081 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
1084 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1085 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
1087 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
1091 void VectorGraphicOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1092 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1093 GBool inlineImg, int mask, int*maskColors,
1094 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1096 /* the code in this function is *old*. It's not pretty, but it works. */
1098 double x1,y1,x2,y2,x3,y3,x4,y4;
1099 ImageStream *imgStr;
1100 Guchar pixBuf[4];
1101 GfxRGB rgb;
1102 int ncomps = 1;
1103 int bits = 1;
1104 unsigned char* maskbitmap = 0;
1106 if(colorMap) {
1107 ncomps = colorMap->getNumPixelComps();
1108 bits = colorMap->getBits();
1111 if(maskStr) {
1112 int x,y;
1113 unsigned char buf[8];
1114 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1115 if(maskColorMap) {
1116 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1117 imgMaskStr->reset();
1118 unsigned char pal[256];
1119 int n = 1 << colorMap->getBits();
1120 int t;
1121 for(t=0;t<n;t++) {
1122 GfxGray gray;
1123 pixBuf[0] = t;
1124 maskColorMap->getGray(pixBuf, &gray);
1125 pal[t] = colToByte(gray);
1127 for (y = 0; y < maskHeight; y++) {
1128 for (x = 0; x < maskWidth; x++) {
1129 imgMaskStr->getPixel(buf);
1130 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1133 delete imgMaskStr;
1134 } else {
1135 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1136 imgMaskStr->reset();
1137 for (y = 0; y < maskHeight; y++) {
1138 for (x = 0; x < maskWidth; x++) {
1139 imgMaskStr->getPixel(buf);
1140 buf[0]^=maskInvert;
1141 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1144 delete imgMaskStr;
1146 maskStr->close();
1149 imgStr = new ImageStream(str, width, ncomps,bits);
1150 imgStr->reset();
1152 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
1154 msg("<verbose> Ignoring %d by %d image", width, height);
1155 unsigned char buf[8];
1156 int x,y;
1157 for (y = 0; y < height; ++y)
1158 for (x = 0; x < width; ++x) {
1159 imgStr->getPixel(buf);
1161 delete imgStr;
1162 if(maskbitmap)
1163 free(maskbitmap);
1164 return;
1167 this->transformXY(state, 0, 1, &x1, &y1);
1168 this->transformXY(state, 0, 0, &x2, &y2);
1169 this->transformXY(state, 1, 0, &x3, &y3);
1170 this->transformXY(state, 1, 1, &x4, &y4);
1172 if(type3active) {
1173 /* as type 3 bitmaps are antialized, we need to place them
1174 at integer coordinates, otherwise flash player's antializing
1175 will kick in and make everything blurry */
1176 x1 = (int)(x1);y1 = (int)(y1);
1177 x2 = (int)(x2);y2 = (int)(y2);
1178 x3 = (int)(x3);y3 = (int)(y3);
1179 x4 = (int)(x4);y4 = (int)(y4);
1182 if(!(str->getKind()==strDCT)) {
1183 if(!type3active) {
1184 if(mask) infofeature("masked pbm pictures");
1185 else infofeature("pbm pictures");
1187 if(mask)
1188 msg("<verbose> drawing %d by %d masked picture", width, height);
1190 if(str->getKind()==strDCT) {
1191 infofeature("jpeg pictures");
1194 if(mask) {
1195 unsigned char buf[8];
1196 int x,y;
1197 unsigned char*pic = new unsigned char[width*height];
1198 gfxcolor_t pal[256];
1199 GfxRGB rgb;
1200 state->getFillRGB(&rgb);
1202 memset(pal,255,sizeof(pal));
1203 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1204 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1205 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1206 pal[0].a = 255; pal[1].a = 0;
1208 int numpalette = 2;
1209 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1210 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1211 for (y = 0; y < height; ++y)
1212 for (x = 0; x < width; ++x)
1214 imgStr->getPixel(buf);
1215 if(invert)
1216 buf[0]=1-buf[0];
1217 pic[width*y+x] = buf[0];
1220 if(type3active) {
1221 unsigned char*pic2 = 0;
1222 numpalette = 16;
1224 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1226 if(!pic2) {
1227 delete[] pic;
1228 delete imgStr;
1229 return;
1232 width = realwidth;
1233 height = realheight;
1234 delete[] pic;
1235 pic = pic2;
1237 /* make a black/white palette */
1239 float r = 255./(float)(numpalette-1);
1240 int t;
1241 for(t=0;t<numpalette;t++) {
1242 pal[t].r = colToByte(rgb.r);
1243 pal[t].g = colToByte(rgb.g);
1244 pal[t].b = colToByte(rgb.b);
1245 pal[t].a = (unsigned char)(t*r);
1250 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1251 for (y = 0; y < height; ++y) {
1252 for (x = 0; x < width; ++x) {
1253 pic2[width*y+x] = pal[pic[y*width+x]];
1256 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
1257 delete[] pic2;
1258 delete[] pic;
1259 delete imgStr;
1260 if(maskbitmap) free(maskbitmap);
1261 return;
1264 int x,y;
1266 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1267 gfxcolor_t*pic=new gfxcolor_t[width*height];
1268 for (y = 0; y < height; ++y) {
1269 for (x = 0; x < width; ++x) {
1270 imgStr->getPixel(pixBuf);
1271 colorMap->getRGB(pixBuf, &rgb);
1272 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1273 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1274 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1275 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1276 if(maskbitmap) {
1277 int x1 = x*maskWidth/width;
1278 int y1 = y*maskHeight/height;
1279 int x2 = (x+1)*maskWidth/width;
1280 int y2 = (y+1)*maskHeight/height;
1281 int xx,yy;
1282 unsigned int alpha=0;
1283 unsigned int count=0;
1284 for(xx=x1;xx<x2;xx++)
1285 for(yy=y1;yy<y2;yy++) {
1286 alpha += maskbitmap[yy*maskWidth+xx];
1287 count ++;
1289 if(count) {
1290 pic[width*y+x].a = alpha / count;
1291 } else {
1292 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
1297 if(str->getKind()==strDCT)
1298 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
1299 else
1300 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
1301 delete[] pic;
1302 delete imgStr;
1303 if(maskbitmap) free(maskbitmap);
1304 return;
1305 } else {
1306 gfxcolor_t*pic=new gfxcolor_t[width*height];
1307 gfxcolor_t pal[256];
1308 int n = 1 << colorMap->getBits();
1309 int t;
1310 for(t=0;t<256;t++) {
1311 pixBuf[0] = t;
1312 colorMap->getRGB(pixBuf, &rgb);
1313 pal[t].r = (unsigned char)(colToByte(rgb.r));
1314 pal[t].g = (unsigned char)(colToByte(rgb.g));
1315 pal[t].b = (unsigned char)(colToByte(rgb.b));
1316 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1318 for (y = 0; y < height; ++y) {
1319 for (x = 0; x < width; ++x) {
1320 imgStr->getPixel(pixBuf);
1321 pic[width*y+x] = pal[pixBuf[0]];
1322 if(maskColors && *maskColors==pixBuf[0]) {
1323 pic[width*y+x].a = 0;
1327 if(maskbitmap) {
1328 if(maskWidth < width && maskHeight < height) {
1329 for(y = 0; y < height; y++) {
1330 for (x = 0; x < width; x++) {
1331 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1334 } else {
1335 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
1336 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
1337 double dx = width / (double)maskWidth;
1338 double dy = height / (double)maskHeight;
1339 double yy = 0;
1340 for(y = 0; y < maskHeight; y++) {
1341 double xx = 0;
1342 for (x = 0; x < maskWidth; x++) {
1343 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
1344 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
1345 xx += dx;
1347 yy += dy;
1349 delete[] pic;
1350 pic = newpic;
1351 width = maskWidth;
1352 height = maskHeight;
1355 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
1357 delete[] pic;
1358 delete imgStr;
1359 if(maskbitmap) free(maskbitmap);
1360 return;
1364 void VectorGraphicOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1365 int width, int height, GBool invert,
1366 GBool inlineImg)
1368 if(config_textonly) {
1369 OutputDev::drawImageMask(state,ref,str,width,height,invert,inlineImg);
1370 return;
1372 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1373 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1374 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1377 void VectorGraphicOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1378 int width, int height, GfxImageColorMap *colorMap,
1379 int *maskColors, GBool inlineImg)
1381 if(config_textonly) {
1382 OutputDev::drawImage(state,ref,str,width,height,colorMap,maskColors,inlineImg);
1383 return;
1385 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1386 colorMap?"colorMap":"no colorMap",
1387 maskColors?"maskColors":"no maskColors",
1388 inlineImg);
1389 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1390 colorMap?"colorMap":"no colorMap",
1391 maskColors?"maskColors":"no maskColors",
1392 inlineImg);
1393 if(colorMap)
1394 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
1395 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1396 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1399 void VectorGraphicOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1400 int width, int height,
1401 GfxImageColorMap *colorMap,
1402 Stream *maskStr, int maskWidth, int maskHeight,
1403 GBool maskInvert)
1405 if(config_textonly) {
1406 OutputDev::drawMaskedImage(state,ref,str,width,height,colorMap,maskStr,maskWidth,maskHeight,maskInvert);
1407 return;
1409 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1410 colorMap?"colorMap":"no colorMap",
1411 maskWidth, maskHeight);
1412 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1413 colorMap?"colorMap":"no colorMap",
1414 maskWidth, maskHeight);
1415 if(colorMap)
1416 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
1417 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1418 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1421 void VectorGraphicOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1422 int width, int height,
1423 GfxImageColorMap *colorMap,
1424 Stream *maskStr,
1425 int maskWidth, int maskHeight,
1426 GfxImageColorMap *maskColorMap)
1428 if(config_textonly) {
1429 OutputDev::drawSoftMaskedImage(state,ref,str,width,height,colorMap,maskStr,maskWidth,maskHeight,maskColorMap);
1430 return;
1432 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1433 colorMap?"colorMap":"no colorMap",
1434 maskWidth, maskHeight);
1435 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1436 colorMap?"colorMap":"no colorMap",
1437 maskWidth, maskHeight);
1438 if(colorMap)
1439 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
1440 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1441 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1445 void VectorGraphicOutputDev::stroke(GfxState *state)
1447 if(config_textonly) {return;}
1449 dbg("stroke");
1451 GfxPath * path = state->getPath();
1452 gfxline_t*line= gfxPath_to_gfxline(state, path, 0);
1453 strokeGfxline(state, line, 0);
1454 gfxline_free(line);
1457 void VectorGraphicOutputDev::fill(GfxState *state)
1459 if(config_textonly) {return;}
1461 gfxcolor_t col = gfxstate_getfillcolor(state);
1462 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1464 GfxPath * path = state->getPath();
1465 gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
1466 if(!config_disable_polygon_conversion) {
1467 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1468 gfxline_free(line);
1469 line = line2;
1471 fillGfxLine(state, line, 0);
1472 gfxline_free(line);
1475 void VectorGraphicOutputDev::eoFill(GfxState *state)
1477 if(config_textonly) {return;}
1479 gfxcolor_t col = gfxstate_getfillcolor(state);
1480 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1482 GfxPath * path = state->getPath();
1483 gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
1484 fillGfxLine(state, line, 1);
1485 gfxline_free(line);
1489 void VectorGraphicOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
1490 GfxColorSpace *blendingColorSpace,
1491 GBool isolated, GBool knockout,
1492 GBool forSoftMask)
1494 const char*colormodename = "";
1496 if(blendingColorSpace) {
1497 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
1499 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);
1500 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);
1502 //states[statepos].createsoftmask |= forSoftMask;
1503 states[statepos].createsoftmask = forSoftMask;
1504 states[statepos].transparencygroup = !forSoftMask;
1505 states[statepos].isolated = isolated;
1507 states[statepos].olddevice = this->device;
1508 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
1509 dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
1511 gfxdevice_record_init(this->device, 0);
1513 /*if(!forSoftMask) { ////???
1514 state->setFillOpacity(0.0);
1516 dbgindent+=2;
1519 void VectorGraphicOutputDev::endTransparencyGroup(GfxState *state)
1521 dbgindent-=2;
1522 gfxdevice_t*r = this->device;
1524 dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
1526 this->device = states[statepos].olddevice;
1527 if(!this->device) {
1528 msg("<error> Invalid state nesting");
1530 states[statepos].olddevice = 0;
1532 gfxresult_t*recording = r->finish(r);
1534 dbg(" forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
1535 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
1537 if(states[statepos].createsoftmask) {
1538 states[statepos-1].softmaskrecording = recording;
1539 } else {
1540 states[statepos-1].grouprecording = recording;
1543 states[statepos].createsoftmask = 0;
1544 states[statepos].transparencygroup = 0;
1545 free(r);
1548 void VectorGraphicOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
1550 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
1551 "colordodge","colorburn","hardlight","softlight","difference",
1552 "exclusion","hue","saturation","color","luminosity"};
1554 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
1555 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
1557 if(state->getBlendMode() == gfxBlendNormal)
1558 infofeature("transparency groups");
1559 else {
1560 char buffer[80];
1561 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
1562 warnfeature(buffer, 0);
1565 gfxresult_t*grouprecording = states[statepos].grouprecording;
1567 int blendmode = state->getBlendMode();
1568 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
1569 int alpha = (int)(state->getFillOpacity()*255);
1570 if(blendmode == gfxBlendMultiply && alpha>200)
1571 alpha = 128;
1572 gfxdevice_t ops;
1573 dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
1574 gfxdevice_ops_init(&ops, this->device, alpha);
1575 gfxresult_record_replay(grouprecording, &ops, 0);
1576 ops.finish(&ops);
1578 grouprecording->destroy(grouprecording);
1580 states[statepos].grouprecording = 0;
1583 void VectorGraphicOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
1585 if(states[statepos].softmask) {
1586 /* shouldn't happen, but *does* happen */
1587 clearSoftMask(state);
1590 /* alpha = 1: retrieve mask values from alpha layer
1591 alpha = 0: retrieve mask values from luminance */
1593 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
1594 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
1595 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
1596 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
1597 if(!alpha)
1598 infofeature("soft masks");
1599 else
1600 warnfeature("soft masks from alpha channel",0);
1602 if(states[statepos].olddevice) {
1603 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
1604 exit(1);
1606 states[statepos].olddevice = this->device;
1607 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
1608 gfxdevice_record_init(this->device, 0);
1610 dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
1612 states[statepos].softmask = 1;
1613 states[statepos].softmask_alpha = alpha;
1616 static inline Guchar div255(int x) {
1617 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
1620 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
1622 if(c < min) c = min;
1623 if(c > max) c = max;
1624 return c;
1627 void VectorGraphicOutputDev::clearSoftMask(GfxState *state)
1629 if(!states[statepos].softmask)
1630 return;
1631 states[statepos].softmask = 0;
1632 dbg("clearSoftMask statepos=%d", statepos);
1633 msg("<verbose> clearSoftMask statepos=%d", statepos);
1635 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
1636 msg("<error> Error in softmask/tgroup ordering");
1637 return;
1640 gfxresult_t*mask = states[statepos].softmaskrecording;
1641 gfxresult_t*below = this->device->finish(this->device);free(this->device);
1642 this->device = states[statepos].olddevice;
1644 /* get outline of all objects below the soft mask */
1645 gfxdevice_t uniondev;
1646 gfxdevice_union_init(&uniondev, 0);
1647 gfxresult_record_replay(below, &uniondev, 0);
1648 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
1649 uniondev.finish(&uniondev);
1650 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
1651 gfxline_free(belowoutline);belowoutline=0;
1652 #if 0
1653 this->device->startclip(this->device, belowoutline);
1654 gfxresult_record_replay(below, this->device, 0);
1655 gfxresult_record_replay(mask, this->device, 0);
1656 this->device->endclip(this->device);
1657 #endif
1659 int width = (int)bbox.xmax,height = (int)bbox.ymax;
1660 if(width<=0 || height<=0)
1661 return;
1663 gfxdevice_t belowrender;
1664 gfxdevice_render_init(&belowrender);
1665 if(states[statepos+1].isolated) {
1666 belowrender.setparameter(&belowrender, "fillwhite", "1");
1668 belowrender.setparameter(&belowrender, "antialize", "2");
1669 belowrender.startpage(&belowrender, width, height);
1670 gfxresult_record_replay(below, &belowrender, 0);
1671 belowrender.endpage(&belowrender);
1672 gfxresult_t* belowresult = belowrender.finish(&belowrender);
1673 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
1674 //png_write("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
1676 gfxdevice_t maskrender;
1677 gfxdevice_render_init(&maskrender);
1678 maskrender.startpage(&maskrender, width, height);
1679 gfxresult_record_replay(mask, &maskrender, 0);
1680 maskrender.endpage(&maskrender);
1681 gfxresult_t* maskresult = maskrender.finish(&maskrender);
1682 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
1684 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
1685 msg("<fatal> Internal error in mask drawing");
1686 return;
1689 int y,x;
1690 for(y=0;y<height;y++) {
1691 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
1692 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
1693 for(x=0;x<width;x++) {
1694 int alpha;
1695 if(states[statepos].softmask_alpha) {
1696 alpha = l1->a;
1697 } else {
1698 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
1701 l2->a = div255(alpha*l2->a);
1703 /* DON'T premultiply alpha- this is done by fillbitmap,
1704 depending on the output device */
1705 //l2->r = div255(alpha*l2->r);
1706 //l2->g = div255(alpha*l2->g);
1707 //l2->b = div255(alpha*l2->b);
1709 l1++;
1710 l2++;
1713 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
1715 gfxmatrix_t matrix;
1716 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
1717 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
1719 if(!config_textonly) {
1720 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
1723 mask->destroy(mask);
1724 below->destroy(below);
1725 maskresult->destroy(maskresult);
1726 belowresult->destroy(belowresult);
1727 states[statepos].softmaskrecording = 0;
1730 //class MemCheck
1732 // public: ~MemCheck()
1733 // {
1734 // delete globalParams;globalParams=0;
1735 // Object::memCheck(stderr);
1736 // gMemReport(stderr);
1737 // }
1738 //} myMemCheck;