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