no option dont_clip_characters
[swftools.git] / lib / filters / flatten.c
blob76736f1a8b6267486f75caf9e88bfad281bd0e47
1 /* flatten.c
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 */
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #ifndef WIN32
25 #include <unistd.h>
26 #endif
27 #include <memory.h>
28 #include <assert.h>
29 #include <string.h>
30 #include <math.h>
31 #include "../mem.h"
32 #include "../gfxdevice.h"
33 #include "../gfxfilter.h"
34 #include "../gfxtools.h"
35 #include "../gfxpoly.h"
36 #include "../log.h"
38 typedef struct _clip {
39 gfxpoly_t*poly;
40 int openclips;
41 struct _clip*next;
42 } clip_t;
44 typedef struct _internal {
45 clip_t*clip;
47 char config_dont_clip_characters;
49 int good_polygons;
50 int bad_polygons;
51 } internal_t;
53 static int verbose = 0;
55 static void dbg(char*format, ...)
57 if(!verbose)
58 return;
59 char buf[1024];
60 int l;
61 va_list arglist;
62 va_start(arglist, format);
63 vsnprintf(buf, sizeof(buf)-1, format, arglist);
64 va_end(arglist);
65 l = strlen(buf);
66 while(l && buf[l-1]=='\n') {
67 buf[l-1] = 0;
68 l--;
70 printf("(device-polyops) %s\n", buf);
71 fflush(stdout);
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);
98 if(poly)
99 i->good_polygons++;
100 else
101 i->bad_polygons++;
103 gfxpoly_t* currentclip = 0;
104 int type = 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
110 might go awry
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);
117 currentclip = 0;
118 type = 1;
119 } else if(!poly && oldclip) {
120 gfxline_t*oldclipline = gfxline_from_gfxpoly(oldclip);
121 out->startclip(out,oldclipline);
122 out->startclip(out,line);
123 currentclip = 0;
124 type = 2;
125 } else if(poly && oldclip) {
126 gfxpoly_t*intersection = gfxpoly_intersect(poly, oldclip);
127 if(intersection) {
128 i->good_polygons++;
129 // this case is what usually happens
130 gfxpoly_destroy(poly);poly=0;
131 currentclip = intersection;
132 type = 0;
133 } else {
134 i->bad_polygons++;
135 gfxline_t*oldclipline = gfxline_from_gfxpoly(oldclip);
136 out->startclip(out, oldclipline);
137 currentclip = poly;
138 type = 1;
140 } else if(poly && !oldclip) {
141 currentclip = poly;
142 type = 0;
145 clip_t*n = i->clip;
146 i->clip = (clip_t*)rfx_calloc(sizeof(clip_t));
147 i->clip->next = n;
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;
157 if(!i->clip) {
158 msg("<error> endclip without startclip (in: polyops)\n");
159 return;
162 clip_t*old = i->clip;
163 i->clip = i->clip->next;
164 if(old->poly) {
165 gfxpoly_destroy(old->poly);old->poly = 0;
167 int t;
168 for(t=0;t<old->openclips;t++)
169 out->endclip(out);
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;
178 if(poly) {
179 poly = gfxpoly_intersect(poly, i->clip->poly);
180 gfxpoly_destroy(old);
184 if(poly)
185 i->good_polygons++;
186 else
187 i->bad_polygons++;
189 gfxline_t*gfxline = 0;
190 if(poly) {
191 // this is the case where everything went right
192 gfxline_t*line = gfxline_from_gfxpoly(poly);
193 gfxpoly_destroy(poly);
194 *ok = 1;
195 return line;
196 } else {
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++;
206 return 0;
207 } else {
208 return 0;
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);
219 char ok = 0;
220 gfxline_t*line2 = handle_poly(i, poly, &ok, out);
222 if(ok) {
223 if(out && line2) out->fill(out, line2, color);
224 gfxline_free(line2);
225 } else {
226 msg("<error> ..");
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)
233 dbg("flatten_fill");
234 internal_t*i = (internal_t*)dev->internal;
236 gfxpoly_t*poly = gfxpoly_from_fill(line, DEFAULT_GRID);
237 char ok = 0;
238 gfxline_t*line2 = handle_poly(i, poly, &ok, out);
240 if(ok) {
241 if(out && line2) out->fill(out, line2, color);
242 gfxline_free(line2);
243 } else {
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);
254 char ok = 0;
255 gfxline_t*line2 = handle_poly(i, poly, &ok, out);
257 if(ok) {
258 if(out && line2) out->fillbitmap(out, line2, img, matrix, cxform);
259 gfxline_free(line2);
260 } else {
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);
271 char ok = 0;
272 gfxline_t*line2 = handle_poly(i, poly, &ok, out);
274 if(ok) {
275 if(out && line2) out->fillgradient(out, line2, gradient, type, matrix);
276 gfxline_free(line2);
277 } else {
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");
292 if(!font)
293 return;
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);
305 char ok=0;
306 gfxline_t*gfxline = handle_poly(i, dummybox, &ok, out);
307 if(ok) {
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)",
317 glyphnr,
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);
322 } else {
323 if(bbox.xmin < bbox.xmax &&
324 bbox.ymin < bbox.ymax) {
325 out->drawchar(out, font, glyphnr, color, matrix);
328 } else {
329 out->drawchar(out, font, glyphnr, color, matrix);
331 } else {
332 out->drawchar(out, font, glyphnr, color, matrix);
334 gfxline_free(gfxline);
335 } else {
336 out->drawchar(out, font, glyphnr, color, matrix);
339 gfxline_free(glyph);
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;
353 out->endpage(out);
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));
375 f->internal = i;
376 f->name = "flatten";
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;