diff options
Diffstat (limited to 'rufl_metrics.c')
-rw-r--r-- | rufl_metrics.c | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/rufl_metrics.c b/rufl_metrics.c new file mode 100644 index 0000000..9309139 --- /dev/null +++ b/rufl_metrics.c @@ -0,0 +1,289 @@ +/* + * This file is part of RUfl + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license + * Copyright 2005 John-Mark Bell <jmb202@ecs.soton.ac.uk> + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include "oslib/font.h" + +#include "rufl_internal.h" + +static int rufl_unicode_map_search_cmp(const void *keyval, const void *datum); + +/** + * Read a font's metrics (sized for a 1pt font) + */ +rufl_code rufl_font_metrics(const char *font_family, rufl_style font_style, + os_box *bbox, int *xkern, int *ykern, int *italic, + int *ascent, int *descent, + int *xheight, int *cap_height, + signed char *uline_position, unsigned char *uline_thickness) +{ + unsigned int font; + font_f f; + int misc_size; + font_metrics_misc_info *misc_info; + rufl_code code; + + code = rufl_find_font_family(font_family, font_style, &font, + NULL, NULL); + if (code != rufl_OK) + return code; + + code = rufl_find_font(font, 16 /* 1pt */, NULL, &f); + if (code != rufl_OK) + return code; + + rufl_fm_error = xfont_read_font_metrics(f, 0, 0, 0, 0, 0, + 0, 0, 0, 0, &misc_size, 0); + if (rufl_fm_error) { + LOG("xfont_read_font_metrics: 0x%x: %s", + rufl_fm_error->errnum, + rufl_fm_error->errmess); + return rufl_FONT_MANAGER_ERROR; + } + + if (misc_size == 0) { + LOG("No font metrics in file", 0); + /** \todo better error code */ + return rufl_FONT_NOT_FOUND; + } + + misc_info = (font_metrics_misc_info *)malloc(misc_size); + if (!misc_info) + return rufl_OUT_OF_MEMORY; + + rufl_fm_error = xfont_read_font_metrics(f, 0, 0, 0, misc_info, 0, + 0, 0, 0, 0, 0, 0); + if (rufl_fm_error) { + LOG("xfont_read_font_metrics: 0x%x: %s", + rufl_fm_error->errnum, + rufl_fm_error->errmess); + free(misc_info); + return rufl_FONT_MANAGER_ERROR; + } + + /* and fill in output */ + if (bbox) { + bbox->x0 = misc_info->x0; + bbox->y0 = misc_info->y0; + bbox->x1 = misc_info->x1; + bbox->y1 = misc_info->y1; + } + + if (xkern) + (*xkern) = misc_info->xkern; + + if (ykern) + (*ykern) = misc_info->ykern; + + if (italic) + (*italic) = misc_info->italic_correction; + + if (ascent) + (*ascent) = misc_info->ascender; + + if (descent) + (*descent) = misc_info->descender; + + if (xheight) + (*xheight) = misc_info->xheight; + + if (cap_height) + (*cap_height) = misc_info->cap_height; + + if (uline_position) + (*uline_position) = misc_info->underline_position; + + if (uline_thickness) + (*uline_thickness) = misc_info->underline_thickness; + + free(misc_info); + + return rufl_OK; +} + +/** + * Read a glyph's metrics + */ +rufl_code rufl_glyph_metrics(const char *font_family, + rufl_style font_style, unsigned int font_size, + const char *string, size_t length, + int *x_bearing, int *y_bearing, + int *width, int *height, + int *x_advance, int *y_advance) +{ + unsigned int font, font1, u; + unsigned short u1[2]; + struct rufl_character_set *charset; + font_f f; + rufl_code code; + font_scan_block block; + font_string_flags flags; + int xa, ya; + + code = rufl_find_font_family(font_family, font_style, + &font, NULL, &charset); + if (code != rufl_OK) + return code; + + rufl_utf8_read(string, length, u); + if (charset && rufl_character_set_test(charset, u)) + font1 = font; + else if (u < 0x10000) + font1 = rufl_substitution_table[u]; + else + font1 = rufl_CACHE_CORPUS; + + code = rufl_find_font(font1, font_size, NULL, &f); + if (code != rufl_OK) + return code; + + /* + * Glyph Metrics for horizontal text: + * + * ^ x0 x1 ¦ + * | ¦ ¦ ¦ Xbearing : x0 - oX + * | +-----+---¦----- y1 Ybearing : y1 - oY + * | | | ¦ Xadvance : aX - oX + * | | | ¦ Yadvance : 0 + * o---|-----|---a--> Glyph width : x1 - x0 + * | | | ¦ Glyph height : y1 - y0 + * | +-----+---¦----- y0 Right side bearing: aX - x1 + * | ¦ + * + * The rectangle (x0,y0),(x1,y1) is the glyph bounding box. + * + * Glyph Metrics for vertical text: + * + * -------o---------> + * y1--+--|--+ Xbearing : x0 - oX + * | | | Ybearing : oY - y1 + * | | | Xadvance : 0 + * | | | Yadvance : aY - oY + * | | | Glyph width : x1 - x0 + * y0--+-----+ Glyph height : y1 - y0 + * ----¦--a--¦--------- Right side bearing: N/A + * x0 v x1 + * + * The rectangle (x0,y0),(x1,y1) is the glyph bounding box. + * + * + * In order to extract the information we want from the + * Font Manager, a little bit of hackery is required. + * + * Firstly, we can take the origin as being (0,0). This is an + * arbitrary choice but makes the maths simpler. + * + * Secondly, the bounding box returned by Font_CharBBox / + * Font_ScanString / Font_StringBBox represents the ink area of + * the glyph (i.e. the smallest box needed to contain all the + * glyph path segments). This means that, for glyphs with no + * displayed content (such as a space), the bounding box will be 0. + * These SWIs therefore allow us to retrieve the (x0,y0),(x1,y1) + * coordinates marked in the diagrams above. + * + * Finally, we need to retrieve the glyph advance distance. This is + * returned in R3/R4 on exit from Font_ScanString (providing bit 17 + * of the flags word on entry is clear). It is important to note, + * however, that the height will be returned as 0 for fonts with no + * Yadvance values in the font data file. Therefore, in order to + * achieve vertical layout of text, further work will be needed + * (We're also ignoring the fact that the X coordinates of all + * values will be in the wrong place and the Y coordinates will have + * the wrong sign due to the differing definitions of the Y axis for + * horizontal and vertical text.) + * + * Note that all values (that we're interested in, at least) + * returned by the SWIs mentioned above are in _millipoints_. + */ + + block.space.x = block.space.y = 0; + block.letter.x = block.letter.y = 0; + block.split_char = -1; + + flags = font_GIVEN_BLOCK | font_GIVEN_LENGTH | font_GIVEN_FONT | + font_RETURN_BBOX; + + u1[0] = (unsigned short)u; + u1[1] = 0; + + if (font1 == rufl_CACHE_CORPUS) { + /* Fallback Glyph */ + /** \todo implement this properly */ + xa = 1000 * font_size; + ya = 0; + block.bbox.x0 = block.bbox.y0 = 0; + block.bbox.x1 = block.bbox.y1 = xa; + } else if (rufl_old_font_manager) { + /* Old Font Manager */ + char s[2]; + struct rufl_unicode_map_entry *entry; + entry = bsearch(&u1[0], rufl_font_list[font1].umap->map, + rufl_font_list[font1].umap->entries, + sizeof rufl_font_list[font1].umap->map[0], + rufl_unicode_map_search_cmp); + s[0] = entry->c; + s[1] = 0; + + rufl_fm_error = xfont_scan_string(f, s, flags, + 0x7fffffff, 0x7fffffff, &block, 0, 1, + 0, &xa, &ya, 0); + if (rufl_fm_error) { + LOG("xfont_scan_string: 0x%x: %s", + rufl_fm_error->errnum, + rufl_fm_error->errmess); + return rufl_FONT_MANAGER_ERROR; + } + } else { + /* UCS Font Manager */ + rufl_fm_error = xfont_scan_string(f, (const char *)u1, + flags | font_GIVEN16_BIT, + 0x7fffffff, 0x7fffffff, &block, 0, 2, + 0, &xa, &ya, 0); + if (rufl_fm_error) { + LOG("xfont_scan_string: 0x%x: %s", + rufl_fm_error->errnum, + rufl_fm_error->errmess); + return rufl_FONT_MANAGER_ERROR; + } + } + + /** \todo handle vertical text */ + if (x_bearing) + (*x_bearing) = block.bbox.x0; + + if (y_bearing) + (*y_bearing) = block.bbox.y1; + + if (width) + (*width) = block.bbox.x1 - block.bbox.x0; + + if (height) + (*height) = block.bbox.y1 - block.bbox.y0; + + if (x_advance) + (*x_advance) = xa; + + if (y_advance) + (*y_advance) = ya; + + return rufl_OK; +} + + +int rufl_unicode_map_search_cmp(const void *keyval, const void *datum) +{ + const unsigned short *key = keyval; + const struct rufl_unicode_map_entry *entry = datum; + if (*key < entry->u) + return -1; + else if (entry->u < *key) + return 1; + return 0; +} |