Subversion Repositories Kolibri OS

Rev

Rev 4364 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3584 sourcerer 1
/*
2
 * Copyright 2005 James Bursa 
3
 *           2008 Vincent Sanders 
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 .
18
 */
19
 
20
#include 
21
#include 
22
 
23
#include 
24
#include FT_CACHE_H
25
 
26
#include "css/css.h"
27
#include "css/utils.h"
28
#include "render/font.h"
29
#include "utils/filepath.h"
30
#include "utils/utf8.h"
31
#include "utils/log.h"
32
#include "desktop/options.h"
33
 
34
#include "framebuffer/gui.h"
35
#include "framebuffer/font.h"
36
#include "framebuffer/findfile.h"
37
#include "font_freetype.h"
38
 
39
/* glyph cache minimum size */
40
#define CACHE_MIN_SIZE (100 * 1024)
41
 
42
#define BOLD_WEIGHT 700
43
 
5043 ashmew2 44
#ifdef DBG
45
#undef DBG
46
#endif
47
//#define DBG(s) __menuet__debug_out(s) /* For the debug messages in BOARD */
48
#define DBG(s) LOG((s))            /* So that we see debug in Netsurf's LOG files */
49
 
3584 sourcerer 50
static FT_Library library;
51
static FTC_Manager ft_cmanager;
52
static FTC_CMapCache ft_cmap_cache ;
53
static FTC_ImageCache ft_image_cache;
54
 
55
int ft_load_type;
56
 
57
/* cache manager faceID data to create freetype faceid on demand */
58
typedef struct fb_faceid_s {
59
        char *fontfile; /* path to font */
60
        int index; /* index of font */
61
        int cidx; /* character map index for unicode */
62
} fb_faceid_t;
63
 
64
 
65
enum fb_face_e {
66
	FB_FACE_SANS_SERIF = 0,
67
	FB_FACE_SANS_SERIF_BOLD = 1,
68
	FB_FACE_SANS_SERIF_ITALIC = 2,
69
	FB_FACE_SANS_SERIF_ITALIC_BOLD = 3,
70
	FB_FACE_SERIF = 4,
71
	FB_FACE_SERIF_BOLD = 5,
72
	FB_FACE_MONOSPACE = 6,
73
	FB_FACE_MONOSPACE_BOLD = 7,
74
	FB_FACE_CURSIVE = 8,
75
	FB_FACE_FANTASY = 9,
76
};
77
 
78
/* defines for accesing the faces */
79
#define FB_FACE_DEFAULT 0
80
#define FB_FACE_COUNT 10
81
 
82
static fb_faceid_t *fb_faces[FB_FACE_COUNT];
83
 
84
 
85
utf8_convert_ret utf8_to_local_encoding(const char *string,
86
				       size_t len,
87
				       char **result)
88
{
89
	return utf8_to_enc(string, "UTF-8", len, result);
90
}
91
 
92
utf8_convert_ret utf8_from_local_encoding(const char *string,
93
					size_t len,
94
					char **result)
95
{
96
	*result = malloc(len + 1);
97
	if (*result == NULL) {
98
		return UTF8_CONVERT_NOMEM;
99
	}
100
 
101
	memcpy(*result, string, len);
102
 
103
	(*result)[len] = '\0';
104
 
105
	return UTF8_CONVERT_OK;
106
}
107
 
108
/* map cache manager handle to face id */
109
static FT_Error ft_face_requester(FTC_FaceID face_id, FT_Library  library, FT_Pointer request_data, FT_Face *face )
110
{
111
        FT_Error error;
112
        fb_faceid_t *fb_face = (fb_faceid_t *)face_id;
113
        int cidx;
114
 
115
        error = FT_New_Face(library, fb_face->fontfile, fb_face->index, face);
116
        if (error) {
117
                LOG(("Could not find font (code %d)\n", error));
118
        } else {
119
 
120
                error = FT_Select_Charmap(*face, FT_ENCODING_UNICODE);
121
                if (error) {
122
                        LOG(("Could not select charmap (code %d)\n", error));
123
                } else {
124
                        for (cidx = 0; cidx < (*face)->num_charmaps; cidx++) {
125
                                if ((*face)->charmap == (*face)->charmaps[cidx]) {
126
                                        fb_face->cidx = cidx;
127
                                        break;
128
                                }
129
                        }
130
                }
131
        }
132
        LOG(("Loaded face from %s\n", fb_face->fontfile));
133
 
134
        return error;
135
}
136
 
