summaryrefslogtreecommitdiff
path: root/frontends/amiga/font_scan.c
diff options
context:
space:
mode:
Diffstat (limited to 'frontends/amiga/font_scan.c')
-rw-r--r--frontends/amiga/font_scan.c522
1 files changed, 522 insertions, 0 deletions
diff --git a/frontends/amiga/font_scan.c b/frontends/amiga/font_scan.c
new file mode 100644
index 000000000..b65798d6e
--- /dev/null
+++ b/frontends/amiga/font_scan.c
@@ -0,0 +1,522 @@
+/*
+ * 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 "amiga/os3support.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __amigaos4__
+#include <proto/bullet.h>
+#endif
+#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/libs.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 fallback 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] = WindowObj,
+ 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] = LayoutVObj,
+ LAYOUT_AddChild, fsw->objects[FS_GID_FONTS] = FuelGaugeObj,
+ 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] = FuelGaugeObj,
+ 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 = NULL;
+ struct GlyphWidthEntry *gwnode;
+ ULONG foundglyphs = 0;
+ lwc_error lerror;
+ ULONG unicoderanges = 0;
+
+ ofont = OpenOutlineFont(fontname, NULL, OFF_OPEN);
+
+ if(!ofont) return 0;
+
+#ifndef __amigaos4__
+ struct BulletBase *BulletBase = ofont->BulletBase;
+#endif
+
+ if(ESetInfo(AMI_OFONT_ENGINE,
+ OT_PointHeight, 10 * (1 << 16),
+ OT_GlyphCode, 0x0000,
+ OT_GlyphCode2, 0xffff,
+ TAG_END) == OTERR_Success)
+ {
+ if(EObtainInfo(AMI_OFONT_ENGINE,
+ 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), &glypharray[gwnode->gwe_Code]);
+ if(lerror != lwc_error_ok) continue;
+ foundglyphs++;
+ }
+ } while((gwnode = (struct GlyphWidthEntry *)GetSucc((struct Node *)gwnode)));
+ EReleaseInfo(AMI_OFONT_ENGINE,
+ OT_WidthList, widthlist,
+ TAG_END);
+ }
+ }
+#ifdef __amigaos4__
+ if(EObtainInfo(AMI_OFONT_ENGINE, OT_UnicodeRanges, &unicoderanges, TAG_END) == 0) {
+ if(unicoderanges & UCR_SURROGATES) {
+ LOG("%s supports UTF-16 surrogates", fontname);
+ if (nsoption_charp(font_surrogate) == NULL) {
+ nsoption_set_charp(font_surrogate, (char *)strdup(fontname));
+ }
+ }
+ EReleaseInfo(AMI_OFONT_ENGINE,
+ OT_UnicodeRanges, unicoderanges,
+ TAG_END);
+ }
+#endif
+ 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) {
+ char *p = 0;
+ if((p = strrchr(af[i].af_Attr.ta_Name, '.'))) *p = '\0';
+ 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 *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) {
+ LOG("Creating new font glyph cache");
+ if((list = NewObjList())) {
+
+ /* add preferred fonts list */
+ if(nsoption_charp(font_unicode) &&
+ (csv = strdup(nsoption_charp(font_unicode))))
+ {
+ char *p;
+
+ while((p = strsep(&csv, ","))) {
+ if(p != NULL) {
+ node = AddObject(list, AMINS_UNKNOWN);
+ if(node) node->dtz_Node.ln_Name = strdup(p);
+ 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);
+}
+