keep advances positive when transforming a font
[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 }
170
171 static gfxresult_t* pass1_finish(gfxfilter_t*f, gfxdevice_t*out)
172 {
173     internal_t*i = (internal_t*)f->internal;
174     DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
175         gfxfont_t*font = fd->font = rfx_calloc(sizeof(gfxfont_t));
176         char id[80];
177         static int fontcount=0;
178         sprintf(id, "font%d", fontcount++);
179         font->id = strdup(id);
180         int t;
181         int count=0;
182         for(t=0;t<fd->orig->num_glyphs;t++) {
183             if(fd->used[t]) 
184                 count++;
185         }
186         font->num_glyphs = count;
187         font->glyphs = rfx_calloc(sizeof(gfxglyph_t)*font->num_glyphs);
188         count = 0;
189         for(t=0;t<fd->orig->num_glyphs;t++) {
190             if(fd->used[t]) {
191                 font->glyphs[count] = fd->orig->glyphs[t];
192                 glyph_transform(&font->glyphs[count], &fd->matrix);
193                 fd->used[t] = count;
194                 count++;
195             }
196         }
197
198         /* adjust the origin so that every character is to the
199            right of the origin */
200         gfxbbox_t total = {0,0,0,0};
201         double average_xmax = 0;
202         for(t=0;t<count;t++) {
203             gfxline_t*line = font->glyphs[t].line;
204             gfxbbox_t b = gfxline_getbbox(line);
205             total = gfxbbox_expand_to_bbox(total, b);
206             if(b.xmax > 0)
207                 font->glyphs[t].advance = b.xmax;
208         }
209         if(count) 
210             average_xmax /= count;
211
212         fd->dx = -total.xmin;
213         fd->dy = 0;
214
215         double adx = fd->dx>0?fd->dx:0;
216         
217         for(t=0;t<count;t++) {
218             gfxline_t*line = font->glyphs[t].line;
219             font->glyphs[t].advance += adx;
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_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix, gfxdevice_t*out)
234 {
235     internal_t*i = (internal_t*)f->internal;
236
237     if(i->first) {
238         i->first = 0;
239         DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
240             out->addfont(out, fd->font);
241         }
242     }
243
244     mymatrix_t m;
245     gfxmatrix_t scalematrix;
246     matrix_convert(matrix, font->id, &m, &scalematrix);
247     transformedfont_t*d = dict_lookup(i->matrices, &m);
248     scalematrix.tx -= d->dx;
249     scalematrix.ty -= d->dy;
250     out->drawchar(out, d->font, d->used[glyphnr], color, &scalematrix);
251 }
252
253 void gfxtwopassfilter_remove_font_transforms_init(gfxtwopassfilter_t*f)
254 {
255     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
256
257     memset(f, 0, sizeof(gfxtwopassfilter_t));
258     f->type = gfxfilter_twopass;
259
260     f->pass1.name = "remove font transforms pass 1";
261     f->pass1.drawchar = pass1_drawchar;
262     f->pass1.finish = pass1_finish;
263     f->pass1.internal = i;
264
265     f->pass2.name = "remove font transforms pass 2";
266     f->pass2.drawchar = pass2_drawchar;
267     f->pass2.internal = i;
268
269     i->matrices = dict_new2(&mymatrix_type);
270     i->first = 1;
271 }
272