137
/* create new framebuffer face and cause it to be loaded to check its ok */
138
static fb_faceid_t *
139
fb_new_face(const char *option, const char *resname, const char *fontname)
140
{
141
        fb_faceid_t *newf;
142
        FT_Error error;
143
        FT_Face aface;
144
	char buf[PATH_MAX];
145
 
146
        newf = calloc(1, sizeof(fb_faceid_t));
147
 
148
        if (option != NULL) {
149
                newf->fontfile = strdup(option);
150
        } else {
151
		filepath_sfind(respaths, buf, fontname);
152
                newf->fontfile = strdup(buf);
153
        }
154
 
155
        error = FTC_Manager_LookupFace(ft_cmanager, (FTC_FaceID)newf, &aface);
156
        if (error) {
157
                LOG(("Could not find font face %s (code %d)\n", fontname, error));
158
                free(newf);
159
                newf = NULL;
160
        }
161
 
162
        return newf;
163
}
164
 
165
#include 
166
 
167
/* initialise font handling */
168
bool fb_font_init(void)
169
{
170
        FT_Error error;
171
        FT_ULong max_cache_size;
172
        FT_UInt max_faces = 6;
173
	fb_faceid_t *fb_face;
174
 
175
LOG(("Freetype init..."));
176
 
177
	nsoptions.fb_font_monochrome = false;
178
	nsoptions.fb_font_cachesize = 2048;
179
	nsoptions.fb_face_sans_serif = NULL;
180
	nsoptions.fb_face_sans_serif_bold = NULL;
181
	nsoptions.fb_face_sans_serif_italic = NULL;
182
	nsoptions.fb_face_sans_serif_italic_bold = NULL;
183
	nsoptions.fb_face_serif = NULL;
184
	nsoptions.fb_face_serif_bold = NULL;
185
	nsoptions.fb_face_monospace = NULL;
186
	nsoptions.fb_face_monospace_bold = NULL;
187
	nsoptions.fb_face_cursive = NULL;
188
	nsoptions.fb_face_fantasy = NULL;
189
 
190
 
191
        /* freetype library initialise */
192
        error = FT_Init_FreeType( &library );
193
        if (error) {
194
                LOG(("Freetype could not initialised (code %d)\n", error));
195
                return false;
196
        }
197
 
198
        /* set the Glyph cache size up */
199
        max_cache_size = nsoption_int(fb_font_cachesize) * 1024;
200
 
201
	if (max_cache_size < CACHE_MIN_SIZE) {
202
		max_cache_size = CACHE_MIN_SIZE;
203
	}
204
 
205
LOG(("Freetype cache..."));
5043 ashmew2 206
DBG("Ft cache\n");
3584 sourcerer 207
        /* cache manager initialise */
208
        error = FTC_Manager_New(library,
209
                                max_faces,
210
                                0,
211
                                max_cache_size,
212
                                ft_face_requester,
213
                                NULL,
214
                                &ft_cmanager);
215
        if (error) {
216
                LOG(("Freetype could not initialise cache manager (code %d)\n", error));
217
                FT_Done_FreeType(library);
218
                return false;
219
        }
220
 
221
 
222
LOG(("Freetype map cache..."));
5043 ashmew2 223
DBG("Ft map cache\n");
3584 sourcerer 224
        error = FTC_CMapCache_New(ft_cmanager, &ft_cmap_cache);
225
 
226
        error = FTC_ImageCache_New(ft_cmanager, &ft_image_cache);
227
 
228
	/* need to obtain the generic font faces */
229
 
230
 
231
LOG(("Freetype load fonts..."));
5043 ashmew2 232
DBG("Ft load fonts\n");
3584 sourcerer 233
 
234
	/* Start with the sans serif font */
235
	fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif),
236
			      "sans_serif.ttf",
237
			      NETSURF_FB_FONT_SANS_SERIF);
238
	if (fb_face == NULL) {
239
 
240
LOG(("Freetype load fonts failed due SANS unavailable :(..."));
5043 ashmew2 241
DBG("Ft Z:(((\n");
3584 sourcerer 242
		/* The sans serif font is the default and must be found. */
243
                LOG(("Could not find the default font\n"));
244
                FTC_Manager_Done(ft_cmanager);
245
                FT_Done_FreeType(library);
246
                return false;
247
        } else {
248
		fb_faces[FB_FACE_SANS_SERIF] = fb_face;
249
	}
250
 
251
LOG(("Freetype loaded sans.."));
5043 ashmew2 252
DBG("Ft sans loaded:)\n");
3584 sourcerer 253
 
254
	/* Bold sans serif face */
