dashpattern 0/0 now makes lines invisible
[swftools.git] / lib / pdf / VectorGraphicOutputDev.cc
blob715aa8f8d5ede7e96cca75a50adb0ca8b03d8cbe
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);
131 this->charDev->setParameter(key, value);
134 void VectorGraphicOutputDev::setDevice(gfxdevice_t*dev)
136 this->device = dev;
137 charDev->setDevice(dev);
140 //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");}
141 //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");}
143 void dump_outline(gfxline_t*line)
145 /*gfxbbox_t*r = gfxline_isrectangle(line);
146 if(!r)
147 printf("is not a rectangle\n");
148 else
149 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
152 while(line) {
153 if(line->type == gfx_moveTo) {
154 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
155 } else if(line->type == gfx_lineTo) {
156 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
157 } else if(line->type == gfx_splineTo) {
158 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
160 line = line->next;
164 void gfxPath_dump(GfxPath*path)
166 int num = path->getNumSubpaths();
167 int t;
168 int cpos=0;
169 for(t = 0; t < num; t++) {
170 GfxSubpath *subpath = path->getSubpath(t);
171 int subnum = subpath->getNumPoints();
172 int s;
173 for(s=0;s<subnum;s++) {
174 double x=subpath->getX(s);
175 double y=subpath->getY(s);
176 if(s==0 && !subpath->getCurve(s)) {
177 printf("M %f %f\n", x, y);
178 } else if(s==0 && subpath->getCurve(s)) {
179 printf("E %f %f\n", x, y);
180 } else if(subpath->getCurve(s)) {
181 printf("C %f %f\n", x, y);
182 } else {
183 printf("T %f %f\n", x, y);
189 gfxline_t* VectorGraphicOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed)
191 int num = path->getNumSubpaths();
192 int s,t;
193 int cpos = 0;
194 double lastx=0,lasty=0,posx=0,posy=0;
195 int needsfix=0;
196 if(!num) {
197 msg("<warning> empty path");
198 return 0;
200 gfxdrawer_t draw;
201 gfxdrawer_target_gfxline(&draw);
203 for(t = 0; t < num; t++) {
204 GfxSubpath *subpath = path->getSubpath(t);
205 int subnum = subpath->getNumPoints();
206 double bx=0,by=0,cx=0,cy=0;
208 for(s=0;s<subnum;s++) {
209 double x,y;
211 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
213 if(s==0) {
214 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
215 draw.lineTo(&draw, lastx, lasty);
217 draw.moveTo(&draw, x,y);
218 posx = lastx = x;
219 posy = lasty = y;
220 cpos = 0;
221 needsfix = 0;
222 } else if(subpath->getCurve(s) && cpos==0) {
223 bx = x;
224 by = y;
225 cpos = 1;
226 } else if(subpath->getCurve(s) && cpos==1) {
227 cx = x;
228 cy = y;
229 cpos = 2;
230 } else {
231 posx = x;
232 posy = y;
233 if(cpos==0) {
234 draw.lineTo(&draw, x,y);
235 } else {
236 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
238 needsfix = 1;
239 cpos = 0;
243 /* fix non-closed lines */
244 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
245 draw.lineTo(&draw, lastx, lasty);
247 gfxline_t*result = (gfxline_t*)draw.result(&draw);
249 gfxline_optimize(result);
251 return result;
254 GBool VectorGraphicOutputDev::useTilingPatternFill()
256 infofeature("tiled patterns");
257 // if(config_convertgradients)
258 // return gTrue;
259 return gFalse;
261 GBool VectorGraphicOutputDev::useShadedFills()
263 infofeature("shaded fills");
264 if(config_convertgradients)
265 return gTrue;
266 return gFalse;
269 POPPLER_TILING_PATERN_RETURN VectorGraphicOutputDev::tilingPatternFill(GfxState *state,
270 POPPLER_TILING_PATERN_GFX
271 Object *str,
272 int paintType, Dict *resDict,
273 double *mat, double *bbox,
274 int x0, int y0, int x1, int y1,
275 double xStep, double yStep)
277 msg("<debug> tilingPatternFill");
278 infofeature("tiling pattern fills");
279 #ifdef HAVE_POPPLER
280 // since we don't implement this method yet,
281 // reduce it to a series of other drawing operations.
282 return gFalse;
283 #endif
286 GBool VectorGraphicOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
288 msg("<error> functionShadedFill not supported yet");
289 infofeature("function shaded fills");
290 return gFalse;
292 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
294 gfxcolor_t c;
295 GfxRGB rgb;
296 colspace->getRGB(col, &rgb);
297 c.r = colToByte(rgb.r);
298 c.g = colToByte(rgb.g);
299 c.b = colToByte(rgb.b);
300 c.a = 255;
301 return c;
304 GBool VectorGraphicOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
306 if(config_textonly) {return gTrue;}
308 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
309 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
310 x1=x0+r1;y1=y0;
311 x2=x0; y2=y0+r1;
312 this->transformXY(state, x0,y0, &x0,&y0);
313 this->transformXY(state, x1,y1, &x1,&y1);
314 this->transformXY(state, x2,y2, &x2,&y2);
316 GfxColor color0;
317 GfxColor color1;
318 GfxColor color2;
319 shading->getColor(0.0, &color0);
320 shading->getColor(0.5, &color1);
321 shading->getColor(1.0, &color2);
323 GfxColorSpace* colspace = shading->getColorSpace();
325 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
326 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
327 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
328 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
329 infofeature("radial shaded fills");
331 gfxgradient_t gr[3];
332 gfxgradient_t*g = &gr[0];
333 g[0].next = &g[1];
334 g[1].next = &g[2];
335 g[2].next = 0;
336 g[0].color = col2col(colspace, &color0);
337 g[1].color = col2col(colspace, &color1);
338 g[2].color = col2col(colspace, &color2);
339 g[0].pos = 0.0;
340 g[1].pos = 0.5;
341 g[2].pos = 1.0;
343 gfxbbox_t b = states[statepos].clipbbox;
344 gfxline_t p1,p2,p3,p4,p5;
345 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
346 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
347 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
348 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
349 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
351 gfxmatrix_t m;
352 //m.m00 = (x3-x0); m.m10 = (x1-x0);
353 //m.m01 = (y3-y0); m.m11 = (y1-y0);
354 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
355 m.m00 = (x1-x0); m.m10 = (x2-x0);
356 m.m01 = (y1-y0); m.m11 = (y2-y0);
357 m.tx = x0 - 0.5;
358 m.ty = y0 - 0.5;
360 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
361 return gTrue;
364 GBool VectorGraphicOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
366 if(config_textonly) {return gTrue;}
368 double x0,y0,x1,y1;
369 shading->getCoords(&x0,&y0,&x1,&y1);
370 this->transformXY(state, x0,y0,&x0,&y0);
371 this->transformXY(state, x1,y1,&x1,&y1);
373 GfxColor color0;
374 GfxColor color1;
375 GfxColor color2;
376 shading->getColor(0.0, &color0);
377 shading->getColor(0.5, &color1);
378 shading->getColor(1.0, &color2);
380 GfxColorSpace* colspace = shading->getColorSpace();
382 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
383 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
384 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
385 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
387 infofeature("axial shaded fills");
389 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
390 g[0].next = &g[1];
391 g[1].next = &g[2];
392 g[2].next = 0;
393 g[0].color = col2col(colspace, &color0);
394 g[1].color = col2col(colspace, &color1);
395 g[2].color = col2col(colspace, &color2);
396 g[0].pos = 0.0;
397 g[1].pos = 0.5;
398 g[2].pos = 1.0;
400 double xMin,yMin,xMax,yMax;
401 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
402 this->transformXY(state, xMin, yMin, &xMin, &yMin);
403 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
405 xMin = 0; yMin = 0;
406 xMin = 1024; yMin = 1024;
408 gfxbbox_t b = states[statepos].clipbbox;
409 gfxline_t p1,p2,p3,p4,p5;
410 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
411 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
412 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
413 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
414 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
416 /* the gradient starts at (-1.0,0.0), so move (0,0) to
417 the middle of the two control points */
418 gfxmatrix_t m;
419 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
420 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
421 m.tx = (x0 + x1)/2 - 0.5;
422 m.ty = (y0 + y1)/2 - 0.5;
424 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
426 free(g);
427 return gTrue;
430 GBool VectorGraphicOutputDev::useDrawForm()
432 infofeature("forms");
433 return gFalse;
435 void VectorGraphicOutputDev::drawForm(Ref id)
437 msg("<error> drawForm not implemented");
439 GBool VectorGraphicOutputDev::needNonText()
441 return gTrue;
443 void VectorGraphicOutputDev::endPage()
445 msg("<verbose> endPage (VectorGraphicOutputDev)");
446 charDev->endPage(); // link postprocessing
447 if(outer_clip_box) {
448 device->endclip(device);
449 outer_clip_box = 0;
452 void VectorGraphicOutputDev::setDefaultCTM(double *ctm)
454 charDev->setDefaultCTM(ctm);
455 OutputDev::setDefaultCTM(ctm);
458 static inline double sqr(double x) {return x*x;}
460 #define STROKE_FILL 1
461 #define STROKE_CLIP 2
462 void VectorGraphicOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
464 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
465 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
466 double miterLimit = state->getMiterLimit();
467 double width = state->getTransformedLineWidth();
469 GfxRGB rgb;
470 double opaq = state->getStrokeOpacity();
471 if(type3active)
472 state->getFillRGB(&rgb);
473 else
474 state->getStrokeRGB(&rgb);
475 gfxcolor_t col;
476 col.r = colToByte(rgb.r);
477 col.g = colToByte(rgb.g);
478 col.b = colToByte(rgb.b);
479 col.a = (unsigned char)(opaq*255);
481 gfx_capType capType = gfx_capRound;
482 if(lineCap == 0) capType = gfx_capButt;
483 else if(lineCap == 1) capType = gfx_capRound;
484 else if(lineCap == 2) capType = gfx_capSquare;
485 else msg("<error> Invalid line cap type");
487 gfx_joinType joinType = gfx_joinRound;
488 if(lineJoin == 0) joinType = gfx_joinMiter;
489 else if(lineJoin == 1) joinType = gfx_joinRound;
490 else if(lineJoin == 2) joinType = gfx_joinBevel;
491 else msg("<error> Invalid line join type");
493 gfxline_t*line2 = 0;
495 int dashLength = states[statepos].dashLength;
496 double*dashPattern = states[statepos].dashPattern;
497 double dashStart = states[statepos].dashStart;
498 if(dashLength && dashPattern) {
499 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
500 int t;
502 /* try to find out how much the transformation matrix would
503 stretch the dashes, and factor that into the dash lengths.
504 This is not the entirely correct approach- it would be
505 better to first convert the path to an unscaled version,
506 then apply dashing, and then transform the path using
507 the current transformation matrix. However there are few
508 PDFs which actually stretch a dashed path in a non-orthonormal
509 way */
510 double tx1, ty1, tx2, ty2, tx3, ty3;
511 this->transformXY(state, 0, 0, &tx1, &ty1);
512 this->transformXY(state, 0, 1, &tx2, &ty2);
513 this->transformXY(state, 1, 0, &tx3, &ty3);
514 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
515 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
516 if(fabs(d1-d2)>0.5)
517 warnfeature("non-ortogonally dashed strokes", 0);
518 double f = (d1+d2)/2;
520 if(!dashStart && dashLength==1 && !dashPattern[0]) {
521 // zero phase and zero dashlength make the line invisible
522 return;
525 msg("<trace> %d dashes", dashLength);
526 msg("<trace> | phase: %f", dashStart);
527 for(t=0;t<dashLength;t++) {
528 dash[t] = (float)dashPattern[t] * f;
529 if(!dash[t]) {
530 dash[t] = 1e-37;
532 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
534 dash[dashLength] = -1;
535 if(getLogLevel() >= LOGLEVEL_TRACE) {
536 dump_outline(line);
539 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
540 line = line2;
542 free(dash);
543 msg("<trace> After dashing:");
546 if(getLogLevel() >= LOGLEVEL_TRACE) {
547 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
548 width,
549 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
550 lineCap==0?"butt": (lineCap==1?"round":"square"),
551 dashLength,
552 col.r,col.g,col.b,col.a
554 dump_outline(line);
557 if(flags&STROKE_FILL) {
558 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
559 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
560 if(getLogLevel() >= LOGLEVEL_TRACE) {
561 dump_outline(gfxline);
563 if(!gfxline) {
564 msg("<warning> Empty polygon (resulting from stroked line)");
566 if(flags&STROKE_CLIP) {
567 device->startclip(device, gfxline);
568 states[statepos].clipping++;
569 } else {
570 device->fill(device, gfxline, &col);
572 gfxline_free(gfxline);
573 gfxpoly_destroy(poly);
574 } else {
575 if(flags&STROKE_CLIP)
576 msg("<error> Stroke&clip not supported at the same time");
577 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
580 if(line2)
581 gfxline_free(line2);
584 void VectorGraphicOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
586 gfxcolor_t col = gfxstate_getfillcolor(state);
588 if(getLogLevel() >= LOGLEVEL_TRACE) {
589 msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
590 dump_outline(line);
592 device->fill(device, line, &col);
595 void VectorGraphicOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
597 if(getLogLevel() >= LOGLEVEL_TRACE) {
598 msg("<trace> %sclip", evenodd?"eo":"");
599 dump_outline(line);
601 gfxbbox_t bbox = gfxline_getbbox(line);
602 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
604 device->startclip(device, line);
605 states[statepos].clipping++;
608 void VectorGraphicOutputDev::clip(GfxState *state)
610 GfxPath * path = state->getPath();
611 msg("<trace> clip");
612 gfxline_t*line = gfxPath_to_gfxline(state, path, 1);
613 if(!config_disable_polygon_conversion) {
614 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
615 gfxline_free(line);
616 line = line2;
618 clipToGfxLine(state, line, 0);
619 gfxline_free(line);
622 void VectorGraphicOutputDev::eoClip(GfxState *state)
624 GfxPath * path = state->getPath();
625 gfxline_t*line = gfxPath_to_gfxline(state, path, 1);
626 clipToGfxLine(state, line, 1);
627 gfxline_free(line);
629 void VectorGraphicOutputDev::clipToStrokePath(GfxState *state)
631 GfxPath * path = state->getPath();
632 gfxline_t*line= gfxPath_to_gfxline(state, path, 0);
634 if(getLogLevel() >= LOGLEVEL_TRACE) {
635 double width = state->getTransformedLineWidth();
636 msg("<trace> cliptostrokepath width=%f", width);
637 dump_outline(line);
640 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
641 gfxline_free(line);
644 void VectorGraphicOutputDev::finish()
646 if(outer_clip_box) {
647 if(device) {
648 device->endclip(device);
650 outer_clip_box = 0;
654 VectorGraphicOutputDev::~VectorGraphicOutputDev()
656 finish();
657 delete charDev;charDev=0;
659 GBool VectorGraphicOutputDev::upsideDown()
661 return gTrue;
663 GBool VectorGraphicOutputDev::useDrawChar()
665 return gTrue;
668 void VectorGraphicOutputDev::updateFontMatrix(GfxState*state)
670 this->current_font_matrix = gfxmatrix_from_state(state);
671 charDev->updateTextMat(state);
674 void VectorGraphicOutputDev::updateFont(GfxState*state)
676 charDev->updateFont(state);
679 void VectorGraphicOutputDev::processLink(Link *link, Catalog *catalog)
681 charDev->processLink(link, catalog);
684 void VectorGraphicOutputDev::beginString(GfxState *state, GString *s)
686 int render = state->getRender();
687 if(current_text_stroke) {
688 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
690 charDev->beginString(state, s);
693 static gfxline_t* mkEmptyGfxShape(double x, double y)
695 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
696 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
697 return line;
700 void addfont_callback(gfxdevice_t*dev, gfxfont_t*font)
702 VectorGraphicOutputDev*self = (VectorGraphicOutputDev*)dev->internal;
703 self->device->addfont(self->device, font);
705 void drawchar_callback(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
707 VectorGraphicOutputDev*self = (VectorGraphicOutputDev*)dev->internal;
708 self->gfxfont_from_callback = font;
709 self->glyphnr_from_callback = glyph;
710 memcpy(&self->textcolor_from_callback, color, sizeof(gfxcolor_t));
711 memcpy(&self->textmatrix_from_callback, matrix, sizeof(gfxmatrix_t));
714 void VectorGraphicOutputDev::drawChar(GfxState *state, double x, double y,
715 double dx, double dy,
716 double originX, double originY,
717 CharCode charid, int nBytes, Unicode *_u, int uLen)
719 int render = state->getRender();
720 if(((render == RENDER_FILL) ||
721 (render == RENDER_FILLSTROKE && state->getTransformedLineWidth()<1.0) ||
722 (render == RENDER_INVISIBLE))) {
723 charDev->drawChar(state, x, y, dx, dy, originX, originY, charid, nBytes, _u, uLen);
724 return;
727 msg("<debug> Drawing glyph %d as shape", charid);
728 infofeature("text rendered as shape");
730 charDev->setDevice(&char_output_dev);
731 this->gfxfont_from_callback = 0;
732 this->glyphnr_from_callback = 0;
733 charDev->drawChar(state, x, y, dx, dy, originX, originY, charid, nBytes, _u, uLen);
734 charDev->setDevice(device);
736 if(!gfxfont_from_callback) {
737 // some chars are ignored by CharOutputDev
738 return;
740 gfxline_t*glyph = gfxfont_from_callback->glyphs[glyphnr_from_callback].line;
742 gfxline_t*tglyph = gfxline_clone(glyph);
743 gfxline_transform(tglyph, &textmatrix_from_callback);
744 if((render&3) != RENDER_INVISIBLE) {
745 gfxline_t*add = gfxline_clone(tglyph);
746 current_text_stroke = gfxline_append(current_text_stroke, add);
748 if(render&RENDER_CLIP) {
749 gfxline_t*add = gfxline_clone(tglyph);
750 current_text_clip = gfxline_append(current_text_clip, add);
751 if(!current_text_clip) {
752 current_text_clip = mkEmptyGfxShape(textmatrix_from_callback.tx, textmatrix_from_callback.ty);
755 gfxline_free(tglyph);
758 void VectorGraphicOutputDev::endString(GfxState *state)
760 int render = state->getRender();
761 msg("<trace> endString() render=%d textstroke=%p", render, current_text_stroke);
763 if(current_text_stroke) {
764 /* fillstroke and stroke text rendering objects we can process right
765 now (as there may be texts of other rendering modes in this
766 text object)- clipping objects have to wait until endTextObject,
767 however */
768 device->setparameter(device, "mark","TXT");
769 if((render&3) == RENDER_FILL) {
770 fillGfxLine(state, current_text_stroke, 0);
771 gfxline_free(current_text_stroke);
772 current_text_stroke = 0;
773 } else if((render&3) == RENDER_FILLSTROKE) {
774 fillGfxLine(state, current_text_stroke, 0);
775 strokeGfxline(state, current_text_stroke,0);
776 gfxline_free(current_text_stroke);
777 current_text_stroke = 0;
778 } else if((render&3) == RENDER_STROKE) {
779 strokeGfxline(state, current_text_stroke,0);
780 gfxline_free(current_text_stroke);
781 current_text_stroke = 0;
783 device->setparameter(device, "mark","");
787 void VectorGraphicOutputDev::endTextObject(GfxState *state)
789 int render = state->getRender();
790 msg("<trace> endTextObject() render=%d textstroke=%p clipstroke=%p", render, current_text_stroke, current_text_clip);
792 if(current_text_clip) {
793 device->setparameter(device, "mark","TXT");
794 clipToGfxLine(state, current_text_clip, 0);
795 device->setparameter(device, "mark","");
796 gfxline_free(current_text_clip);
797 current_text_clip = 0;
801 /* the logic seems to be as following:
802 first, beginType3Char is called, with the charcode and the coordinates.
803 if this function returns true, it already knew about the char and has now drawn it.
804 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
805 called with some parameters.
806 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
807 at the position first passed to beginType3Char). the char ends with endType3Char.
809 The drawing operations between beginType3Char and endType3Char are somewhat different to
810 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
811 color determines the color of a font)
814 GBool VectorGraphicOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
816 return charDev->beginType3Char(state, x, y, dx, dy, charid, u, uLen);
819 void VectorGraphicOutputDev::type3D0(GfxState *state, double wx, double wy)
821 charDev->type3D0(state, wx, wy);
823 void VectorGraphicOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury)
825 charDev->type3D1(state, wx, wy, llx, lly, urx, ury);
828 void VectorGraphicOutputDev::endType3Char(GfxState *state)
830 charDev->endType3Char(state);
833 GBool VectorGraphicOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
834 int rotate, GBool useMediaBox, GBool crop,
835 int sliceX, int sliceY, int sliceW, int sliceH,
836 GBool printing, Catalog *catalog,
837 GBool (*abortCheckCbk)(void *data),
838 void *abortCheckCbkData)
840 this->setPage(page);
841 charDev->setPage(page);
842 return gTrue;
846 void VectorGraphicOutputDev::beginPage(GfxState *state, int pageNum)
848 this->currentpage = pageNum;
849 int rot = doc->getPageRotate(1);
850 gfxcolor_t white = {255,255,255,255};
851 gfxcolor_t black = {255,0,0,0};
852 laststate = state;
853 gfxline_t clippath[5];
855 msg("<notice> processing PDF page %d (%dx%d:%d:%d)", pageNum, this->width, this->height, -this->movex, -this->movey);
856 if(rot!=0)
857 msg("<verbose> page is rotated %d degrees", rot);
859 clippath[0].type = gfx_moveTo;clippath[0].x = 0; clippath[0].y = 0; clippath[0].next = &clippath[1];
860 clippath[1].type = gfx_lineTo;clippath[1].x = width; clippath[1].y = 0; clippath[1].next = &clippath[2];
861 clippath[2].type = gfx_lineTo;clippath[2].x = width; clippath[2].y = height; clippath[2].next = &clippath[3];
862 clippath[3].type = gfx_lineTo;clippath[3].x = 0; clippath[3].y = height; clippath[3].next = &clippath[4];
863 clippath[4].type = gfx_lineTo;clippath[4].x = 0; clippath[4].y = 0; clippath[4].next = 0;
865 device->startclip(device, clippath); outer_clip_box = 1;
866 if(!config_transparent) {
867 device->fill(device, clippath, &white);
869 states[statepos].clipbbox.xmin = 0;
870 states[statepos].clipbbox.ymin = 0;
871 states[statepos].clipbbox.xmax = this->width;
872 states[statepos].clipbbox.ymax = this->height;
874 states[statepos].dashPattern = 0;
875 states[statepos].dashLength = 0;
876 states[statepos].dashStart = 0;
878 charDev->startPage(pageNum, state);
881 void VectorGraphicOutputDev::saveState(GfxState *state) {
882 dbg("saveState %p", state); dbgindent+=2;
884 msg("<trace> saveState %p", state);
885 updateAll(state);
886 if(statepos>=64) {
887 msg("<fatal> Too many nested states in pdf.");
888 exit(1);
890 statepos ++;
891 states[statepos].state = state;
892 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
893 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
894 states[statepos].clipping = 0;
895 states[statepos].olddevice = 0;
896 states[statepos].clipbbox = states[statepos-1].clipbbox;
898 states[statepos].dashPattern = states[statepos-1].dashPattern;
899 states[statepos].dashStart = states[statepos-1].dashStart;
900 states[statepos].dashLength = states[statepos-1].dashLength;
903 void VectorGraphicOutputDev::restoreState(GfxState *state) {
904 dbgindent-=2; dbg("restoreState %p", state);
906 if(statepos==0) {
907 msg("<fatal> Invalid restoreState");
908 exit(1);
910 msg("<trace> restoreState %p%s%s", state,
911 states[statepos].softmask?" (end softmask)":"",
912 states[statepos].clipping?" (end clipping)":"");
913 if(states[statepos].softmask) {
914 clearSoftMask(state);
917 if(states[statepos].dashPattern) {
918 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
919 free(states[statepos].dashPattern);
920 states[statepos].dashPattern = 0;
924 updateAll(state);
926 while(states[statepos].clipping) {
927 device->endclip(device);
928 states[statepos].clipping--;
930 if(states[statepos].state!=state) {
931 msg("<fatal> bad state nesting");
932 if(verbose) {
933 int t;
934 for(t=0;t<=statepos;t++) {
935 printf("%p ", states[t].state);
937 printf("\n");
939 exit(1);
941 states[statepos].state=0;
942 statepos--;
945 void VectorGraphicOutputDev::updateLineDash(GfxState *state)
947 if(states[statepos].dashPattern &&
948 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
949 free(states[statepos].dashPattern);
950 states[statepos].dashPattern = 0;
952 double *pattern = 0;
953 int dashLength;
954 double dashStart;
955 state->getLineDash(&pattern, &dashLength, &dashStart);
956 msg("<debug> updateLineDash, %d dashes", dashLength);
957 if(!dashLength) {
958 states[statepos].dashPattern = 0;
959 states[statepos].dashLength = 0;
960 } else {
961 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
962 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
963 states[statepos].dashPattern = p;
964 states[statepos].dashLength = dashLength;
965 states[statepos].dashStart = dashStart;
969 #define SQR(x) ((x)*(x))
970 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
972 if((newwidth<1 || newheight<1) ||
973 (width<=newwidth || height<=newheight))
974 return 0;
975 unsigned char*newdata;
976 int x,y;
977 newdata= (unsigned char*)malloc(newwidth*newheight);
978 double fx = ((double)width)/newwidth;
979 double fy = ((double)height)/newheight;
980 double px = 0;
981 int blocksize = (int)(8192/(fx*fy));
982 int r = 8192*256/palettesize;
983 for(x=0;x<newwidth;x++) {
984 double ex = px + fx;
985 int fromx = (int)px;
986 int tox = (int)ex;
987 int xweight1 = (int)((1-(px-fromx))*256);
988 int xweight2 = (int)((ex-tox)*256);
989 double py =0;
990 for(y=0;y<newheight;y++) {
991 double ey = py + fy;
992 int fromy = (int)py;
993 int toy = (int)ey;
994 int yweight1 = (int)((1-(py-fromy))*256);
995 int yweight2 = (int)((ey-toy)*256);
996 int a = 0;
997 int xx,yy;
998 if(tox>=width)
999 tox = width-1;
1000 if(toy>=height)
1001 toy = height-1;
1002 for(xx=fromx;xx<=tox;xx++)
1003 for(yy=fromy;yy<=toy;yy++) {
1004 int b = 1-data[width*yy+xx];
1005 int weight=256;
1006 if(xx==fromx) weight = (weight*xweight1)/256;
1007 if(xx==tox) weight = (weight*xweight2)/256;
1008 if(yy==fromy) weight = (weight*yweight1)/256;
1009 if(yy==toy) weight = (weight*yweight2)/256;
1010 a+=b*weight;
1012 //if(a) a=(palettesize-1)*r/blocksize;
1013 newdata[y*newwidth+x] = (a*blocksize)/r;
1014 py = ey;
1016 px = ex;
1018 return newdata;
1021 #define IMAGE_TYPE_JPEG 0
1022 #define IMAGE_TYPE_LOSSLESS 1
1024 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1025 double x1,double y1,
1026 double x2,double y2,
1027 double x3,double y3,
1028 double x4,double y4, int type, int multiply)
1030 gfxcolor_t*newpic=0;
1032 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1033 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1035 gfxline_t p1,p2,p3,p4,p5;
1036 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1037 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1038 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1039 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1040 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1042 {p1.x = (int)(p1.x*20)/20.0;
1043 p1.y = (int)(p1.y*20)/20.0;
1044 p2.x = (int)(p2.x*20)/20.0;
1045 p2.y = (int)(p2.y*20)/20.0;
1046 p3.x = (int)(p3.x*20)/20.0;
1047 p3.y = (int)(p3.y*20)/20.0;
1048 p4.x = (int)(p4.x*20)/20.0;
1049 p4.y = (int)(p4.y*20)/20.0;
1050 p5.x = (int)(p5.x*20)/20.0;
1051 p5.y = (int)(p5.y*20)/20.0;
1054 gfxmatrix_t m;
1055 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1056 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1058 m.tx = p1.x - 0.5*multiply;
1059 m.ty = p1.y - 0.5*multiply;
1061 gfximage_t img;
1062 img.data = (gfxcolor_t*)data;
1063 img.width = sizex;
1064 img.height = sizey;
1066 if(type == IMAGE_TYPE_JPEG)
1067 /* TODO: pass image_dpi to device instead */
1068 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1070 dump_outline(&p1);
1071 dev->fillbitmap(dev, &p1, &img, &m, 0);
1074 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1075 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
1077 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
1080 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1081 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
1083 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
1087 void VectorGraphicOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1088 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1089 GBool inlineImg, int mask, int*maskColors,
1090 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1092 /* the code in this function is *old*. It's not pretty, but it works. */
1094 double x1,y1,x2,y2,x3,y3,x4,y4;
1095 ImageStream *imgStr;
1096 Guchar pixBuf[4];
1097 GfxRGB rgb;
1098 int ncomps = 1;
1099 int bits = 1;
1100 unsigned char* maskbitmap = 0;
1102 if(colorMap) {
1103 ncomps = colorMap->getNumPixelComps();
1104 bits = colorMap->getBits();
1107 if(maskStr) {
1108 int x,y;
1109 unsigned char buf[8];
1110 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1111 if(maskColorMap) {
1112 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1113 imgMaskStr->reset();
1114 unsigned char pal[256];
1115 int n = 1 << colorMap->getBits();
1116 int t;
1117 for(t=0;t<n;t++) {
1118 GfxGray gray;
1119 pixBuf[0] = t;
1120 maskColorMap->getGray(pixBuf, &gray);
1121 pal[t] = colToByte(gray);
1123 for (y = 0; y < maskHeight; y++) {
1124 for (x = 0; x < maskWidth; x++) {
1125 imgMaskStr->getPixel(buf);
1126 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1129 delete imgMaskStr;
1130 } else {
1131 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1132 imgMaskStr->reset();
1133 for (y = 0; y < maskHeight; y++) {
1134 for (x = 0; x < maskWidth; x++) {
1135 imgMaskStr->getPixel(buf);
1136 buf[0]^=maskInvert;
1137 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1140 delete imgMaskStr;
1142 maskStr->close();
1145 imgStr = new ImageStream(str, width, ncomps,bits);
1146 imgStr->reset();
1148 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
1150 msg("<verbose> Ignoring %d by %d image", width, height);
1151 unsigned char buf[8];
1152 int x,y;
1153 for (y = 0; y < height; ++y)
1154 for (x = 0; x < width; ++x) {
1155 imgStr->getPixel(buf);
1157 delete imgStr;
1158 if(maskbitmap)
1159 free(maskbitmap);
1160 return;
1163 this->transformXY(state, 0, 1, &x1, &y1);
1164 this->transformXY(state, 0, 0, &x2, &y2);
1165 this->transformXY(state, 1, 0, &x3, &y3);
1166 this->transformXY(state, 1, 1, &x4, &y4);
1168 if(type3active) {
1169 /* as type 3 bitmaps are antialized, we need to place them
1170 at integer coordinates, otherwise flash player's antializing
1171 will kick in and make everything blurry */
1172 x1 = (int)(x1);y1 = (int)(y1);
1173 x2 = (int)(x2);y2 = (int)(y2);
1174 x3 = (int)(x3);y3 = (int)(y3);
1175 x4 = (int)(x4);y4 = (int)(y4);
1178 if(!(str->getKind()==strDCT)) {
1179 if(!type3active) {
1180 if(mask) infofeature("masked pbm pictures");
1181 else infofeature("pbm pictures");
1183 if(mask)
1184 msg("<verbose> drawing %d by %d masked picture", width, height);
1186 if(str->getKind()==strDCT) {
1187 infofeature("jpeg pictures");
1190 if(mask) {
1191 unsigned char buf[8];
1192 int x,y;
1193 unsigned char*pic = new unsigned char[width*height];
1194 gfxcolor_t pal[256];
1195 GfxRGB rgb;
1196 state->getFillRGB(&rgb);
1198 memset(pal,255,sizeof(pal));
1199 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1200 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1201 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1202 pal[0].a = 255; pal[1].a = 0;
1204 int numpalette = 2;
1205 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1206 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1207 for (y = 0; y < height; ++y)
1208 for (x = 0; x < width; ++x)
1210 imgStr->getPixel(buf);
1211 if(invert)
1212 buf[0]=1-buf[0];
1213 pic[width*y+x] = buf[0];
1216 if(type3active) {
1217 unsigned char*pic2 = 0;
1218 numpalette = 16;
1220 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1222 if(!pic2) {
1223 delete[] pic;
1224 delete imgStr;
1225 return;
1228 width = realwidth;
1229 height = realheight;
1230 delete[] pic;
1231 pic = pic2;
1233 /* make a black/white palette */
1235 float r = 255./(float)(numpalette-1);
1236 int t;
1237 for(t=0;t<numpalette;t++) {
1238 pal[t].r = colToByte(rgb.r);
1239 pal[t].g = colToByte(rgb.g);
1240 pal[t].b = colToByte(rgb.b);
1241 pal[t].a = (unsigned char)(t*r);
1246 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1247 for (y = 0; y < height; ++y) {
1248 for (x = 0; x < width; ++x) {
1249 pic2[width*y+x] = pal[pic[y*width+x]];
1252 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
1253 delete[] pic2;
1254 delete[] pic;
1255 delete imgStr;
1256 if(maskbitmap) free(maskbitmap);
1257 return;
1260 int x,y;
1262 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1263 gfxcolor_t*pic=new gfxcolor_t[width*height];
1264 for (y = 0; y < height; ++y) {
1265 for (x = 0; x < width; ++x) {
1266 imgStr->getPixel(pixBuf);
1267 colorMap->getRGB(pixBuf, &rgb);
1268 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1269 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1270 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1271 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1272 if(maskbitmap) {
1273 int x1 = x*maskWidth/width;
1274 int y1 = y*maskHeight/height;
1275 int x2 = (x+1)*maskWidth/width;
1276 int y2 = (y+1)*maskHeight/height;
1277 int xx,yy;
1278 unsigned int alpha=0;
1279 unsigned int count=0;
1280 for(xx=x1;xx<x2;xx++)
1281 for(yy=y1;yy<y2;yy++) {
1282 alpha += maskbitmap[yy*maskWidth+xx];
1283 count ++;
1285 if(count) {
1286 pic[width*y+x].a = alpha / count;
1287 } else {
1288 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
1293 if(str->getKind()==strDCT)
1294 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
1295 else
1296 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
1297 delete[] pic;
1298 delete imgStr;
1299 if(maskbitmap) free(maskbitmap);
1300 return;
1301 } else {
1302 gfxcolor_t*pic=new gfxcolor_t[width*height];
1303 gfxcolor_t pal[256];
1304 int n = 1 << colorMap->getBits();
1305 int t;
1306 for(t=0;t<256;t++) {
1307 pixBuf[0] = t;
1308 colorMap->getRGB(pixBuf, &rgb);
1309 pal[t].r = (unsigned char)(colToByte(rgb.r));
1310 pal[t].g = (unsigned char)(colToByte(rgb.g));
1311 pal[t].b = (unsigned char)(colToByte(rgb.b));
1312 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1314 for (y = 0; y < height; ++y) {
1315 for (x = 0; x < width; ++x) {
1316 imgStr->getPixel(pixBuf);
1317 pic[width*y+x] = pal[pixBuf[0]];
1318 if(maskColors && *maskColors==pixBuf[0]) {
1319 pic[width*y+x].a = 0;
1323 if(maskbitmap) {
1324 if(maskWidth < width && maskHeight < height) {
1325 for(y = 0; y < height; y++) {
1326 for (x = 0; x < width; x++) {
1327 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1330 } else {
1331 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
1332 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
1333 double dx = width / (double)maskWidth;
1334 double dy = height / (double)maskHeight;
1335 double yy = 0;
1336 for(y = 0; y < maskHeight; y++) {
1337 double xx = 0;
1338 for (x = 0; x < maskWidth; x++) {
1339 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
1340 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
1341 xx += dx;
1343 yy += dy;
1345 delete[] pic;
1346 pic = newpic;
1347 width = maskWidth;
1348 height = maskHeight;
1351 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
1353 delete[] pic;
1354 delete imgStr;
1355 if(maskbitmap) free(maskbitmap);
1356 return;
1360 void VectorGraphicOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1361 int width, int height, GBool invert,
1362 GBool inlineImg)
1364 if(config_textonly) {
1365 OutputDev::drawImageMask(state,ref,str,width,height,invert,inlineImg);
1366 return;
1368 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1369 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1370 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1373 void VectorGraphicOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1374 int width, int height, GfxImageColorMap *colorMap,
1375 int *maskColors, GBool inlineImg)
1377 if(config_textonly) {
1378 OutputDev::drawImage(state,ref,str,width,height,colorMap,maskColors,inlineImg);
1379 return;
1381 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1382 colorMap?"colorMap":"no colorMap",
1383 maskColors?"maskColors":"no maskColors",
1384 inlineImg);
1385 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1386 colorMap?"colorMap":"no colorMap",
1387 maskColors?"maskColors":"no maskColors",
1388 inlineImg);
1389 if(colorMap)
1390 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
1391 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1392 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1395 void VectorGraphicOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1396 int width, int height,
1397 GfxImageColorMap *colorMap,
1398 Stream *maskStr, int maskWidth, int maskHeight,
1399 GBool maskInvert)
1401 if(config_textonly) {
1402 OutputDev::drawMaskedImage(state,ref,str,width,height,colorMap,maskStr,maskWidth,maskHeight,maskInvert);
1403 return;
1405 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1406 colorMap?"colorMap":"no colorMap",
1407 maskWidth, maskHeight);
1408 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1409 colorMap?"colorMap":"no colorMap",
1410 maskWidth, maskHeight);
1411 if(colorMap)
1412 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
1413 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1414 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1417 void VectorGraphicOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1418 int width, int height,
1419 GfxImageColorMap *colorMap,
1420 Stream *maskStr,
1421 int maskWidth, int maskHeight,
1422 GfxImageColorMap *maskColorMap)
1424 if(config_textonly) {
1425 OutputDev::drawSoftMaskedImage(state,ref,str,width,height,colorMap,maskStr,maskWidth,maskHeight,maskColorMap);
1426 return;
1428 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1429 colorMap?"colorMap":"no colorMap",
1430 maskWidth, maskHeight);
1431 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1432 colorMap?"colorMap":"no colorMap",
1433 maskWidth, maskHeight);
1434 if(colorMap)
1435 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
1436 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1437 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1441 void VectorGraphicOutputDev::stroke(GfxState *state)
1443 if(config_textonly) {return;}
1445 dbg("stroke");
1447 GfxPath * path = state->getPath();
1448 gfxline_t*line= gfxPath_to_gfxline(state, path, 0);
1449 strokeGfxline(state, line, 0);
1450 gfxline_free(line);
1453 void VectorGraphicOutputDev::fill(GfxState *state)
1455 if(config_textonly) {return;}
1457 gfxcolor_t col = gfxstate_getfillcolor(state);
1458 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1460 GfxPath * path = state->getPath();
1461 gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
1462 if(!config_disable_polygon_conversion) {
1463 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1464 gfxline_free(line);
1465 line = line2;
1467 fillGfxLine(state, line, 0);
1468 gfxline_free(line);
1471 void VectorGraphicOutputDev::eoFill(GfxState *state)
1473 if(config_textonly) {return;}
1475 gfxcolor_t col = gfxstate_getfillcolor(state);
1476 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1478 GfxPath * path = state->getPath();
1479 gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
1480 fillGfxLine(state, line, 1);
1481 gfxline_free(line);
1485 void VectorGraphicOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
1486 GfxColorSpace *blendingColorSpace,
1487 GBool isolated, GBool knockout,
1488 GBool forSoftMask)
1490 const char*colormodename = "";
1492 if(blendingColorSpace) {
1493 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
1495 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);
1496 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);
1498 //states[statepos].createsoftmask |= forSoftMask;
1499 states[statepos].createsoftmask = forSoftMask;
1500 states[statepos].transparencygroup = !forSoftMask;
1501 states[statepos].isolated = isolated;
1503 states[statepos].olddevice = this->device;
1504 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
1505 dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
1507 gfxdevice_record_init(this->device, 0);
1509 /*if(!forSoftMask) { ////???
1510 state->setFillOpacity(0.0);
1512 dbgindent+=2;
1515 void VectorGraphicOutputDev::endTransparencyGroup(GfxState *state)
1517 dbgindent-=2;
1518 gfxdevice_t*r = this->device;
1520 dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
1522 this->device = states[statepos].olddevice;
1523 if(!this->device) {
1524 msg("<error> Invalid state nesting");
1526 states[statepos].olddevice = 0;
1528 gfxresult_t*recording = r->finish(r);
1530 dbg(" forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
1531 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
1533 if(states[statepos].createsoftmask) {
1534 states[statepos-1].softmaskrecording = recording;
1535 } else {
1536 states[statepos-1].grouprecording = recording;
1539 states[statepos].createsoftmask = 0;
1540 states[statepos].transparencygroup = 0;
1541 free(r);
1544 void VectorGraphicOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
1546 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
1547 "colordodge","colorburn","hardlight","softlight","difference",
1548 "exclusion","hue","saturation","color","luminosity"};
1550 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
1551 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
1553 if(state->getBlendMode() == gfxBlendNormal)
1554 infofeature("transparency groups");
1555 else {
1556 char buffer[80];
1557 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
1558 warnfeature(buffer, 0);
1561 gfxresult_t*grouprecording = states[statepos].grouprecording;
1563 int blendmode = state->getBlendMode();
1564 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
1565 int alpha = (int)(state->getFillOpacity()*255);
1566 if(blendmode == gfxBlendMultiply && alpha>200)
1567 alpha = 128;
1568 gfxdevice_t ops;
1569 dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
1570 gfxdevice_ops_init(&ops, this->device, alpha);
1571 gfxresult_record_replay(grouprecording, &ops, 0);
1572 ops.finish(&ops);
1574 grouprecording->destroy(grouprecording);
1576 states[statepos].grouprecording = 0;
1579 void VectorGraphicOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
1581 if(states[statepos].softmask) {
1582 /* shouldn't happen, but *does* happen */
1583 clearSoftMask(state);
1586 /* alpha = 1: retrieve mask values from alpha layer
1587 alpha = 0: retrieve mask values from luminance */
1589 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
1590 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
1591 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
1592 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
1593 if(!alpha)
1594 infofeature("soft masks");
1595 else
1596 warnfeature("soft masks from alpha channel",0);
1598 if(states[statepos].olddevice) {
1599 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
1600 exit(1);
1602 states[statepos].olddevice = this->device;
1603 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
1604 gfxdevice_record_init(this->device, 0);
1606 dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
1608 states[statepos].softmask = 1;
1609 states[statepos].softmask_alpha = alpha;
1612 static inline Guchar div255(int x) {
1613 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
1616 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
1618 if(c < min) c = min;
1619 if(c > max) c = max;
1620 return c;
1623 void VectorGraphicOutputDev::clearSoftMask(GfxState *state)
1625 if(!states[statepos].softmask)
1626 return;
1627 states[statepos].softmask = 0;
1628 dbg("clearSoftMask statepos=%d", statepos);
1629 msg("<verbose> clearSoftMask statepos=%d", statepos);
1631 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
1632 msg("<error> Error in softmask/tgroup ordering");
1633 return;
1636 gfxresult_t*mask = states[statepos].softmaskrecording;
1637 gfxresult_t*below = this->device->finish(this->device);free(this->device);
1638 this->device = states[statepos].olddevice;
1640 /* get outline of all objects below the soft mask */
1641 gfxdevice_t uniondev;
1642 gfxdevice_union_init(&uniondev, 0);
1643 gfxresult_record_replay(below, &uniondev, 0);
1644 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
1645 uniondev.finish(&uniondev);
1646 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
1647 gfxline_free(belowoutline);belowoutline=0;
1648 #if 0
1649 this->device->startclip(this->device, belowoutline);
1650 gfxresult_record_replay(below, this->device, 0);
1651 gfxresult_record_replay(mask, this->device, 0);
1652 this->device->endclip(this->device);
1653 #endif
1655 int width = (int)bbox.xmax,height = (int)bbox.ymax;
1656 if(width<=0 || height<=0)
1657 return;
1659 gfxdevice_t belowrender;
1660 gfxdevice_render_init(&belowrender);
1661 if(states[statepos+1].isolated) {
1662 belowrender.setparameter(&belowrender, "fillwhite", "1");
1664 belowrender.setparameter(&belowrender, "antialize", "2");
1665 belowrender.startpage(&belowrender, width, height);
1666 gfxresult_record_replay(below, &belowrender, 0);
1667 belowrender.endpage(&belowrender);
1668 gfxresult_t* belowresult = belowrender.finish(&belowrender);
1669 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
1670 //png_write("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
1672 gfxdevice_t maskrender;
1673 gfxdevice_render_init(&maskrender);
1674 maskrender.startpage(&maskrender, width, height);
1675 gfxresult_record_replay(mask, &maskrender, 0);
1676 maskrender.endpage(&maskrender);
1677 gfxresult_t* maskresult = maskrender.finish(&maskrender);
1678 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
1680 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
1681 msg("<fatal> Internal error in mask drawing");
1682 return;
1685 int y,x;
1686 for(y=0;y<height;y++) {
1687 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
1688 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
1689 for(x=0;x<width;x++) {
1690 int alpha;
1691 if(states[statepos].softmask_alpha) {
1692 alpha = l1->a;
1693 } else {
1694 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
1697 l2->a = div255(alpha*l2->a);
1699 /* DON'T premultiply alpha- this is done by fillbitmap,
1700 depending on the output device */
1701 //l2->r = div255(alpha*l2->r);
1702 //l2->g = div255(alpha*l2->g);
1703 //l2->b = div255(alpha*l2->b);
1705 l1++;
1706 l2++;
1709 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
1711 gfxmatrix_t matrix;
1712 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
1713 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
1715 if(!config_textonly) {
1716 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
1719 mask->destroy(mask);
1720 below->destroy(below);
1721 maskresult->destroy(maskresult);
1722 belowresult->destroy(belowresult);
1723 states[statepos].softmaskrecording = 0;
1726 //class MemCheck
1728 // public: ~MemCheck()
1729 // {
1730 // delete globalParams;globalParams=0;
1731 // Object::memCheck(stderr);
1732 // gMemReport(stderr);
1733 // }
1734 //} myMemCheck;