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