fixed merge conflict
[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             f->used[t]=1; //always preserve the space char in fonts
150     }
151     return f;
152 }
153
154 static void pass1_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix, gfxdevice_t*out)
155 {
156     internal_t*i = (internal_t*)f->internal;
157     mymatrix_t m;
158     if(!font->id) 
159         msg("<error> Font has no ID");
160     matrix_convert(matrix, font->id?font->id:"unknown", &m, 0, color->a);
161     transformedfont_t*fd = dict_lookup(i->matrices, &m);
162     if(!fd) {
163         fd = transformedfont_new(font, &m);
164         dict_put(i->matrices, &m, fd);
165     }
166     fd->used[glyphnr]=1;
167     out->drawchar(out, font, glyphnr, color, matrix);
168 }
169
170 static void glyph_transform(gfxglyph_t*g, mymatrix_t*mm)
171 {
172     gfxmatrix_t m;
173     m.m00 = mm->m00;
174     m.m01 = mm->m01;
175     m.m10 = mm->m10;
176     m.m11 = mm->m11;
177     m.tx = 0;
178     m.ty = 0;
179     if(m.m00>0)
180         g->advance *= m.m00;
181     g->line = gfxline_clone(g->line);
182     gfxline_transform(g->line, &m);
183 }
184
185 static gfxresult_t* pass1_finish(gfxfilter_t*f, gfxdevice_t*out)
186 {
187     internal_t*i = (internal_t*)f->internal;
188     DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
189         gfxfont_t*font = fd->font = rfx_calloc(sizeof(gfxfont_t));
190         char id[80];
191         static int fontcount=0;
192         sprintf(id, "font%d", fontcount++);
193         font->id = strdup(id);
194         int t;
195         int count=0;
196         for(t=0;t<fd->orig->num_glyphs;t++) {
197             if(fd->used[t]) 
198                 count++;
199         }
200         font->num_glyphs = count;
201         font->glyphs = rfx_calloc(sizeof(gfxglyph_t)*font->num_glyphs);
202         count = 0;
203         for(t=0;t<fd->orig->num_glyphs;t++) {
204             if(fd->used[t]) {
205                 font->glyphs[count] = fd->orig->glyphs[t];
206                 glyph_transform(&font->glyphs[count], &fd->matrix);
207                 fd->used[t] = count + 1;
208                 count++;
209             }
210         }
211
212         /* adjust the origin so that every character is to the
213            right of the origin */
214         gfxbbox_t total = {0,0,0,0};
215         double average_xmax = 0;
216         for(t=0;t<count;t++) {
217             gfxline_t*line = font->glyphs[t].line;
218             gfxbbox_t b = gfxline_getbbox(line);
219             total = gfxbbox_expand_to_bbox(total, b);
220         }
221         if(count) 
222             average_xmax /= count;
223
224         fd->dx = -total.xmin;
225
226         font->ascent = total.ymax;
227         font->descent = -total.ymin;
228
229         for(t=0;t<count;t++) {
230             gfxglyph_t*g = &font->glyphs[t];
231             gfxline_t*line = font->glyphs[t].line;
232
233             if(fd->matrix.alpha) {
234                 while(line) {
235                     line->x += fd->dx;
236                     line->sx += fd->dx;
237                     line = line->next;
238                 }
239             } else {
240                 gfxline_free(g->line);
241                 /* for OCR: remove the outlines of characters that are only
242                    ever displayed with alpha=0 */
243                 g->line = (gfxline_t*)rfx_calloc(sizeof(gfxline_t));
244                 g->line->type = gfx_moveTo;
245                 g->line->x = g->advance;
246             }
247         }
248
249         if(fd->matrix.m00>0) {
250             /* subset kerning table */
251             count = 0;
252             for(t=0;t<fd->orig->kerning_size;t++) {
253                 int char1 = fd->used[fd->orig->kerning[t].c1]-1;
254                 int char2 = fd->used[fd->orig->kerning[t].c2]-1;
255                 if(char1>=0 && char2>=0) {
256                     count++;
257                 }
258             }
259             font->kerning = malloc(sizeof(font->kerning[0])*count);
260             font->kerning_size = count;
261             count = 0;
262             for(t=0;t<fd->orig->kerning_size;t++) {
263                 int char1 = fd->used[fd->orig->kerning[t].c1]-1;
264                 int char2 = fd->used[fd->orig->kerning[t].c2]-1;
265                 if(char1>=0 && char2>=0) {
266                     font->kerning[count].c1 = char1;
267                     font->kerning[count].c2 = char2;
268                     font->kerning[count].advance = fd->orig->kerning[t].advance * fd->matrix.m00;
269                     count++;
270                 }
271             }
272         }
273         gfxfont_fix_unicode(font);
274     }
275     return out->finish(out);
276 }
277
278 static void pass2_addfont(gfxfilter_t*f, gfxfont_t*font, gfxdevice_t*out)
279 {
280     /* we throw away original fonts, and do the addfont() for the transformed
281        fonts in the first drawchar() */
282 }
283
284 static void pass2_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*_color, gfxmatrix_t*matrix, gfxdevice_t*out)
285 {
286     internal_t*i = (internal_t*)f->internal;
287     gfxcolor_t color = *_color;
288
289     if(i->first) {
290         i->first = 0;
291         DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
292             out->addfont(out, fd->font);
293         }
294     }
295
296     mymatrix_t m;
297     gfxmatrix_t scalematrix;
298     matrix_convert(matrix, font->id?font->id:"unknown", &m, &scalematrix, color.a);
299     transformedfont_t*d = dict_lookup(i->matrices, &m);
300     scalematrix.tx -= d->dx*scalematrix.m00;
301
302     /* if this character is invisible (alpha=0), then we will have removed the
303        outline, so we make set the alpha color channel to "fully visible" again to allow
304        output devices to be more performant (transparency is expensive) */
305     if(!m.alpha) 
306         color.a = 255;
307
308     out->drawchar(out, d->font, d->used[glyphnr]-1, &color, &scalematrix);
309 }
310
311 static gfxresult_t* pass2_finish(gfxfilter_t*f, gfxdevice_t*out)
312 {
313     internal_t*i = (internal_t*)f->internal;
314     DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
315         if(fd->used) {
316             free(fd->used);fd->used=0;
317         }
318         free(fd);
319     }
320     dict_destroy(i->matrices);i->matrices=0;
321     return out->finish(out);
322 }
323
324 void gfxtwopassfilter_remove_font_transforms_init(gfxtwopassfilter_t*f)
325 {
326     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
327
328     memset(f, 0, sizeof(gfxtwopassfilter_t));
329     f->type = gfxfilter_twopass;
330
331     f->pass1.name = "remove font transforms pass 1";
332     f->pass1.drawchar = pass1_drawchar;
333     f->pass1.finish = pass1_finish;
334     f->pass1.internal = i;
335
336     f->pass2.name = "remove font transforms pass 2";
337     f->pass2.addfont = pass2_addfont;
338     f->pass2.drawchar = pass2_drawchar;
339     f->pass2.finish = pass2_finish;
340     f->pass2.internal = i;
341
342     i->matrices = dict_new2(&mymatrix_type);
343     i->first = 1;
344 }
345