d40b6c8427eb3e6bbd7edf4aa7b37af2da650cf3
[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
92 typedef struct _transformedfont {
93     gfxfont_t*orig;
94     gfxfont_t*font;
95     mymatrix_t matrix;
96     int*used;
97     double dx;
98     struct _transformedfont *group_next;
99 } transformedfont_t;
100
101 typedef struct _fontgroup {
102     U64*bitmap; 
103     int bitmap_len;
104     transformedfont_t*fonts;
105     struct _fontgroup *next;
106 } fontgroup_t;
107
108 typedef struct _internal {
109     dict_t*matrices;
110     fontgroup_t*groups;
111     char first;
112     char config_recombine;
113 } internal_t;
114
115
116 #ifdef __GNUC__
117 int __attribute__((noinline)) 
118      matrix_convert(gfxmatrix_t*in, const char*id, mymatrix_t*out, gfxmatrix_t*scalematrix, unsigned char alpha)
119 #else
120 int matrix_convert(gfxmatrix_t*in, const char*id, mymatrix_t*out, gfxmatrix_t*scalematrix, unsigned char alpha)
121 #endif
122 {
123     double l1 = sqrt(in->m00 * in->m00 + in->m01 * in->m01);
124     double l2 = sqrt(in->m10 * in->m10 + in->m11 * in->m11);
125     double l = (l1+l2)/2.0;
126     if(l < 1e-10) {
127         memset(out, 0, sizeof(*out));
128         return 0;
129     }
130     out->m00 = in->m00 / l;
131     out->m10 = in->m10 / l;
132     out->m01 = -in->m01 / l;
133     out->m11 = -in->m11 / l;
134     out->id = (char*)id;
135     out->alpha = alpha?1:0;
136
137     if(scalematrix) {
138         scalematrix->m00 = l;
139         scalematrix->m01 = 0;
140         scalematrix->m10 = 0;
141         scalematrix->m11 = -l;
142         scalematrix->tx = in->tx;
143         scalematrix->ty = in->ty;
144     }
145     return 1;
146 }
147
148 typedef struct _matrixdata {
149     gfxfontlist_t*fonts;
150 } matrixdata_t;
151
152 static transformedfont_t* transformedfont_new(gfxfont_t*orig, mymatrix_t*m)
153 {
154     transformedfont_t*f = rfx_calloc(sizeof(transformedfont_t));
155     f->orig = orig;
156     f->matrix = *m;
157     f->used = rfx_calloc(sizeof(f->used[0])*orig->num_glyphs);
158     int t;
159     for(t=0;t<orig->num_glyphs;t++) {
160         if(orig->glyphs[t].unicode==32 && 
161            (!orig->glyphs[t].line || 
162             !orig->glyphs[t].line->next || 
163             !orig->glyphs[t].line->next->next))
164             f->used[t]=1; //always preserve the space char in fonts
165     }
166     return f;
167 }
168
169 static void pass1_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix, gfxdevice_t*out)
170 {
171     internal_t*i = (internal_t*)f->internal;
172     mymatrix_t m;
173     if(!font->id) 
174         msg("<error> Font has no ID");
175     if(!matrix_convert(matrix, font->id?font->id:"unknown", &m, 0, color->a))
176         return;
177     transformedfont_t*fd = dict_lookup(i->matrices, &m);
178     if(!fd) {
179         fd = transformedfont_new(font, &m);
180         dict_put(i->matrices, &m, fd);
181     }
182     fd->used[glyphnr]=1;
183     out->drawchar(out, font, glyphnr, color, matrix);
184 }
185
186 static void glyph_transform(gfxglyph_t*g, mymatrix_t*mm)
187 {
188     gfxmatrix_t m;
189     m.m00 = mm->m00;
190     m.m01 = mm->m01;
191     m.m10 = mm->m10;
192     m.m11 = mm->m11;
193     m.tx = 0;
194     m.ty = 0;
195     if(m.m00>0)
196         g->advance *= m.m00;
197     g->line = gfxline_clone(g->line);
198     gfxline_transform(g->line, &m);
199 }
200
201 void fontgroup_add_to_bitmap(fontgroup_t*g, transformedfont_t*f)
202 {
203     int t;
204     int max = 0;
205     for(t=0;t<f->orig->num_glyphs;t++) {
206         if(f->orig->glyphs[t].unicode < 0xe000 && f->orig->glyphs[t].unicode > max) {
207             max = f->orig->glyphs[t].unicode;
208         }
209     }
210     int max64 = (max+63) >> 6;
211     if(max64 > g->bitmap_len) {
212         g->bitmap = rfx_realloc(g->bitmap, max64*sizeof(U64));
213         memset(g->bitmap+g->bitmap_len*8, 0, (max64-g->bitmap_len)*8);
214         g->bitmap_len = max64;
215     }
216     for(t=0;t<f->orig->num_glyphs;t++) {
217         int u = f->orig->glyphs[t].unicode;
218         if(u < 0xe000 && f->used[t]) {
219             g->bitmap[u >> 6] |= (1ll<<(u&63));
220         }
221     }
222     g->bitmap[0] &= ~(1ll<<32); // map out space char
223 }
224
225 int fontgroup_intersect(fontgroup_t*g1, fontgroup_t*g2)
226 {
227     int min = g1->bitmap_len < g2->bitmap_len? g1->bitmap_len : g2->bitmap_len;
228     int t;
229     for(t=0;t<min;t++) {
230         if(g1->bitmap[t]&g2->bitmap[t]) {
231             return 1;
232         }
233     }
234     return 0;
235 }
236
237 fontgroup_t* fontgroup_combine(fontgroup_t*fontgroup)
238 {
239     fontgroup_t*fg = fontgroup;
240     while(fg) {
241         fontgroup_t*fg2 = fg->next;
242         fontgroup_t*old = fg;
243         while(fg2) {
244             fontgroup_t*next = fg2->next;
245             if(!fontgroup_intersect(fg,fg2)) {
246                 printf("combine %s and %s\n", fg->fonts->orig->id, fg2->fonts->orig->id);
247                 transformedfont_t*last = fg->fonts;
248                 while(last->group_next) {
249                     last = last->group_next;
250                 }
251                 last->group_next = fg2->fonts;
252                 fg2->fonts = 0;
253                 old->next = next;
254                 free(fg2->bitmap);
255                 free(fg2);
256             } else {
257                 old = fg2;
258             }
259             fg2 = next;
260         }
261         fg = fg->next;
262     }
263     return fontgroup;
264 }
265
266 static gfxresult_t* pass1_finish(gfxfilter_t*f, gfxdevice_t*out)
267 {
268     internal_t*i = (internal_t*)f->internal;
269
270     fontgroup_t*fontgroup=0;
271     DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
272         NEW(fontgroup_t,fg);
273         fg->fonts = fd;
274         fontgroup_add_to_bitmap(fg, fd);
275         fg->next = fontgroup;
276         fontgroup = fg;
277     }
278    
279     if(i->config_recombine) {
280         fontgroup = fontgroup_combine(fontgroup);
281     }
282     i->groups = fontgroup;
283
284     fontgroup_t*fg;
285     for(fg = fontgroup;fg;fg = fg->next) {
286         transformedfont_t*fd = fg->fonts;
287         while(fd) {
288             gfxfont_t*font = fd->font = rfx_calloc(sizeof(gfxfont_t));
289             char id[80];
290             static int fontcount=0;
291             sprintf(id, "font%d", fontcount++);
292             font->id = strdup(id);
293             int t;
294             int count=0;
295             for(t=0;t<fd->orig->num_glyphs;t++) {
296                 if(fd->used[t]) 
297                     count++;
298             }
299             font->num_glyphs = count;
300             font->glyphs = rfx_calloc(sizeof(gfxglyph_t)*font->num_glyphs);
301             count = 0;
302             for(t=0;t<fd->orig->num_glyphs;t++) {
303                 if(fd->used[t]) {
304                     font->glyphs[count] = fd->orig->glyphs[t];
305                     glyph_transform(&font->glyphs[count], &fd->matrix);
306                     fd->used[t] = count + 1;
307                     count++;
308                 }
309             }
310
311             /* adjust the origin so that every character is to the
312                right of the origin */
313             gfxbbox_t total = {0,0,0,0};
314             double average_xmax = 0;
315             for(t=0;t<count;t++) {
316                 gfxline_t*line = font->glyphs[t].line;
317                 gfxbbox_t b = gfxline_getbbox(line);
318                 total = gfxbbox_expand_to_bbox(total, b);
319             }
320             if(count) 
321                 average_xmax /= count;
322
323             font->ascent = total.ymax;
324             font->descent = -total.ymin;
325
326             if(!fd->matrix.alpha) {
327                 /* for OCR: remove the outlines of characters that are only
328                    ever displayed with alpha=0 */
329                 for(t=0;t<count;t++) {
330                     gfxglyph_t*g = &font->glyphs[t];
331                     gfxline_free(g->line);
332                     g->line = (gfxline_t*)rfx_calloc(sizeof(gfxline_t));
333                     g->line->type = gfx_moveTo;
334                     g->line->x = g->advance;
335                 }
336             }
337
338             if(fd->matrix.m00>0) {
339                 /* subset kerning table */
340                 count = 0;
341                 for(t=0;t<fd->orig->kerning_size;t++) {
342                     int char1 = fd->used[fd->orig->kerning[t].c1]-1;
343                     int char2 = fd->used[fd->orig->kerning[t].c2]-1;
344                     if(char1>=0 && char2>=0) {
345                         count++;
346                     }
347                 }
348                 font->kerning = malloc(sizeof(font->kerning[0])*count);
349                 font->kerning_size = count;
350                 count = 0;
351                 for(t=0;t<fd->orig->kerning_size;t++) {
352                     int char1 = fd->used[fd->orig->kerning[t].c1]-1;
353                     int char2 = fd->used[fd->orig->kerning[t].c2]-1;
354                     if(char1>=0 && char2>=0) {
355                         font->kerning[count].c1 = char1;
356                         font->kerning[count].c2 = char2;
357                         font->kerning[count].advance = fd->orig->kerning[t].advance * fd->matrix.m00;
358                         count++;
359                     }
360                 }
361             }
362             gfxfont_fix_unicode(font);
363
364             fd = fd->group_next;
365         }
366     }
367     return out->finish(out);
368 }
369
370 static void pass2_addfont(gfxfilter_t*f, gfxfont_t*font, gfxdevice_t*out)
371 {
372     /* we throw away original fonts, and do the addfont() for the transformed
373        fonts in the first drawchar() */
374 }
375
376 static void pass2_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*_color, gfxmatrix_t*matrix, gfxdevice_t*out)
377 {
378     internal_t*i = (internal_t*)f->internal;
379     gfxcolor_t color = *_color;
380
381     if(i->first) {
382         i->first = 0;
383         i->groups;
384         fontgroup_t*g;
385         for(g = i->groups;g;g=g->next) {
386             transformedfont_t*fd = g->fonts;
387             while(fd) {
388                 out->addfont(out, fd->font);
389                 fd = fd->group_next;
390             }
391         }
392     }
393
394     mymatrix_t m;
395     gfxmatrix_t scalematrix;
396     matrix_convert(matrix, font->id?font->id:"unknown", &m, &scalematrix, color.a);
397     transformedfont_t*d = dict_lookup(i->matrices, &m);
398     scalematrix.tx -= d->dx*scalematrix.m00;
399
400     /* if this character is invisible (alpha=0), then we will have removed the
401        outline, so we make set the alpha color channel to "fully visible" again to allow
402        output devices to be more performant (transparency is expensive) */
403     if(!m.alpha) 
404         color.a = 255;
405
406     out->drawchar(out, d->font, d->used[glyphnr]-1, &color, &scalematrix);
407 }
408
409 static gfxresult_t* pass2_finish(gfxfilter_t*f, gfxdevice_t*out)
410 {
411     internal_t*i = (internal_t*)f->internal;
412     DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
413         if(fd->used) {
414             free(fd->used);fd->used=0;
415         }
416         free(fd);
417     }
418     dict_destroy(i->matrices);i->matrices=0;
419     return out->finish(out);
420 }
421
422 void gfxtwopassfilter_remove_font_transforms_init(gfxtwopassfilter_t*f)
423 {
424     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
425
426     memset(f, 0, sizeof(gfxtwopassfilter_t));
427     f->type = gfxfilter_twopass;
428
429     f->pass1.name = "remove font transforms pass 1";
430     f->pass1.drawchar = pass1_drawchar;
431     f->pass1.finish = pass1_finish;
432     f->pass1.internal = i;
433
434     f->pass2.name = "remove font transforms pass 2";
435     f->pass2.addfont = pass2_addfont;
436     f->pass2.drawchar = pass2_drawchar;
437     f->pass2.finish = pass2_finish;
438     f->pass2.internal = i;
439
440     i->matrices = dict_new2(&mymatrix_type);
441     i->first = 1;
442     i->config_recombine = 1;
443 }
444