1 /* remove_font_transform.c
3 Part of the swftools package.
5 Copyright (c) 2010 Matthias Kramm <kramm@quiss.org>
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.
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.
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 */
24 #include "../gfxfilter.h"
25 #include "../gfxtools.h"
26 #include "../gfxfont.h"
32 typedef struct _mymatrix {
33 float m00,m01,m10,m11;
38 static void* mymatrix_clone(const void*_m) {
41 const mymatrix_t*m1=_m;
42 mymatrix_t*m2 = malloc(sizeof(mymatrix_t));
44 m2->id = strdup(m1->id);
47 static unsigned int mymatrix_hash(const void*_m) {
50 const mymatrix_t*m = (mymatrix_t*)_m;
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);
60 static void mymatrix_destroy(void*_m) {
61 mymatrix_t*m = (mymatrix_t*)_m;
65 static char mymatrix_equals(const void*_m1, const void*_m2) {
66 const mymatrix_t*m1=_m1;
67 const mymatrix_t*m2=_m2;
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);
84 type_t mymatrix_type = {
87 free: mymatrix_destroy,
88 equals: mymatrix_equals
92 typedef struct _transformedfont {
98 struct _transformedfont *group_next;
101 typedef struct _fontgroup {
104 transformedfont_t*fonts;
105 struct _fontgroup *next;
108 typedef struct _internal {
112 char config_recombine;
117 int __attribute__((noinline))
118 matrix_convert(gfxmatrix_t*in, const char*id, mymatrix_t*out, gfxmatrix_t*scalematrix, unsigned char alpha)
120 int matrix_convert(gfxmatrix_t*in, const char*id, mymatrix_t*out, gfxmatrix_t*scalematrix, unsigned char alpha)
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;
127 memset(out, 0, sizeof(*out));
130 out->m00 = in->m00 / l;
131 out->m10 = in->m10 / l;
132 out->m01 = -in->m01 / l;
133 out->m11 = -in->m11 / l;
135 out->alpha = alpha?1:0;
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;
148 typedef struct _matrixdata {
152 static transformedfont_t* transformedfont_new(gfxfont_t*orig, mymatrix_t*m)
154 transformedfont_t*f = rfx_calloc(sizeof(transformedfont_t));
157 f->used = rfx_calloc(sizeof(f->used[0])*orig->num_glyphs);
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
169 static void pass1_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix, gfxdevice_t*out)
171 internal_t*i = (internal_t*)f->internal;
174 msg("<error> Font has no ID");
175 if(!matrix_convert(matrix, font->id?font->id:"unknown", &m, 0, color->a))
177 transformedfont_t*fd = dict_lookup(i->matrices, &m);
179 fd = transformedfont_new(font, &m);
180 dict_put(i->matrices, &m, fd);
183 out->drawchar(out, font, glyphnr, color, matrix);
186 static void glyph_transform(gfxglyph_t*g, mymatrix_t*mm)
197 g->line = gfxline_clone(g->line);
198 gfxline_transform(g->line, &m);
201 void fontgroup_add_to_bitmap(fontgroup_t*g, transformedfont_t*f)
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;
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;
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));
222 g->bitmap[0] &= ~(1ll<<32); // map out space char
225 int fontgroup_intersect(fontgroup_t*g1, fontgroup_t*g2)
227 int min = g1->bitmap_len < g2->bitmap_len? g1->bitmap_len : g2->bitmap_len;
230 if(g1->bitmap[t]&g2->bitmap[t]) {
237 fontgroup_t* fontgroup_combine(fontgroup_t*fontgroup)
239 fontgroup_t*fg = fontgroup;
241 fontgroup_t*fg2 = fg->next;
242 fontgroup_t*old = fg;
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;
251 last->group_next = fg2->fonts;
266 static gfxresult_t* pass1_finish(gfxfilter_t*f, gfxdevice_t*out)
268 internal_t*i = (internal_t*)f->internal;
270 fontgroup_t*fontgroup=0;
271 DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
274 fontgroup_add_to_bitmap(fg, fd);
275 fg->next = fontgroup;
279 if(i->config_recombine) {
280 fontgroup = fontgroup_combine(fontgroup);
282 i->groups = fontgroup;
285 for(fg = fontgroup;fg;fg = fg->next) {
286 transformedfont_t*fd = fg->fonts;
288 gfxfont_t*font = fd->font = rfx_calloc(sizeof(gfxfont_t));
290 static int fontcount=0;
291 sprintf(id, "font%d", fontcount++);
292 font->id = strdup(id);
295 for(t=0;t<fd->orig->num_glyphs;t++) {
299 font->num_glyphs = count;
300 font->glyphs = rfx_calloc(sizeof(gfxglyph_t)*font->num_glyphs);
302 for(t=0;t<fd->orig->num_glyphs;t++) {
304 font->glyphs[count] = fd->orig->glyphs[t];
305 glyph_transform(&font->glyphs[count], &fd->matrix);
306 fd->used[t] = count + 1;
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);
321 average_xmax /= count;
323 font->ascent = total.ymax;
324 font->descent = -total.ymin;
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;
338 if(fd->matrix.m00>0) {
339 /* subset kerning table */
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) {
348 font->kerning = malloc(sizeof(font->kerning[0])*count);
349 font->kerning_size = count;
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;
362 gfxfont_fix_unicode(font);
367 return out->finish(out);
370 static void pass2_addfont(gfxfilter_t*f, gfxfont_t*font, gfxdevice_t*out)
372 /* we throw away original fonts, and do the addfont() for the transformed
373 fonts in the first drawchar() */
376 static void pass2_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*_color, gfxmatrix_t*matrix, gfxdevice_t*out)
378 internal_t*i = (internal_t*)f->internal;
379 gfxcolor_t color = *_color;
385 for(g = i->groups;g;g=g->next) {
386 transformedfont_t*fd = g->fonts;
388 out->addfont(out, fd->font);
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;
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) */
406 out->drawchar(out, d->font, d->used[glyphnr]-1, &color, &scalematrix);
409 static gfxresult_t* pass2_finish(gfxfilter_t*f, gfxdevice_t*out)
411 internal_t*i = (internal_t*)f->internal;
412 DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
414 free(fd->used);fd->used=0;
418 dict_destroy(i->matrices);i->matrices=0;
419 return out->finish(out);
422 void gfxtwopassfilter_remove_font_transforms_init(gfxtwopassfilter_t*f)
424 internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
426 memset(f, 0, sizeof(gfxtwopassfilter_t));
427 f->type = gfxfilter_twopass;
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;
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;
440 i->matrices = dict_new2(&mymatrix_type);
442 i->config_recombine = 1;