/* * libfbf.c - FBF font decoder * * Copyright (c) 2000, 2001, 2002 Fabrice Bellard. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "libfbf.h" //#define DEBUG #define free(ptr) do_not_use_free #define malloc(size) do_not_use_malloc static void init_context_tables(void); static int unicode_to_johab(int code); static int decode_hangul_glyph(UniFontData *uf, GlyphEntry **glyph_entry_ptr, int code); static inline void *uf_malloc(UniFontData *uf, int size) { return uf->fbf_malloc(uf->mem_opaque, size); } static inline void uf_free(UniFontData *uf, void *ptr) { uf->fbf_free(uf->mem_opaque, ptr); } /* totally unoptimised get bit functions */ static void get_bit_init(UniFontData *uf) { uf->bitcnt = 0; } static int get_bit(UniFontData *uf) { if (uf->bitcnt == 0) { uf->bitbuf = uf->fbf_getc(uf->infile); uf->bitcnt = 7; } else { uf->bitcnt--; } return (uf->bitbuf >> uf->bitcnt) & 1; } static int get_bits(UniFontData *uf, int n) { int val, i; val = 0; for (i = 0; i < n; i++) { val = (val << 1) | get_bit(uf); } return val; } /* read log encoded number */ static int read_num(UniFontData *uf) { int l, n; l = 0; while (get_bit(uf) != 0) l++; if (l <= 1) return l; n = get_bits(uf, l - 1) | (1 << (l -1)); return n; } static void read_segments(UniFontData *uf) { int n, code, delta, size, seg; get_bit_init(uf); seg = 0; n = 0; code = 0; while (n < uf->nb_glyphs && seg < uf->nb_segs) { /* read one segment */ delta = read_num(uf); size = read_num(uf) + 1; code += delta; uf->seg_table[seg].glyph = n; uf->seg_table[seg].start = code; uf->seg_table[seg].size = size; //printf("%05x: %d\n", code, size); code += size + 1; n += size; seg++; } } static int get_be16(UniFontData *uf) { int val; val = uf->fbf_getc(uf->infile) << 8; val |= uf->fbf_getc(uf->infile); return val; } static int get_be32(UniFontData *uf) { int val; val = uf->fbf_getc(uf->infile) << 24; val |= uf->fbf_getc(uf->infile) << 16; val |= uf->fbf_getc(uf->infile) << 8; val |= uf->fbf_getc(uf->infile); return val; } static void fbf_get_str(UniFontData *uf, char *buf, int buf_size) { int i, len, c; char *q; len = uf->fbf_getc(uf->infile); q = buf; for (i = 0; i < len; i++) { c = uf->fbf_getc(uf->infile); if ((q - buf) < (buf_size - 1)) *q++ = c; } *q = '\0'; } int fbf_load_font(UniFontData *uf) { UnifontHeader h; int i; static int init_done = 0; if (!init_done) { init_context_tables(); init_done = 1; } h.magic = get_be32(uf); if (h.magic != UNIFONT_MAGIC) return -1; h.version = get_be32(uf); if (h.version != 1) return -1; h.nb_glyphs = get_be32(uf); h.compressed_segment_size = get_be32(uf); h.flags = get_be32(uf); h.max_width = get_be16(uf); h.max_height = get_be16(uf); uf->x_res = get_be16(uf); uf->y_res = get_be16(uf); uf->pt_size = get_be16(uf); uf->ascent = get_be16(uf); uf->descent = get_be16(uf); uf->line_height = get_be16(uf); uf->underline_position = get_be16(uf); uf->underline_thickness = get_be16(uf); h.nb_segments = get_be16(uf); uf->family_type = uf->fbf_getc(uf->infile); uf->fbf_getc(uf->infile); fbf_get_str(uf, uf->family_name, sizeof(uf->family_name)); uf->nb_glyphs = h.nb_glyphs; uf->compressed_segment_size = h.compressed_segment_size; uf->max_width = h.max_width; uf->max_height = h.max_height; uf->flags = h.flags; uf->nb_segs = h.nb_segments; /* compressed segments offsets */ uf->nb_csegs = (uf->nb_glyphs + uf->compressed_segment_size - 1) / uf->compressed_segment_size; uf->csegs_offsets = uf_malloc(uf, uf->nb_csegs * 2 * sizeof(int)); if (!uf->csegs_offsets) goto fail; uf->msegs_offsets = uf->csegs_offsets + uf->nb_csegs; for (i = 0; i < uf->nb_csegs; i++) { uf->msegs_offsets[i] = get_be32(uf); } for (i = 0; i < uf->nb_csegs; i++) { uf->csegs_offsets[i] = get_be32(uf); } /* unicode to glyph index conversion table */ uf->seg_table = uf_malloc(uf, uf->nb_segs * sizeof(SegData)); if (!uf->seg_table) goto fail; read_segments(uf); /* decoding context */ if (uf->fbf_read(uf->infile, uf->ctx1, sizeof(uf->ctx1)) != sizeof(uf->ctx1)) goto fail; uf->nb_glyphs_total = uf->nb_glyphs; /* if hangul composite glyphs, increase virtual number of glyphs */ if (uf->flags & UF_FLAG_HANGUL) { uf->nb_glyphs_total += 11172; } return 0; fail: uf_free(uf, uf->seg_table); if (uf) { if (uf->csegs_offsets) uf_free(uf, uf->csegs_offsets); } return -1; } void fbf_free_font(UniFontData *uf) { GlyphSegment *gseg; int i; for (i = 0; i < CSEG_CACHE_SIZE; i++) { gseg = uf->cseg_cache[i]; if (gseg) { uf_free(uf, gseg->bitmap_table); uf_free(uf, gseg); } } uf_free(uf, uf->csegs_offsets); uf_free(uf, uf->seg_table); } /* arithmetic font decoder */ static unsigned char ctx_incr[2][256]; static unsigned char ctx_shift[256]; static void init_context_tables(void) { int ctxval, val, bit, freq0, freq1, shift; /* table for context frequency update */ for (bit = 0; bit < 2; bit++) { for (ctxval = 0; ctxval < 256; ctxval++) { freq0 = 2*(ctxval >> 4) + 1; freq1 = 2*(ctxval & 0xf) + 1; if (!bit) freq0 += 2; else freq1 += 2; if (freq0 > 31 || freq1 > 31) { freq0 = (freq0 + 1) >> 1; freq1 = (freq1 + 1) >> 1; } val = (((freq0 - 1) >> 1) << 4) | ((freq1 - 1) >> 1); ctx_incr[bit][ctxval] = val; // printf("%d %02x %02x\n", bit, ctxval, val); } } /* table for arith coding */ for (ctxval = 0; ctxval < 256; ctxval++) { unsigned int sum, m, s; freq0 = 2*(ctxval >> 4) + 1; freq1 = 2*(ctxval & 0xf) + 1; val = 0; sum = freq0 + freq1; /* invert bit */ if (freq0 > freq1) { val = 0x80; freq0 = freq1; } shift = 0; m=freq0*8; s=sum*5; while (marange = 0x1000000; /* data buffer */ uf->alow = uf->fbf_getc(uf->infile) << 16; uf->alow |= uf->fbf_getc(uf->infile) << 8; uf->alow |= uf->fbf_getc(uf->infile); } static inline unsigned int decode_ctx(UniFontData *uf, unsigned char *ctx) { unsigned int ctxval, range, shift, b; unsigned int alow, arange; alow = uf->alow; arange = uf->arange; ctxval = ctx[0]; #ifdef DEBUG printf("low=%x range=%x ctx=%x ", alow, arange, ctxval); #endif /* compute decision level */ shift = ctx_shift[ctxval]; range = arange; if (shift & 0x40) range = range * 3; range = range >> (shift & 0x3f); /* which bit is was encoded ? */ b = (alow >= range); if (b) { alow -= range; arange -= range; } else { arange = range; } /* increment context */ b = b ^ (shift >> 7); ctx[0] = ctx_incr[b][ctxval]; /* renormalize arith state */ if (arange < RANGE_MIN) { alow = ((alow << 8) | uf->fbf_getc(uf->infile)) & 0xffffff; arange <<= 8; } uf->arange = arange; uf->alow = alow; #ifdef DEBUG printf("b=%u\n", b); #endif return b; } /* glyph decoder */ static inline int get_ctx(int x, int y, unsigned char *p) { int v; v = 0; v = v * 3; if (y >= 4) { v += p[-3 * WRAP] << 0; // M } else { v += 2; } v = v * 3; if (x >= 4) { v += (p[-3]) << 0; // T } else { v += 2; } /* distance of 2 */ v = v << 2; v += p[-2 * WRAP + 2] << 0; // K v += p[-2 * WRAP - 2] << 1; // G v = v << 1; v += p[-2] << 0; // E v = v << 3; v += p[-2 * WRAP - 1] << 0; // H v += p[-2 * WRAP] << 1; // I v += p[-2 * WRAP + 1] << 2; // J v = v << 4; v += p[-WRAP - 1] << 0; // B v += p[-WRAP + 1] << 1; // D v += p[-WRAP + 2] << 2; // L v += p[-WRAP - 2] << 3; // F v = v << 2; v += p[-1] << 0; // A v += p[-WRAP] << 1; // C return v; } static void decode_glyph(UniFontData *uf, unsigned char *ctx1, unsigned char *ctx_adapt, unsigned char *outbuf, int w, int h) { unsigned char *p; unsigned char bitmap[WRAP * (MAXHEIGHT + MAXDIST)]; int v, v1, x, y, b, i; unsigned int lbuf, w1; memset(bitmap, 1, sizeof(bitmap)); p = bitmap + MAXDIST + WRAP * MAXDIST; w1 = (w + 7) >> 3; for (y = 0; y < h; y++) { lbuf = 0; for (x = 0; x < w; x++) { v = get_ctx(x, y, p); if (ctx_adapt[v] == 0x00 || ctx_adapt[v] == 0x10 || ctx_adapt[v] == 0x01) { v1 = v & (NB_CTX1 - 1); b = decode_ctx(uf, &ctx1[v1]); ctx_adapt[v] = ctx_incr[b][ctx_adapt[v]]; } else { b = decode_ctx(uf, &ctx_adapt[v]); } *p++ = b; lbuf = (lbuf << 1) | b; if ((x & 7) == 7) { *outbuf++ = lbuf; } } p += WRAP - w; /* output remaning bits */ i = (w & 7); if (i != 0) { *outbuf++ = lbuf << (8 - i); } } } typedef struct EncodeLogContext { unsigned char log_ctx[16]; unsigned char sign_ctx; } EncodeLogContext; static int read_num1(UniFontData *uf, EncodeLogContext *c, int is_signed) { int l, n, i; unsigned char ctx; l = 0; while (decode_ctx(uf, &c->log_ctx[l]) != 0) l++; if (l == 0) { return 0; } n = 1 << (l - 1); for (i = l-2; i >= 0; i--) { ctx = 0; n |= decode_ctx(uf, &ctx) << i; } if (is_signed) { if (decode_ctx(uf, &c->sign_ctx)) n = -n; } return n; } typedef struct { EncodeLogContext metric_ctx[6]; int last_w, last_h, last_x, last_y, last_xincr; } MetricContext; static void decode_glyph_metric(UniFontData *uf, MetricContext *m, GlyphEntry *g) { g->w = read_num1(uf, &m->metric_ctx[1], 1) + m->last_w; g->h = read_num1(uf, &m->metric_ctx[2], 1) + m->last_h; g->x = read_num1(uf, &m->metric_ctx[3], 1) + m->last_x; g->y = read_num1(uf, &m->metric_ctx[4], 1) + m->last_y; g->xincr = read_num1(uf, &m->metric_ctx[5], 1) + m->last_xincr; m->last_w = g->w; m->last_h = g->h; m->last_x = g->x; m->last_y = g->y; m->last_xincr = g->xincr; } /* decode one segment of 'compressed_segment_size' glyph metrics */ static GlyphSegment *decode_metrics_segment(UniFontData *uf, int segment) { int i, glyph_start, glyph_end, nb_glyphs; GlyphSegment *g; GlyphEntry *m; MetricContext metric_ctx; glyph_start = segment * uf->compressed_segment_size; if (glyph_start >= uf->nb_glyphs) return NULL; glyph_end = glyph_start + uf->compressed_segment_size; if (glyph_end > uf->nb_glyphs) glyph_end = uf->nb_glyphs; nb_glyphs = glyph_end - glyph_start; g = uf_malloc(uf, sizeof(GlyphSegment) + (nb_glyphs - 1) * sizeof(GlyphEntry)); if (!g) return NULL; g->first_glyph = glyph_start; g->nb_glyphs = nb_glyphs; g->bitmap_table = NULL; uf->fbf_seek(uf->infile, uf->msegs_offsets[segment]); arith_init(uf); memset(&metric_ctx, 0, sizeof(metric_ctx)); for (i = 0; i < nb_glyphs; i++) { m = &g->metrics[i]; decode_glyph_metric(uf, &metric_ctx, m); } return g; } /* decode one segment of 'compressed_segment_size' glyphs */ static int decode_glyphs_segment(UniFontData *uf, GlyphSegment *g, int segment) { int glyph_start, glyph_end, size, nb_glyphs, bitmap_size, i; unsigned char *ctx, ctx1[NB_CTX1], *data; GlyphEntry *m; glyph_start = segment * uf->compressed_segment_size; if (glyph_start >= uf->nb_glyphs) return -1; glyph_end = glyph_start + uf->compressed_segment_size; if (glyph_end > uf->nb_glyphs) glyph_end = uf->nb_glyphs; nb_glyphs = glyph_end - glyph_start; uf->fbf_seek(uf->infile, uf->csegs_offsets[segment]); /* allocate bitmap */ bitmap_size = 0; for (i = 0; i < nb_glyphs; i++) { m = &g->metrics[i]; size = ((m->w + 7) >> 3) * m->h; bitmap_size += size; } // printf("segment=%d bitmap_size=%d\n", segment, bitmap_size); g->bitmap_table = uf_malloc(uf, bitmap_size); if (!g->bitmap_table) return -1; /* allocate compression context */ ctx = uf_malloc(uf, NB_CTX); if (!ctx) return -1; memset(ctx, 0, NB_CTX); memcpy(ctx1, uf->ctx1, NB_CTX1); arith_init(uf); data = g->bitmap_table; for (i = 0; i < nb_glyphs; i++) { m = &g->metrics[i]; m->bitmap = data; decode_glyph(uf, ctx1, ctx, data, m->w, m->h); size = ((m->w + 7) >> 3) * m->h; data += size; } uf_free(uf, ctx); return 0; } int fbf_decode_glyph(UniFontData *uf, GlyphEntry **glyph_entry_ptr, int index) { GlyphSegment *gseg, **pgseg; int i, segment, use_count_min, cseg_min; if (index >= uf->nb_glyphs) { /* special case for algorithmically generated hangul glyphs */ return decode_hangul_glyph(uf, glyph_entry_ptr, index - uf->nb_glyphs); } redo: for (i = 0; i < CSEG_CACHE_SIZE; i++) { gseg = uf->cseg_cache[i]; if (gseg && index >= gseg->first_glyph && index < gseg->first_glyph + gseg->nb_glyphs) { gseg->use_count++; *glyph_entry_ptr = &gseg->metrics[index - gseg->first_glyph]; return 0; } } /* no glyph found : decode a new segment */ /* free least used segment selected segment */ cseg_min = 0; use_count_min = 0x7fffffff; for (i = 0; i < CSEG_CACHE_SIZE; i++) { if (uf->cseg_cache[i] == NULL) { cseg_min = i; break; } if (uf->cseg_cache[i]->use_count < use_count_min) { use_count_min = uf->cseg_cache[i]->use_count; cseg_min = i; } } pgseg = &uf->cseg_cache[cseg_min]; gseg = *pgseg; if (gseg) { uf_free(uf, gseg->bitmap_table); uf_free(uf, gseg); *pgseg = NULL; } /* create a glyph segment and decode only its metrics */ segment = index / uf->compressed_segment_size; gseg = decode_metrics_segment(uf, segment); if (!gseg) return -1; /* decode the glyphs */ if (decode_glyphs_segment(uf, gseg, segment) < 0) return -1; gseg->use_count = 0; *pgseg = gseg; goto redo; } /* XXX: could be faster with a table for the high order bits of 'code' */ int fbf_unicode_to_glyph(UniFontData *uf, int code) { int start, end, mid, size, k; /* special hangul case */ if (code >= 0x1100 && code <= 0x11ff) { code = unicode_to_johab(code); } else if (code >= 0xAC00 && code < (0xAC00 + 11172)) { /* map to composite glyph area */ return code - 0xAC00 + uf->nb_glyphs; } start = 0; end = uf->nb_segs - 1; while (end >= start) { mid = (start + end) >> 1; k = uf->seg_table[mid].start; size = uf->seg_table[mid].size; if (code >= k && code < (k + size)) { return uf->seg_table[mid].glyph + code - k; } else if (code < k) end = mid - 1; else { start = mid + 1; } } return -1; } /************************************************************/ /* HANGUL composite glyph handling */ // The base font index for leading consonants static const unsigned char lconBase[] = { 1, 11, 21, 31, 41, 51, 61, 71, 81, 91, 101, 111, 121, 131, 141, 151, 161, 171, 181 }; // The base font index for vowels static const unsigned short vowBase[] = { 0,311,314,317,320,323, // (Fill), A, AE, YA, YAE, EO 326,329,332,335,339,343, // E, YEO, YE, O, WA, WAE 347,351,355,358,361,364, // OI, YO, U, WEO, WE, WI 367,370,374,378 // YU, EU, UI, I }; // The base font index for trailing consonants static const unsigned short tconBase[] = { // modern trailing consonants (filler + 27) 0, 405, 409, 413, 417, 421, 425, 429, 433, 437, 441, 445, 449, 453, 457, 461, 465, 469, 473, 477, 481, 485, 489, 493, 497, 501, 505, 509 }; // The mapping from vowels to leading consonant type // in absence of trailing consonant static const unsigned char lconMap1[] = { 0,0,0,0,0,0, // (Fill), A, AE, YA, YAE, EO 0,0,0,1,3,3, // E, YEO, YE, O, WA, WAE 3,1,2,4,4,4, // OI, YO, U, WEO, WE, WI 2,1,3,0 // YU, EU, UI, I }; // The mapping from vowels to leading consonant type // in presence of trailing consonant static const unsigned char lconMap2[] = { 5,5,5,5,5,5, // (Fill), A, AE, YA, YAE, EO 5,5,5,6,8,8, // E, YEO, YE, O, WA, WAE 8,6,7,9,9,9, // OI, YO, U, WEO, WE, WI 7,6,8,5 // YU, EU, UI, I }; // vowel type ; 1 = o and its alikes, 0 = others static const unsigned char vowType[] = { 0,0,0,0,0,0, 0,0,0,1,1,1, 1,1,0,0,0,0, 0,1,1,0 }; // The mapping from trailing consonants to vowel type static const unsigned char tconType[] = { 0, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; // The mapping from vowels to trailing consonant type static const unsigned char tconMap[] = { 0, 0, 2, 0, 2, 1, // (Fill), A, AE, YA, YAE, EO 2, 1, 2, 3, 0, 0, // E, YEO, YE, O, WA, WAE 0, 3, 3, 1, 1, 1, // OI, YO, U, WEO, WE, WI 3, 3, 0, 1 // YU, EU, UI, I }; /* put src at position (x,y) in dst. src has a size of (w, h) */ static inline void bitmap_or(unsigned char *dst, int dst_wrap, unsigned char *src, int src_wrap, int x, int y, int w, int h) { int i, j, bit, x1; dst += dst_wrap * y; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { bit = (src[j >> 3] >> (7 - (j & 7))) & 1; x1 = x + j; dst[x1 >> 3] |= bit << (7 - (x1 & 7)); } dst += dst_wrap; src += src_wrap; } } /* XXX: suppress that by modifying unicode to glyph mapping in ufencode */ static int unicode_to_johab(int code) { int j; if (code >= 0x1100 && code <= 0x1100 + 18) { j = code - 0x1100; code = JOHAB_BASE + lconBase[j] + 9; } else if (code >= 0x1161 && code < 0x1161 + 21) { j = code - 0x1161 + 1; code = JOHAB_BASE + vowBase[j]+1+vowType[j]; } else if (code >= 0x11A8 && code < 0x11A8 + 27) { j = code - 0x11A8 + 1; code = JOHAB_BASE + tconBase[j] + 3; } return code; } /* 0 <= code < 11172 */ static int decode_hangul_glyph(UniFontData *uf, GlyphEntry **glyph_entry_ptr, int code) { int i, index, l, m, f, ind[3], wrap; GlyphEntry *glyph_entry, *glyph1; glyph_entry = &uf->tmp_glyph_entry; glyph_entry->bitmap = uf->tmp_buffer; /* XXX: size is hardcoded for unifont */ glyph_entry->w = 16; glyph_entry->h = 16; glyph_entry->x = 0; glyph_entry->y = 0; glyph_entry->xincr = 16; wrap = (glyph_entry->w + 7) >> 3; l = code / (21 * 28); m = ((code / 28) % 21) + 1; f = code % 28; /* first glyph */ ind[0] = lconBase[l] + ((f > 0) ? lconMap2[m] : lconMap1[m]); /* second glyph */ ind[1] = vowBase[m]; if (vowType[m] == 1) { ind[1] += ((l == 0 || l == 15) ? 0 : 1) + (f > 0 ? 2 : 0); } else { ind[1] += tconType[f]; } /* third glyph */ ind[2] = f ? tconBase[f] + tconMap[m] : 0; /* render the three glyphs & supperpose them */ memset(glyph_entry->bitmap, 0, glyph_entry->h * wrap); for (i = 0; i < 3; i++) { index = fbf_unicode_to_glyph(uf, JOHAB_BASE + ind[i]); if (index < 0) continue; if (fbf_decode_glyph(uf, &glyph1, index) < 0) continue; bitmap_or(glyph_entry->bitmap, wrap, glyph1->bitmap, (glyph1->w + 7) >> 3, glyph1->x, glyph1->y, glyph1->w, glyph1->h); } *glyph_entry_ptr = glyph_entry; return 0; }