NetSurf
font.c
Go to the documentation of this file.
1/*
2 * Copyright 2006 James Bursa <bursa@users.sourceforge.net>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * \file
21 * RISC OS implementation of Font handling.
22 *
23 * The RUfl is used to handle and render fonts.
24 */
25
26#include "utils/config.h"
27
28#include <assert.h>
29#include <string.h>
30#include <oslib/wimp.h>
31#include <oslib/wimpreadsysinfo.h>
32
33#include "utils/nsoption.h"
34#include "utils/log.h"
35#include "utils/messages.h"
36#include "utils/utils.h"
37#include "netsurf/layout.h"
38#include "netsurf/plot_style.h"
39
40#include "riscos/gui.h"
41#include "riscos/font.h"
42
43
44/** desktop font, size and style being used */
47rufl_style ro_gui_desktop_font_style = rufl_WEIGHT_400;
48bool no_font_blending = false;
49
50
51/**
52 * Check that at least Homerton.Medium is available.
53 */
54static void nsfont_check_fonts(void)
55{
56 char s[252];
57 font_f font;
58 os_error *error;
59
60 error = xfont_find_font("Homerton.Medium\\ELatin1",
61 160, 160, 0, 0, &font, 0, 0);
62 if (error) {
63 if (error->errnum == error_FILE_NOT_FOUND) {
64 xwimp_start_task("TaskWindow -wimpslot 200K -quit "
65 "<NetSurf$Dir>.FixFonts", 0);
66 die("FontBadInst");
67 } else {
68 NSLOG(netsurf, INFO, "xfont_find_font: 0x%x: %s",
69 error->errnum, error->errmess);
70 snprintf(s, sizeof s, messages_get("FontError"),
71 error->errmess);
72 die(s);
73 }
74 }
75
76 error = xfont_lose_font(font);
77 if (error) {
78 NSLOG(netsurf, INFO, "xfont_lose_font: 0x%x: %s",
79 error->errnum, error->errmess);
80 snprintf(s, sizeof s, messages_get("FontError"),
81 error->errmess);
82 die(s);
83 }
84}
85
86
87/**
88 * Check that a font option is valid, and fix it if not.
89 *
90 * \param option pointer to option, as used by options.[ch]
91 * \param family font family to use if option is not set, or the set
92 * family is not available
93 * \param fallback font family to use if family is not available either
94 */
95static void nsfont_check_option(char **option, const char *family,
96 const char *fallback)
97{
98 if (*option && !nsfont_exists(*option)) {
99 free(*option);
100 *option = 0;
101 }
102 if (!*option) {
103 if (nsfont_exists(family))
104 *option = strdup(family);
105 else
106 *option = strdup(fallback);
107 }
108}
109
110
111/**
112 * Initialize font handling.
113 *
114 * Exits through die() on error.
115 */
116void nsfont_init(void)
117{
118 const char *fallback;
119 rufl_code code;
120
122
123 NSLOG(netsurf, INFO, "Initialise RUfl");
124 code = rufl_init();
125 if (code != rufl_OK) {
126 if (code == rufl_FONT_MANAGER_ERROR)
127 NSLOG(netsurf, INFO,
128 "rufl_init: rufl_FONT_MANAGER_ERROR: 0x%x: %s",
129 rufl_fm_error->errnum,
130 rufl_fm_error->errmess);
131 else
132 NSLOG(netsurf, INFO, "rufl_init: 0x%x", code);
133 die("The Unicode font library could not be initialized. "
134 "Please report this to the developers.");
135 }
136 NSLOG(netsurf, INFO, "RUfl initialised");
137
138 if (rufl_family_list_entries == 0)
139 die("No fonts could be found. At least one font must be "
140 "installed.");
141
142 fallback = nsfont_fallback_font();
143
144 nsfont_check_option(&nsoption_charp(font_sans), "Homerton", fallback);
145 nsfont_check_option(&nsoption_charp(font_serif), "Trinity", fallback);
146 nsfont_check_option(&nsoption_charp(font_mono), "Corpus", fallback);
147 nsfont_check_option(&nsoption_charp(font_cursive), "Churchill", fallback);
148 nsfont_check_option(&nsoption_charp(font_fantasy), "Sassoon", fallback);
149
150 if (nsoption_int(font_default) != PLOT_FONT_FAMILY_SANS_SERIF &&
151 nsoption_int(font_default) != PLOT_FONT_FAMILY_SERIF &&
152 nsoption_int(font_default) != PLOT_FONT_FAMILY_MONOSPACE &&
153 nsoption_int(font_default) != PLOT_FONT_FAMILY_CURSIVE &&
154 nsoption_int(font_default) != PLOT_FONT_FAMILY_FANTASY) {
156 }
157}
158
159
160/**
161 * Retrieve the fallback font name
162 *
163 * \return Fallback font name
164 */
165const char *nsfont_fallback_font(void)
166{
167 const char *fallback = "Homerton";
168
169 if (!nsfont_exists(fallback)) {
170 NSLOG(netsurf, INFO,
171 "Homerton not found, dumping RUfl family list");
172 for (unsigned int i = 0; i < rufl_family_list_entries; i++) {
173 NSLOG(netsurf, INFO, "'%s'", rufl_family_list[i]);
174 }
175 fallback = rufl_family_list[0];
176 }
177
178 return fallback;
179}
180
181
182/**
183 * bsearch comparison routine
184 */
185static int nsfont_list_cmp(const void *keyval, const void *datum)
186{
187 const char *key = keyval;
188 const char * const *entry = datum;
189 return strcasecmp(key, *entry);
190}
191
192
193/**
194 * Check if a font family is available.
195 *
196 * \param font_family name of font family
197 * \return true if the family is available
198 */
199bool nsfont_exists(const char *font_family)
200{
201 if (bsearch(font_family, rufl_family_list,
202 rufl_family_list_entries, sizeof rufl_family_list[0],
204 return true;
205 return false;
206}
207
208
209/**
210 * Measure the width of a string.
211 *
212 * \param fstyle plot style for this text
213 * \param string UTF-8 string to measure
214 * \param length length of string
215 * \param width updated to width of string[0..length)
216 * \return true on success, false on error and error reported
217 */
218static nserror
220 const char *string, size_t length,
221 int *width)
222{
223 const char *font_family;
224 unsigned int font_size;
225 rufl_style font_style;
226 rufl_code code;
227
228 nsfont_read_style(fstyle, &font_family, &font_size, &font_style);
229 if (font_size == 0) {
230 *width = 0;
231 return NSERROR_OK;
232 }
233
234 code = rufl_width(font_family, font_style, font_size,
235 string, length,
236 width);
237 if (code != rufl_OK) {
238 if (code == rufl_FONT_MANAGER_ERROR)
239 NSLOG(netsurf, INFO,
240 "rufl_width: rufl_FONT_MANAGER_ERROR: 0x%x: %s",
241 rufl_fm_error->errnum,
242 rufl_fm_error->errmess);
243 else
244 NSLOG(netsurf, INFO, "rufl_width: 0x%x", code);
245/* ro_warn_user("MiscError", "font error"); */
246 *width = 0;
247 return NSERROR_INVALID;
248 }
249
250 *width /= 2;
251 return NSERROR_OK;
252}
253
254
255/**
256 * Find the position in a string where an x coordinate falls.
257 *
258 * \param fstyle style for this text
259 * \param string UTF-8 string to measure
260 * \param length length of string
261 * \param x x coordinate to search for
262 * \param char_offset updated to offset in string of actual_x, [0..length]
263 * \param actual_x updated to x coordinate of character closest to x
264 * \return true on success, false on error and error reported
265 */
266static nserror
268 const char *string, size_t length,
269 int x, size_t *char_offset, int *actual_x)
270{
271 const char *font_family;
272 unsigned int font_size;
273 rufl_style font_style;
274 rufl_code code;
275
276 nsfont_read_style(fstyle, &font_family, &font_size, &font_style);
277 if (font_size == 0) {
278 *char_offset = 0;
279 *actual_x = 0;
280 return NSERROR_OK;
281 }
282
283 code = rufl_x_to_offset(font_family, font_style, font_size,
284 string, length,
285 x * 2, char_offset, actual_x);
286 if (code != rufl_OK) {
287 if (code == rufl_FONT_MANAGER_ERROR)
288 NSLOG(netsurf, INFO,
289 "rufl_x_to_offset: rufl_FONT_MANAGER_ERROR: ""0x%x: %s",
290 rufl_fm_error->errnum,
291 rufl_fm_error->errmess);
292 else
293 NSLOG(netsurf, INFO, "rufl_x_to_offset: 0x%x", code);
294/* ro_warn_user("MiscError", "font error"); */
295 *char_offset = 0;
296 *actual_x = 0;
297 return NSERROR_INVALID;
298 }
299
300 *actual_x /= 2;
301
302 return NSERROR_OK;
303}
304
305
306/**
307 * Find where to split a string to make it fit a width.
308 *
309 * \param fstyle style for this text
310 * \param string UTF-8 string to measure
311 * \param length length of string, in bytes
312 * \param x width available
313 * \param char_offset updated to offset in string of actual_x, [1..length]
314 * \param actual_x updated to x coordinate of character closest to x
315 * \return true on success, false on error and error reported
316 *
317 * On exit, char_offset indicates first character after split point.
318 *
319 * Note: char_offset of 0 should never be returned.
320 *
321 * Returns:
322 * char_offset giving split point closest to x, where actual_x <= x
323 * else
324 * char_offset giving split point closest to x, where actual_x > x
325 *
326 * Returning char_offset == length means no split possible
327 */
328static nserror
330 const char *string, size_t length,
331 int x, size_t *char_offset, int *actual_x)
332{
333 const char *font_family;
334 unsigned int font_size;
335 rufl_style font_style;
336 rufl_code code;
337
338 nsfont_read_style(fstyle, &font_family, &font_size, &font_style);
339 if (font_size == 0) {
340 *char_offset = 0;
341 *actual_x = 0;
342 return NSERROR_OK;
343 }
344
345 code = rufl_split(font_family, font_style, font_size,
346 string, length,
347 x * 2, char_offset, actual_x);
348 if (code != rufl_OK) {
349 if (code == rufl_FONT_MANAGER_ERROR) {
350 NSLOG(netsurf, INFO,
351 "rufl_split: rufl_FONT_MANAGER_ERROR: ""0x%x: %s",
352 rufl_fm_error->errnum,
353 rufl_fm_error->errmess);
354 } else {
355 NSLOG(netsurf, INFO, "rufl_split: 0x%x", code);
356 }
357/* ro_warn_user("MiscError", "font error"); */
358 *char_offset = 0;
359 *actual_x = 0;
360 return NSERROR_INVALID;
361 }
362
363 if (*char_offset != length) {
364 /* we found something to split at */
365 size_t orig = *char_offset;
366
367 /* ensure a space at <= the split point we found */
368 while (*char_offset && string[*char_offset] != ' ') {
369 (*char_offset)--;
370 }
371
372 /* nothing valid found <= split point, advance to next space */
373 if (*char_offset == 0) {
374 *char_offset = orig;
375 while ((*char_offset != length) &&
376 (string[*char_offset] != ' ')) {
377 (*char_offset)++;
378 }
379 }
380 }
381
382 code = rufl_width(font_family, font_style, font_size,
383 string, *char_offset,
384 actual_x);
385 if (code != rufl_OK) {
386 if (code == rufl_FONT_MANAGER_ERROR) {
387 NSLOG(netsurf, INFO,
388 "rufl_width: rufl_FONT_MANAGER_ERROR: 0x%x: %s",
389 rufl_fm_error->errnum,
390 rufl_fm_error->errmess);
391 } else {
392 NSLOG(netsurf, INFO, "rufl_width: 0x%x", code);
393 }
394/* ro_warn_user("MiscError", "font error"); */
395 *char_offset = 0;
396 *actual_x = 0;
397 return NSERROR_INVALID;
398 }
399
400 *actual_x /= 2;
401 return NSERROR_OK;
402}
403
404
405/**
406 * Paint a string.
407 *
408 * \param fstyle plot style for this text
409 * \param string UTF-8 string to measure
410 * \param length length of string
411 * \param x x coordinate
412 * \param y y coordinate
413 * \return true on success, false on error and error reported
414 */
415bool nsfont_paint(const plot_font_style_t *fstyle, const char *string,
416 size_t length, int x, int y)
417{
418 const char *font_family;
419 unsigned int font_size;
420 unsigned int flags = rufl_BLEND_FONT;
421 rufl_style font_style;
422 rufl_code code;
423
424 nsfont_read_style(fstyle, &font_family, &font_size, &font_style);
425 if (font_size == 0)
426 return true;
427
429 flags = 0;
430
431 code = rufl_paint(font_family, font_style, font_size,
432 string, length, x, y, flags);
433 if (code != rufl_OK) {
434 if (code == rufl_FONT_MANAGER_ERROR) {
435 NSLOG(netsurf, INFO,
436 "rufl_paint: rufl_FONT_MANAGER_ERROR: 0x%x: %s",
437 rufl_fm_error->errnum,
438 rufl_fm_error->errmess);
439 } else {
440 NSLOG(netsurf, INFO, "rufl_paint: 0x%x", code);
441 }
442 }
443
444 return true;
445}
446
447
448/**
449 * Convert a font style to a font family, size and rufl_style.
450 *
451 * \param fstyle plot style for this text
452 * \param font_family updated to font family
453 * \param font_size updated to font size
454 * \param font_style updated to font style
455 */
457 const char **font_family, unsigned int *font_size,
458 rufl_style *font_style)
459{
460 static const rufl_style weight_table[] = {
461 rufl_WEIGHT_100,
462 rufl_WEIGHT_200,
463 rufl_WEIGHT_300,
464 rufl_WEIGHT_400,
465 rufl_WEIGHT_500,
466 rufl_WEIGHT_600,
467 rufl_WEIGHT_700,
468 rufl_WEIGHT_800,
469 rufl_WEIGHT_900
470 };
471
472 *font_size = (fstyle->size * 16) / PLOT_STYLE_SCALE;
473 if (1600 < *font_size)
474 *font_size = 1600;
475
476 switch (fstyle->family) {
478 *font_family = nsoption_charp(font_sans);
479 break;
481 *font_family = nsoption_charp(font_serif);
482 break;
484 *font_family = nsoption_charp(font_mono);
485 break;
487 *font_family = nsoption_charp(font_cursive);
488 break;
490 *font_family = nsoption_charp(font_fantasy);
491 break;
492 default:
493 *font_family = nsoption_charp(font_sans);
494 break;
495 }
496
497 if ((fstyle->flags & FONTF_ITALIC) || (fstyle->flags & FONTF_OBLIQUE)) {
498 *font_style = rufl_SLANTED;
499 } else {
500 *font_style = 0;
501 }
502
503 *font_style |= weight_table[(fstyle->weight / 100) - 1];
504}
505
506
507/**
508 * Looks up the current desktop font and converts that to a family name,
509 * font size and style flags suitable for passing directly to rufl
510 *
511 * \param family buffer to receive font family
512 * \param family_size buffer size
513 * \param psize receives the font size in 1/16 points
514 * \param pstyle receives the style settings to be passed to rufl
515 */
516static void
518 size_t family_size,
519 int *psize,
520 rufl_style *pstyle)
521{
522 rufl_style style = rufl_WEIGHT_400;
523 os_error *error;
524 int ptx, pty;
525 font_f font_handle;
526 int used;
527
528 assert(family);
529 assert(20 < family_size);
530 assert(psize);
531 assert(pstyle);
532
533 error = xwimpreadsysinfo_font(&font_handle, NULL);
534 if (error) {
535 NSLOG(netsurf, INFO, "xwimpreadsysinfo_font: 0x%x: %s",
536 error->errnum, error->errmess);
537 ro_warn_user("WimpError", error->errmess);
538 goto failsafe;
539 }
540
541 if (font_handle == font_SYSTEM) {
542 /* Er, yeah; like that's ever gonna work with RUfl */
543 goto failsafe;
544 }
545
546 error = xfont_read_identifier(font_handle, NULL, &used);
547 if (error) {
548 NSLOG(netsurf, INFO, "xfont_read_identifier: 0x%x: %s",
549 error->errnum, error->errmess);
550 ro_warn_user("MiscError", error->errmess);
551 goto failsafe;
552 }
553
554 if (family_size < (size_t) used + 1) {
555 NSLOG(netsurf, INFO, "desktop font name too long");
556 goto failsafe;
557 }
558
559 error = xfont_read_defn(font_handle, (byte *) family,
560 &ptx, &pty, NULL, NULL, NULL, NULL);
561 if (error) {
562 NSLOG(netsurf, INFO, "xfont_read_defn: 0x%x: %s",
563 error->errnum, error->errmess);
564 ro_warn_user("MiscError", error->errmess);
565 goto failsafe;
566 }
567
568 for (size_t i = 0; i != (size_t) used; i++) {
569 if (family[i] < ' ') {
570 family[i] = 0;
571 break;
572 }
573 }
574
575 NSLOG(netsurf, INFO, "desktop font \"%s\"", family);
576
577 if (strcasestr(family, ".Medium"))
578 style = rufl_WEIGHT_500;
579 else if (strcasestr(family, ".Bold"))
580 style = rufl_WEIGHT_700;
581 if (strcasestr(family, ".Italic") || strcasestr(family, ".Oblique"))
582 style |= rufl_SLANTED;
583
584 char *dot = strchr(family, '.');
585 if (dot)
586 *dot = 0;
587
588 *psize = max(ptx, pty);
589 *pstyle = style;
590
591 NSLOG(netsurf, INFO, "family \"%s\", size %i, style %i", family,
592 *psize, style);
593
594 return;
595
596failsafe:
597 strcpy(family, "Homerton");
598 *psize = 12*16;
599 *pstyle = rufl_WEIGHT_400;
600}
601
602
603/**
604 * Retrieve the current desktop font family, size and style from
605 * the WindowManager in a form suitable for passing to rufl
606 */
608{
613}
614
615
618 .position = ro_font_position,
619 .split = ro_font_split,
620};
621
void die(const char *error)
Cause an abnormal program termination.
Definition: misc.c:69
char * strcasestr(const char *haystack, const char *needle)
Case insensitive strstr implementation.
Definition: utils.c:310
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_INVALID
Invalid data.
Definition: errors.h:49
@ NSERROR_OK
No error.
Definition: errors.h:30
void nsfont_init(void)
Initialize font handling.
Definition: font.c:116
static void ro_gui_wimp_desktop_font(char *family, size_t family_size, int *psize, rufl_style *pstyle)
Looks up the current desktop font and converts that to a family name, font size and style flags suita...
Definition: font.c:517
void ro_gui_wimp_get_desktop_font(void)
Retrieve the current desktop font family, size and style from the WindowManager in a form suitable fo...
Definition: font.c:607
rufl_style ro_gui_desktop_font_style
Definition: font.c:47
const char * nsfont_fallback_font(void)
Retrieve the fallback font name.
Definition: font.c:165
static nserror ro_font_position(const plot_font_style_t *fstyle, const char *string, size_t length, int x, size_t *char_offset, int *actual_x)
Find the position in a string where an x coordinate falls.
Definition: font.c:267
static void nsfont_check_fonts(void)
Check that at least Homerton.Medium is available.
Definition: font.c:54
bool no_font_blending
Definition: font.c:48
static nserror ro_font_split(const plot_font_style_t *fstyle, const char *string, size_t length, int x, size_t *char_offset, int *actual_x)
Find where to split a string to make it fit a width.
Definition: font.c:329
int ro_gui_desktop_font_size
Definition: font.c:46
bool nsfont_exists(const char *font_family)
Check if a font family is available.
Definition: font.c:199
static void nsfont_check_option(char **option, const char *family, const char *fallback)
Check that a font option is valid, and fix it if not.
Definition: font.c:95
char ro_gui_desktop_font_family[80]
desktop font, size and style being used
Definition: font.c:45
static nserror ro_font_width(const plot_font_style_t *fstyle, const char *string, size_t length, int *width)
Measure the width of a string.
Definition: font.c:219
static struct gui_layout_table layout_table
Definition: font.c:616
static int nsfont_list_cmp(const void *keyval, const void *datum)
bsearch comparison routine
Definition: font.c:185
void nsfont_read_style(const plot_font_style_t *fstyle, const char **font_family, unsigned int *font_size, rufl_style *font_style)
Convert a font style to a font family, size and rufl_style.
Definition: font.c:456
struct gui_layout_table * riscos_layout_table
Definition: font.c:622
bool nsfont_paint(const plot_font_style_t *fstyle, const char *string, size_t length, int x, int y)
Paint a string.
Definition: font.c:415
RISC OS font interface.
Interface to platform-specific layout operation table.
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
const char * messages_get(const char *key)
Fast lookup of a message by key from the standard Messages hash.
Definition: messages.c:241
Localised message support (interface).
plotter style interfaces, generic styles and style colour helpers.
@ FONTF_ITALIC
Definition: plot_style.h:103
@ FONTF_OBLIQUE
Definition: plot_style.h:104
@ PLOT_FONT_FAMILY_CURSIVE
Definition: plot_style.h:92
@ PLOT_FONT_FAMILY_SANS_SERIF
Definition: plot_style.h:89
@ PLOT_FONT_FAMILY_FANTASY
Definition: plot_style.h:93
@ PLOT_FONT_FAMILY_MONOSPACE
Definition: plot_style.h:91
@ PLOT_FONT_FAMILY_SERIF
Definition: plot_style.h:90
#define PLOT_STYLE_SCALE
Scaling factor for plot styles.
Definition: plot_style.h:45
int width
Definition: gui.c:159
nserror ro_warn_user(const char *warning, const char *detail)
Display a warning for a serious problem (eg memory exhaustion).
Definition: gui.c:2076
bool print_active
Definition: print.c:81
Interface to utility string handling.
nserror(* width)(const struct plot_font_style *fstyle, const char *string, size_t length, int *width)
Measure the width of a string.
Definition: layout.h:49
Font style for plotting.
Definition: plot_style.h:111
plot_font_generic_family_t family
Generic family to plot with.
Definition: plot_style.h:118
plot_font_flags_t flags
Font flags.
Definition: plot_style.h:121
plot_style_fixed size
Font size, in pt.
Definition: plot_style.h:119
int weight
Font weight: value in range [100,900] as per CSS.
Definition: plot_style.h:120
Option reading and saving interface.
#define nsoption_charp(OPTION)
Get the value of a string option.
Definition: nsoption.h:297
#define nsoption_int(OPTION)
Get the value of an integer option.
Definition: nsoption.h:279
#define nsoption_set_int(OPTION, VALUE)
set an integer option in the default table
Definition: nsoption.h:314
Interface to a number of general purpose functionality.
#define max(x, y)
Definition: utils.h:50