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