fixed space char detection bug
[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 #include "../log.h"
31
32 typedef struct _mymatrix {
33     float m00,m01,m10,m11;
34     char*id;
35     unsigned char alpha;
36 } mymatrix_t;
37
38 static void* mymatrix_clone(const void*_m) {
39     if(_m==0) 
40         return 0;
41     const mymatrix_t*m1=_m;
42     mymatrix_t*m2 = malloc(sizeof(mymatrix_t));
43     *m2 = *m1;
44     m2->id = strdup(m1->id);
45     return m2;
46 }
47 static unsigned int mymatrix_hash(const void*_m) {
48     if(!_m)
49         return 0;
50     const mymatrix_t*m = (mymatrix_t*)_m;
51     unsigned int h=0;
52     h = crc32_add_bytes(h, (char*)&m->m00, sizeof(m->m00));
53     h = crc32_add_bytes(h, (char*)&m->m01, sizeof(m->m01));
54     h = crc32_add_bytes(h, (char*)&m->m10, sizeof(m->m10));
55     h = crc32_add_bytes(h, (char*)&m->m11, sizeof(m->m11));
56     h = crc32_add_bytes(h, (char*)&m->alpha, 1);
57     h = crc32_add_string(h, m->id);
58     return h;
59 }
60 static void mymatrix_destroy(void*_m) {
61     mymatrix_t*m = (mymatrix_t*)_m;
62     free(m->id);m->id=0;
63     free(m);
64 }
65 static char mymatrix_equals(const void*_m1, const void*_m2) {
66     const mymatrix_t*m1=_m1;
67     const mymatrix_t*m2=_m2;
68     if(!m1 || !m2) 
69         return m1==m2;
70     
71     /* we do a binary comparison of the float32
72        bits here instead of a numerical comparison
73        to prevent the compiler from e.g. removing the
74        (float) cast during optimization, which would break
75        the equivalence between equals() and hash() (as
76        the hash is derived from the float32 values) */
77     return *(U32*)&m1->m00 == *(U32*)&m2->m00 &&
78            *(U32*)&m1->m01 == *(U32*)&m2->m01 &&
79            *(U32*)&m1->m10 == *(U32*)&m2->m10 &&
80            *(U32*)&m1->m11 == *(U32*)&m2->m11 &&
81            m1->alpha == m2->alpha &&
82            !strcmp(m1->id, m2->id);
83 }
84 type_t mymatrix_type = {
85     dup: mymatrix_clone,
86     hash: mymatrix_hash,
87     free: mymatrix_destroy,
88     equals: mymatrix_equals
89 };
90
91 typedef struct _internal {
92     dict_t*matrices;
93     char first;
94 } internal_t;
95
96
97 #ifdef __GNUC__
98 void __attribute__((noinline)) 
99      matrix_convert(gfxmatrix_t*in, const char*id, mymatrix_t*out, gfxmatrix_t*scalematrix, unsigned char alpha)
100 #else
101 void matrix_convert(gfxmatrix_t*in, const char*id, mymatrix_t*out, gfxmatrix_t*scalematrix, unsigned char alpha)
102 #endif
103 {
104     double l1 = sqrt(in->m00 * in->m00 + in->m01 * in->m01);
105     double l2 = sqrt(in->m10 * in->m10 + in->m11 * in->m11);
106     double l = (l1+l2)/2.0;
107     if(l < 1e-10) {
108         memset(out, 0, sizeof(*out));
109         return;
110     }
111     out->m00 = in->m00 / l;
112     out->m10 = in->m10 / l;
113     out->m01 = -in->m01 / l;
114     out->m11 = -in->m11 / l;
115     out->id = (char*)id;
116     out->alpha = alpha?1:0;
117
118     if(scalematrix) {
119         scalematrix->m00 = l;
120         scalematrix->m01 = 0;
121         scalematrix->m10 = 0;
122         scalematrix->m11 = -l;
123         scalematrix->tx = in->tx;
124         scalematrix->ty = in->ty;
125     }
126 }
127
128 typedef struct _matrixdata {
129     gfxfontlist_t*fonts;
130 } matrixdata_t;
131
132 typedef struct _transformedfont {
133     gfxfont_t*orig;
134     gfxfont_t*font;
135     mymatrix_t matrix;
136     int*used;
137     double dx;
138 } transformedfont_t;
139
140 static transformedfont_t* transformedfont_new(gfxfont_t*orig, mymatrix_t*m)
141 {
142     transformedfont_t*f = rfx_calloc(sizeof(transformedfont_t));
143     f->orig = orig;
144     f->matrix = *m;
145     f->used = rfx_calloc(sizeof(f->used[0])*orig->num_glyphs);
146     int t;
147     for(t=0;t<orig->num_glyphs;t++) {
148         if(orig->glyphs[t].unicode==32 && 
149            (!orig->glyphs[t].line || 
150             !orig->glyphs[t].line->next || 
151             !orig->glyphs[t].line->next->next))
152             f->used[t]=1; //always preserve the space char in fonts
153     }
154     return f;
155 }
156
157 static void pass1_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix, gfxdevice_t*out)
158 {
159     internal_t*i = (internal_t*)f->internal;
160     mymatrix_t m;
161     if(!font->id) 
162         msg("<error> Font has no ID");
163     matrix_convert(matrix, font->id?font->id:"unknown", &m, 0, color->a);
164     transformedfont_t*fd = dict_lookup(i->matrices, &m);
165     if(!fd) {
166         fd = transformedfont_new(font, &m);
167         dict_put(i->matrices, &m, fd);
168     }
169     fd->used[glyphnr]=1;
170     out->drawchar(out, font, glyphnr, color, matrix);
171 }
172
173 static void glyph_transform(gfxglyph_t*g, mymatrix_t*mm)
174 {
175     gfxmatrix_t m;
176     m.m00 = mm->m00;
177     m.m01 = mm->m01;
178     m.m10 = mm->m10;
179     m.m11 = mm->m11;
180     m.tx = 0;
181     m.ty = 0;
182     if(m.m00>0)
183         g->advance *= m.m00;
184     g->line = gfxline_clone(g->line);
185     gfxline_transform(g->line, &m);
186 }
187
188 static gfxresult_t* pass1_finish(gfxfilter_t*f, gfxdevice_t*out)
189 {
190     internal_t*i = (internal_t*)f->internal;
191     DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
192         gfxfont_t*font = fd->font = rfx_calloc(sizeof(gfxfont_t));
193         char id[80];
194         static int fontcount=0;
195         sprintf(id, "font%d", fontcount++);
196         font->id = strdup(id);
197         int t;
198         int count=0;
199         for(t=0;t<fd->orig->num_glyphs;t++) {
200             if(fd->used[t]) 
201                 count++;
202         }
203         font->num_glyphs = count;
204         font->glyphs = rfx_calloc(sizeof(gfxglyph_t)*font->num_glyphs);
205         count = 0;
206         for(t=0;t<fd->orig->num_glyphs;t++) {
207             if(fd->used[t]) {
208                 font->glyphs[count] = fd->orig->glyphs[t];
209                 glyph_transform(&font->glyphs[count], &fd->matrix);
210                 fd->used[t] = count + 1;
211                 count++;
212             }
213         }
214
215         /* adjust the origin so that every character is to the
216            right of the origin */
217         gfxbbox_t total = {0,0,0,0};
218         double average_xmax = 0;
219         for(t=0;t<count;t++) {
220             gfxline_t*line = font->glyphs[t].line;
221             gfxbbox_t b = gfxline_getbbox(line);
222             total = gfxbbox_expand_to_bbox(total, b);
223         }
224         if(count) 
225             average_xmax /= count;
226
227         fd->dx = -total.xmin;
228
229         font->ascent = total.ymax;
230         font->descent = -total.ymin;
231
232         for(t=0;t<count;t++) {
233             gfxglyph_t*g = &font->glyphs[t];
234             gfxline_t*line = font->glyphs[t].line;
235
236             if(fd->matrix.alpha) {
237                 while(line) {
238                     line->x += fd->dx;
239                     line->sx += fd->dx;
240                     line = line->next;
241                 }
242             } else {
243                 gfxline_free(g->line);
244                 /* for OCR: remove the outlines of characters that are only
245                    ever displayed with alpha=0 */
246                 g->line = (gfxline_t*)rfx_calloc(sizeof(gfxline_t));
247                 g->line->type = gfx_moveTo;
248                 g->line->x = g->advance;
249             }
250         }
251
252         if(fd->matrix.m00>0) {
253             /* subset kerning table */
254             count = 0;
255             for(t=0;t<fd->orig->kerning_size;t++) {
256                 int char1 = fd->used[fd->orig->kerning[t].c1]-1;
257                 int char2 = fd->used[fd->orig->kerning[t].c2]-1;
258                 if(char1>=0 && char2>=0) {
259                     count++;
260                 }
261             }
262             font->kerning = malloc(sizeof(font->kerning[0])*count);
263             font->kerning_size = count;
264             count = 0;
265             for(t=0;t<fd->orig->kerning_size;t++) {
266                 int char1 = fd->used[fd->orig->kerning[t].c1]-1;
267                 int char2 = fd->used[fd->orig->kerning[t].c2]-1;
268                 if(char1>=0 && char2>=0) {
269                     font->kerning[count].c1 = char1;
270                     font->kerning[count].c2 = char2;
271                     font->kerning[count].advance = fd->orig->kerning[t].advance * fd->matrix.m00;
272                     count++;
273                 }
274             }
275         }
276         gfxfont_fix_unicode(font);
277     }
278     return out->finish(out);
279 }
280
281 static void pass2_addfont(gfxfilter_t*f, gfxfont_t*font, gfxdevice_t*out)
282 {
283     /* we throw away original fonts, and do the addfont() for the transformed
284        fonts in the first drawchar() */
285 }
286
287 static void pass2_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*_color, gfxmatrix_t*matrix, gfxdevice_t*out)
288 {
289     internal_t*i = (internal_t*)f->internal;
290     gfxcolor_t color = *_color;
291
292     if(i->first) {
293         i->first = 0;
294         DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
295             out->addfont(out, fd->font);
296         }
297     }
298
299     mymatrix_t m;
300     gfxmatrix_t scalematrix;
301     matrix_convert(matrix, font->id?font->id:"unknown", &m, &scalematrix, color.a);
302     transformedfont_t*d = dict_lookup(i->matrices, &m);
303     scalematrix.tx -= d->dx*scalematrix.m00;
304
305     /* if this character is invisible (alpha=0), then we will have removed the
306        outline, so we make set the alpha color channel to "fully visible" again to allow
307        output devices to be more performant (transparency is expensive) */
308     if(!m.alpha) 
309         color.a = 255;
310
311     out->drawchar(out, d->font, d->used[glyphnr]-1, &color, &scalematrix);
312 }
313
314 static gfxresult_t* pass2_finish(gfxfilter_t*f, gfxdevice_t*out)
315 {
316     internal_t*i = (internal_t*)f->internal;
317     DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
318         if(fd->used) {
319             free(fd->used);fd->used=0;
320         }
321         free(fd);
322     }
323     dict_destroy(i->matrices);i->matrices=0;
324     return out->finish(out);
325 }
326
327 void gfxtwopassfilter_remove_font_transforms_init(gfxtwopassfilter_t*f)
328 {
329     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
330
331     memset(f, 0, sizeof(gfxtwopassfilter_t));
332     f->type = gfxfilter_twopass;
333
334     f->pass1.name = "remove font transforms pass 1";
335     f->pass1.drawchar = pass1_drawchar;
336     f->pass1.finish = pass1_finish;
337     f->pass1.internal = i;
338
339     f->pass2.name = "remove font transforms pass 2";
340     f->pass2.addfont = pass2_addfont;
341     f->pass2.drawchar = pass2_drawchar;
342     f->pass2.finish = pass2_finish;
343     f->pass2.internal = i;
344
345     i->matrices = dict_new2(&mymatrix_type);
346     i->first = 1;
347 }
348