NetSurf
font_freetype.c
Go to the documentation of this file.
1/*
2 * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
3 * 2008 Vincent Sanders <vince@simtec.co.uk>
4 *
5 * This file is part of NetSurf, http://www.netsurf-browser.org/
6 *
7 * NetSurf is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * NetSurf is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <assert.h>
21
22#include <ft2build.h>
23#include FT_CACHE_H
24
25#include "netsurf/inttypes.h"
26#include "utils/filepath.h"
27#include "utils/utf8.h"
28#include "utils/log.h"
29#include "utils/nsoption.h"
30#include "netsurf/utf8.h"
31#include "netsurf/layout.h"
32#include "netsurf/browser.h"
33#include "netsurf/plot_style.h"
34
35#include "framebuffer/gui.h"
36#include "framebuffer/font.h"
38
39/* glyph cache minimum size */
40#define CACHE_MIN_SIZE (100 * 1024)
41
42#define BOLD_WEIGHT 700
43
44static FT_Library library;
45static FTC_Manager ft_cmanager;
46static FTC_CMapCache ft_cmap_cache ;
47static FTC_ImageCache ft_image_cache;
48
50
51/* cache manager faceID data to create freetype faceid on demand */
52typedef struct fb_faceid_s {
53 char *fontfile; /* path to font */
54 int index; /* index of font */
55 int cidx; /* character map index for unicode */
57
58
71};
72
73/* defines for accesing the faces */
74#define FB_FACE_DEFAULT 0
75
77
78/**
79 * map cache manager handle to face id
80 */
81static FT_Error
82ft_face_requester(FTC_FaceID face_id,
83 FT_Library library,
84 FT_Pointer request_data,
85 FT_Face *face )
86{
87 FT_Error error;
88 fb_faceid_t *fb_face = (fb_faceid_t *)face_id;
89 int cidx;
90
91 error = FT_New_Face(library, fb_face->fontfile, fb_face->index, face);
92 if (error) {
93 NSLOG(netsurf, INFO, "Could not find font (code %d)", error);
94 } else {
95
96 error = FT_Select_Charmap(*face, FT_ENCODING_UNICODE);
97 if (error) {
98 NSLOG(netsurf, INFO,
99 "Could not select charmap (code %d)", error);
100 } else {
101 for (cidx = 0; cidx < (*face)->num_charmaps; cidx++) {
102 if ((*face)->charmap == (*face)->charmaps[cidx]) {
103 fb_face->cidx = cidx;
104 break;
105 }
106 }
107 }
108 }
109 NSLOG(netsurf, INFO, "Loaded face from %s", fb_face->fontfile);
110
111 return error;
112}
113
114/**
115 * create new framebuffer face and cause it to be loaded to check its ok
116 */
117static fb_faceid_t *
118fb_new_face(const char *option, const char *resname, const char *fontname)
119{
120 fb_faceid_t *newf;
121 FT_Error error;
122 FT_Face aface;
123 char buf[PATH_MAX];
124
125 newf = calloc(1, sizeof(fb_faceid_t));
126
127 if (option != NULL) {
128 newf->fontfile = strdup(option);
129 } else {
130 filepath_sfind(respaths, buf, fontname);
131 newf->fontfile = strdup(buf);
132 }
133
134 error = FTC_Manager_LookupFace(ft_cmanager, (FTC_FaceID)newf, &aface);
135 if (error) {
136 NSLOG(netsurf, INFO, "Could not find font face %s (code %d)",
137 fontname, error);
138 free(newf->fontfile);
139 free(newf);
140 newf = NULL;
141 }
142
143 return newf;
144}
145
146/* exported interface documented in framebuffer/font.h */
147bool fb_font_init(void)
148{
149 FT_Error error;
150 FT_ULong max_cache_size;
151 FT_UInt max_faces = 6;
152 fb_faceid_t *fb_face;
153
154 /* freetype library initialise */
155 error = FT_Init_FreeType( &library );
156 if (error) {
157 NSLOG(netsurf, INFO,
158 "Freetype could not initialised (code %d)", error);
159 return false;
160 }
161
162 /* set the Glyph cache size up */
163 max_cache_size = nsoption_int(fb_font_cachesize) * 1024;
164
165 if (max_cache_size < CACHE_MIN_SIZE) {
166 max_cache_size = CACHE_MIN_SIZE;
167 }
168
169 /* cache manager initialise */
170 error = FTC_Manager_New(library,
171 max_faces,
172 0,
173 max_cache_size,
175 NULL,
176 &ft_cmanager);
177 if (error) {
178 NSLOG(netsurf, INFO,
179 "Freetype could not initialise cache manager (code %d)",
180 error);
181 FT_Done_FreeType(library);
182 return false;
183 }
184
185 error = FTC_CMapCache_New(ft_cmanager, &ft_cmap_cache);
186
187 error = FTC_ImageCache_New(ft_cmanager, &ft_image_cache);
188
189 /* need to obtain the generic font faces */
190
191 /* Start with the sans serif font */
192 fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif),
193 "sans_serif.ttf",
194 NETSURF_FB_FONT_SANS_SERIF);
195 if (fb_face == NULL) {
196 /* The sans serif font is the default and must be found. */
197 NSLOG(netsurf, INFO, "Could not find the default font");
198 FTC_Manager_Done(ft_cmanager);
199 FT_Done_FreeType(library);
200 return false;
201 } else {
202 fb_faces[FB_FACE_SANS_SERIF] = fb_face;
203 }
204
205 /* Bold sans serif face */
206 fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_bold),
207 "sans_serif_bold.ttf",
208 NETSURF_FB_FONT_SANS_SERIF_BOLD);
209 if (fb_face == NULL) {
210 /* seperate bold face unavailabe use the normal weight version */
212 } else {
214 }
215
216 /* Italic sans serif face */
217 fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_italic),
218 "sans_serif_italic.ttf",
219 NETSURF_FB_FONT_SANS_SERIF_ITALIC);
220 if (fb_face == NULL) {
221 /* seperate italic face unavailabe use the normal weight version */
223 } else {
225 }
226
227 /* Bold italic sans serif face */
228 fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_italic_bold),
229 "sans_serif_italic_bold.ttf",
230 NETSURF_FB_FONT_SANS_SERIF_ITALIC_BOLD);
231 if (fb_face == NULL) {
232 /* seperate italic face unavailabe use the normal weight version */
234 } else {
236 }
237
238 /* serif face */
239 fb_face = fb_new_face(nsoption_charp(fb_face_serif),
240 "serif.ttf",
241 NETSURF_FB_FONT_SERIF);
242 if (fb_face == NULL) {
243 /* serif face unavailabe use the default */
245 } else {
246 fb_faces[FB_FACE_SERIF] = fb_face;
247 }
248
249 /* bold serif face*/
250 fb_face = fb_new_face(nsoption_charp(fb_face_serif_bold),
251 "serif_bold.ttf",
252 NETSURF_FB_FONT_SERIF_BOLD);
253 if (fb_face == NULL) {
254 /* bold serif face unavailabe use the normal weight */
256 } else {
257 fb_faces[FB_FACE_SERIF_BOLD] = fb_face;
258 }
259
260
261 /* monospace face */
262 fb_face = fb_new_face(nsoption_charp(fb_face_monospace),
263 "monospace.ttf",
264 NETSURF_FB_FONT_MONOSPACE);
265 if (fb_face == NULL) {
266 /* serif face unavailabe use the default */
268 } else {
269 fb_faces[FB_FACE_MONOSPACE] = fb_face;
270 }
271
272 /* bold monospace face*/
273 fb_face = fb_new_face(nsoption_charp(fb_face_monospace_bold),
274 "monospace_bold.ttf",
275 NETSURF_FB_FONT_MONOSPACE_BOLD);
276 if (fb_face == NULL) {
277 /* bold serif face unavailabe use the normal weight */
279 } else {
281 }
282
283 /* cursive face */
284 fb_face = fb_new_face(nsoption_charp(fb_face_cursive),
285 "cursive.ttf",
286 NETSURF_FB_FONT_CURSIVE);
287 if (fb_face == NULL) {
288 /* cursive face unavailabe use the default */
290 } else {
291 fb_faces[FB_FACE_CURSIVE] = fb_face;
292 }
293
294 /* fantasy face */
295 fb_face = fb_new_face(nsoption_charp(fb_face_fantasy),
296 "fantasy.ttf",
297 NETSURF_FB_FONT_FANTASY);
298 if (fb_face == NULL) {
299 /* fantasy face unavailabe use the default */
301 } else {
302 fb_faces[FB_FACE_FANTASY] = fb_face;
303 }
304
305
306 /* set the default render mode */
307 if (nsoption_bool(fb_font_monochrome) == true)
308 ft_load_type = FT_LOAD_MONOCHROME; /* faster but less pretty */
309 else
310 ft_load_type = 0;
311
312 return true;
313}
314
315/* exported interface documented in framebuffer/font.h */
317{
318 int i, j;
319
320 FTC_Manager_Done(ft_cmanager);
321 FT_Done_FreeType(library);
322
323 for (i = 0; i < FB_FACE_COUNT; i++) {
324 if (fb_faces[i] == NULL)
325 continue;
326
327 /* Unset any faces that duplicate this one */
328 for (j = i + 1; j < FB_FACE_COUNT; j++) {
329 if (fb_faces[i] == fb_faces[j])
330 fb_faces[j] = NULL;
331 }
332
333 free(fb_faces[i]->fontfile);
334 free(fb_faces[i]);
335
336 fb_faces[i] = NULL;
337 }
338
339 return true;
340}
341
342/**
343 * fill freetype scalar
344 */
345static void fb_fill_scalar(const plot_font_style_t *fstyle, FTC_Scaler srec)
346{
347 int selected_face = FB_FACE_DEFAULT;
348
349 switch (fstyle->family) {
350
352 if (fstyle->weight >= BOLD_WEIGHT) {
353 selected_face = FB_FACE_SERIF_BOLD;
354 } else {
355 selected_face = FB_FACE_SERIF;
356 }
357 break;
358
360 if (fstyle->weight >= BOLD_WEIGHT) {
361 selected_face = FB_FACE_MONOSPACE_BOLD;
362 } else {
363 selected_face = FB_FACE_MONOSPACE;
364 }
365 break;
366
368 selected_face = FB_FACE_CURSIVE;
369 break;
370
372 selected_face = FB_FACE_FANTASY;
373 break;
374
376 default:
377 if ((fstyle->flags & FONTF_ITALIC) ||
378 (fstyle->flags & FONTF_OBLIQUE)) {
379 if (fstyle->weight >= BOLD_WEIGHT) {
380 selected_face = FB_FACE_SANS_SERIF_ITALIC_BOLD;
381 } else {
382 selected_face = FB_FACE_SANS_SERIF_ITALIC;
383 }
384 } else {
385 if (fstyle->weight >= BOLD_WEIGHT) {
386 selected_face = FB_FACE_SANS_SERIF_BOLD;
387 } else {
388 selected_face = FB_FACE_SANS_SERIF;
389 }
390 }
391 }
392
393 srec->face_id = (FTC_FaceID)fb_faces[selected_face];
394
395 srec->width = srec->height = (fstyle->size * 64) / PLOT_STYLE_SCALE;
396 srec->pixel = 0;
397
398 srec->x_res = srec->y_res = browser_get_dpi();
399}
400
401/* exported interface documented in framebuffer/freetype_font.h */
402FT_Glyph fb_getglyph(const plot_font_style_t *fstyle, uint32_t ucs4)
403{
404 FT_UInt glyph_index;
405 FTC_ScalerRec srec;
406 FT_Glyph glyph;
407 FT_Error error;
408 fb_faceid_t *fb_face;
409
410 fb_fill_scalar(fstyle, &srec);
411
412 fb_face = (fb_faceid_t *)srec.face_id;
413
414 glyph_index = FTC_CMapCache_Lookup(ft_cmap_cache, srec.face_id,
415 fb_face->cidx, ucs4);
416
417 error = FTC_ImageCache_LookupScaler(ft_image_cache,
418 &srec,
419 FT_LOAD_RENDER |
420 FT_LOAD_FORCE_AUTOHINT |
422 glyph_index,
423 &glyph,
424 NULL);
425 if (error != 0)
426 return NULL;
427
428 return glyph;
429}
430
431
432/* exported interface documented in framebuffer/freetype_font.h */
435 const char *string, size_t length,
436 int *width)
437{
438 uint32_t ucs4;
439 size_t nxtchr = 0;
440 FT_Glyph glyph;
441
442 *width = 0;
443 while (nxtchr < length) {
444 ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
445 nxtchr = utf8_next(string, length, nxtchr);
446
447 glyph = fb_getglyph(fstyle, ucs4);
448 if (glyph == NULL)
449 continue;
450
451 *width += glyph->advance.x >> 16;
452 }
453 return NSERROR_OK;
454}
455
456
457/* exported interface documented in framebuffer/freetype_font.h */
460 const char *string, size_t length,
461 int x, size_t *char_offset, int *actual_x)
462{
463 uint32_t ucs4;
464 size_t nxtchr = 0;
465 FT_Glyph glyph;
466 int prev_x = 0;
467
468 *actual_x = 0;
469 while (nxtchr < length) {
470 ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
471
472 glyph = fb_getglyph(fstyle, ucs4);
473 if (glyph == NULL)
474 continue;
475
476 *actual_x += glyph->advance.x >> 16;
477 if (*actual_x > x)
478 break;
479
480 prev_x = *actual_x;
481 nxtchr = utf8_next(string, length, nxtchr);
482 }
483
484 /* choose nearest of previous and last x */
485 if (abs(*actual_x - x) > abs(prev_x - x))
486 *actual_x = prev_x;
487
488 *char_offset = nxtchr;
489 return NSERROR_OK;
490}
491
492
493/**
494 * Find where to split a string to make it fit a width.
495 *
496 * \param fstyle style for this text
497 * \param string UTF-8 string to measure
498 * \param length length of string, in bytes
499 * \param x width available
500 * \param char_offset updated to offset in string of actual_x, [1..length]
501 * \param actual_x updated to x coordinate of character closest to x
502 * \return true on success, false on error and error reported
503 *
504 * On exit, char_offset indicates first character after split point.
505 *
506 * Note: char_offset of 0 should never be returned.
507 *
508 * Returns:
509 * char_offset giving split point closest to x, where actual_x <= x
510 * else
511 * char_offset giving split point closest to x, where actual_x > x
512 *
513 * Returning char_offset == length means no split possible
514 */
515static nserror
517 const char *string, size_t length,
518 int x, size_t *char_offset, int *actual_x)
519{
520 uint32_t ucs4;
521 size_t nxtchr = 0;
522 int last_space_x = 0;
523 int last_space_idx = 0;
524 FT_Glyph glyph;
525
526 *actual_x = 0;
527 while (nxtchr < length) {
528 ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
529
530 glyph = fb_getglyph(fstyle, ucs4);
531 if (glyph == NULL)
532 continue;
533
534 if (ucs4 == 0x20) {
535 last_space_x = *actual_x;
536 last_space_idx = nxtchr;
537 }
538
539 *actual_x += glyph->advance.x >> 16;
540 if (*actual_x > x && last_space_idx != 0) {
541 /* string has exceeded available width and we've
542 * found a space; return previous space */
543 *actual_x = last_space_x;
544 *char_offset = last_space_idx;
545 return NSERROR_OK;
546 }
547
548 nxtchr = utf8_next(string, length, nxtchr);
549 }
550
551 *char_offset = nxtchr;
552
553 return NSERROR_OK;
554}
555
558 .position = fb_font_position,
559 .split = fb_font_split,
560};
561
563
564
566
567/*
568 * Local Variables:
569 * c-basic-offset:8
570 * End:
571 */
#define PATH_MAX
Definition: gui.h:31
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_OK
No error.
Definition: errors.h:30
char * filepath_sfind(char **respathv, char *filepath, const char *filename)
Searches an array of resource paths for a file.
Definition: filepath.c:109
Utility routines to obtain paths to file resources.
char ** respaths
resource search path vector
Definition: findfile.c:28
static fb_faceid_t * fb_faces[FB_FACE_COUNT]
Definition: font_freetype.c:76
struct gui_utf8_table * framebuffer_utf8_table
fb_face_e
Definition: font_freetype.c:59
@ FB_FACE_MONOSPACE
Definition: font_freetype.c:66
@ FB_FACE_SERIF_BOLD
Definition: font_freetype.c:65
@ FB_FACE_FANTASY
Definition: font_freetype.c:69
@ FB_FACE_SANS_SERIF_ITALIC
Definition: font_freetype.c:62
@ FB_FACE_MONOSPACE_BOLD
Definition: font_freetype.c:67
@ FB_FACE_SERIF
Definition: font_freetype.c:64
@ FB_FACE_SANS_SERIF_ITALIC_BOLD
Definition: font_freetype.c:63
@ FB_FACE_COUNT
Definition: font_freetype.c:70
@ FB_FACE_SANS_SERIF
Definition: font_freetype.c:60
@ FB_FACE_CURSIVE
Definition: font_freetype.c:68
@ FB_FACE_SANS_SERIF_BOLD
Definition: font_freetype.c:61
#define CACHE_MIN_SIZE
Definition: font_freetype.c:40
static FTC_ImageCache ft_image_cache
Definition: font_freetype.c:47
bool fb_font_finalise(void)
Finalise framebuffer font handling.
bool fb_font_init(void)
Initialise framebuffer font handling.
static FT_Error ft_face_requester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *face)
map cache manager handle to face id
Definition: font_freetype.c:82
struct gui_layout_table * framebuffer_layout_table
static FTC_CMapCache ft_cmap_cache
Definition: font_freetype.c:46
nserror fb_font_width(const plot_font_style_t *fstyle, const char *string, size_t length, int *width)
struct fb_faceid_s fb_faceid_t
#define BOLD_WEIGHT
Definition: font_freetype.c:42
static nserror fb_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.
int ft_load_type
Definition: font_freetype.c:49
static FT_Library library
Definition: font_freetype.c:44
static fb_faceid_t * fb_new_face(const char *option, const char *resname, const char *fontname)
create new framebuffer face and cause it to be loaded to check its ok
nserror fb_font_position(const plot_font_style_t *fstyle, const char *string, size_t length, int x, size_t *char_offset, int *actual_x)
static struct gui_layout_table layout_table
static void fb_fill_scalar(const plot_font_style_t *fstyle, FTC_Scaler srec)
fill freetype scalar
static FTC_Manager ft_cmanager
Definition: font_freetype.c:45
FT_Glyph fb_getglyph(const plot_font_style_t *fstyle, uint32_t ucs4)
#define FB_FACE_DEFAULT
Definition: font_freetype.c:74
Browser interfaces.
int browser_get_dpi(void)
Get the browser DPI.
Definition: browser.c:45
Interface to platform-specific layout operation table.
Interface to platform-specific utf8 operations.
Netsurf additional integer type formatting macros.
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
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
char * fontfile
Definition: font_freetype.c:53
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
User interface utf8 characterset conversion routines.
Definition: utf8.h:31
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_bool(OPTION)
Get the value of a boolean option.
Definition: nsoption.h:270
uint32_t utf8_to_ucs4(const char *s_in, size_t l)
Convert a UTF-8 multibyte sequence into a single UCS4 character.
Definition: utf8.c:41
size_t utf8_next(const char *s, size_t l, size_t o)
Find next legal UTF-8 char in string.
Definition: utf8.c:129
UTF-8 manipulation functions (interface).