summaryrefslogtreecommitdiff
path: root/riscos/font.c
blob: 0fea4b542c7393654e44013f1c946a83ae65dc1d (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
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
/*
 * This file is part of NetSurf, http://netsurf.sourceforge.net/
 * Licensed under the GNU General Public License,
 *                http://www.opensource.org/licenses/gpl-license
 * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
 * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
 */

/** \file
 * Font handling (RISC OS implementation).
 *
 * The Font Manager is used to handle and render fonts.
 */

#include <assert.h>
#include <stdio.h>
#include "oslib/font.h"
#include "netsurf/css/css.h"
#include "netsurf/desktop/gui.h"
#include "netsurf/render/font.h"
#include "netsurf/riscos/gui.h"
#include "netsurf/utils/log.h"
#include "netsurf/utils/utils.h"

#define FONT_FAMILIES 5 /* Number of families */
#define FONT_FACES 8    /* Number of faces */

/* Font Variants */
#define FONT_SMALLCAPS 4

/* Font Styles */
#define FONT_BOLD 2
#define FONT_SLANTED 1

/* Font families */
#define FONT_SANS_SERIF (0 * FONT_FACES)
#define FONT_SERIF      (1 * FONT_FACES)
#define FONT_MONOSPACE  (2 * FONT_FACES)
#define FONT_CURSIVE    (3 * FONT_FACES)
#define FONT_FANTASY    (4 * FONT_FACES)

/* a font_set is just a linked list of font_data for each face for now */
struct font_set {
	struct font_data *font[FONT_FAMILIES * FONT_FACES];
};

/** Table of font names.
 *
 * font id = font family * 8 + smallcaps * 4 + bold * 2 + slanted
 *
 * font family: 0 = sans-serif, 1 = serif, 2 = monospace, 3 = cursive
 *              4 = fantasy
 */

const char * const font_table[FONT_FAMILIES * FONT_FACES] = {
	/* sans-serif */
/*0*/	"Homerton.Medium\\ELatin1",
/*1*/	"Homerton.Medium.Oblique\\ELatin1",
/*2*/	"Homerton.Bold\\ELatin1",
/*3*/	"Homerton.Bold.Oblique\\ELatin1",
        "Homerton.Medium.SmallCaps\\ELatin1",
        "Homerton.Medium.SmallCaps\\ELatin1\\M65536 0 13930 65536 0 0",
        "Homerton.Bold.SmallCaps\\ELatin1",
        "Homerton.Bold.SmallCaps\\ELatin1\\M65536 0 13930 65536 0 0",
	/* serif */
/*8*/	"Trinity.Medium\\ELatin1",
/*9*/	"Trinity.Medium.Italic\\ELatin1",
/*10*/	"Trinity.Bold\\ELatin1",
/*11*/	"Trinity.Bold.Italic\\ELatin1",
        "Trinity.Medium.SmallCaps\\ELatin1",
        "Trinity.Medium.Italic.SmallCaps\\ELatin1",
        "Trinity.Bold.SmallCaps\\ELatin1",
        "Trinity.Bold.Italic.SmallCaps\\ELatin1",
	/* monospace */
/*16*/	"Corpus.Medium\\ELatin1",
/*17*/	"Corpus.Medium.Oblique\\ELatin1",
/*18*/	"Corpus.Bold\\ELatin1",
/*19*/	"Corpus.Bold.Oblique\\ELatin1",
        "Corpus.Medium.SmallCaps\\ELatin1",
        "Corpus.Medium.SmallCaps\\ELatin1\\M65536 0 13930 65536 0 0",
        "Corpus.Bold.SmallCaps\\ELatin1",
        "Corpus.Bold.SmallCaps\\ELatin1\\M65536 0 13930 65536 0 0",
	/* cursive */
/*24*/	"Churchill.Medium\\ELatin1",
/*25*/	"Churchill.Medium\\ELatin1\\M65536 0 13930 65536 0 0",
/*26*/	"Churchill.Bold\\ELatin1",
/*27*/	"Churchill.Bold\\ELatin1\\M65536 0 13930 65536 0 0",
        "Churchill.Medium.SmallCaps\\ELatin1",
        "Churchill.Medium.SmallCaps\\ELatin1\\M65536 0 13930 65536 0 0",
        "Churchill.Bold.SmallCaps\\ELatin1",
        "Churchill.Bold.SmallCaps\\ELatin1\\M65536 0 13930 65536 0 0",
        /* fantasy */
/*32*/	"Sassoon.Primary\\ELatin1",
/*33*/	"Sassoon.Primary\\ELatin1\\M65536 0 13930 65536 0 0",
/*34*/	"Sassoon.Primary.Bold\\ELatin1",
/*35*/	"Sassoon.Primary.Bold\\ELatin1\\M65536 0 13930 65536 0 0",
        "Sassoon.Primary.SmallCaps\\ELatin1",
        "Sassoon.Primary.SmallCaps\\ELatin1\\M65536 0 13930 65536 0 0",
        "Sassoon.Primary.Bold.SmallCaps\\ELatin1",
        "Sassoon.Primary.Bold.SmallCaps\\ELatin1\\M65536 0 13930 65536 0 0",
};


/**
 * Create an empty font_set.
 *
 * \return an opaque struct font_set.
 */

struct font_set *font_new_set()
{
	struct font_set *set = xcalloc(1, sizeof(*set));
	unsigned int i;

	for (i = 0; i < FONT_FAMILIES * FONT_FACES; i++)
		set->font[i] = 0;

	return set;
}

/**
 * Font enumerator
 *
 * Call this multiple times to enumerate all available font names.
 * *handle should be zero (0) on first call.
 *
 * Returns a NULL pointer and a handle of -1 if there are no more fonts.
 */
const char *enumerate_fonts(struct font_set* set, int *handle)
{
        int i;

        assert(set);

        if (*handle < 0 || handle == 0) { /* a bit of sanity checking */
                *handle = -1;
                return NULL;
        }

        for (i = *handle; i!=FONT_FAMILIES*FONT_FACES && set->font[i]==0;
             i++) ; /* find next font in use */

        if (i == FONT_FAMILIES*FONT_FACES) { /* no more fonts */
                *handle = -1;
                return NULL;
        }

        *handle = i+1; /* update handle for next call */
        return font_table[i];
}

/**
 * Open a font for use based on a css_style.
 *
 * \param set a font_set, as returned by font_new_set()
 * \param style a css_style which describes the font
 * \return a struct font_data, with a RISC OS font handle in handle
 *
 * The set is updated to include the font, if it was not present.
 */

struct font_data *font_open(struct font_set *set, struct css_style *style)
{
	struct font_data *data;
	unsigned int size = option_font_size * 1.6;
	unsigned int f = 0;
	font_f handle;
	os_error *error;

	assert(set);
	assert(style);

	if (style->font_size.size == CSS_FONT_SIZE_LENGTH)
		size = len(&style->font_size.value.length, style) *
				72.0 / 90.0 * 16;
	if (size < option_font_min_size * 1.6)
		size = option_font_min_size * 1.6;
	if (1600 < size)
		size = 1600;

	switch (style->font_family) {
	        case CSS_FONT_FAMILY_SANS_SERIF:
	                f += FONT_SANS_SERIF;
	                break;
	        case CSS_FONT_FAMILY_SERIF:
	                f += FONT_SERIF;
	                break;
	        case CSS_FONT_FAMILY_MONOSPACE:
	                f += FONT_MONOSPACE;
	                break;
	        case CSS_FONT_FAMILY_CURSIVE:
	                f += FONT_CURSIVE;
	                break;
	        case CSS_FONT_FAMILY_FANTASY:
	                f += FONT_FANTASY;
	                break;
	        default:
	                break;
	}

	switch (style->font_variant) {
	       case CSS_FONT_VARIANT_SMALL_CAPS:
	                f += FONT_SMALLCAPS;
	                break;
	       default:
	                break;
	}

	switch (style->font_weight) {
		case CSS_FONT_WEIGHT_BOLD:
		case CSS_FONT_WEIGHT_600:
		case CSS_FONT_WEIGHT_700:
		case CSS_FONT_WEIGHT_800:
		case CSS_FONT_WEIGHT_900:
			f += FONT_BOLD;
			break;
		default:
			break;
	}

	switch (style->font_style) {
		case CSS_FONT_STYLE_ITALIC:
		case CSS_FONT_STYLE_OBLIQUE:
			f += FONT_SLANTED;
			break;
		default:
			break;
	}

	for (data = set->font[f]; data != 0; data = data->next)
		if (data->size == size)
	        	return data;

	data = xcalloc(1, sizeof(*data));

	error = xfont_find_font(font_table[f], (int)size, (int)size,
	                        0, 0, &handle, 0, 0);

	if (error) { /* fall back to Homerton */
	        LOG(("font_find_font failed; falling back to Homerton"));
	        error = xfont_find_font(font_table[f % 4],
	                                (int)size, (int)size,
	                                0, 0, &handle, 0, 0);
	        if (error) {
		        LOG(("%i: %s\n", error->errnum, error->errmess));
		        die("font_find_font failed");
	        }
	}

        data->id = f;
	data->handle = handle;
	data->size = size;
	data->space_width = font_width(data, " ", 1);

	data->next = set->font[f];
	set->font[f] = data;

	return data;
}


/**
 * Frees all the fonts in a font_set.
 *
 * \param set a font_set as returned by font_new_set()
 */

void font_free_set(struct font_set *set)
{
	unsigned int i;
	struct font_data *data, *next;

	assert(set != 0);

	for (i = 0; i < FONT_FAMILIES * FONT_FACES; i++) {
		for (data = set->font[i]; data != 0; data = next) {
			next = data->next;
			font_lose_font((font_f)(data->handle));
			free(data);
		}
        }

	free(set);
}


/**
 * Find the width of some text in a font.
 *
 * \param font a font_data, as returned by font_open()
 * \param text string to measure
 * \param length length of text
 * \return width of text in pixels
 */

unsigned long font_width(struct font_data *font, const char * text, unsigned int length)
{
	int width;
	os_error * error;

	assert(font != 0 && text != 0);

	if (length == 0)
		return 0;

	error = xfont_scan_string((font_f)(font->handle), text,
			font_GIVEN_FONT | font_KERN | font_GIVEN_LENGTH,
			0x7fffffff, 0x7fffffff,
			0,
			0, (int)length,
			0, &width, 0, 0);
	if (error != 0) {
		fprintf(stderr, "%s\n", error->errmess);
		die("font_width: font_scan_string failed");
	}

	return width / 800;
}


/**
 * Find where in a string a x coordinate falls.
 *
 * For example, used to find where to position the caret in response to mouse
 * click.
 *
 * \param text a string
 * \param font a font_data, as returned by font_open()
 * \param length length of text
 * \param x horizontal position in pixels
 * \param char_offset updated to give the offset in the string
 * \param pixel_offset updated to give the coordinate of the character in pixels
 */

void font_position_in_string(const char *text, struct font_data *font,
		unsigned int length, unsigned long x,
		int *char_offset, int *pixel_offset)
{
	font_scan_block block;
	char *split_point;
	int x_out, y_out, length_out;
	os_error *error;

	assert(font != 0 && text != 0);

	block.space.x = block.space.y = 0;
	block.letter.x = block.letter.y = 0;
	block.split_char = -1;

	error = xfont_scan_string((font_f)(font->handle), text,
			font_GIVEN_BLOCK | font_GIVEN_FONT | font_KERN |
			font_RETURN_CARET_POS | font_GIVEN_LENGTH,
			x * 2 * 400,
			0x7fffffff,
			&block, 0, (int)length,
			&split_point, &x_out, &y_out, &length_out);
	if (error) {
		fprintf(stderr, "%s\n", error->errmess);
		die("font_width: font_scan_string failed");
	}

	*char_offset = (int)(split_point - text);
	*pixel_offset = x_out / 2 / 400;
}


/**
 * Find where to split a string to fit in a width.
 *
 * For example, used when wrapping paragraphs.
 *
 * \param data a font_data, as returned by font_open()
 * \param text string to split
 * \param length length of text
 * \param width available width
 * \param used_width updated to actual width used
 * \return pointer to character which does not fit
 */

char * font_split(struct font_data *data, const char * text, unsigned int length,
		unsigned int width, unsigned int *used_width)
{
	os_error *error;
	font_scan_block block;
	char *split;

	block.space.x = block.space.y = block.letter.x = block.letter.y = 0;
	block.split_char = ' ';

	error = xfont_scan_string((font_f)(data->handle), text,
			font_GIVEN_BLOCK | font_GIVEN_FONT | font_KERN | font_GIVEN_LENGTH,
			width * 2 * 400, 0x7fffffff,
			&block,
			0,
			(int)length,
			&split,
			used_width, 0, 0);
	if (error != 0) {
		fprintf(stderr, "%s\n", error->errmess);
		die("font_split: font_scan_string failed");
	}

	*used_width = *used_width / 2 / 400;

	return split;
}


#ifdef TEST

int main(void)
{
	unsigned int i;
	struct font_set *set;
	struct css_style style;

        style.font_family = CSS_FONT_FAMILY_SANS_SERIF;
	style.font_size.size = CSS_FONT_SIZE_LENGTH;
	style.font_weight = CSS_FONT_WEIGHT_BOLD;
	style.font_style = CSS_FONT_STYLE_ITALIC;

	set = font_new_set();

	for (i = 10; i != 100; i += 10) {
		style.font_size.value.length.value = i;
		font_open(set, &style);
	}

	font_free_set(set);

	return 0;
}

#endif