summaryrefslogtreecommitdiff
path: root/rufl_metrics.c
blob: af4727fc0c8a1fde31f158e7ab2a169f7b33a3d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/*
 * 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 miscellaneous information in metrics for %s",
				rufl_font_list[font].identifier);
		/** \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)
{
	const char *font_encoding = NULL;
	unsigned int font, font1, u;
	unsigned short u1[2];
	struct rufl_character_set *charset;
	struct rufl_unicode_map_entry *umap_entry = NULL;
	font_f f;
	rufl_code code;
	font_scan_block block;
	font_string_flags flags;
	int xa, ya;

	/* Find font family containing glyph */
	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;

	/* Old font managers need the font encoding, too */
	if (rufl_old_font_manager && font1 != rufl_CACHE_CORPUS) {
		unsigned int i;
		unsigned short u16 = (unsigned short) u;

		for (i = 0; i < rufl_font_list[font1].num_umaps; i++) {
			struct rufl_unicode_map *map =
					rufl_font_list[font1].umap + i;

			umap_entry = bsearch(&u16, map->map, map->entries,
					sizeof map->map[0],
					rufl_unicode_map_search_cmp);
			if (umap_entry) {
				font_encoding = map->encoding;
				break;
			}
		}

		assert(umap_entry != NULL);
	}

	code = rufl_find_font(font1, font_size, font_encoding, &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];

		/* We found the correct umap entry when 
		 * looking for the font encoding */
		s[0] = umap_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;
}