3 Part of the swftools package.
5 Copyright (c) 2008-2011 Matthias Kramm <kramm@quiss.org>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
32 #include "../gfxdevice.h"
33 #include "../gfxfilter.h"
34 #include "../gfxtools.h"
35 #include "../gfxpoly.h"
38 typedef struct _clip
{
44 typedef struct _internal
{
47 char config_dont_clip_characters
;
53 static int verbose
= 0;
55 static void dbg(char*format
, ...)
62 va_start(arglist
, format
);
63 vsnprintf(buf
, sizeof(buf
)-1, format
, arglist
);
66 while(l
&& buf
[l
-1]=='\n') {
70 printf("(device-polyops) %s\n", buf
);
74 int flatten_setparameter(gfxfilter_t
*dev
, const char*key
, const char*value
, gfxdevice_t
*out
)
76 dbg("flatten_setparameter");
77 internal_t
*i
= (internal_t
*)dev
->internal
;
78 if(!strcmp(key
, "dont_clip_characters")) {
79 i
->config_dont_clip_characters
= atoi(value
);
81 return out
->setparameter(out
,key
,value
);
84 void flatten_startpage(gfxfilter_t
*dev
, int width
, int height
, gfxdevice_t
*out
)
86 dbg("flatten_startpage");
87 internal_t
*i
= (internal_t
*)dev
->internal
;
88 out
->startpage(out
,width
,height
);
91 void flatten_startclip(gfxfilter_t
*dev
, gfxline_t
*line
, gfxdevice_t
*out
)
93 dbg("flatten_startclip");
94 internal_t
*i
= (internal_t
*)dev
->internal
;
96 gfxpoly_t
* oldclip
= i
->clip
?i
->clip
->poly
:0;
97 gfxpoly_t
* poly
= gfxpoly_from_fill(line
, DEFAULT_GRID
);
103 gfxpoly_t
* currentclip
= 0;
106 /* we can't rely on gfxpoly actually being able to convert
107 a gfxline into a gfxpoly- for polygons which are too
108 complex or just degenerate, this might fail. So handle
109 all the cases where polygon conversion or intersection
111 UPDATE: this is not needed anymore. The new gfxpoly
112 implementation is stable enough so it always returns
113 a valid result. Still, it's good practice.
115 if(!poly
&& !oldclip
) {
116 out
->startclip(out
,line
);
119 } else if(!poly
&& oldclip
) {
120 gfxline_t
*oldclipline
= gfxline_from_gfxpoly(oldclip
);
121 out
->startclip(out
,oldclipline
);
122 out
->startclip(out
,line
);
125 } else if(poly
&& oldclip
) {
126 gfxpoly_t
*intersection
= gfxpoly_intersect(poly
, oldclip
);
129 // this case is what usually happens
130 gfxpoly_destroy(poly
);poly
=0;
131 currentclip
= intersection
;
135 gfxline_t
*oldclipline
= gfxline_from_gfxpoly(oldclip
);
136 out
->startclip(out
, oldclipline
);
140 } else if(poly
&& !oldclip
) {
146 i
->clip
= (clip_t
*)rfx_calloc(sizeof(clip_t
));
148 i
->clip
->poly
= currentclip
;
149 i
->clip
->openclips
= type
;
152 void flatten_endclip(gfxfilter_t
*dev
, gfxdevice_t
*out
)
154 dbg("flatten_endclip");
155 internal_t
*i
= (internal_t
*)dev
->internal
;
158 msg("<error> endclip without startclip (in: polyops)\n");
162 clip_t
*old
= i
->clip
;
163 i
->clip
= i
->clip
->next
;
165 gfxpoly_destroy(old
->poly
);old
->poly
= 0;
168 for(t
=0;t
<old
->openclips
;t
++)
171 old
->next
= 0;free(old
);
174 static gfxline_t
* handle_poly(internal_t
*i
, gfxpoly_t
*poly
, char*ok
, gfxdevice_t
*out
)
176 if(i
->clip
&& i
->clip
->poly
) {
177 gfxpoly_t
*old
= poly
;
179 poly
= gfxpoly_intersect(poly
, i
->clip
->poly
);
180 gfxpoly_destroy(old
);
189 gfxline_t
*gfxline
= 0;
191 // this is the case where everything went right
192 gfxline_t
*line
= gfxline_from_gfxpoly(poly
);
193 gfxpoly_destroy(poly
);
197 if(i
->clip
&& i
->clip
->poly
) {
198 /* convert current clipping from a polygon to an
199 actual "startclip" written to the output */
200 assert(i
->clip
->openclips
<= 1);
201 gfxline_t
*clipline
= gfxline_from_gfxpoly(i
->clip
->poly
);
202 out
->startclip(out
, clipline
);
203 gfxline_free(clipline
);
204 gfxpoly_destroy(i
->clip
->poly
);i
->clip
->poly
= 0;
205 i
->clip
->openclips
++;
213 void flatten_stroke(gfxfilter_t
*dev
, gfxline_t
*line
, gfxcoord_t width
, gfxcolor_t
*color
, gfx_capType cap_style
, gfx_joinType joint_style
, gfxcoord_t miterLimit
, gfxdevice_t
*out
)
215 dbg("flatten_stroke");
216 internal_t
*i
= (internal_t
*)dev
->internal
;
218 gfxpoly_t
* poly
= gfxpoly_from_stroke(line
, width
, cap_style
, joint_style
, miterLimit
, DEFAULT_GRID
);
220 gfxline_t
*line2
= handle_poly(i
, poly
, &ok
, out
);
223 if(out
&& line2
) out
->fill(out
, line2
, color
);
227 out
->stroke(out
, line
, width
, color
, cap_style
, joint_style
, miterLimit
);
231 void flatten_fill(gfxfilter_t
*dev
, gfxline_t
*line
, gfxcolor_t
*color
, gfxdevice_t
*out
)
234 internal_t
*i
= (internal_t
*)dev
->internal
;
236 gfxpoly_t
*poly
= gfxpoly_from_fill(line
, DEFAULT_GRID
);
238 gfxline_t
*line2
= handle_poly(i
, poly
, &ok
, out
);
241 if(out
&& line2
) out
->fill(out
, line2
, color
);
244 out
->fill(out
, line
, color
);
248 void flatten_fillbitmap(gfxfilter_t
*dev
, gfxline_t
*line
, gfximage_t
*img
, gfxmatrix_t
*matrix
, gfxcxform_t
*cxform
, gfxdevice_t
*out
)
250 dbg("flatten_fillbitmap");
251 internal_t
*i
= (internal_t
*)dev
->internal
;
253 gfxpoly_t
*poly
= gfxpoly_from_fill(line
, DEFAULT_GRID
);
255 gfxline_t
*line2
= handle_poly(i
, poly
, &ok
, out
);
258 if(out
&& line2
) out
->fillbitmap(out
, line2
, img
, matrix
, cxform
);
261 out
->fillbitmap(out
, line
, img
, matrix
, cxform
);
265 void flatten_fillgradient(gfxfilter_t
*dev
, gfxline_t
*line
, gfxgradient_t
*gradient
, gfxgradienttype_t type
, gfxmatrix_t
*matrix
, gfxdevice_t
*out
)
267 dbg("flatten_fillgradient");
268 internal_t
*i
= (internal_t
*)dev
->internal
;
270 gfxpoly_t
*poly
= gfxpoly_from_fill(line
, DEFAULT_GRID
);
272 gfxline_t
*line2
= handle_poly(i
, poly
, &ok
, out
);
275 if(out
&& line2
) out
->fillgradient(out
, line2
, gradient
, type
, matrix
);
278 out
->fillgradient(out
, line
, gradient
, type
, matrix
);
282 void flatten_addfont(gfxfilter_t
*dev
, gfxfont_t
*font
, gfxdevice_t
*out
)
284 dbg("flatten_addfont");
285 internal_t
*i
= (internal_t
*)dev
->internal
;
286 out
->addfont(out
, font
);
289 void flatten_drawchar(gfxfilter_t
*dev
, gfxfont_t
*font
, int glyphnr
, gfxcolor_t
*color
, gfxmatrix_t
*matrix
, gfxdevice_t
*out
)
291 dbg("flatten_drawchar");
294 internal_t
*i
= (internal_t
*)dev
->internal
;
295 gfxline_t
*glyph
= gfxline_clone(font
->glyphs
[glyphnr
].line
);
296 gfxline_transform(glyph
, matrix
);
298 if(i
->clip
&& i
->clip
->poly
) {
299 gfxbbox_t bbox
= gfxline_getbbox(glyph
);
300 gfxpoly_t
*dummybox
= gfxpoly_createbox(bbox
.xmin
,bbox
.ymin
,bbox
.xmax
,bbox
.ymax
, DEFAULT_GRID
);
301 gfxline_t
*dummybox2
= gfxline_from_gfxpoly(dummybox
);
302 bbox
= gfxline_getbbox(dummybox2
);
303 gfxline_free(dummybox2
);
306 gfxline_t
*gfxline
= handle_poly(i
, dummybox
, &ok
, out
);
308 gfxbbox_t bbox2
= gfxline_getbbox(gfxline
);
309 double w
= bbox2
.xmax
- bbox2
.xmin
;
310 double h
= bbox2
.ymax
- bbox2
.ymin
;
311 if(fabs((bbox
.xmax
- bbox
.xmin
) - w
) > DEFAULT_GRID
*2 ||
312 fabs((bbox
.ymax
- bbox
.ymin
) - h
) > DEFAULT_GRID
*2) {
313 /* notable change in character size: character was clipped
314 TODO: how to deal with diagonal cuts?
316 msg("<trace> Character %d was clipped: (%f,%f,%f,%f) -> (%f,%f,%f,%f)",
318 bbox
.xmin
,bbox
.ymin
,bbox
.xmax
,bbox
.ymax
,
319 bbox2
.xmin
,bbox2
.ymin
,bbox2
.xmax
,bbox2
.ymax
);
320 if(!i
->config_dont_clip_characters
) {
321 flatten_fill(dev
, glyph
, color
, out
);
323 if(bbox
.xmin
< bbox
.xmax
&&
324 bbox
.ymin
< bbox
.ymax
) {
325 out
->drawchar(out
, font
, glyphnr
, color
, matrix
);
329 out
->drawchar(out
, font
, glyphnr
, color
, matrix
);
332 out
->drawchar(out
, font
, glyphnr
, color
, matrix
);
334 gfxline_free(gfxline
);
336 out
->drawchar(out
, font
, glyphnr
, color
, matrix
);
342 void flatten_drawlink(gfxfilter_t
*dev
, gfxline_t
*line
, const char*action
, const char*text
, gfxdevice_t
*out
)
344 dbg("flatten_drawlink");
345 internal_t
*i
= (internal_t
*)dev
->internal
;
346 out
->drawlink(out
, line
, action
, text
);
349 void flatten_endpage(gfxfilter_t
*dev
, gfxdevice_t
*out
)
351 dbg("flatten_endpage");
352 internal_t
*i
= (internal_t
*)dev
->internal
;
356 gfxresult_t
* flatten_finish(gfxfilter_t
*dev
, gfxdevice_t
*out
)
358 dbg("flatten_finish");
359 internal_t
*i
= (internal_t
*)dev
->internal
;
361 if(i
->bad_polygons
) {
362 msg("<notice> --flatten success rate: %.1f%% (%d failed polygons)", i
->good_polygons
*100.0 / (i
->good_polygons
+ i
->bad_polygons
), i
->bad_polygons
);
365 free(i
);dev
->internal
= 0;
366 return out
->finish(out
);
369 void gfxfilter_flatten_init(gfxfilter_t
*f
)
371 dbg("flatten filter");
372 memset(f
, 0, sizeof(gfxfilter_t
));
373 internal_t
*i
= (internal_t
*)rfx_calloc(sizeof(internal_t
));
377 f
->type
= gfxfilter_onepass
;
379 f
->setparameter
= flatten_setparameter
;
380 f
->startpage
= flatten_startpage
;
381 f
->startclip
= flatten_startclip
;
382 f
->endclip
= flatten_endclip
;
383 f
->stroke
= flatten_stroke
;
384 f
->fill
= flatten_fill
;
385 f
->fillbitmap
= flatten_fillbitmap
;
386 f
->fillgradient
= flatten_fillgradient
;
387 f
->addfont
= flatten_addfont
;
388 f
->drawchar
= flatten_drawchar
;
389 f
->drawlink
= flatten_drawlink
;
390 f
->endpage
= flatten_endpage
;
391 f
->finish
= flatten_finish
;