507 lines
13 KiB
C
507 lines
13 KiB
C
/*
|
|
* Copyright 2012 Chris Young <chris@unsatisfactorysoftware.co.uk>
|
|
*
|
|
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
|
*
|
|
* NetSurf is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* NetSurf is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/** \file
|
|
* Font glyph scanner for Unicode substitutions.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <proto/diskfont.h>
|
|
#include <proto/dos.h>
|
|
#include <proto/exec.h>
|
|
#include <proto/intuition.h>
|
|
#include <diskfont/diskfonttag.h>
|
|
#include <diskfont/oterrors.h>
|
|
|
|
#include <proto/window.h>
|
|
#include <proto/layout.h>
|
|
#include <proto/fuelgauge.h>
|
|
#include <classes/window.h>
|
|
#include <gadgets/fuelgauge.h>
|
|
#include <gadgets/layout.h>
|
|
|
|
#include <reaction/reaction_macros.h>
|
|
|
|
#include "utils/nsoption.h"
|
|
#include "utils/log.h"
|
|
#include "utils/messages.h"
|
|
#include "desktop/mouse.h"
|
|
#include "desktop/gui_window.h"
|
|
|
|
#include "amiga/font_scan.h"
|
|
#include "amiga/gui.h"
|
|
#include "amiga/object.h"
|
|
#include "amiga/utf8.h"
|
|
|
|
enum {
|
|
FS_OID_MAIN = 0,
|
|
FS_GID_MAIN,
|
|
FS_GID_FONTS,
|
|
FS_GID_GLYPHS,
|
|
FS_GID_LAST
|
|
};
|
|
|
|
struct ami_font_scan_window {
|
|
struct Window *win;
|
|
Object *objects[FS_GID_LAST];
|
|
char *title;
|
|
char *glyphtext;
|
|
};
|
|
|
|
/**
|
|
* Lookup a font that contains a UTF-16 codepoint
|
|
*
|
|
* \param code UTF-16 codepoint to lookup
|
|
* \param glypharray an array of 0xffff lwc_string pointers
|
|
* \return font name or NULL
|
|
*/
|
|
const char *ami_font_scan_lookup(const uint16 *code, lwc_string **glypharray)
|
|
{
|
|
if(*code >= 0xd800 && *code <= 0xdbff) {
|
|
/* This is a multi-byte character, we don't support falback for these yet. */
|
|
return NULL;
|
|
}
|
|
|
|
if(glypharray[*code] == NULL) return NULL;
|
|
else return lwc_string_data(glypharray[*code]);
|
|
}
|
|
|
|
/**
|
|
* Open GUI to show font scanning progress
|
|
*
|
|
* \param fonts number of fonts that are being scanned
|
|
* \return pointer to a struct ami_font_scan_window
|
|
*/
|
|
static struct ami_font_scan_window *ami_font_scan_gui_open(int32 fonts)
|
|
{
|
|
struct ami_font_scan_window *fsw =
|
|
AllocVecTagList(sizeof(struct ami_font_scan_window), NULL);
|
|
|
|
if(fsw == NULL) return NULL;
|
|
|
|
fsw->title = ami_utf8_easy(messages_get("FontScanning"));
|
|
fsw->glyphtext = ami_utf8_easy(messages_get("FontGlyphs"));
|
|
|
|
fsw->objects[FS_OID_MAIN] = WindowObject,
|
|
WA_ScreenTitle, ami_gui_get_screen_title(),
|
|
WA_Title, fsw->title,
|
|
WA_Activate, TRUE,
|
|
WA_DepthGadget, TRUE,
|
|
WA_DragBar, TRUE,
|
|
WA_CloseGadget, FALSE,
|
|
WA_SizeGadget, TRUE,
|
|
WA_PubScreen, scrn,
|
|
WA_BusyPointer, TRUE,
|
|
WA_Width, 400,
|
|
WINDOW_UserData, fsw,
|
|
WINDOW_IconifyGadget, FALSE,
|
|
WINDOW_Position, WPOS_CENTERSCREEN,
|
|
WINDOW_LockHeight, TRUE,
|
|
WINDOW_ParentGroup, fsw->objects[FS_GID_MAIN] = VGroupObject,
|
|
LAYOUT_AddChild, fsw->objects[FS_GID_FONTS] = FuelGaugeObject,
|
|
GA_ID, FS_GID_FONTS,
|
|
GA_Text, fsw->title,
|
|
FUELGAUGE_Min, 0,
|
|
FUELGAUGE_Max, fonts,
|
|
FUELGAUGE_Level, 0,
|
|
FUELGAUGE_Ticks, 11,
|
|
FUELGAUGE_ShortTicks, TRUE,
|
|
FUELGAUGE_Percent, FALSE,
|
|
FUELGAUGE_Justification, FGJ_CENTER,
|
|
FuelGaugeEnd,
|
|
CHILD_NominalSize, TRUE,
|
|
CHILD_WeightedHeight, 0,
|
|
LAYOUT_AddChild, fsw->objects[FS_GID_GLYPHS] = FuelGaugeObject,
|
|
GA_ID, FS_GID_GLYPHS,
|
|
//GA_Text, "Glyphs",
|
|
FUELGAUGE_Min, 0x0000,
|
|
FUELGAUGE_Max, 0xffff,
|
|
FUELGAUGE_Level, 0,
|
|
FUELGAUGE_Ticks,11,
|
|
FUELGAUGE_ShortTicks, TRUE,
|
|
FUELGAUGE_Percent, FALSE,
|
|
FUELGAUGE_Justification, FGJ_CENTER,
|
|
FuelGaugeEnd,
|
|
CHILD_NominalSize, TRUE,
|
|
CHILD_WeightedHeight, 0,
|
|
EndGroup,
|
|
EndWindow;
|
|
|
|
fsw->win = (struct Window *)RA_OpenWindow(fsw->objects[FS_OID_MAIN]);
|
|
|
|
return fsw;
|
|
}
|
|
|
|
/**
|
|
* Update GUI showing font scanning progress
|
|
*
|
|
* \param fsw pointer to a struct ami_font_scan_window
|
|
* \param font current font being scanned
|
|
* \param font_num font number being scanned
|
|
* \param glyphs number of unique glyphs found
|
|
*/
|
|
static void ami_font_scan_gui_update(struct ami_font_scan_window *fsw, const char *font,
|
|
ULONG font_num, ULONG glyphs)
|
|
{
|
|
ULONG va[2];
|
|
|
|
if(fsw) {
|
|
RefreshSetGadgetAttrs((struct Gadget *)fsw->objects[FS_GID_FONTS],
|
|
fsw->win, NULL,
|
|
FUELGAUGE_Level, font_num,
|
|
GA_Text, font,
|
|
TAG_DONE);
|
|
|
|
va[0] = glyphs;
|
|
va[1] = 0;
|
|
|
|
RefreshSetGadgetAttrs((struct Gadget *)fsw->objects[FS_GID_GLYPHS],
|
|
fsw->win, NULL,
|
|
GA_Text, fsw->glyphtext,
|
|
FUELGAUGE_VarArgs, va,
|
|
FUELGAUGE_Level, glyphs,
|
|
TAG_DONE);
|
|
} else {
|
|
printf("Found %ld glyphs\n", glyphs);
|
|
printf("Scanning font #%ld (%s)...\n", font_num, font);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Close GUI showing font scanning progress
|
|
*
|
|
* \param fsw pointer to a struct ami_font_scan_window
|
|
*/
|
|
static void ami_font_scan_gui_close(struct ami_font_scan_window *fsw)
|
|
{
|
|
if(fsw) {
|
|
DisposeObject(fsw->objects[FS_OID_MAIN]);
|
|
ami_utf8_free(fsw->title);
|
|
FreeVec(fsw);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Scan a font for glyphs not present in glypharray.
|
|
*
|
|
* \param fontname font to scan
|
|
* \param glypharray an array of 0xffff lwc_string pointers
|
|
* \return number of new glyphs found
|
|
*/
|
|
static ULONG ami_font_scan_font(const char *fontname, lwc_string **glypharray)
|
|
{
|
|
struct OutlineFont *ofont;
|
|
struct MinList *widthlist;
|
|
struct GlyphWidthEntry *gwnode;
|
|
ULONG foundglyphs = 0;
|
|
lwc_error lerror;
|
|
ULONG unicoderanges = 0;
|
|
|
|
ofont = OpenOutlineFont(fontname, NULL, OFF_OPEN);
|
|
|
|
if(!ofont) return 0;
|
|
|
|
if(ESetInfo(&ofont->olf_EEngine,
|
|
OT_PointHeight, 10 * (1 << 16),
|
|
OT_GlyphCode, 0x0000,
|
|
OT_GlyphCode2, 0xffff,
|
|
TAG_END) == OTERR_Success)
|
|
{
|
|
if(EObtainInfo(&ofont->olf_EEngine,
|
|
OT_WidthList, &widthlist,
|
|
TAG_END) == 0)
|
|
{
|
|
gwnode = (struct GlyphWidthEntry *)GetHead((struct List *)widthlist);
|
|
do {
|
|
if(gwnode && (glypharray[gwnode->gwe_Code] == NULL)) {
|
|
lerror = lwc_intern_string(fontname, strlen(fontname) - 5, &glypharray[gwnode->gwe_Code]);
|
|
if(lerror != lwc_error_ok) continue;
|
|
foundglyphs++;
|
|
}
|
|
} while((gwnode = (struct GlyphWidthEntry *)GetSucc((struct Node *)gwnode)));
|
|
EReleaseInfo(&ofont->olf_EEngine,
|
|
OT_WidthList, widthlist,
|
|
TAG_END);
|
|
}
|
|
}
|
|
|
|
if(EObtainInfo(&ofont->olf_EEngine, OT_UnicodeRanges, &unicoderanges, TAG_END) == 0) {
|
|
if(unicoderanges & UCR_SURROGATES) LOG(("%s supports UTF-16 surrogates", fontname));
|
|
EReleaseInfo(&ofont->olf_EEngine,
|
|
OT_UnicodeRanges, unicoderanges,
|
|
TAG_END);
|
|
}
|
|
|
|
CloseOutlineFont(ofont, NULL);
|
|
|
|
return foundglyphs;
|
|
}
|
|
|
|
/**
|
|
* Scan all fonts for glyphs.
|
|
*
|
|
* \param list min list
|
|
* \param win scan window
|
|
* \param glypharray an array of 0xffff lwc_string pointers
|
|
* \return number of glyphs found
|
|
*/
|
|
static ULONG ami_font_scan_fonts(struct MinList *list,
|
|
struct ami_font_scan_window *win, lwc_string **glypharray)
|
|
{
|
|
ULONG found, total = 0, font_num = 0;
|
|
struct nsObject *node;
|
|
struct nsObject *nnode;
|
|
|
|
if(IsMinListEmpty(list)) return 0;
|
|
|
|
node = (struct nsObject *)GetHead((struct List *)list);
|
|
|
|
do {
|
|
nnode = (struct nsObject *)GetSucc((struct Node *)node);
|
|
ami_font_scan_gui_update(win, node->dtz_Node.ln_Name, font_num, total);
|
|
LOG(("Scanning %s", node->dtz_Node.ln_Name));
|
|
found = ami_font_scan_font(node->dtz_Node.ln_Name, glypharray);
|
|
total += found;
|
|
LOG(("Found %ld new glyphs (total = %ld)", found, total));
|
|
font_num++;
|
|
} while((node = nnode));
|
|
|
|
return total;
|
|
}
|
|
|
|
/**
|
|
* Add OS fonts to a list.
|
|
*
|
|
* \param list list to add font names to
|
|
* \return number of fonts found
|
|
*/
|
|
static ULONG ami_font_scan_list(struct MinList *list)
|
|
{
|
|
int afShortage, afSize = 100;
|
|
struct AvailFontsHeader *afh;
|
|
struct AvailFonts *af;
|
|
ULONG found = 0;
|
|
struct nsObject *node;
|
|
|
|
do {
|
|
if((afh = (struct AvailFontsHeader *)AllocVecTagList(afSize, NULL))) {
|
|
if(((afShortage = AvailFonts((STRPTR)afh, afSize,
|
|
AFF_DISK | AFF_OTAG | AFF_SCALED)))) {
|
|
FreeVec(afh);
|
|
afSize += afShortage;
|
|
}
|
|
} else {
|
|
/* out of memory, bail out */
|
|
return 0;
|
|
}
|
|
} while (afShortage);
|
|
|
|
if(afh) {
|
|
af = (struct AvailFonts *)&(afh[1]);
|
|
|
|
for(int i = 0; i < afh->afh_NumEntries; i++) {
|
|
if(af[i].af_Attr.ta_Style == FS_NORMAL) {
|
|
if(af[i].af_Attr.ta_Name != NULL) {
|
|
node = (struct nsObject *)FindIName((struct List *)list,
|
|
af[i].af_Attr.ta_Name);
|
|
if(node == NULL) {
|
|
node = AddObject(list, AMINS_UNKNOWN);
|
|
if(node) {
|
|
node->dtz_Node.ln_Name = strdup(af[i].af_Attr.ta_Name);
|
|
found++;
|
|
LOG(("Added %s", af[i].af_Attr.ta_Name));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
FreeVec(afh);
|
|
} else {
|
|
return 0;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/**
|
|
* Load a font glyph cache
|
|
*
|
|
* \param filename name of cache file to load
|
|
* \param glypharray an array of 0xffff lwc_string pointers
|
|
* \return number of glyphs loaded
|
|
*/
|
|
static ULONG ami_font_scan_load(const char *filename, lwc_string **glypharray)
|
|
{
|
|
ULONG found = 0;
|
|
BPTR fh = 0;
|
|
lwc_error lerror;
|
|
char buffer[256];
|
|
struct RDArgs *rargs = NULL;
|
|
CONST_STRPTR template = "CODE/A,FONT/A";
|
|
long rarray[] = {0,0};
|
|
|
|
enum {
|
|
A_CODE = 0,
|
|
A_FONT
|
|
};
|
|
|
|
rargs = AllocDosObjectTags(DOS_RDARGS, TAG_DONE);
|
|
|
|
if((fh = FOpen(filename, MODE_OLDFILE, 0))) {
|
|
LOG(("Loading font glyph cache from %s", filename));
|
|
|
|
while(FGets(fh, (STRPTR)&buffer, 256) != 0)
|
|
{
|
|
rargs->RDA_Source.CS_Buffer = (char *)&buffer;
|
|
rargs->RDA_Source.CS_Length = 256;
|
|
rargs->RDA_Source.CS_CurChr = 0;
|
|
|
|
rargs->RDA_DAList = NULL;
|
|
rargs->RDA_Buffer = NULL;
|
|
rargs->RDA_BufSiz = 0;
|
|
rargs->RDA_ExtHelp = NULL;
|
|
rargs->RDA_Flags = 0;
|
|
|
|
if(ReadArgs(template, rarray, rargs))
|
|
{
|
|
lerror = lwc_intern_string((const char *)rarray[A_FONT],
|
|
strlen((const char *)rarray[A_FONT]),
|
|
&glypharray[strtoul((const char *)rarray[A_CODE], NULL, 0)]);
|
|
if(lerror != lwc_error_ok) continue;
|
|
found++;
|
|
}
|
|
}
|
|
FClose(fh);
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
/**
|
|
* Save a font glyph cache
|
|
*
|
|
* \param filename name of cache file to save
|
|
* \param glypharray an array of 0xffff lwc_string pointers
|
|
*/
|
|
void ami_font_scan_save(const char *filename, lwc_string **glypharray)
|
|
{
|
|
ULONG i;
|
|
BPTR fh = 0;
|
|
|
|
if((fh = FOpen(filename, MODE_NEWFILE, 0))) {
|
|
LOG(("Writing font glyph cache to %s", filename));
|
|
FPrintf(fh, "; This file is auto-generated. To re-create the cache, delete this file.\n");
|
|
FPrintf(fh, "; This file is parsed using ReadArgs() with the following template:\n");
|
|
FPrintf(fh, "; CODE/A,FONT/A\n;\n");
|
|
|
|
for(i=0x0000; i<=0xffff; i++)
|
|
{
|
|
if(glypharray[i]) {
|
|
FPrintf(fh, "0x%04lx \"%s\"\n", i, lwc_string_data(glypharray[i]));
|
|
}
|
|
}
|
|
FClose(fh);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finalise the font glyph cache.
|
|
*
|
|
* \param glypharray an array of 0xffff lwc_string pointers to free
|
|
*/
|
|
void ami_font_scan_fini(lwc_string **glypharray)
|
|
{
|
|
ULONG i;
|
|
|
|
for(i=0x0000; i<=0xffff; i++)
|
|
{
|
|
if(glypharray[i]) {
|
|
lwc_string_unref(glypharray[i]);
|
|
glypharray[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialise the font glyph cache.
|
|
* Reads an existing file or, if not present, generates a new cache.
|
|
*
|
|
* \param filename cache file to attempt to read
|
|
* \param force_scan force re-creation of cache
|
|
* \param save save the cache
|
|
* \param glypharray an array of 0xffff lwc_string pointers
|
|
*/
|
|
void ami_font_scan_init(const char *filename, bool force_scan, bool save,
|
|
lwc_string **glypharray)
|
|
{
|
|
ULONG i, found = 0, entries = 0;
|
|
struct MinList *list;
|
|
struct nsObject *node;
|
|
char *unicode_font, *csv;
|
|
struct ami_font_scan_window *win = NULL;
|
|
|
|
/* Ensure array zeroed */
|
|
for(i=0x0000; i<=0xffff; i++)
|
|
glypharray[i] = NULL;
|
|
|
|
if(force_scan == false)
|
|
found = ami_font_scan_load(filename, glypharray);
|
|
|
|
if(found == 0) {
|
|
if((list = NewObjList())) {
|
|
|
|
/* add preferred fonts list */
|
|
if(nsoption_charp(font_unicode) &&
|
|
(csv = strdup(nsoption_charp(font_unicode))))
|
|
{
|
|
char *p;
|
|
|
|
while((p = strsep(&csv, ","))) {
|
|
asprintf(&unicode_font, "%s.font", p);
|
|
if(unicode_font != NULL) {
|
|
node = AddObject(list, AMINS_UNKNOWN);
|
|
if(node) node->dtz_Node.ln_Name = unicode_font;
|
|
entries++;
|
|
}
|
|
}
|
|
free(csv);
|
|
}
|
|
|
|
if(nsoption_bool(font_unicode_only) == false)
|
|
entries += ami_font_scan_list(list);
|
|
|
|
LOG(("Found %ld fonts", entries));
|
|
|
|
win = ami_font_scan_gui_open(entries);
|
|
found = ami_font_scan_fonts(list, win, glypharray);
|
|
ami_font_scan_gui_close(win);
|
|
|
|
FreeObjList(list);
|
|
|
|
if(save == true)
|
|
ami_font_scan_save(filename, glypharray);
|
|
}
|
|
}
|
|
|
|
LOG(("Initialised with %ld glyphs", found));
|
|
}
|
|
|
|
|