748ba224d65e163c6abbecc17e20792a8975c509
[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;
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 = malloc(sizeof(f->used[0])*orig->num_glyphs);
141     return f;
142 }
143
144 static void pass1_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix, gfxdevice_t*out)
145 {
146     internal_t*i = (internal_t*)f->internal;
147     mymatrix_t m;
148     matrix_convert(matrix, font->id, &m, 0);
149     transformedfont_t*fd = dict_lookup(i->matrices, &m);
150     if(!fd) {
151         fd = transformedfont_new(font, &m);
152         dict_put(i->matrices, &m, fd);
153     }
154     fd->used[glyphnr]=1;
155     out->drawchar(out, font, glyphnr, color, matrix);
156 }
157
158 static void glyph_transform(gfxglyph_t*g, mymatrix_t*mm)
159 {
160     gfxmatrix_t m;
161     m.m00 = mm->m00;
162     m.m01 = mm->m01;
163     m.m10 = mm->m10;
164     m.m11 = mm->m11;
165     m.tx = 0;
166     m.ty = 0;
167     g->line = gfxline_clone(g->line);
168     gfxline_transform(g->line, &m);
169     if(m.m00>0)
170         g->advance *= m.m00;
171 }
172
173 static gfxresult_t* pass1_finish(gfxfilter_t*f, gfxdevice_t*out)
174 {
175     internal_t*i = (internal_t*)f->internal;
176     DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
177         gfxfont_t*font = fd->font = rfx_calloc(sizeof(gfxfont_t));
178         char id[80];
179         static int fontcount=0;
180         sprintf(id, "font%d", fontcount++);
181         font->id = strdup(id);
182         int t;
183         int count=0;
184         for(t=0;t<fd->orig->num_glyphs;t++) {
185             if(fd->used[t]) 
186                 count++;
187         }
188         font->num_glyphs = count;
189         font->glyphs = rfx_calloc(sizeof(gfxglyph_t)*font->num_glyphs);
190         count = 0;
191         for(t=0;t<fd->orig->num_glyphs;t++) {
192             if(fd->used[t]) {
193                 font->glyphs[count] = fd->orig->glyphs[t];
194                 glyph_transform(&font->glyphs[count], &fd->matrix);
195                 fd->used[t] = count;
196                 count++;
197             }
198         }
199
200         /* adjust the origin so that every character is to the
201            right of the origin */
202         gfxbbox_t total = {0,0,0,0};
203         double average_xmax = 0;
204         for(t=0;t<count;t++) {
205             gfxline_t*line = font->glyphs[t].line;
206             gfxbbox_t b = gfxline_getbbox(line);
207             total = gfxbbox_expand_to_bbox(total, b);
208         }
209         if(count) 
210             average_xmax /= count;
211
212         fd->dx = -total.xmin;
213
214         font->ascent = total.ymax;
215         font->descent = total.ymin;
216
217         for(t=0;t<count;t++) {
218             gfxline_t*line = font->glyphs[t].line;
219             while(line) {
220                 line->x += fd->dx;
221                 line->sx += fd->dx;
222                 line = line->next;
223             }
224         }
225         gfxfont_fix_unicode(font);
226     }
227     return out->finish(out);
228 }
229
230 static void pass2_addfont(gfxfilter_t*f, gfxfont_t*font, gfxdevice_t*out)
231 {
232     /* we throw away original fonts, and do the addfont() for the transformed
233        fonts in the first drawchar() */
234 }
235
236 static void pass2_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix, gfxdevice_t*out)
237 {
238     internal_t*i = (internal_t*)f->internal;
239
240     if(i->first) {
241         i->first = 0;
242         DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
243             out->addfont(out, fd->font);
244         }
245     }
246
247     mymatrix_t m;
248     gfxmatrix_t scalematrix;
249     matrix_convert(matrix, font->id, &m, &scalematrix);
250     transformedfont_t*d = dict_lookup(i->matrices, &m);
251     scalematrix.tx -= d->dx*scalematrix.m00;
252     out->drawchar(out, d->font, d->used[glyphnr], color, &scalematrix);
253 }
254
255 void gfxtwopassfilter_remove_font_transforms_init(gfxtwopassfilter_t*f)
256 {
257     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
258
259     memset(f, 0, sizeof(gfxtwopassfilter_t));
260     f->type = gfxfilter_twopass;
261
262     f->pass1.name = "remove font transforms pass 1";
263     f->pass1.drawchar = pass1_drawchar;
264     f->pass1.finish = pass1_finish;
265     f->pass1.internal = i;
266
267     f->pass2.name = "remove font transforms pass 2";
268     f->pass2.addfont = pass2_addfont;
269     f->pass2.drawchar = pass2_drawchar;
270     f->pass2.internal = i;
271
272     i->matrices = dict_new2(&mymatrix_type);
273     i->first = 1;
274 }
275