255
	fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_bold),
256
                            "sans_serif_bold.ttf",
257
                            NETSURF_FB_FONT_SANS_SERIF_BOLD);
258
	if (fb_face == NULL) {
259
		/* seperate bold face unavailabe use the normal weight version */
260
		fb_faces[FB_FACE_SANS_SERIF_BOLD] = fb_faces[FB_FACE_SANS_SERIF];
261
	} else {
262
		fb_faces[FB_FACE_SANS_SERIF_BOLD] = fb_face;
263
	}
264
 
265
	/* Italic sans serif face */
266
	fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_italic),
267
			      "sans_serif_italic.ttf",
268
			      NETSURF_FB_FONT_SANS_SERIF_ITALIC);
269
	if (fb_face == NULL) {
270
		/* seperate italic face unavailabe use the normal weight version */
271
		fb_faces[FB_FACE_SANS_SERIF_ITALIC] = fb_faces[FB_FACE_SANS_SERIF];
272
	} else {
273
		fb_faces[FB_FACE_SANS_SERIF_ITALIC] = fb_face;
274
	}
275
 
276
	/* Bold italic sans serif face */
277
	fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_italic_bold),
278
			      "sans_serif_italic_bold.ttf",
279
			      NETSURF_FB_FONT_SANS_SERIF_ITALIC_BOLD);
280
	if (fb_face == NULL) {
281
		/* seperate italic face unavailabe use the normal weight version */
282
		fb_faces[FB_FACE_SANS_SERIF_ITALIC_BOLD] = fb_faces[FB_FACE_SANS_SERIF];
283
	} else {
284
		fb_faces[FB_FACE_SANS_SERIF_ITALIC_BOLD] = fb_face;
285
	}
286
 
287
	/* serif face */
288
	fb_face = fb_new_face(nsoption_charp(fb_face_serif),
289
                            "serif.ttf",
290
			      NETSURF_FB_FONT_SERIF);
291
	if (fb_face == NULL) {
292
		/* serif face unavailabe use the default */
293
		fb_faces[FB_FACE_SERIF] = fb_faces[FB_FACE_SANS_SERIF];
294
	} else {
295
		fb_faces[FB_FACE_SERIF] = fb_face;
296
	}
297
 
298
	/* bold serif face*/
299
	fb_face = fb_new_face(nsoption_charp(fb_face_serif_bold),
300
			      "serif_bold.ttf",
301
			      NETSURF_FB_FONT_SERIF_BOLD);
302
	if (fb_face == NULL) {
303
		/* bold serif face unavailabe use the normal weight */
304
		fb_faces[FB_FACE_SERIF_BOLD] = fb_faces[FB_FACE_SERIF];
305
	} else {
306
		fb_faces[FB_FACE_SERIF_BOLD] = fb_face;
307
	}
308
 
309
 
310
	/* monospace face */
311
	fb_face = fb_new_face(nsoption_charp(fb_face_monospace),
312
			      "monospace.ttf",
313
			      NETSURF_FB_FONT_MONOSPACE);
314
	if (fb_face == NULL) {
315
		/* serif face unavailabe use the default */
316
		fb_faces[FB_FACE_MONOSPACE] = fb_faces[FB_FACE_SANS_SERIF];
317
	} else {
318
		fb_faces[FB_FACE_MONOSPACE] = fb_face;
319
	}
320
 
321
	/* bold monospace face*/
322
	fb_face = fb_new_face(nsoption_charp(fb_face_monospace_bold),
323
			      "monospace_bold.ttf",
324
			      NETSURF_FB_FONT_MONOSPACE_BOLD);
325
	if (fb_face == NULL) {
326
		/* bold serif face unavailabe use the normal weight */
327
		fb_faces[FB_FACE_MONOSPACE_BOLD] = fb_faces[FB_FACE_MONOSPACE];
328
	} else {
329
		fb_faces[FB_FACE_MONOSPACE_BOLD] = fb_face;
330
	}
331
 
332
	/* cursive face */
333
	fb_face = fb_new_face(nsoption_charp(fb_face_cursive),
334
			      "cursive.ttf",
335
			      NETSURF_FB_FONT_CURSIVE);
336
	if (fb_face == NULL) {
337
		/* cursive face unavailabe use the default */
338
		fb_faces[FB_FACE_CURSIVE] = fb_faces[FB_FACE_SANS_SERIF];
339
	} else {
340
		fb_faces[FB_FACE_CURSIVE] = fb_face;
341
	}
342
 
343
	/* fantasy face */
