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 */
30 #include "../../config.h"
33 #include "popplercompat.h"
34 #include "VectorGraphicOutputDev.h"
36 // swftools header files
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"
53 static int verbose
= 0;
54 static int dbgindent
= 1;
55 static void dbg(const char*format
, ...)
62 va_start(arglist
, format
);
63 vsnprintf(buf
, sizeof(buf
)-1, format
, arglist
);
66 while(l
&& buf
[l
-1]=='\n') {
71 int indent
= dbgindent
;
80 GFXOutputState::GFXOutputState() {
82 this->createsoftmask
= 0;
83 this->transparencygroup
= 0;
85 this->grouprecording
= 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;
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
)
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);
149 printf("is not a rectangle\n");
151 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
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
);
166 void gfxPath_dump(GfxPath
*path
)
168 int num
= path
->getNumSubpaths();
171 for(t
= 0; t
< num
; t
++) {
172 GfxSubpath
*subpath
= path
->getSubpath(t
);
173 int subnum
= subpath
->getNumPoints();
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
);
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();
196 double lastx
=0,lasty
=0,posx
=0,posy
=0;
199 msg("<warning> empty path");
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
++) {
213 this->transformXY(state
, subpath
->getX(s
),subpath
->getY(s
),&x
,&y
);
216 if(closed
&& needsfix
&& (fabs(posx
-lastx
)+fabs(posy
-lasty
))>0.001) {
217 draw
.lineTo(&draw
, lastx
, lasty
);
219 draw
.moveTo(&draw
, x
,y
);
224 } else if(subpath
->getCurve(s
) && cpos
==0) {
228 } else if(subpath
->getCurve(s
) && cpos
==1) {
236 draw
.lineTo(&draw
, x
,y
);
238 gfxdraw_cubicTo(&draw
, bx
,by
, cx
,cy
, x
,y
, 0.05);
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
);
256 GBool
VectorGraphicOutputDev::useTilingPatternFill()
258 infofeature("tiled patterns");
259 // if(config_convertgradients)
261 if(config_disable_tiling_pattern_fills
)
265 GBool
VectorGraphicOutputDev::useShadedFills()
267 infofeature("shaded fills");
268 if(config_convertgradients
)
273 POPPLER_TILING_PATERN_RETURN
VectorGraphicOutputDev::tilingPatternFill(GfxState
*state
,
274 POPPLER_TILING_PATERN_GFX
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");
284 // since we don't implement this method yet,
285 // reduce it to a series of other drawing operations.
290 GBool
VectorGraphicOutputDev::functionShadedFill(GfxState
*state
, GfxFunctionShading
*shading
)
292 msg("<error> functionShadedFill not supported yet");
293 infofeature("function shaded fills");
296 static gfxcolor_t
col2col(GfxColorSpace
*colspace
, GfxColor
* col
)
300 colspace
->getRGB(col
, &rgb
);
301 c
.r
= colToByte(rgb
.r
);
302 c
.g
= colToByte(rgb
.g
);
303 c
.b
= colToByte(rgb
.b
);
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
);
316 this->transformXY(state
, x0
,y0
, &x0
,&y0
);
317 this->transformXY(state
, x1
,y1
, &x1
,&y1
);
318 this->transformXY(state
, x2
,y2
, &x2
,&y2
);
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");
336 gfxgradient_t
*g
= &gr
[0];
340 g
[0].color
= col2col(colspace
, &color0
);
341 g
[1].color
= col2col(colspace
, &color1
);
342 g
[2].color
= col2col(colspace
, &color2
);
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;
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
);
364 device
->fillgradient(device
, &p1
, g
, gfxgradient_radial
, &m
);
368 GBool
VectorGraphicOutputDev::axialShadedFill(GfxState
*state
, GfxAxialShading
*shading
)
370 if(config_textonly
) {return gTrue
;}
373 shading
->getCoords(&x0
,&y0
,&x1
,&y1
);
374 this->transformXY(state
, x0
,y0
,&x0
,&y0
);
375 this->transformXY(state
, x1
,y1
,&x1
,&y1
);
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);
397 g
[0].color
= col2col(colspace
, &color0
);
398 g
[1].color
= col2col(colspace
, &color1
);
399 g
[2].color
= col2col(colspace
, &color2
);
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
);
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 */
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
);
434 GBool
VectorGraphicOutputDev::useDrawForm()
436 infofeature("forms");
439 void VectorGraphicOutputDev::drawForm(Ref id
)
441 msg("<error> drawForm not implemented");
443 GBool
VectorGraphicOutputDev::needNonText()
447 void VectorGraphicOutputDev::endPage()
449 msg("<verbose> endPage (VectorGraphicOutputDev)");
450 charDev
->endPage(); // link postprocessing
452 device
->endclip(device
);
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();
474 double opaq
= state
->getStrokeOpacity();
476 state
->getFillRGB(&rgb
);
478 state
->getStrokeRGB(&rgb
);
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");
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));
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
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
));
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
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
;
536 msg("<trace> | d%-3d: %f", t
, dashPattern
[t
]);
538 dash
[dashLength
] = -1;
539 if(getLogLevel() >= LOGLEVEL_TRACE
) {
543 line2
= gfxtool_dash_line(line
, dash
, (float)(dashStart
*f
));
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",
553 lineJoin
==0?"miter": (lineJoin
==1?"round":"bevel"),
554 lineCap
==0?"butt": (lineCap
==1?"round":"square"),
556 col
.r
,col
.g
,col
.b
,col
.a
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
);
568 msg("<warning> Empty polygon (resulting from stroked line)");
570 if(flags
&STROKE_CLIP
) {
571 device
->startclip(device
, gfxline
);
572 states
[statepos
].clipping
++;
574 device
->fill(device
, gfxline
, &col
);
576 gfxline_free(gfxline
);
577 gfxpoly_destroy(poly
);
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
);
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
);
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":"");
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();
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
);
622 clipToGfxLine(state
, line
, 0);
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);
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
);
644 strokeGfxline(state
, line
, STROKE_FILL
|STROKE_CLIP
);
648 void VectorGraphicOutputDev::finish()
652 device
->endclip(device
);
658 VectorGraphicOutputDev::~VectorGraphicOutputDev()
661 delete charDev
;charDev
=0;
663 GBool
VectorGraphicOutputDev::upsideDown()
667 GBool
VectorGraphicOutputDev::useDrawChar()
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;
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
);
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
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,
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
)
845 charDev
->setPage(page
);
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};
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
);
861 msg("<verbose> page is rotated %d degrees", rot
);
863 clippath
[0].type
= gfx_moveTo
;clippath
[0].x
= 0; clippath
[0].y
= 0; clippath
[0].next
= &clippath
[1];
864 clippath
[1].type
= gfx_lineTo
;clippath
[1].x
= width
; clippath
[1].y
= 0; clippath
[1].next
= &clippath
[2];
865 clippath
[2].type
= gfx_lineTo
;clippath
[2].x
= width
; clippath
[2].y
= height
; clippath
[2].next
= &clippath
[3];
866 clippath
[3].type
= gfx_lineTo
;clippath
[3].x
= 0; clippath
[3].y
= height
; clippath
[3].next
= &clippath
[4];
867 clippath
[4].type
= gfx_lineTo
;clippath
[4].x
= 0; clippath
[4].y
= 0; clippath
[4].next
= 0;
869 device
->startclip(device
, clippath
); outer_clip_box
= 1;
870 if(!config_transparent
) {
871 device
->fill(device
, clippath
, &white
);
874 states
[statepos
].clipbbox
.xmin
= 0;
875 states
[statepos
].clipbbox
.ymin
= 0;
876 states
[statepos
].clipbbox
.xmax
= this->width
;
877 states
[statepos
].clipbbox
.ymax
= this->height
;
879 states
[statepos
].dashPattern
= 0;
880 states
[statepos
].dashLength
= 0;
881 states
[statepos
].dashStart
= 0;
883 charDev
->startPage(pageNum
, state
);
886 void VectorGraphicOutputDev::saveState(GfxState
*state
) {
887 dbg("saveState %p", state
); dbgindent
+=2;
889 msg("<trace> saveState %p", state
);
892 msg("<fatal> Too many nested states in pdf.");
896 states
[statepos
].state
= state
;
897 states
[statepos
].createsoftmask
= states
[statepos
-1].createsoftmask
;
898 states
[statepos
].transparencygroup
= states
[statepos
-1].transparencygroup
;
899 states
[statepos
].clipping
= 0;
900 states
[statepos
].olddevice
= 0;
901 states
[statepos
].clipbbox
= states
[statepos
-1].clipbbox
;
903 states
[statepos
].dashPattern
= states
[statepos
-1].dashPattern
;
904 states
[statepos
].dashStart
= states
[statepos
-1].dashStart
;
905 states
[statepos
].dashLength
= states
[statepos
-1].dashLength
;
908 void VectorGraphicOutputDev::restoreState(GfxState
*state
) {
909 dbgindent
-=2; dbg("restoreState %p", state
);
912 msg("<fatal> Invalid restoreState");
915 msg("<trace> restoreState %p%s%s", state
,
916 states
[statepos
].softmask
?" (end softmask)":"",
917 states
[statepos
].clipping
?" (end clipping)":"");
918 if(states
[statepos
].softmask
) {
919 clearSoftMask(state
);
922 if(states
[statepos
].dashPattern
) {
923 if(!statepos
|| states
[statepos
-1].dashPattern
!= states
[statepos
].dashPattern
) {
924 free(states
[statepos
].dashPattern
);
925 states
[statepos
].dashPattern
= 0;
931 while(states
[statepos
].clipping
) {
932 device
->endclip(device
);
933 states
[statepos
].clipping
--;
935 if(states
[statepos
].state
!=state
) {
936 msg("<fatal> bad state nesting");
939 for(t
=0;t
<=statepos
;t
++) {
940 printf("%p ", states
[t
].state
);
946 states
[statepos
].state
=0;
950 void VectorGraphicOutputDev::updateLineDash(GfxState
*state
)
952 if(states
[statepos
].dashPattern
&&
953 (!statepos
|| states
[statepos
-1].dashPattern
!= states
[statepos
].dashPattern
)) {
954 free(states
[statepos
].dashPattern
);
955 states
[statepos
].dashPattern
= 0;
960 state
->getLineDash(&pattern
, &dashLength
, &dashStart
);
961 msg("<debug> updateLineDash, %d dashes", dashLength
);
963 states
[statepos
].dashPattern
= 0;
964 states
[statepos
].dashLength
= 0;
966 double*p
= (double*)malloc(dashLength
*sizeof(states
[statepos
].dashPattern
[0]));
967 memcpy(p
, pattern
, dashLength
*sizeof(states
[statepos
].dashPattern
[0]));
968 states
[statepos
].dashPattern
= p
;
969 states
[statepos
].dashLength
= dashLength
;
970 states
[statepos
].dashStart
= dashStart
;
974 #define SQR(x) ((x)*(x))
975 unsigned char* antialize(unsigned char*data
, int width
, int height
, int newwidth
, int newheight
, int palettesize
)
977 if((newwidth
<1 || newheight
<1) ||
978 (width
<=newwidth
|| height
<=newheight
))
980 unsigned char*newdata
;
982 newdata
= (unsigned char*)malloc(newwidth
*newheight
);
983 double fx
= ((double)width
)/newwidth
;
984 double fy
= ((double)height
)/newheight
;
986 int blocksize
= (int)(8192/(fx
*fy
));
987 int r
= 8192*256/palettesize
;
988 for(x
=0;x
<newwidth
;x
++) {
992 int xweight1
= (int)((1-(px
-fromx
))*256);
993 int xweight2
= (int)((ex
-tox
)*256);
995 for(y
=0;y
<newheight
;y
++) {
999 int yweight1
= (int)((1-(py
-fromy
))*256);
1000 int yweight2
= (int)((ey
-toy
)*256);
1007 for(xx
=fromx
;xx
<=tox
;xx
++)
1008 for(yy
=fromy
;yy
<=toy
;yy
++) {
1009 int b
= 1-data
[width
*yy
+xx
];
1011 if(xx
==fromx
) weight
= (weight
*xweight1
)/256;
1012 if(xx
==tox
) weight
= (weight
*xweight2
)/256;
1013 if(yy
==fromy
) weight
= (weight
*yweight1
)/256;
1014 if(yy
==toy
) weight
= (weight
*yweight2
)/256;
1017 //if(a) a=(palettesize-1)*r/blocksize;
1018 newdata
[y
*newwidth
+x
] = (a
*blocksize
)/r
;
1026 #define IMAGE_TYPE_JPEG 0
1027 #define IMAGE_TYPE_LOSSLESS 1
1029 static void drawimage(gfxdevice_t
*dev
, gfxcolor_t
* data
, int sizex
,int sizey
,
1030 double x1
,double y1
,
1031 double x2
,double y2
,
1032 double x3
,double y3
,
1033 double x4
,double y4
, int type
, int multiply
)
1035 gfxcolor_t
*newpic
=0;
1037 double l1
= sqrt((x4
-x1
)*(x4
-x1
) + (y4
-y1
)*(y4
-y1
));
1038 double l2
= sqrt((x2
-x1
)*(x2
-x1
) + (y2
-y1
)*(y2
-y1
));
1040 gfxline_t p1
,p2
,p3
,p4
,p5
;
1041 p1
.type
=gfx_moveTo
;p1
.x
=x1
; p1
.y
=y1
;p1
.next
=&p2
;
1042 p2
.type
=gfx_lineTo
;p2
.x
=x2
; p2
.y
=y2
;p2
.next
=&p3
;
1043 p3
.type
=gfx_lineTo
;p3
.x
=x3
; p3
.y
=y3
;p3
.next
=&p4
;
1044 p4
.type
=gfx_lineTo
;p4
.x
=x4
; p4
.y
=y4
;p4
.next
=&p5
;
1045 p5
.type
=gfx_lineTo
;p5
.x
=x1
; p5
.y
=y1
;p5
.next
=0;
1047 {p1
.x
= (int)(p1
.x
*20)/20.0;
1048 p1
.y
= (int)(p1
.y
*20)/20.0;
1049 p2
.x
= (int)(p2
.x
*20)/20.0;
1050 p2
.y
= (int)(p2
.y
*20)/20.0;
1051 p3
.x
= (int)(p3
.x
*20)/20.0;
1052 p3
.y
= (int)(p3
.y
*20)/20.0;
1053 p4
.x
= (int)(p4
.x
*20)/20.0;
1054 p4
.y
= (int)(p4
.y
*20)/20.0;
1055 p5
.x
= (int)(p5
.x
*20)/20.0;
1056 p5
.y
= (int)(p5
.y
*20)/20.0;
1060 m
.m00
= (p4
.x
-p1
.x
)/sizex
; m
.m10
= (p2
.x
-p1
.x
)/sizey
;
1061 m
.m01
= (p4
.y
-p1
.y
)/sizex
; m
.m11
= (p2
.y
-p1
.y
)/sizey
;
1063 m
.tx
= p1
.x
- 0.5*multiply
;
1064 m
.ty
= p1
.y
- 0.5*multiply
;
1067 img
.data
= (gfxcolor_t
*)data
;
1071 if(type
== IMAGE_TYPE_JPEG
)
1072 /* TODO: pass image_dpi to device instead */
1073 dev
->setparameter(dev
, "next_bitmap_is_jpeg", "1");
1076 dev
->fillbitmap(dev
, &p1
, &img
, &m
, 0);
1079 void drawimagejpeg(gfxdevice_t
*dev
, gfxcolor_t
*mem
, int sizex
,int sizey
,
1080 double x1
,double y1
, double x2
,double y2
, double x3
,double y3
, double x4
,double y4
, int multiply
)
1082 drawimage(dev
,mem
,sizex
,sizey
,x1
,y1
,x2
,y2
,x3
,y3
,x4
,y4
, IMAGE_TYPE_JPEG
, multiply
);
1085 void drawimagelossless(gfxdevice_t
*dev
, gfxcolor_t
*mem
, int sizex
,int sizey
,
1086 double x1
,double y1
, double x2
,double y2
, double x3
,double y3
, double x4
,double y4
, int multiply
)
1088 drawimage(dev
,mem
,sizex
,sizey
,x1
,y1
,x2
,y2
,x3
,y3
,x4
,y4
, IMAGE_TYPE_LOSSLESS
, multiply
);
1092 void VectorGraphicOutputDev::drawGeneralImage(GfxState
*state
, Object
*ref
, Stream
*str
,
1093 int width
, int height
, GfxImageColorMap
*colorMap
, GBool invert
,
1094 GBool inlineImg
, int mask
, int*maskColors
,
1095 Stream
*maskStr
, int maskWidth
, int maskHeight
, GBool maskInvert
, GfxImageColorMap
*maskColorMap
)
1097 /* the code in this function is *old*. It's not pretty, but it works. */
1099 double x1
,y1
,x2
,y2
,x3
,y3
,x4
,y4
;
1100 ImageStream
*imgStr
;
1105 unsigned char* maskbitmap
= 0;
1108 ncomps
= colorMap
->getNumPixelComps();
1109 bits
= colorMap
->getBits();
1114 unsigned char buf
[8];
1115 maskbitmap
= (unsigned char*)malloc(maskHeight
*maskWidth
);
1117 ImageStream
*imgMaskStr
= new ImageStream(maskStr
, maskWidth
, maskColorMap
->getNumPixelComps(), maskColorMap
->getBits());
1118 imgMaskStr
->reset();
1119 unsigned char pal
[256];
1120 int n
= 1 << colorMap
->getBits();
1125 maskColorMap
->getGray(pixBuf
, &gray
);
1126 pal
[t
] = colToByte(gray
);
1128 for (y
= 0; y
< maskHeight
; y
++) {
1129 for (x
= 0; x
< maskWidth
; x
++) {
1130 imgMaskStr
->getPixel(buf
);
1131 maskbitmap
[y
*maskWidth
+x
] = pal
[buf
[0]];
1136 ImageStream
*imgMaskStr
= new ImageStream(maskStr
, maskWidth
, 1, 1);
1137 imgMaskStr
->reset();
1138 for (y
= 0; y
< maskHeight
; y
++) {
1139 for (x
= 0; x
< maskWidth
; x
++) {
1140 imgMaskStr
->getPixel(buf
);
1142 maskbitmap
[y
*maskWidth
+x
] = (buf
[0]^1)*255;
1150 imgStr
= new ImageStream(str
, width
, ncomps
,bits
);
1153 if(!width
|| !height
|| ((height
+width
)<=1 && (maskWidth
+maskHeight
)<=1))
1155 msg("<verbose> Ignoring %d by %d image", width
, height
);
1156 unsigned char buf
[8];
1158 for (y
= 0; y
< height
; ++y
)
1159 for (x
= 0; x
< width
; ++x
) {
1160 imgStr
->getPixel(buf
);
1168 this->transformXY(state
, 0, 1, &x1
, &y1
);
1169 this->transformXY(state
, 0, 0, &x2
, &y2
);
1170 this->transformXY(state
, 1, 0, &x3
, &y3
);
1171 this->transformXY(state
, 1, 1, &x4
, &y4
);
1174 /* as type 3 bitmaps are antialized, we need to place them
1175 at integer coordinates, otherwise flash player's antializing
1176 will kick in and make everything blurry */
1177 x1
= (int)(x1
);y1
= (int)(y1
);
1178 x2
= (int)(x2
);y2
= (int)(y2
);
1179 x3
= (int)(x3
);y3
= (int)(y3
);
1180 x4
= (int)(x4
);y4
= (int)(y4
);
1183 if(!(str
->getKind()==strDCT
)) {
1185 if(mask
) infofeature("masked pbm pictures");
1186 else infofeature("pbm pictures");
1189 msg("<verbose> drawing %d by %d masked picture", width
, height
);
1191 if(str
->getKind()==strDCT
) {
1192 infofeature("jpeg pictures");
1196 unsigned char buf
[8];
1198 unsigned char*pic
= new unsigned char[width
*height
];
1199 gfxcolor_t pal
[256];
1201 state
->getFillRGB(&rgb
);
1203 memset(pal
,255,sizeof(pal
));
1204 pal
[0].r
= (int)(colToByte(rgb
.r
)); pal
[1].r
= 0;
1205 pal
[0].g
= (int)(colToByte(rgb
.g
)); pal
[1].g
= 0;
1206 pal
[0].b
= (int)(colToByte(rgb
.b
)); pal
[1].b
= 0;
1207 pal
[0].a
= 255; pal
[1].a
= 0;
1210 int realwidth
= (int)sqrt(SQR(x2
-x3
) + SQR(y2
-y3
));
1211 int realheight
= (int)sqrt(SQR(x1
-x2
) + SQR(y1
-y2
));
1212 for (y
= 0; y
< height
; ++y
)
1213 for (x
= 0; x
< width
; ++x
)
1215 imgStr
->getPixel(buf
);
1218 pic
[width
*y
+x
] = buf
[0];
1222 unsigned char*pic2
= 0;
1225 pic2
= antialize(pic
,width
,height
,realwidth
,realheight
,numpalette
);
1234 height
= realheight
;
1238 /* make a black/white palette */
1240 float r
= 255./(float)(numpalette
-1);
1242 for(t
=0;t
<numpalette
;t
++) {
1243 pal
[t
].r
= colToByte(rgb
.r
);
1244 pal
[t
].g
= colToByte(rgb
.g
);
1245 pal
[t
].b
= colToByte(rgb
.b
);
1246 pal
[t
].a
= (unsigned char)(t
*r
);
1251 gfxcolor_t
*pic2
= new gfxcolor_t
[width
*height
];
1252 for (y
= 0; y
< height
; ++y
) {
1253 for (x
= 0; x
< width
; ++x
) {
1254 pic2
[width
*y
+x
] = pal
[pic
[y
*width
+x
]];
1257 drawimagelossless(device
, pic2
, width
, height
, x1
,y1
,x2
,y2
,x3
,y3
,x4
,y4
, config_multiply
);
1261 if(maskbitmap
) free(maskbitmap
);
1267 if(colorMap
->getNumPixelComps()!=1 || str
->getKind()==strDCT
) {
1268 gfxcolor_t
*pic
=new gfxcolor_t
[width
*height
];
1269 for (y
= 0; y
< height
; ++y
) {
1270 for (x
= 0; x
< width
; ++x
) {
1271 imgStr
->getPixel(pixBuf
);
1272 colorMap
->getRGB(pixBuf
, &rgb
);
1273 pic
[width
*y
+x
].r
= (unsigned char)(colToByte(rgb
.r
));
1274 pic
[width
*y
+x
].g
= (unsigned char)(colToByte(rgb
.g
));
1275 pic
[width
*y
+x
].b
= (unsigned char)(colToByte(rgb
.b
));
1276 pic
[width
*y
+x
].a
= 255;//(U8)(rgb.a * 255 + 0.5);
1278 int x1
= x
*maskWidth
/width
;
1279 int y1
= y
*maskHeight
/height
;
1280 int x2
= (x
+1)*maskWidth
/width
;
1281 int y2
= (y
+1)*maskHeight
/height
;
1283 unsigned int alpha
=0;
1284 unsigned int count
=0;
1285 for(xx
=x1
;xx
<x2
;xx
++)
1286 for(yy
=y1
;yy
<y2
;yy
++) {
1287 alpha
+= maskbitmap
[yy
*maskWidth
+xx
];
1291 pic
[width
*y
+x
].a
= alpha
/ count
;
1293 pic
[width
*y
+x
].a
= maskbitmap
[y1
*maskWidth
+x1
];
1298 if(str
->getKind()==strDCT
)
1299 drawimagejpeg(device
, pic
, width
, height
, x1
,y1
,x2
,y2
,x3
,y3
,x4
,y4
, config_multiply
);
1301 drawimagelossless(device
, pic
, width
, height
, x1
,y1
,x2
,y2
,x3
,y3
,x4
,y4
, config_multiply
);
1304 if(maskbitmap
) free(maskbitmap
);
1307 gfxcolor_t
*pic
=new gfxcolor_t
[width
*height
];
1308 gfxcolor_t pal
[256];
1309 int n
= 1 << colorMap
->getBits();
1311 for(t
=0;t
<256;t
++) {
1313 colorMap
->getRGB(pixBuf
, &rgb
);
1314 pal
[t
].r
= (unsigned char)(colToByte(rgb
.r
));
1315 pal
[t
].g
= (unsigned char)(colToByte(rgb
.g
));
1316 pal
[t
].b
= (unsigned char)(colToByte(rgb
.b
));
1317 pal
[t
].a
= 255;//(U8)(rgb.b * 255 + 0.5);
1319 for (y
= 0; y
< height
; ++y
) {
1320 for (x
= 0; x
< width
; ++x
) {
1321 imgStr
->getPixel(pixBuf
);
1322 pic
[width
*y
+x
] = pal
[pixBuf
[0]];
1323 if(maskColors
&& *maskColors
==pixBuf
[0]) {
1324 pic
[width
*y
+x
].a
= 0;
1329 if(maskWidth
< width
&& maskHeight
< height
) {
1330 for(y
= 0; y
< height
; y
++) {
1331 for (x
= 0; x
< width
; x
++) {
1332 pic
[width
*y
+x
].a
= maskbitmap
[(y
*maskHeight
/height
)*maskWidth
+(x
*maskWidth
/width
)];
1336 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width
, height
, maskWidth
, maskHeight
);
1337 gfxcolor_t
*newpic
=new gfxcolor_t
[maskWidth
*maskHeight
];
1338 double dx
= width
/ (double)maskWidth
;
1339 double dy
= height
/ (double)maskHeight
;
1341 for(y
= 0; y
< maskHeight
; y
++) {
1343 for (x
= 0; x
< maskWidth
; x
++) {
1344 newpic
[maskWidth
*y
+x
] = pic
[int(yy
)*width
+int(xx
)];
1345 newpic
[maskWidth
*y
+x
].a
= maskbitmap
[maskWidth
*y
+x
];
1353 height
= maskHeight
;
1356 drawimagelossless(device
, pic
, width
, height
, x1
,y1
,x2
,y2
,x3
,y3
,x4
,y4
, config_multiply
);
1360 if(maskbitmap
) free(maskbitmap
);
1365 void VectorGraphicOutputDev::drawImageMask(GfxState
*state
, Object
*ref
, Stream
*str
,
1366 int width
, int height
, GBool invert
,
1369 if(config_textonly
) {
1370 OutputDev::drawImageMask(state
,ref
,str
,width
,height
,invert
,inlineImg
);
1373 dbg("drawImageMask %dx%d, invert=%d inline=%d", width
, height
, invert
, inlineImg
);
1374 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width
, height
, invert
, inlineImg
);
1375 drawGeneralImage(state
,ref
,str
,width
,height
,0,invert
,inlineImg
,1, 0, 0,0,0,0, 0);
1378 void VectorGraphicOutputDev::drawImage(GfxState
*state
, Object
*ref
, Stream
*str
,
1379 int width
, int height
, GfxImageColorMap
*colorMap
,
1380 int *maskColors
, GBool inlineImg
)
1382 if(config_textonly
) {
1383 OutputDev::drawImage(state
,ref
,str
,width
,height
,colorMap
,maskColors
,inlineImg
);
1386 dbg("drawImage %dx%d, %s, %s, inline=%d", width
, height
,
1387 colorMap
?"colorMap":"no colorMap",
1388 maskColors
?"maskColors":"no maskColors",
1390 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width
, height
,
1391 colorMap
?"colorMap":"no colorMap",
1392 maskColors
?"maskColors":"no maskColors",
1395 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap
->getNumPixelComps(),
1396 colorMap
->getBits(),colorMap
->getColorSpace()->getMode());
1397 drawGeneralImage(state
,ref
,str
,width
,height
,colorMap
,0,inlineImg
,0,maskColors
, 0,0,0,0, 0);
1400 void VectorGraphicOutputDev::drawMaskedImage(GfxState
*state
, Object
*ref
, Stream
*str
,
1401 int width
, int height
,
1402 GfxImageColorMap
*colorMap
,
1403 Stream
*maskStr
, int maskWidth
, int maskHeight
,
1406 if(config_textonly
) {
1407 OutputDev::drawMaskedImage(state
,ref
,str
,width
,height
,colorMap
,maskStr
,maskWidth
,maskHeight
,maskInvert
);
1410 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width
, height
,
1411 colorMap
?"colorMap":"no colorMap",
1412 maskWidth
, maskHeight
);
1413 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width
, height
,
1414 colorMap
?"colorMap":"no colorMap",
1415 maskWidth
, maskHeight
);
1417 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap
->getNumPixelComps(),
1418 colorMap
->getBits(),colorMap
->getColorSpace()->getMode());
1419 drawGeneralImage(state
,ref
,str
,width
,height
,colorMap
,0,0,0,0, maskStr
, maskWidth
, maskHeight
, maskInvert
, 0);
1422 void VectorGraphicOutputDev::drawSoftMaskedImage(GfxState
*state
, Object
*ref
, Stream
*str
,
1423 int width
, int height
,
1424 GfxImageColorMap
*colorMap
,
1426 int maskWidth
, int maskHeight
,
1427 GfxImageColorMap
*maskColorMap
)
1429 if(config_textonly
) {
1430 OutputDev::drawSoftMaskedImage(state
,ref
,str
,width
,height
,colorMap
,maskStr
,maskWidth
,maskHeight
,maskColorMap
);
1433 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width
, height
,
1434 colorMap
?"colorMap":"no colorMap",
1435 maskWidth
, maskHeight
);
1436 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width
, height
,
1437 colorMap
?"colorMap":"no colorMap",
1438 maskWidth
, maskHeight
);
1440 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap
->getNumPixelComps(),
1441 colorMap
->getBits(),colorMap
->getColorSpace()->getMode());
1442 drawGeneralImage(state
,ref
,str
,width
,height
,colorMap
,0,0,0,0, maskStr
, maskWidth
, maskHeight
, 0, maskColorMap
);
1446 void VectorGraphicOutputDev::stroke(GfxState
*state
)
1448 if(config_textonly
) {return;}
1452 GfxPath
* path
= state
->getPath();
1453 gfxline_t
*line
= gfxPath_to_gfxline(state
, path
, 0);
1454 strokeGfxline(state
, line
, 0);
1458 void VectorGraphicOutputDev::fill(GfxState
*state
)
1460 if(config_textonly
) {return;}
1462 gfxcolor_t col
= gfxstate_getfillcolor(state
);
1463 dbg("fill %02x%02x%02x%02x",col
.r
,col
.g
,col
.b
,col
.a
);
1465 GfxPath
* path
= state
->getPath();
1466 gfxline_t
*line
= gfxPath_to_gfxline(state
, path
, 1);
1467 if(!config_disable_polygon_conversion
) {
1468 gfxline_t
*line2
= gfxpoly_circular_to_evenodd(line
, DEFAULT_GRID
);
1472 fillGfxLine(state
, line
, 0);
1476 void VectorGraphicOutputDev::eoFill(GfxState
*state
)
1478 if(config_textonly
) {return;}
1480 gfxcolor_t col
= gfxstate_getfillcolor(state
);
1481 dbg("eofill %02x%02x%02x%02x",col
.r
,col
.g
,col
.b
,col
.a
);
1483 GfxPath
* path
= state
->getPath();
1484 gfxline_t
*line
= gfxPath_to_gfxline(state
, path
, 1);
1485 fillGfxLine(state
, line
, 1);
1490 void VectorGraphicOutputDev::beginTransparencyGroup(GfxState
*state
, double *bbox
,
1491 GfxColorSpace
*blendingColorSpace
,
1492 GBool isolated
, GBool knockout
,
1495 const char*colormodename
= "";
1497 if(blendingColorSpace
) {
1498 colormodename
= GfxColorSpace::getColorSpaceModeName(blendingColorSpace
->getMode());
1500 dbg("beginTransparencyGroup device=%p %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", device
, bbox
[0],bbox
[1],bbox
[2],bbox
[3], colormodename
, isolated
, knockout
, forSoftMask
);
1501 msg("<verbose> beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox
[0],bbox
[1],bbox
[2],bbox
[3], colormodename
, isolated
, knockout
, forSoftMask
);
1503 //states[statepos].createsoftmask |= forSoftMask;
1504 states
[statepos
].createsoftmask
= forSoftMask
;
1505 states
[statepos
].transparencygroup
= !forSoftMask
;
1506 states
[statepos
].isolated
= isolated
;
1508 states
[statepos
].olddevice
= this->device
;
1509 this->device
= (gfxdevice_t
*)rfx_calloc(sizeof(gfxdevice_t
));
1510 dbg("this->device now %p (old: %p)", this->device
, states
[statepos
].olddevice
);
1512 gfxdevice_record_init(this->device
, 0);
1514 /*if(!forSoftMask) { ////???
1515 state->setFillOpacity(0.0);
1520 void VectorGraphicOutputDev::endTransparencyGroup(GfxState
*state
)
1523 gfxdevice_t
*r
= this->device
;
1525 dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states
[statepos
].olddevice
, this->device
);
1527 this->device
= states
[statepos
].olddevice
;
1529 msg("<error> Invalid state nesting");
1531 states
[statepos
].olddevice
= 0;
1533 gfxresult_t
*recording
= r
->finish(r
);
1535 dbg(" forsoftmask=%d recording=%p/%p", states
[statepos
].createsoftmask
, r
, recording
);
1536 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states
[statepos
].createsoftmask
, r
, recording
);
1538 if(states
[statepos
].createsoftmask
) {
1539 states
[statepos
-1].softmaskrecording
= recording
;
1541 states
[statepos
-1].grouprecording
= recording
;
1544 states
[statepos
].createsoftmask
= 0;
1545 states
[statepos
].transparencygroup
= 0;
1549 void VectorGraphicOutputDev::paintTransparencyGroup(GfxState
*state
, double *bbox
)
1551 const char*blendmodes
[] = {"normal","multiply","screen","overlay","darken", "lighten",
1552 "colordodge","colorburn","hardlight","softlight","difference",
1553 "exclusion","hue","saturation","color","luminosity"};
1555 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes
[state
->getBlendMode()], states
[statepos
].softmask
, states
[statepos
].grouprecording
);
1556 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes
[state
->getBlendMode()], states
[statepos
].softmask
);
1558 if(state
->getBlendMode() == gfxBlendNormal
)
1559 infofeature("transparency groups");
1562 sprintf(buffer
, "%s blended transparency groups", blendmodes
[state
->getBlendMode()]);
1563 warnfeature(buffer
, 0);
1566 gfxresult_t
*grouprecording
= states
[statepos
].grouprecording
;
1568 int blendmode
= state
->getBlendMode();
1569 if(blendmode
== gfxBlendNormal
|| blendmode
== gfxBlendMultiply
) {
1570 int alpha
= (int)(state
->getFillOpacity()*255);
1571 if(blendmode
== gfxBlendMultiply
&& alpha
>200)
1574 dbg("this->device=%p, this->device->name=%s\n", this->device
, this->device
->name
);
1575 gfxdevice_ops_init(&ops
, this->device
, alpha
);
1576 gfxresult_record_replay(grouprecording
, &ops
, 0);
1579 grouprecording
->destroy(grouprecording
);
1581 states
[statepos
].grouprecording
= 0;
1584 void VectorGraphicOutputDev::setSoftMask(GfxState
*state
, double *bbox
, GBool alpha
, Function
*transferFunc
, GfxColor
*rgb
)
1586 if(states
[statepos
].softmask
) {
1587 /* shouldn't happen, but *does* happen */
1588 clearSoftMask(state
);
1591 /* alpha = 1: retrieve mask values from alpha layer
1592 alpha = 0: retrieve mask values from luminance */
1594 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
1595 bbox
[0], bbox
[1], bbox
[2], bbox
[3], alpha
, colToByte(rgb
->c
[0]), colToByte(rgb
->c
[1]), colToByte(rgb
->c
[2]));
1596 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
1597 bbox
[0], bbox
[1], bbox
[2], bbox
[3], alpha
, colToByte(rgb
->c
[0]), colToByte(rgb
->c
[1]), colToByte(rgb
->c
[2]));
1599 infofeature("soft masks");
1601 warnfeature("soft masks from alpha channel",0);
1603 if(states
[statepos
].olddevice
) {
1604 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
1607 states
[statepos
].olddevice
= this->device
;
1608 this->device
= (gfxdevice_t
*)rfx_calloc(sizeof(gfxdevice_t
));
1609 gfxdevice_record_init(this->device
, 0);
1611 dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states
[statepos
].softmaskrecording
, this->device
, statepos
);
1613 states
[statepos
].softmask
= 1;
1614 states
[statepos
].softmask_alpha
= alpha
;
1617 static inline Guchar
div255(int x
) {
1618 return (Guchar
)((x
+ (x
>> 8) + 0x80) >> 8);
1621 static unsigned char clampU8(unsigned char c
, unsigned char min
, unsigned char max
)
1623 if(c
< min
) c
= min
;
1624 if(c
> max
) c
= max
;
1628 void VectorGraphicOutputDev::clearSoftMask(GfxState
*state
)
1630 if(!states
[statepos
].softmask
)
1632 states
[statepos
].softmask
= 0;
1633 dbg("clearSoftMask statepos=%d", statepos
);
1634 msg("<verbose> clearSoftMask statepos=%d", statepos
);
1636 if(!states
[statepos
].softmaskrecording
|| strcmp(this->device
->name
, "record")) {
1637 msg("<error> Error in softmask/tgroup ordering");
1641 gfxresult_t
*mask
= states
[statepos
].softmaskrecording
;
1642 gfxresult_t
*below
= this->device
->finish(this->device
);free(this->device
);
1643 this->device
= states
[statepos
].olddevice
;
1645 /* get outline of all objects below the soft mask */
1646 gfxdevice_t uniondev
;
1647 gfxdevice_union_init(&uniondev
, 0);
1648 gfxresult_record_replay(below
, &uniondev
, 0);
1649 gfxline_t
*belowoutline
= gfxdevice_union_getunion(&uniondev
);
1650 uniondev
.finish(&uniondev
);
1651 gfxbbox_t bbox
= gfxline_getbbox(belowoutline
);
1652 gfxline_free(belowoutline
);belowoutline
=0;
1654 this->device
->startclip(this->device
, belowoutline
);
1655 gfxresult_record_replay(below
, this->device
, 0);
1656 gfxresult_record_replay(mask
, this->device
, 0);
1657 this->device
->endclip(this->device
);
1660 int width
= (int)bbox
.xmax
,height
= (int)bbox
.ymax
;
1661 if(width
<=0 || height
<=0)
1664 gfxdevice_t belowrender
;
1665 gfxdevice_render_init(&belowrender
);
1666 if(states
[statepos
+1].isolated
) {
1667 belowrender
.setparameter(&belowrender
, "fillwhite", "1");
1669 belowrender
.setparameter(&belowrender
, "antialize", "2");
1670 belowrender
.startpage(&belowrender
, width
, height
);
1671 gfxresult_record_replay(below
, &belowrender
, 0);
1672 belowrender
.endpage(&belowrender
);
1673 gfxresult_t
* belowresult
= belowrender
.finish(&belowrender
);
1674 gfximage_t
* belowimg
= (gfximage_t
*)belowresult
->get(belowresult
,"page0");
1675 //png_write("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
1677 gfxdevice_t maskrender
;
1678 gfxdevice_render_init(&maskrender
);
1679 maskrender
.startpage(&maskrender
, width
, height
);
1680 gfxresult_record_replay(mask
, &maskrender
, 0);
1681 maskrender
.endpage(&maskrender
);
1682 gfxresult_t
* maskresult
= maskrender
.finish(&maskrender
);
1683 gfximage_t
* maskimg
= (gfximage_t
*)maskresult
->get(maskresult
,"page0");
1685 if(belowimg
->width
!= maskimg
->width
|| belowimg
->height
!= maskimg
->height
) {
1686 msg("<fatal> Internal error in mask drawing");
1691 for(y
=0;y
<height
;y
++) {
1692 gfxcolor_t
* l1
= &maskimg
->data
[maskimg
->width
*y
];
1693 gfxcolor_t
* l2
= &belowimg
->data
[belowimg
->width
*y
];
1694 for(x
=0;x
<width
;x
++) {
1696 if(states
[statepos
].softmask_alpha
) {
1699 alpha
= (77*l1
->r
+ 151*l1
->g
+ 28*l1
->b
) >> 8;
1702 l2
->a
= div255(alpha
*l2
->a
);
1704 /* DON'T premultiply alpha- this is done by fillbitmap,
1705 depending on the output device */
1706 //l2->r = div255(alpha*l2->r);
1707 //l2->g = div255(alpha*l2->g);
1708 //l2->b = div255(alpha*l2->b);
1714 gfxline_t
*line
= gfxline_makerectangle(0,0,width
,height
);
1717 matrix
.m00
= 1.0; matrix
.m10
= 0.0; matrix
.tx
= 0.0;
1718 matrix
.m01
= 0.0; matrix
.m11
= 1.0; matrix
.ty
= 0.0;
1720 if(!config_textonly
) {
1721 this->device
->fillbitmap(this->device
, line
, belowimg
, &matrix
, 0);
1724 mask
->destroy(mask
);
1725 below
->destroy(below
);
1726 maskresult
->destroy(maskresult
);
1727 belowresult
->destroy(belowresult
);
1728 states
[statepos
].softmaskrecording
= 0;
1733 // public: ~MemCheck()
1735 // delete globalParams;globalParams=0;
1736 // Object::memCheck(stderr);
1737 // gMemReport(stderr);