NetSurf
font_scan.c
Go to the documentation of this file.
1/*
2 * Copyright 2012 Chris Young <chris@unsatisfactorysoftware.co.uk>
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/** \file
20 * Font glyph scanner for Unicode substitutions.
21*/
22
23#include "amiga/os3support.h"
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29#ifndef __amigaos4__
30#include <proto/bullet.h>
31#endif
32#include <proto/diskfont.h>
33#include <proto/dos.h>
34#include <proto/exec.h>
35#include <proto/intuition.h>
36#include <diskfont/diskfonttag.h>
37#include <diskfont/oterrors.h>
38
39#include <proto/window.h>
40#include <proto/layout.h>
41#include <proto/fuelgauge.h>
42#include <classes/window.h>
43#include <gadgets/fuelgauge.h>
44#include <gadgets/layout.h>
45
46#include <reaction/reaction_macros.h>
47
48#include "utils/nsoption.h"
49#include "utils/log.h"
50#include "utils/messages.h"
51#include "netsurf/mouse.h"
52#include "netsurf/window.h"
53
54#include "amiga/font_scan.h"
55#include "amiga/gui.h"
56#include "amiga/libs.h"
57#include "amiga/object.h"
58#include "amiga/utf8.h"
59
60enum {
66};
67
69 struct Window *win;
71 char *title;
72 char *glyphtext;
73};
74
75/**
76 * Lookup a font that contains a UTF-16 codepoint
77 *
78 * \param code UTF-16 codepoint to lookup
79 * \param glypharray an array of 0xffff lwc_string pointers
80 * \return font name or NULL
81 */
82const char *ami_font_scan_lookup(const uint16 *code, lwc_string **glypharray)
83{
84 if(*code >= 0xd800 && *code <= 0xdbff) {
85 /* This is a multi-byte character, we don't support fallback for these yet. */
86 return NULL;
87 }
88
89 if(glypharray[*code] == NULL) return NULL;
90 else return lwc_string_data(glypharray[*code]);
91}
92
93/**
94 * Open GUI to show font scanning progress
95 *
96 * \param fonts number of fonts that are being scanned
97 * \return pointer to a struct ami_font_scan_window
98 */
100{
101 struct ami_font_scan_window *fsw =
102 malloc(sizeof(struct ami_font_scan_window));
103
104 if(fsw == NULL) return NULL;
105
106 fsw->title = ami_utf8_easy(messages_get("FontScanning"));
107 fsw->glyphtext = ami_utf8_easy(messages_get("FontGlyphs"));
108
110 WA_ScreenTitle, ami_gui_get_screen_title(),
111 WA_Title, fsw->title,
112 WA_Activate, TRUE,
113 WA_DepthGadget, TRUE,
114 WA_DragBar, TRUE,
115 WA_CloseGadget, FALSE,
116 WA_SizeGadget, TRUE,
117 WA_PubScreen, ami_gui_get_screen(),
118 WA_BusyPointer, TRUE,
119 WA_Width, 400,
120 WINDOW_UserData, fsw,
121 WINDOW_IconifyGadget, FALSE,
122 WINDOW_Position, WPOS_CENTERSCREEN,
123 WINDOW_LockHeight, TRUE,
124 WINDOW_ParentGroup, fsw->objects[FS_GID_MAIN] = LayoutVObj,
125 LAYOUT_AddChild, fsw->objects[FS_GID_FONTS] = FuelGaugeObj,
126 GA_ID, FS_GID_FONTS,
127 GA_Text, fsw->title,
128 FUELGAUGE_Min, 0,
129 FUELGAUGE_Max, fonts,
130 FUELGAUGE_Level, 0,
131 FUELGAUGE_Ticks, 11,
132 FUELGAUGE_ShortTicks, TRUE,
133 FUELGAUGE_Percent, FALSE,
134 FUELGAUGE_Justification, FGJ_CENTER,
135 FuelGaugeEnd,
136 CHILD_NominalSize, TRUE,
137 CHILD_WeightedHeight, 0,
138 LAYOUT_AddChild, fsw->objects[FS_GID_GLYPHS] = FuelGaugeObj,
139 GA_ID, FS_GID_GLYPHS,
140 //GA_Text, "Glyphs",
141 FUELGAUGE_Min, 0x0000,
142 FUELGAUGE_Max, 0xffff,
143 FUELGAUGE_Level, 0,
144 FUELGAUGE_Ticks,11,
145 FUELGAUGE_ShortTicks, TRUE,
146 FUELGAUGE_Percent, FALSE,
147 FUELGAUGE_Justification, FGJ_CENTER,
148 FuelGaugeEnd,
149 CHILD_NominalSize, TRUE,
150 CHILD_WeightedHeight, 0,
151 EndGroup,
152 EndWindow;
153
154 fsw->win = (struct Window *)RA_OpenWindow(fsw->objects[FS_OID_MAIN]);
155
156 return fsw;
157}
158
159/**
160 * Update GUI showing font scanning progress
161 *
162 * \param fsw pointer to a struct ami_font_scan_window
163 * \param font current font being scanned
164 * \param font_num font number being scanned
165 * \param glyphs number of unique glyphs found
166 */
167static void ami_font_scan_gui_update(struct ami_font_scan_window *fsw, const char *font,
168 ULONG font_num, ULONG glyphs)
169{
170 ULONG va[2];
171
172 if(fsw) {
173 RefreshSetGadgetAttrs((struct Gadget *)fsw->objects[FS_GID_FONTS],
174 fsw->win, NULL,
175 FUELGAUGE_Level, font_num,
176 GA_Text, font,
177 TAG_DONE);
178
179 va[0] = glyphs;
180 va[1] = 0;
181
182 RefreshSetGadgetAttrs((struct Gadget *)fsw->objects[FS_GID_GLYPHS],
183 fsw->win, NULL,
184 GA_Text, fsw->glyphtext,
185 FUELGAUGE_VarArgs, va,
186 FUELGAUGE_Level, glyphs,
187 TAG_DONE);
188 } else {
189 printf("Found %ld glyphs\n", glyphs);
190 printf("Scanning font #%ld (%s)...\n", font_num, font);
191 }
192}
193
194/**
195 * Close GUI showing font scanning progress
196 *
197 * \param fsw pointer to a struct ami_font_scan_window
198 */
200{
201 if(fsw) {
202 DisposeObject(fsw->objects[FS_OID_MAIN]);
203 ami_utf8_free(fsw->title);
204 free(fsw);
205 }
206}
207
208/**
209 * Scan a font for glyphs not present in glypharray.
210 *
211 * \param fontname font to scan
212 * \param glypharray an array of 0xffff lwc_string pointers
213 * \return number of new glyphs found
214 */
215static ULONG ami_font_scan_font(const char *fontname, lwc_string **glypharray)
216{
217 struct OutlineFont *ofont;
218 struct MinList *widthlist = NULL;
219 struct GlyphWidthEntry *gwnode;
220 ULONG foundglyphs = 0;
221 lwc_error lerror;
222 ULONG unicoderanges = 0;
223
224 ofont = OpenOutlineFont(fontname, NULL, OFF_OPEN);
225
226 if(!ofont) return 0;
227
228#ifndef __amigaos4__
229 struct BulletBase *BulletBase = ofont->BulletBase;
230#endif
231
233 OT_PointHeight, 10 * (1 << 16),
234 OT_GlyphCode, 0x0000,
235 OT_GlyphCode2, 0xffff,
236 TAG_END) == OTERR_Success)
237 {
239 OT_WidthList, &widthlist,
240 TAG_END) == 0)
241 {
242 gwnode = (struct GlyphWidthEntry *)GetHead((struct List *)widthlist);
243 do {
244 if(gwnode && (glypharray[gwnode->gwe_Code] == NULL)) {
245 lerror = lwc_intern_string(fontname, strlen(fontname), &glypharray[gwnode->gwe_Code]);
246 if(lerror != lwc_error_ok) continue;
247 foundglyphs++;
248 }
249 } while((gwnode = (struct GlyphWidthEntry *)GetSucc((struct Node *)gwnode)));
251 OT_WidthList, widthlist,
252 TAG_END);
253 }
254 }
255#ifdef __amigaos4__
256 if(EObtainInfo(AMI_OFONT_ENGINE, OT_UnicodeRanges, &unicoderanges, TAG_END) == 0) {
257 if(unicoderanges & UCR_SURROGATES) {
258 NSLOG(netsurf, INFO, "%s supports UTF-16 surrogates",
259 fontname);
260 if (nsoption_charp(font_surrogate) == NULL) {
261 nsoption_set_charp(font_surrogate, (char *)strdup(fontname));
262 }
263 }
265 OT_UnicodeRanges, unicoderanges,
266 TAG_END);
267 }
268#endif
269 CloseOutlineFont(ofont, NULL);
270
271 return foundglyphs;
272}
273
274/**
275 * Scan all fonts for glyphs.
276 *
277 * \param list min list
278 * \param win scan window
279 * \param glypharray an array of 0xffff lwc_string pointers
280 * \return number of glyphs found
281 */
282static ULONG ami_font_scan_fonts(struct MinList *list,
283 struct ami_font_scan_window *win, lwc_string **glypharray)
284{
285 ULONG found, total = 0, font_num = 0;
286 struct nsObject *node;
287 struct nsObject *nnode;
288
289 if(IsMinListEmpty(list)) return 0;
290
291 node = (struct nsObject *)GetHead((struct List *)list);
292
293 do {
294 nnode = (struct nsObject *)GetSucc((struct Node *)node);
295 ami_font_scan_gui_update(win, node->dtz_Node.ln_Name, font_num, total);
296 NSLOG(netsurf, INFO, "Scanning %s", node->dtz_Node.ln_Name);
297 found = ami_font_scan_font(node->dtz_Node.ln_Name, glypharray);
298 total += found;
299 NSLOG(netsurf, INFO, "Found %ld new glyphs (total = %ld)",
300 found, total);
301 font_num++;
302 } while((node = nnode));
303
304 return total;
305}
306
307/**
308 * Add OS fonts to a list.
309 *
310 * \param list list to add font names to
311 * \return number of fonts found
312 */
313static ULONG ami_font_scan_list(struct MinList *list)
314{
315 int afShortage, afSize = 100;
316 struct AvailFontsHeader *afh;
317 struct AvailFonts *af;
318 ULONG found = 0;
319 struct nsObject *node;
320
321 do {
322 if((afh = (struct AvailFontsHeader *)malloc(afSize))) {
323 if(((afShortage = AvailFonts((STRPTR)afh, afSize,
324 AFF_DISK | AFF_OTAG | AFF_SCALED)))) {
325 free(afh);
326 afSize += afShortage;
327 }
328 } else {
329 /* out of memory, bail out */
330 return 0;
331 }
332 } while (afShortage);
333
334 if(afh) {
335 af = (struct AvailFonts *)&(afh[1]);
336
337 for(int i = 0; i < afh->afh_NumEntries; i++) {
338 if(af[i].af_Attr.ta_Style == FS_NORMAL) {
339 if(af[i].af_Attr.ta_Name != NULL) {
340 char *p = 0;
341 if((p = strrchr(af[i].af_Attr.ta_Name, '.'))) *p = '\0';
342 node = (struct nsObject *)FindIName((struct List *)list,
343 af[i].af_Attr.ta_Name);
344 if(node == NULL) {
345 node = AddObject(list, AMINS_UNKNOWN);
346 if(node) {
347 node->dtz_Node.ln_Name = strdup(af[i].af_Attr.ta_Name);
348 found++;
349 NSLOG(netsurf, INFO,
350 "Added %s",
351 af[i].af_Attr.ta_Name);
352 }
353 }
354 }
355 }
356 }
357 free(afh);
358 } else {
359 return 0;
360 }
361 return found;
362}
363
364/**
365 * Load a font glyph cache
366 *
367 * \param filename name of cache file to load
368 * \param glypharray an array of 0xffff lwc_string pointers
369 * \return number of glyphs loaded
370 */
371static ULONG ami_font_scan_load(const char *filename, lwc_string **glypharray)
372{
373 ULONG found = 0;
374 BPTR fh = 0;
375 lwc_error lerror;
376 char buffer[256];
377 struct RDArgs *rargs = NULL;
378 CONST_STRPTR template = "CODE/A,FONT/A";
379 long rarray[] = {0,0};
380
381 enum {
382 A_CODE = 0,
383 A_FONT
384 };
385
386 rargs = AllocDosObjectTags(DOS_RDARGS, TAG_DONE);
387
388 if((fh = FOpen(filename, MODE_OLDFILE, 0))) {
389 NSLOG(netsurf, INFO, "Loading font glyph cache from %s",
390 filename);
391
392 while(FGets(fh, (STRPTR)&buffer, 256) != 0)
393 {
394 rargs->RDA_Source.CS_Buffer = (char *)&buffer;
395 rargs->RDA_Source.CS_Length = 256;
396 rargs->RDA_Source.CS_CurChr = 0;
397
398 rargs->RDA_DAList = NULL;
399 rargs->RDA_Buffer = NULL;
400 rargs->RDA_BufSiz = 0;
401 rargs->RDA_ExtHelp = NULL;
402 rargs->RDA_Flags = 0;
403
404 if(ReadArgs(template, rarray, rargs))
405 {
406 lerror = lwc_intern_string((const char *)rarray[A_FONT],
407 strlen((const char *)rarray[A_FONT]),
408 &glypharray[strtoul((const char *)rarray[A_CODE], NULL, 0)]);
409 if(lerror != lwc_error_ok) continue;
410 found++;
411 }
412 }
413 FClose(fh);
414 }
415
416 return found;
417}
418
419/**
420 * Save a font glyph cache
421 *
422 * \param filename name of cache file to save
423 * \param glypharray an array of 0xffff lwc_string pointers
424 */
425void ami_font_scan_save(const char *filename, lwc_string **glypharray)
426{
427 ULONG i;
428 BPTR fh = 0;
429
430 if((fh = FOpen(filename, MODE_NEWFILE, 0))) {
431 NSLOG(netsurf, INFO, "Writing font glyph cache to %s",
432 filename);
433 FPrintf(fh, "; This file is auto-generated. To re-create the cache, delete this file.\n");
434 FPrintf(fh, "; This file is parsed using ReadArgs() with the following template:\n");
435 FPrintf(fh, "; CODE/A,FONT/A\n;\n");
436
437 for(i=0x0000; i<=0xffff; i++)
438 {
439 if(glypharray[i]) {
440 FPrintf(fh, "0x%04lx \"%s\"\n", i, lwc_string_data(glypharray[i]));
441 }
442 }
443 FClose(fh);
444 }
445}
446
447/**
448 * Finalise the font glyph cache.
449 *
450 * \param glypharray an array of 0xffff lwc_string pointers to free
451 */
453{
454 ULONG i;
455
456 for(i=0x0000; i<=0xffff; i++)
457 {
458 if(glypharray[i]) {
459 lwc_string_unref(glypharray[i]);
460 glypharray[i] = NULL;
461 }
462 }
463}
464
465/**
466 * Initialise the font glyph cache.
467 * Reads an existing file or, if not present, generates a new cache.
468 *
469 * \param filename cache file to attempt to read
470 * \param force_scan force re-creation of cache
471 * \param save save the cache
472 * \param glypharray an array of 0xffff lwc_string pointers
473 */
474void ami_font_scan_init(const char *filename, bool force_scan, bool save,
475 lwc_string **glypharray)
476{
477 ULONG i, found = 0, entries = 0;
478 struct MinList *list;
479 struct nsObject *node;
480 char *csv;
481 struct ami_font_scan_window *win = NULL;
482
483 /* Ensure array zeroed */
484 for(i=0x0000; i<=0xffff; i++)
485 glypharray[i] = NULL;
486
487 if(force_scan == false)
488 found = ami_font_scan_load(filename, glypharray);
489
490 if(found == 0) {
491 NSLOG(netsurf, INFO, "Creating new font glyph cache");
492 if((list = NewObjList())) {
493
494 /* add preferred fonts list */
495 if(nsoption_charp(font_unicode) &&
496 (csv = strdup(nsoption_charp(font_unicode))))
497 {
498 char *p;
499
500 while((p = strsep(&csv, ","))) {
501 if(p != NULL) {
502 node = AddObject(list, AMINS_UNKNOWN);
503 if(node) node->dtz_Node.ln_Name = strdup(p);
504 entries++;
505 }
506 }
507 free(csv);
508 }
509
510 if(nsoption_bool(font_unicode_only) == false)
511 entries += ami_font_scan_list(list);
512
513 NSLOG(netsurf, INFO, "Found %ld fonts", entries);
514
515 win = ami_font_scan_gui_open(entries);
516 found = ami_font_scan_fonts(list, win, glypharray);
518
519 FreeObjList(list);
520
521 if(save == true)
523 }
524 }
525
526 NSLOG(netsurf, INFO, "Initialised with %ld glyphs", found);
527}
528
struct Screen * ami_gui_get_screen(void)
Get a pointer to the screen NetSurf is running on.
Definition: gui.c:403
STRPTR ami_gui_get_screen_title(void)
Get the string for NetSurf's screen titlebar.
Definition: gui.c:974
static osspriteop_area * buffer
The buffer characteristics.
Definition: buffer.c:55
static lwc_string * glypharray[0xffff+1]
Definition: font_bullet.c:135
static struct ami_font_scan_window * ami_font_scan_gui_open(int32 fonts)
Open GUI to show font scanning progress.
Definition: font_scan.c:99
const char * ami_font_scan_lookup(const uint16 *code, lwc_string **glypharray)
Lookup a font that contains a UTF-16 codepoint.
Definition: font_scan.c:82
static ULONG ami_font_scan_font(const char *fontname, lwc_string **glypharray)
Scan a font for glyphs not present in glypharray.
Definition: font_scan.c:215
void ami_font_scan_init(const char *filename, bool force_scan, bool save, lwc_string **glypharray)
Initialise the font glyph cache.
Definition: font_scan.c:474
static ULONG ami_font_scan_load(const char *filename, lwc_string **glypharray)
Load a font glyph cache.
Definition: font_scan.c:371
void ami_font_scan_save(const char *filename, lwc_string **glypharray)
Save a font glyph cache.
Definition: font_scan.c:425
static void ami_font_scan_gui_update(struct ami_font_scan_window *fsw, const char *font, ULONG font_num, ULONG glyphs)
Update GUI showing font scanning progress.
Definition: font_scan.c:167
static ULONG ami_font_scan_list(struct MinList *list)
Add OS fonts to a list.
Definition: font_scan.c:313
@ FS_OID_MAIN
Definition: font_scan.c:61
@ FS_GID_MAIN
Definition: font_scan.c:62
@ FS_GID_FONTS
Definition: font_scan.c:63
@ FS_GID_GLYPHS
Definition: font_scan.c:64
@ FS_GID_LAST
Definition: font_scan.c:65
void ami_font_scan_fini(lwc_string **glypharray)
Finalise the font glyph cache.
Definition: font_scan.c:452
static ULONG ami_font_scan_fonts(struct MinList *list, struct ami_font_scan_window *win, lwc_string **glypharray)
Scan all fonts for glyphs.
Definition: font_scan.c:282
static void ami_font_scan_gui_close(struct ami_font_scan_window *fsw)
Close GUI showing font scanning progress.
Definition: font_scan.c:199
#define AMI_OFONT_ENGINE
Definition: font_scan.h:29
struct MinList * NewObjList(void)
Definition: object.c:71
void FreeObjList(struct MinList *objlist)
Definition: object.c:117
struct nsObject * AddObject(struct MinList *objlist, ULONG otype)
Definition: object.c:77
@ AMINS_UNKNOWN
Definition: object.h:26
void ami_utf8_free(char *ptr)
Definition: utf8.c:104
char * ami_utf8_easy(const char *string)
Definition: utf8.c:109
Core mouse and pointer states.
Interface to platform-specific graphical user interface window operations.
#define FuelGaugeObj
Definition: libs.h:58
#define WindowObj
Definition: libs.h:77
#define LayoutVObj
Definition: libs.h:65
#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).
void CloseOutlineFont(struct OutlineFont *of, struct List *list)
Definition: os3support.c:323
struct OutlineFont * OpenOutlineFont(STRPTR fileName, struct List *list, ULONG flags)
Definition: os3support.c:204
char * strsep(char **s1, const char *s2)
Definition: os3support.c:171
struct Node * GetHead(struct List *list)
Definition: os3support.c:364
ULONG RefreshSetGadgetAttrs(struct Gadget *g, struct Window *w, struct Requester *r, Tag tag1,...)
Definition: os3support.c:429
struct Node * GetSucc(struct Node *node)
Definition: os3support.c:381
Minimal compatibility header for AmigaOS 3.
#define OFF_OPEN
Definition: os3support.h:125
#define ESetInfo
Definition: os3support.h:151
#define EObtainInfo
Definition: os3support.h:150
#define EReleaseInfo
Definition: os3support.h:149
#define AFF_OTAG
Definition: os3support.h:126
#define FOpen(A, B, C)
Definition: os3support.h:158
int32_t int32
Definition: os3support.h:183
uint16_t uint16
Definition: os3support.h:182
#define IsMinListEmpty(L)
Definition: os3support.h:54
#define FindIName
Definition: os3support.h:165
#define FClose(A)
Definition: os3support.h:159
Interface to utility string handling.
struct BulletBase * BulletBase
Definition: os3support.h:202
Object * objects[FS_GID_LAST]
Definition: font_scan.c:70
struct Window * win
Definition: font_scan.c:69
struct Node dtz_Node
Definition: object.h:44
Option reading and saving interface.
#define nsoption_charp(OPTION)
Get the value of a string option.
Definition: nsoption.h:297
#define nsoption_set_charp(OPTION, VALUE)
set string option in default table
Definition: nsoption.h:338
#define nsoption_bool(OPTION)
Get the value of a boolean option.
Definition: nsoption.h:270