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