344
	fb_face = fb_new_face(nsoption_charp(fb_face_fantasy),
345
			      "fantasy.ttf",
346
			      NETSURF_FB_FONT_FANTASY);
347
	if (fb_face == NULL) {
348
		/* fantasy face unavailabe use the default */
349
		fb_faces[FB_FACE_FANTASY] = fb_faces[FB_FACE_SANS_SERIF];
350
	} else {
351
		fb_faces[FB_FACE_FANTASY] = fb_face;
352
	}
353
 
354
LOG(("Freetype fonts ready..."));
5043 ashmew2 355
DBG("Ft ready :)\n");
3584 sourcerer 356
 
357
        /* set the default render mode */
358
        if (nsoption_bool(fb_font_monochrome) == true)
359
                ft_load_type = FT_LOAD_MONOCHROME; /* faster but less pretty */
360
        else
361
                ft_load_type = 0;
362
 
363
        return true;
364
}
365
 
366
bool fb_font_finalise(void)
367
{
368
        FTC_Manager_Done(ft_cmanager );
369
        FT_Done_FreeType(library);
370
        return true;
371
}
372
 
373
static void fb_fill_scalar(const plot_font_style_t *fstyle, FTC_Scaler srec)
374
{
375
        int selected_face = FB_FACE_DEFAULT;
376
 
377
	switch (fstyle->family) {
378
 
379
	case PLOT_FONT_FAMILY_SERIF:
380
		if (fstyle->weight >= BOLD_WEIGHT) {
381
                        selected_face = FB_FACE_SERIF_BOLD;
382
		} else {
383
                        selected_face = FB_FACE_SERIF;
384
                }
385
		break;
386
 
387
	case PLOT_FONT_FAMILY_MONOSPACE:
388
		if (fstyle->weight >= BOLD_WEIGHT) {
389
			selected_face = FB_FACE_MONOSPACE_BOLD;
390
		} else {
391
			selected_face = FB_FACE_MONOSPACE;
392
                }
393
		break;
394
 
395
	case PLOT_FONT_FAMILY_CURSIVE:
396
                selected_face = FB_FACE_CURSIVE;
397
		break;
398
 
399
	case PLOT_FONT_FAMILY_FANTASY:
400
                selected_face = FB_FACE_FANTASY;
401
		break;
402
 
403
	case PLOT_FONT_FAMILY_SANS_SERIF:
404
	default:
405
		if ((fstyle->flags & FONTF_ITALIC) ||
406
		    (fstyle->flags & FONTF_OBLIQUE)) {
407
			if (fstyle->weight >= BOLD_WEIGHT) {
408
                                selected_face = FB_FACE_SANS_SERIF_ITALIC_BOLD;
409
			} else {
410
                                selected_face = FB_FACE_SANS_SERIF_ITALIC;
411
			}
412
		} else {
413
			if (fstyle->weight >= BOLD_WEIGHT) {
414
                                selected_face = FB_FACE_SANS_SERIF_BOLD;
415
                        } else {
416
                                selected_face = FB_FACE_SANS_SERIF;
417
			}
418
                }
419
	}
420
 
421
        srec->face_id = (FTC_FaceID)fb_faces[selected_face];
422
 
423
	srec->width = srec->height = (fstyle->size * 64) / FONT_SIZE_SCALE;
424
	srec->pixel = 0;
425
 
426
	srec->x_res = srec->y_res = FIXTOINT(nscss_screen_dpi);
427
}
428
 
429
FT_Glyph fb_getglyph(const plot_font_style_t *fstyle, uint32_t ucs4)
430
{
431
        FT_UInt glyph_index;
432
        FTC_ScalerRec srec;
433
        FT_Glyph glyph;
434
        FT_Error error;
435
        fb_faceid_t *fb_face;
436
 
437
        fb_fill_scalar(fstyle, &srec);
438
 
439
        fb_face = (fb_faceid_t *)srec.face_id;
440
 
441
        glyph_index = FTC_CMapCache_Lookup(ft_cmap_cache, srec.face_id,
442
			fb_face->cidx, ucs4);
443
 
444
        error = FTC_ImageCache_LookupScaler(ft_image_cache,
445
                                            &srec,
446
                                            FT_LOAD_RENDER |
447
                                            FT_LOAD_FORCE_AUTOHINT |
448
                                            ft_load_type,
449
                                            glyph_index,
450
                                            &glyph,
451
                                            NULL);
452
	if (error != 0)
453
		return NULL;
454
 
455
        return glyph;
456
}
457
 
458
 
