fixes to ttf file format, merged in Mike Lewis name table patch
[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,dy;
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         fd->dy = 0;
214
215         font->ascent = total.ymax;
216         font->descent = total.ymin;
217
218         for(t=0;t<count;t++) {
219             gfxline_t*line = font->glyphs[t].line;
220             while(line) {
221                 line->x += fd->dx;
222                 line->y += fd->dy;
223                 line->sx += fd->dx;
224                 line->sy += fd->dy;
225                 line = line->next;
226             }
227         }
228         gfxfont_fix_unicode(font);
229     }
230     return out->finish(out);
231 }
232
233 static void pass2_addfont(gfxfilter_t*f, gfxfont_t*font, gfxdevice_t*out)
234 {
235     /* we throw away original fonts, and do the addfont() for the transformed
236        fonts in the first drawchar() */
237 }
238
239 static void pass2_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix, gfxdevice_t*out)
240 {
241     internal_t*i = (internal_t*)f->internal;
242
243     if(i->first) {
244         i->first = 0;
245         DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
246             out->addfont(out, fd->font);
247         }
248     }
249
250     mymatrix_t m;
251     gfxmatrix_t scalematrix;
252     matrix_convert(matrix, font->id, &m, &scalematrix);
253     transformedfont_t*d = dict_lookup(i->matrices, &m);
254     scalematrix.tx -= d->dx;
255     scalematrix.ty -= d->dy;
256     out->drawchar(out, d->font, d->used[glyphnr], color, &scalematrix);
257 }
258
259 void gfxtwopassfilter_remove_font_transforms_init(gfxtwopassfilter_t*f)
260 {
261     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
262
263     memset(f, 0, sizeof(gfxtwopassfilter_t));
264     f->type = gfxfilter_twopass;
265
266     f->pass1.name = "remove font transforms pass 1";
267     f->pass1.drawchar = pass1_drawchar;
268     f->pass1.finish = pass1_finish;
269     f->pass1.internal = i;
270
271     f->pass2.name = "remove font transforms pass 2";
272     f->pass2.addfont = pass2_addfont;
273     f->pass2.drawchar = pass2_drawchar;
274     f->pass2.internal = i;
275
276     i->matrices = dict_new2(&mymatrix_type);
277     i->first = 1;
278 }
279