fixed memory initialization bug in remove_font_transforms filter
[swftools.git] / lib / filters / remove_font_transforms.c
1 /* remove_font_transform.c
2
3    Part of the swftools package.
4
5    Copyright (c) 2010 Matthias Kramm <kramm@quiss.org> 
6  
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.
11
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.
16
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 */
20
21 #include <stdlib.h>
22 #include <math.h>
23 #include <memory.h>
24 #include "../gfxfilter.h"
25 #include "../gfxtools.h"
26 #include "../gfxfont.h"
27 #include "../types.h"
28 #include "../mem.h"
29 #include "../q.h"
30
31 typedef struct _mymatrix {
32     float m00,m01,m10,m11;
33     char*id;
34 } mymatrix_t;
35
36 static void* mymatrix_clone(const void*_m) {
37     if(_m==0) 
38         return 0;
39     const mymatrix_t*m1=_m;
40     mymatrix_t*m2 = malloc(sizeof(mymatrix_t));
41     *m2 = *m1;
42     m2->id = strdup(m1->id);
43     return m2;
44 }
45 static unsigned int mymatrix_hash(const void*_m) {
46     if(!_m)
47         return 0;
48     const mymatrix_t*m = (mymatrix_t*)_m;
49     unsigned int h=0;
50     h = crc32_add_bytes(h, (char*)&m->m00, sizeof(m->m00));
51     h = crc32_add_bytes(h, (char*)&m->m01, sizeof(m->m01));
52     h = crc32_add_bytes(h, (char*)&m->m10, sizeof(m->m10));
53     h = crc32_add_bytes(h, (char*)&m->m11, sizeof(m->m11));
54     h = crc32_add_string(h, m->id);
55     return h;
56 }
57 static void mymatrix_destroy(void*_m) {
58     mymatrix_t*m = (mymatrix_t*)_m;
59     free(m->id);m->id=0;
60     free(m);
61 }
62 static char mymatrix_equals(const void*_m1, const void*_m2) {
63     const mymatrix_t*m1=_m1;
64     const mymatrix_t*m2=_m2;
65     if(!m1 || !m2) 
66         return m1==m2;
67     
68     /* we do a binary comparison of the float32
69        bits here instead of a numerical comparison
70        to prevent the compiler from e.g. removing the
71        (float) cast during optimization, which would break
72        the equivalence between equals() and hash() (as
73        the hash is derived from the float32 values) */
74     return *(U32*)&m1->m00 == *(U32*)&m2->m00 &&
75            *(U32*)&m1->m01 == *(U32*)&m2->m01 &&
76            *(U32*)&m1->m10 == *(U32*)&m2->m10 &&
77            *(U32*)&m1->m11 == *(U32*)&m2->m11 &&
78            !strcmp(m1->id, m2->id);
79 }
80 type_t mymatrix_type = {
81     dup: mymatrix_clone,
82     hash: mymatrix_hash,
83     free: mymatrix_destroy,
84     equals: mymatrix_equals
85 };
86
87 typedef struct _internal {
88     dict_t*matrices;
89     char first;
90 } internal_t;
91
92
93 #ifdef __GNUC__
94 void __attribute__((noinline)) 
95      matrix_convert(gfxmatrix_t*in, const char*id, mymatrix_t*out, gfxmatrix_t*scalematrix)
96 #else
97 void matrix_convert(gfxmatrix_t*in, const char*id, mymatrix_t*out, gfxmatrix_t*scalematrix)
98 #endif
99 {
100     double l1 = sqrt(in->m00 * in->m00 + in->m01 * in->m01);
101     double l2 = sqrt(in->m10 * in->m10 + in->m11 * in->m11);
102     double l = (l1+l2)/2.0;
103     if(l < 1e-10) {
104         memset(out, 0, sizeof(*out));
105         return;
106     }
107     out->m00 = in->m00 / l;
108     out->m10 = in->m10 / l;
109     out->m01 = -in->m01 / l;
110     out->m11 = -in->m11 / l;
111     out->id = (char*)id;
112
113     if(scalematrix) {
114         scalematrix->m00 = l;
115         scalematrix->m01 = 0;
116         scalematrix->m10 = 0;
117         scalematrix->m11 = -l;
118         scalematrix->tx = in->tx;
119         scalematrix->ty = in->ty;
120     }
121 }
122
123 typedef struct _matrixdata {
124     gfxfontlist_t*fonts;
125 } matrixdata_t;
126
127 typedef struct _transformedfont {
128     gfxfont_t*orig;
129     gfxfont_t*font;
130     mymatrix_t matrix;
131     int*used;
132     double dx;
133 } transformedfont_t;
134
135 static transformedfont_t* transformedfont_new(gfxfont_t*orig, mymatrix_t*m)
136 {
137     transformedfont_t*f = rfx_calloc(sizeof(transformedfont_t));
138     f->orig = orig;
139     f->matrix = *m;
140     f->used = rfx_calloc(sizeof(f->used[0])*orig->num_glyphs);
141     int t;
142     for(t=0;t<orig->num_glyphs;t++) {
143         if(orig->glyphs[t].unicode==32)
144             f->used[t]=1; //always preserve the space char in fonts
145     }
146     return f;
147 }
148
149 static void pass1_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix, gfxdevice_t*out)
150 {
151     internal_t*i = (internal_t*)f->internal;
152     mymatrix_t m;
153     matrix_convert(matrix, font->id, &m, 0);
154     transformedfont_t*fd = dict_lookup(i->matrices, &m);
155     if(!fd) {
156         fd = transformedfont_new(font, &m);
157         dict_put(i->matrices, &m, fd);
158     }
159     fd->used[glyphnr]=1;
160     out->drawchar(out, font, glyphnr, color, matrix);
161 }
162
163 static void glyph_transform(gfxglyph_t*g, mymatrix_t*mm)
164 {
165     gfxmatrix_t m;
166     m.m00 = mm->m00;
167     m.m01 = mm->m01;
168     m.m10 = mm->m10;
169     m.m11 = mm->m11;
170     m.tx = 0;
171     m.ty = 0;
172     g->line = gfxline_clone(g->line);
173     gfxline_transform(g->line, &m);
174     if(m.m00>0)
175         g->advance *= m.m00;
176 }
177
178 static gfxresult_t* pass1_finish(gfxfilter_t*f, gfxdevice_t*out)
179 {
180     internal_t*i = (internal_t*)f->internal;
181     DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
182         gfxfont_t*font = fd->font = rfx_calloc(sizeof(gfxfont_t));
183         char id[80];
184         static int fontcount=0;
185         sprintf(id, "font%d", fontcount++);
186         font->id = strdup(id);
187         int t;
188         int count=0;
189         for(t=0;t<fd->orig->num_glyphs;t++) {
190             if(fd->used[t]) 
191                 count++;
192         }
193         font->num_glyphs = count;
194         font->glyphs = rfx_calloc(sizeof(gfxglyph_t)*font->num_glyphs);
195         count = 0;
196         for(t=0;t<fd->orig->num_glyphs;t++) {
197             if(fd->used[t]) {
198                 font->glyphs[count] = fd->orig->glyphs[t];
199                 glyph_transform(&font->glyphs[count], &fd->matrix);
200                 fd->used[t] = count;
201                 count++;
202             }
203         }
204
205         /* adjust the origin so that every character is to the
206            right of the origin */
207         gfxbbox_t total = {0,0,0,0};
208         double average_xmax = 0;
209         for(t=0;t<count;t++) {
210             gfxline_t*line = font->glyphs[t].line;
211             gfxbbox_t b = gfxline_getbbox(line);
212             total = gfxbbox_expand_to_bbox(total, b);
213         }
214         if(count) 
215             average_xmax /= count;
216
217         fd->dx = -total.xmin;
218
219         font->ascent = total.ymax;
220         font->descent = -total.ymin;
221
222         for(t=0;t<count;t++) {
223             gfxline_t*line = font->glyphs[t].line;
224             while(line) {
225                 line->x += fd->dx;
226                 line->sx += fd->dx;
227                 line = line->next;
228             }
229         }
230         gfxfont_fix_unicode(font);
231     }
232     return out->finish(out);
233 }
234
235 static void pass2_addfont(gfxfilter_t*f, gfxfont_t*font, gfxdevice_t*out)
236 {
237     /* we throw away original fonts, and do the addfont() for the transformed
238        fonts in the first drawchar() */
239 }
240
241 static void pass2_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix, gfxdevice_t*out)
242 {
243     internal_t*i = (internal_t*)f->internal;
244
245     if(i->first) {
246         i->first = 0;
247         DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
248             out->addfont(out, fd->font);
249         }
250     }
251
252     mymatrix_t m;
253     gfxmatrix_t scalematrix;
254     matrix_convert(matrix, font->id, &m, &scalematrix);
255     transformedfont_t*d = dict_lookup(i->matrices, &m);
256     scalematrix.tx -= d->dx*scalematrix.m00;
257     out->drawchar(out, d->font, d->used[glyphnr], color, &scalematrix);
258 }
259
260 void gfxtwopassfilter_remove_font_transforms_init(gfxtwopassfilter_t*f)
261 {
262     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
263
264     memset(f, 0, sizeof(gfxtwopassfilter_t));
265     f->type = gfxfilter_twopass;
266
267     f->pass1.name = "remove font transforms pass 1";
268     f->pass1.drawchar = pass1_drawchar;
269     f->pass1.finish = pass1_finish;
270     f->pass1.internal = i;
271
272     f->pass2.name = "remove font transforms pass 2";
273     f->pass2.addfont = pass2_addfont;
274     f->pass2.drawchar = pass2_drawchar;
275     f->pass2.internal = i;
276
277     i->matrices = dict_new2(&mymatrix_type);
278     i->first = 1;
279 }
280