459
/**
460
 * Measure the width of a string.
461
 *
462
 * \param  fstyle  style for this text
463
 * \param  string  UTF-8 string to measure
464
 * \param  length  length of string
465
 * \param  width   updated to width of string[0..length)
466
 * \return  true on success, false on error and error reported
467
 */
468
static bool nsfont_width(const plot_font_style_t *fstyle,
469
                         const char *string, size_t length,
470
                         int *width)
471
{
472
        uint32_t ucs4;
473
        size_t nxtchr = 0;
474
        FT_Glyph glyph;
475
 
476
        *width = 0;
477
        while (nxtchr < length) {
478
                ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
479
                nxtchr = utf8_next(string, length, nxtchr);
480
 
481
                glyph = fb_getglyph(fstyle, ucs4);
482
                if (glyph == NULL)
483
                        continue;
484
 
485
                *width += glyph->advance.x >> 16;
486
        }
487
 
488
	return true;
489
}
490
 
491
/**
492
 * Find the position in a string where an x coordinate falls.
493
 *
494
 * \param  fstyle       style for this text
495
 * \param  string       UTF-8 string to measure
496
 * \param  length       length of string
497
 * \param  x            x coordinate to search for
498
 * \param  char_offset  updated to offset in string of actual_x, [0..length]
499
 * \param  actual_x     updated to x coordinate of character closest to x
500
 * \return  true on success, false on error and error reported
501
 */
502
 
503
static bool nsfont_position_in_string(const plot_font_style_t *fstyle,
504
		const char *string, size_t length,
505
		int x, size_t *char_offset, int *actual_x)
506
{
507
        uint32_t ucs4;
508
        size_t nxtchr = 0;
509
        FT_Glyph glyph;
510
        int prev_x = 0;
511
 
512
        *actual_x = 0;
513
        while (nxtchr < length) {
514
                ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
515
 
516
                glyph = fb_getglyph(fstyle, ucs4);
517
                if (glyph == NULL)
518
                        continue;
519
 
520
                *actual_x += glyph->advance.x >> 16;
521
                if (*actual_x > x)
522
                        break;
523
 
524
                prev_x = *actual_x;
525
                nxtchr = utf8_next(string, length, nxtchr);
526
        }
527
 
528
        /* choose nearest of previous and last x */
529
        if (abs(*actual_x - x) > abs(prev_x - x))
530
                *actual_x = prev_x;
531
 
532
        *char_offset = nxtchr;
533
	return true;
534
}
535
 
536
 
537
/**
538
 * Find where to split a string to make it fit a width.
539
 *
540
 * \param  fstyle       style for this text
541
 * \param  string       UTF-8 string to measure
542
 * \param  length       length of string
543
 * \param  x            width available
544
 * \param  char_offset  updated to offset in string of actual_x, [0..length]
545
 * \param  actual_x     updated to x coordinate of character closest to x
546
 * \return  true on success, false on error and error reported
547
 *
548
 * On exit, [char_offset == 0 ||
549
 *           string[char_offset] == ' ' ||
550
 *           char_offset == length]
551
 */
552
 
553
static bool nsfont_split(const plot_font_style_t *fstyle,
554
		const char *string, size_t length,
555
		int x, size_t *char_offset, int *actual_x)
556
{
557
        uint32_t ucs4;
558
        size_t nxtchr = 0;
559
        int last_space_x = 0;
560
        int last_space_idx = 0;
561
        FT_Glyph glyph;
562
 
563
        *actual_x = 0;
564
        while (nxtchr < length) {
565
                ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
566
 
567
                glyph = fb_getglyph(fstyle, ucs4);
568
                if (glyph == NULL)
569
                        continue;
570
 
571
                if (ucs4 == 0x20) {
572
                        last_space_x = *actual_x;
573
                        last_space_idx = nxtchr;
574
                }
575
 
576
                *actual_x += glyph->advance.x >> 16;
577
                if (*actual_x > x) {
578
                        /* string has exceeded available width return previous
579
                         * space
580
                         */
581
                        *actual_x = last_space_x;
582
                        *char_offset = last_space_idx;
583
                        return true;
584
                }
585
 
586
                nxtchr = utf8_next(string, length, nxtchr);
587
        }
588
 
589
        *char_offset = nxtchr;
590
 
591
	return true;
592
}
593
 
594
const struct font_functions nsfont = {
595
	nsfont_width,
596
	nsfont_position_in_string,
597
	nsfont_split
598
};
599
 
600
/*
601
 * Local Variables:
602
 * c-basic-offset:8
603
 * End:
604
 */