=> 57cbcc6e864abd368bde93154b3580147936201c
[1mdiff --git a/po/en.po b/po/en.po[m [1mindex 0d5707b1..cc6ea545 100644[m [1m--- a/po/en.po[m [1m+++ b/po/en.po[m [36m@@ -1921,6 +1921,9 @@[m [mmsgstr "Enable \"%s\""[m msgid "fontpack.disable"[m msgstr "Disable \"%s\""[m [m [32m+[m[32mmsgid "fontpack.export"[m [32m+[m[32mmsgstr "View fontpack.ini template"[m [32m+[m #, c-format[m msgid "fontpack.install"[m msgstr "Install \"%s\""[m [36m@@ -1943,3 +1946,21 @@[m [mmsgstr "Do you really want to permanently delete\nthe fontpack \"%s\"?"[m msgid "dlg.fontpack.delete"[m msgstr "Delete Fontpack"[m [m [32m+[m[32mmsgid "fontpack.help"[m [32m+[m[32mmsgstr "Lagrange fontpacks are ZIP archives that contain a set of font files and associated configuration parameters. Once installed, the fonts can be used for document content and the UI. The active fonts are selected using Preferences > Fonts."[m [32m+[m [32m+[m[32mmsgid "fontpack.install.ttf"[m [32m+[m[32mmsgstr "Install TrueType Font"[m [32m+[m [32m+[m[32mmsgid "fontpack.open.fontsdir"[m [32m+[m[32mmsgstr "Open User Fonts Directory"[m [32m+[m [32m+[m[32mmsgid "fontpack.open.aboutfonts"[m [32m+[m[32mmsgstr "Show Installed Fonts"[m [32m+[m [32m+[m[32mmsgid "truetype.help"[m [32m+[m[32mmsgstr "Lagrange attempts to load all individual TrueType files that are copied to the user fonts directory."[m [32m+[m [32m+[m[32mmsgid "truetype.help.installed"[m [32m+[m[32mmsgstr "This font is installed in the user fonts directory."[m [32m+[m [1mdiff --git a/res/lang/de.bin b/res/lang/de.bin[m [1mindex cf3250a5..e6b1bf85 100644[m Binary files a/res/lang/de.bin and b/res/lang/de.bin differ [1mdiff --git a/res/lang/en.bin b/res/lang/en.bin[m [1mindex ffdd4226..2ef67f55 100644[m Binary files a/res/lang/en.bin and b/res/lang/en.bin differ [1mdiff --git a/res/lang/eo.bin b/res/lang/eo.bin[m [1mindex 8ff3294e..89ec6fea 100644[m Binary files a/res/lang/eo.bin and b/res/lang/eo.bin differ [1mdiff --git a/res/lang/es.bin b/res/lang/es.bin[m [1mindex 810fe574..701f0478 100644[m Binary files a/res/lang/es.bin and b/res/lang/es.bin differ [1mdiff --git a/res/lang/es_MX.bin b/res/lang/es_MX.bin[m [1mindex 98234c64..3013590a 100644[m Binary files a/res/lang/es_MX.bin and b/res/lang/es_MX.bin differ [1mdiff --git a/res/lang/fi.bin b/res/lang/fi.bin[m [1mindex 8d307700..edc0df2b 100644[m Binary files a/res/lang/fi.bin and b/res/lang/fi.bin differ [1mdiff --git a/res/lang/fr.bin b/res/lang/fr.bin[m [1mindex 313e4043..c4b482de 100644[m Binary files a/res/lang/fr.bin and b/res/lang/fr.bin differ [1mdiff --git a/res/lang/gl.bin b/res/lang/gl.bin[m [1mindex cee70fde..6b1ca49a 100644[m Binary files a/res/lang/gl.bin and b/res/lang/gl.bin differ [1mdiff --git a/res/lang/ia.bin b/res/lang/ia.bin[m [1mindex e7e4a2ea..7f677aab 100644[m Binary files a/res/lang/ia.bin and b/res/lang/ia.bin differ [1mdiff --git a/res/lang/ie.bin b/res/lang/ie.bin[m [1mindex b29318ae..6488b20f 100644[m Binary files a/res/lang/ie.bin and b/res/lang/ie.bin differ [1mdiff --git a/res/lang/isv.bin b/res/lang/isv.bin[m [1mindex 80bc275f..0f783c31 100644[m Binary files a/res/lang/isv.bin and b/res/lang/isv.bin differ [1mdiff --git a/res/lang/pl.bin b/res/lang/pl.bin[m [1mindex 208e9e3c..e2d8bc6a 100644[m Binary files a/res/lang/pl.bin and b/res/lang/pl.bin differ [1mdiff --git a/res/lang/ru.bin b/res/lang/ru.bin[m [1mindex 2a8554a5..a0f75c6c 100644[m Binary files a/res/lang/ru.bin and b/res/lang/ru.bin differ [1mdiff --git a/res/lang/sk.bin b/res/lang/sk.bin[m [1mindex f5172ada..91bd1358 100644[m Binary files a/res/lang/sk.bin and b/res/lang/sk.bin differ [1mdiff --git a/res/lang/sr.bin b/res/lang/sr.bin[m [1mindex 14ca2240..55a289bd 100644[m Binary files a/res/lang/sr.bin and b/res/lang/sr.bin differ [1mdiff --git a/res/lang/tok.bin b/res/lang/tok.bin[m [1mindex fad37087..938f85d4 100644[m Binary files a/res/lang/tok.bin and b/res/lang/tok.bin differ [1mdiff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin[m [1mindex 2f8e6beb..c6beace1 100644[m Binary files a/res/lang/zh_Hans.bin and b/res/lang/zh_Hans.bin differ [1mdiff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin[m [1mindex 2b11cbd2..20128ba3 100644[m Binary files a/res/lang/zh_Hant.bin and b/res/lang/zh_Hant.bin differ [1mdiff --git a/src/app.c b/src/app.c[m [1mindex 485c2495..46856e22 100644[m [1m--- a/src/app.c[m [1m+++ b/src/app.c[m [36m@@ -836,6 +836,7 @@[m [mstatic void init_App_(iApp *d, int argc, char **argv) {[m loadPalette_Color(dataDir_App_());[m setThemePalette_Color(d->prefs.theme); /* default UI colors */[m loadPrefs_App_(d);[m [32m+[m[32m updateActive_Fonts();[m load_Keys(dataDir_App_());[m /* See if the user wants to override the window size. */ {[m iCommandLineArg *arg = iClob(checkArgument_CommandLine(&d->args, windowWidth_CommandLineOption));[m [36m@@ -3069,13 +3070,7 @@[m [miBool handleCommand_App(const char *cmd) {[m }[m else if (equal_Command(cmd, "fontpack.enable")) {[m const iString *packId = collect_String(suffix_Command(cmd, "id"));[m [31m- if (arg_Command(cmd)) {[m [31m- remove_StringSet(d->prefs.disabledFontPacks, packId);[m [31m- }[m [31m- else {[m [31m- insert_StringSet(d->prefs.disabledFontPacks, packId);[m [31m- }[m [31m- resetFonts_App();[m [32m+[m[32m enablePack_Fonts(packId, arg_Command(cmd));[m postCommand_App("navigate.reload");[m return iTrue;[m }[m [1mdiff --git a/src/fontpack.c b/src/fontpack.c[m [1mindex 226392bc..f22ab8dc 100644[m [1m--- a/src/fontpack.c[m [1m+++ b/src/fontpack.c[m [36m@@ -90,6 +90,14 @@[m [mstatic void load_FontFile_(iFontFile *d, const iBlock *data) {[m #endif[m }[m [m [32m+[m[32mstatic iBool detectMonospace_FontFile_(const iFontFile *d) {[m [32m+[m[32m int em, i, period;[m [32m+[m[32m stbtt_GetCodepointHMetrics(&d->stbInfo, 'M', &em, NULL);[m [32m+[m[32m stbtt_GetCodepointHMetrics(&d->stbInfo, 'i', &i, NULL);[m [32m+[m[32m stbtt_GetCodepointHMetrics(&d->stbInfo, '.', &period, NULL);[m [32m+[m[32m return em == i && em == period;[m [32m+[m[32m}[m [32m+[m static void unload_FontFile_(iFontFile *d) {[m #if defined(LAGRANGE_ENABLE_HARFBUZZ)[m /* HarfBuzz objects. */[m [36m@@ -300,6 +308,8 @@[m [mstatic iBlock *readFile_FontPack_(const iFontPack *d, const iString *path) {[m return data;[m }[m [m [32m+[m[32mstatic const char *styles_[max_FontStyle] = { "regular", "italic", "light", "semibold", "bold" };[m [32m+[m void handleIniKeyValue_FontPack_(void *context, const iString *table, const iString *key,[m const iTomlValue *value) {[m iFontPack *d = context;[m [36m@@ -359,9 +369,8 @@[m [mvoid handleIniKeyValue_FontPack_(void *context, const iString *table, const iStr[m ((int) number_TomlValue(value)) & 1);[m }[m else if (value->type == string_TomlType) {[m [31m- const char *styles[max_FontStyle] = { "regular", "italic", "light", "semibold", "bold" };[m [31m- iForIndices(i, styles) {[m [31m- if (!cmp_String(key, styles[i]) && !d->loadSpec->styles[i]) {[m [32m+[m[32m iForIndices(i, styles_) {[m [32m+[m[32m if (!cmp_String(key, styles_[i]) && !d->loadSpec->styles[i]) {[m iFontFile *ff = NULL;[m iString *fontFileId = concat_Path(d->loadPath, value->value.string);[m if (!(ff = findFile_Fonts_(&fonts_, fontFileId))) {[m [36m@@ -441,6 +450,7 @@[m [mvoid setLoadPath_FontPack(iFontPack *d, const iString *path) {[m /* Pack ID is based on the file name. */[m setRange_String(&d->id, baseName_Path(path));[m setRange_String(&d->id, withoutExtension_Path(&d->id));[m [32m+[m[32m replace_String(&d->id, " ", "-");[m }[m [m const iString *idFromUrl_FontPack(const iString *url) {[m [36m@@ -449,6 +459,7 @@[m [mconst iString *idFromUrl_FontPack(const iString *url) {[m init_Url(&parts, url);[m setRange_String(id, baseName_Path(collectNewRange_String(parts.path)));[m setRange_String(id, withoutExtension_Path(id));[m [32m+[m[32m replace_String(id, " ", "-");[m return collect_String(id);[m }[m [m [36m@@ -592,6 +603,49 @@[m [mvoid init_Fonts(const char *userDir) {[m }[m iRelease(f);[m }[m [32m+[m[32m /* Individual TrueType files in the user fonts directory. */ {[m [32m+[m[32m iForEach(DirFileInfo, entry, iClob(new_DirFileInfo(userFontsDirectory_Fonts_(d)))) {[m [32m+[m[32m const iString *entryPath = path_FileInfo(entry.value);[m [32m+[m[32m if (endsWithCase_String(entryPath, ".ttf")) {[m [32m+[m[32m iFile *f = new_File(entryPath);[m [32m+[m[32m iFontFile *font = NULL;[m [32m+[m[32m if (open_File(f, readOnly_FileMode)) {[m [32m+[m[32m iBlock *data = readAll_File(f);[m [32m+[m[32m font = new_FontFile();[m [32m+[m[32m load_FontFile_(font, data);[m [32m+[m[32m set_String(&font->id, entryPath);[m [32m+[m[32m pushBack_ObjectList(fonts_.files, font); /* centralized ownership */[m [32m+[m[32m iRelease(font);[m [32m+[m[32m delete_Block(data);[m [32m+[m[32m }[m [32m+[m[32m iRelease(f);[m [32m+[m[32m if (!font) {[m [32m+[m[32m fprintf(stderr, "[fonts] failed to load: %s\n", cstr_String(entryPath));[m [32m+[m[32m continue;[m [32m+[m[32m }[m [32m+[m[32m iFontPack *pack = new_FontPack();[m [32m+[m[32m setStandalone_FontPack(pack, iTrue);[m[41m [m [32m+[m[32m iFontSpec *spec = new_FontSpec();[m [32m+[m[32m spec->flags |= user_FontSpecFlag;[m [32m+[m[32m if (detectMonospace_FontFile_(font)) {[m [32m+[m[32m spec->flags |= monospace_FontSpecFlag;[m [32m+[m[32m }[m [32m+[m[32m setRange_String(&spec->id, baseName_Path(collect_String(lower_String(&font->id))));[m [32m+[m[32m setRange_String(&spec->id, withoutExtension_Path(&spec->id));[m[41m [m [32m+[m[32m replace_String(&spec->id, " ", "-");[m [32m+[m[32m setRange_String(&spec->name, baseName_Path(&font->id));[m [32m+[m[32m setRange_String(&spec->name, withoutExtension_Path(&spec->name));[m [32m+[m[32m set_String(&spec->sourcePath, entryPath);[m [32m+[m[32m iForIndices(j, spec->styles) {[m [32m+[m[32m spec->styles[j] = ref_Object(font);[m [32m+[m[32m }[m [32m+[m[32m pushBack_PtrArray(&pack->fonts, spec);[m [32m+[m[32m set_String(&pack->id, &spec->id);[m [32m+[m[32m pack->loadPath = copy_String(entryPath);[m [32m+[m[32m pushBack_PtrArray(&d->packs, pack);[m [32m+[m[32m }[m [32m+[m[32m }[m [32m+[m[32m }[m[41m [m sortSpecs_Fonts_(d);[m }[m [m [36m@@ -679,7 +733,7 @@[m [miString *infoText_FontPack(const iFontPack *d) {[m return str;[m }[m [m [31m-const iArray *actions_FontPack(const iFontPack *d) {[m [32m+[m[32mconst iArray *actions_FontPack(const iFontPack *d, iBool showInstalled) {[m iArray *items = new_Array(sizeof(iMenuItem));[m const iFontPackId fp = id_FontPack(d);[m const char *fpId = cstr_String(fp.id);[m [36m@@ -687,33 +741,44 @@[m [mconst iArray *actions_FontPack(const iFontPack *d) {[m const iBool isEnabled = !isDisabled_FontPack(d);[m if (isInstalled_Fonts(fpId)) {[m if (d->version > installed->version) {[m [31m- pushBack_Array(items, &(iMenuItem){[m [31m- format_Lang(add_Icon " ${fontpack.upgrade}", fpId, d->version),[m [31m- SDLK_RETURN, 0, "fontpack.install"[m [31m- });[m [32m+[m[32m pushBack_Array([m [32m+[m[32m items,[m [32m+[m[32m &(iMenuItem){ format_Lang(add_Icon " ${fontpack.upgrade}", fpId, d->version),[m [32m+[m[32m SDLK_RETURN,[m [32m+[m[32m 0,[m [32m+[m[32m "fontpack.install" });[m }[m [31m- pushBack_Array(items, &(iMenuItem){[m [31m- format_Lang(isEnabled ? close_Icon " ${fontpack.disable}"[m [31m- : leftArrowhead_Icon " ${fontpack.enable}", fpId), 0, 0,[m [31m- format_CStr("fontpack.enable arg:%d id:%s", !isEnabled, fpId) });[m [31m- if (!d->isReadOnly && installed->loadPath && d->loadPath &&[m [32m+[m[32m pushBack_Array([m [32m+[m[32m items,[m [32m+[m[32m &(iMenuItem){ format_Lang(isEnabled ? close_Icon " ${fontpack.disable}"[m [32m+[m[32m : leftArrowhead_Icon " ${fontpack.enable}",[m [32m+[m[32m fpId),[m [32m+[m[32m 0,[m [32m+[m[32m 0,[m [32m+[m[32m format_CStr("fontpack.enable arg:%d id:%s", !isEnabled, fpId) });[m [32m+[m[32m if (!d->isReadOnly && !d->isStandalone && installed->loadPath && d->loadPath &&[m !cmpString_String(installed->loadPath, d->loadPath)) {[m [31m- pushBack_Array(items, &(iMenuItem){[m [31m- format_Lang(delete_Icon " ${fontpack.delete}", fpId), 0, 0,[m [31m- format_CStr("fontpack.delete id:%s", fpId) });[m [32m+[m[32m pushBack_Array(items,[m [32m+[m[32m &(iMenuItem){ format_Lang(delete_Icon " ${fontpack.delete}", fpId),[m [32m+[m[32m 0,[m [32m+[m[32m 0,[m [32m+[m[32m format_CStr("fontpack.delete id:%s", fpId) });[m }[m }[m else if (d->isStandalone) {[m [31m- pushBack_Array(items, &(iMenuItem){[m [31m- format_Lang(add_Icon " ${fontpack.install}", fpId),[m [31m- SDLK_RETURN, 0, "fontpack.install"[m [31m- });[m [31m- pushBack_Array(items, &(iMenuItem){[m [31m- download_Icon " " saveToDownloads_Label,[m [31m- 0,[m [31m- 0,[m [31m- "document.save"[m [31m- });[m [32m+[m[32m pushBack_Array(items,[m [32m+[m[32m &(iMenuItem){ format_Lang(add_Icon " ${fontpack.install}", fpId),[m [32m+[m[32m SDLK_RETURN,[m [32m+[m[32m 0,[m [32m+[m[32m "fontpack.install" });[m [32m+[m[32m pushBack_Array([m [32m+[m[32m items, &(iMenuItem){ download_Icon " " saveToDownloads_Label, 0, 0, "document.save" });[m [32m+[m[32m }[m [32m+[m[32m if (showInstalled) {[m [32m+[m[32m pushBack_Array([m [32m+[m[32m items,[m [32m+[m[32m &(iMenuItem){[m [32m+[m[32m fontpack_Icon " ${fontpack.open.aboutfonts}", 0, 0, "!open url:about:fonts" });[m }[m return collect_Array(items);[m }[m [36m@@ -722,8 +787,64 @@[m [miBool isDisabled_FontPack(const iFontPack *d) {[m return contains_StringSet(prefs_App()->disabledFontPacks, &d->id);[m }[m [m [31m-const iString *infoPage_Fonts(void) {[m [32m+[m[32mconst iPtrArray *disabledSpecs_Fonts_(const iFonts *d) {[m [32m+[m[32m iPtrArray *list = collectNew_PtrArray();[m [32m+[m[32m iConstForEach(PtrArray, i, &d->packs) {[m [32m+[m[32m const iFontPack *pack = i.ptr;[m [32m+[m[32m if (isDisabled_FontPack(pack)) {[m [32m+[m[32m iConstForEach(PtrArray, j, &pack->fonts) {[m [32m+[m[32m pushBack_PtrArray(list, j.ptr);[m [32m+[m[32m }[m [32m+[m[32m }[m [32m+[m[32m }[m [32m+[m[32m return list;[m [32m+[m[32m}[m [32m+[m [32m+[m[32mstatic const char *boolStr_(int value) {[m [32m+[m[32m return value ? "true" : "false";[m [32m+[m[32m}[m [32m+[m [32m+[m[32mstatic const iString *exportFontPackIni_Fonts_(const iFonts *d, const iRangecc packId) {[m [32m+[m[32m iString *str = collectNew_String();[m [32m+[m[32m const iFontPack *pack = pack_Fonts(cstr_Rangecc(packId));[m [32m+[m[32m if (!pack) {[m [32m+[m[32m appendFormat_String(str, "Fontpack \"%s\" not found.\n", cstr_Rangecc(packId));[m [32m+[m[32m return str;[m [32m+[m[32m }[m [32m+[m[32m const iFontPackId fp = id_FontPack(pack);[m [32m+[m[32m appendCStr_String(str, "To create a fontpack, add this fontpack.ini into a ZIP archive whose "[m [32m+[m[32m "name has the .fontpack file extension.\n```Fontpack configuration\n");[m [32m+[m[32m appendFormat_String(str, "version = %d\n", fp.version);[m [32m+[m[32m iConstForEach(PtrArray, i, &pack->fonts) {[m [32m+[m[32m const iFontSpec *spec = i.ptr;[m [32m+[m[32m appendFormat_String(str, "\n[%s]\n", cstr_String(&spec->id));[m [32m+[m[32m appendFormat_String(str, "name = \"%s\"\n", cstrCollect_String(quote_String(&spec->name, iFalse)));[m [32m+[m[32m appendFormat_String(str, "priority = %d\n", spec->priority);[m [32m+[m[32m appendFormat_String(str, "override = %s\n", boolStr_(spec->flags & override_FontSpecFlag));[m [32m+[m[32m appendFormat_String(str, "monospace = %s\n", boolStr_(spec->flags & monospace_FontSpecFlag));[m [32m+[m[32m appendFormat_String(str, "auxiliary = %s\n", boolStr_(spec->flags & auxiliary_FontSpecFlag));[m [32m+[m[32m appendFormat_String(str, "allowspace = %s\n", boolStr_(spec->flags & allowSpacePunct_FontSpecFlag));[m [32m+[m[32m for (int j = 0; j < 2; ++j) {[m [32m+[m[32m const char *scope = (j == 0 ? "ui" : "doc");[m [32m+[m[32m appendFormat_String(str, "%s.height = %.3f\n", scope, spec->heightScale[j]);[m [32m+[m[32m appendFormat_String(str, "%s.glyphscale = %.3f\n", scope, spec->glyphScale[j]);[m [32m+[m[32m appendFormat_String(str, "%s.voffset = %.3f\n", scope, spec->vertOffsetScale[j]);[m [32m+[m[32m }[m [32m+[m[32m iForIndices(j, styles_) {[m [32m+[m[32m appendFormat_String(str, "%s = \"%s\"\n", styles_[j],[m [32m+[m[32m cstrCollect_String(quote_String(&spec->sourcePath, iFalse)));[m [32m+[m[32m }[m [32m+[m[32m }[m [32m+[m[32m appendCStr_String(str, "```\n");[m [32m+[m[32m return str;[m [32m+[m[32m}[m [32m+[m [32m+[m[32mconst iString *infoPage_Fonts(iRangecc query) {[m iFonts *d = &fonts_;[m [32m+[m[32m if (!isEmpty_Range(&query)) {[m [32m+[m[32m query.start++; /* skip the ? */[m [32m+[m[32m return exportFontPackIni_Fonts_(d, query);[m [32m+[m[32m }[m iString *str = collectNewCStr_String("# ${heading.fontpack.meta}\n"[m "=> gemini://skyjake.fi/fonts Download more fonts\n"[m "=> about:command?!open%20newtab:1%20gotoheading:1%20url:about:help Using fonts in Lagrange\n"[m [36m@@ -734,7 +855,7 @@[m [mconst iString *infoPage_Fonts(void) {[m iString *currentSourcePath = collectNew_String();[m for (int group = 0; group < 2; group++) {[m iBool isFirst = iTrue;[m [31m- iConstForEach(PtrArray, i, specsByPack) {[m [32m+[m[32m iConstForEach(PtrArray, i, group == 0 ? specsByPack : disabledSpecs_Fonts_(d)) {[m const iFontSpec *spec = i.ptr;[m if (isEmpty_String(&spec->sourcePath)) {[m continue; /* built-in font */[m [36m@@ -753,15 +874,22 @@[m [mconst iString *infoPage_Fonts(void) {[m isFirst = iFalse;[m }[m const iString *packId = id_FontPack(pack).id;[m [31m- appendFormat_String(str, "### %s\n=> %s ${fontpack.meta.viewfile}\n",[m [32m+[m[32m appendFormat_String(str, "### %s\n",[m isEmpty_String(packId) ? "fonts.ini" :[m [31m- cstr_String(packId),[m [31m- cstrCollect_String(makeFileUrl_String(&spec->sourcePath)));[m [32m+[m[32m cstr_String(packId));[m append_String(str, collect_String(infoText_FontPack(pack)));[m [31m- iConstForEach(Array, a, actions_FontPack(pack)) {[m [32m+[m[32m appendFormat_String(str, "=> %s ${fontpack.meta.viewfile}\n",[m [32m+[m[32m cstrCollect_String(makeFileUrl_String(&spec->sourcePath)));[m [32m+[m[32m if (pack->isStandalone) {[m [32m+[m[32m appendFormat_String(str, "=> about:fonts?%s ${fontpack.export}\n",[m [32m+[m[32m cstr_String(packId));[m [32m+[m[32m }[m [32m+[m[32m iConstForEach(Array, a, actions_FontPack(pack, iFalse)) {[m const iMenuItem *item = a.value;[m [31m- appendFormat_String(str, "=> about:command?%s %s\n",[m [31m- cstr_String(withSpacesEncoded_String(collectNewCStr_String(item->command))),[m [32m+[m[32m appendFormat_String(str,[m [32m+[m[32m "=> about:command?%s %s\n",[m [32m+[m[32m cstr_String(withSpacesEncoded_String([m [32m+[m[32m collectNewCStr_String(item->command))),[m item->label);[m }[m }[m [36m@@ -824,5 +952,32 @@[m [mvoid install_Fonts(const iString *packId, const iBlock *data) {[m reload_Fonts();[m }[m [m [32m+[m[32mvoid installFontFile_Fonts(const iString *fileName, const iBlock *data) {[m [32m+[m[32m iFonts *d = &fonts_;[m [32m+[m[32m iFile *f = new_File(collect_String(concat_Path(userFontsDirectory_Fonts_(d), fileName)));[m [32m+[m[32m if (open_File(f, writeOnly_FileMode)) {[m [32m+[m[32m write_File(f, data);[m [32m+[m[32m }[m [32m+[m[32m iRelease(f);[m [32m+[m[32m reload_Fonts();[m [32m+[m[32m}[m [32m+[m [32m+[m[32mvoid enablePack_Fonts(const iString *packId, iBool enable) {[m [32m+[m[32m iFonts *d = &fonts_;[m [32m+[m[32m if (enable) {[m [32m+[m[32m remove_StringSet(prefs_App()->disabledFontPacks, packId);[m [32m+[m[32m }[m [32m+[m[32m else {[m [32m+[m[32m insert_StringSet(prefs_App()->disabledFontPacks, packId);[m [32m+[m[32m }[m [32m+[m[32m updateActive_Fonts();[m [32m+[m[32m resetFonts_App();[m [32m+[m[32m invalidate_Window(get_MainWindow());[m [32m+[m[32m}[m [32m+[m [32m+[m[32mvoid updateActive_Fonts(void) {[m [32m+[m[32m sortSpecs_Fonts_(&fonts_);[m [32m+[m[32m}[m [32m+[m iDefineClass(FontFile)[m [m [1mdiff --git a/src/fontpack.h b/src/fontpack.h[m [1mindex fb8d757e..5d592822 100644[m [1m--- a/src/fontpack.h[m [1m+++ b/src/fontpack.h[m [36m@@ -110,22 +110,23 @@[m [miDeclareType(FontSpec)[m iDeclareTypeConstruction(FontSpec)[m [m enum iFontSpecFlags {[m [31m- override_FontSpecFlag = iBit(1),[m [31m- monospace_FontSpecFlag = iBit(2), /* can be used in preformatted content */[m [31m- auxiliary_FontSpecFlag = iBit(3), /* only used for looking up glyphs missing from other fonts */[m [31m- allowSpacePunct_FontSpecFlag = iBit(4), /* space/punctuation glyphs from this auxiliary font can be used */[m [32m+[m[32m user_FontSpecFlag = iBit(1), /* user's standalone font, can be used for anything */[m [32m+[m[32m override_FontSpecFlag = iBit(2),[m [32m+[m[32m monospace_FontSpecFlag = iBit(3), /* can be used in preformatted content */[m [32m+[m[32m auxiliary_FontSpecFlag = iBit(4), /* only used for looking up glyphs missing from other fonts */[m [32m+[m[32m allowSpacePunct_FontSpecFlag = iBit(5), /* space/punctuation glyphs from this auxiliary font can be used */[m fixNunitoKerning_FontSpecFlag = iBit(31), /* manual hardcoded kerning tweaks for Nunito */[m };[m [m struct Impl_FontSpec {[m [31m- iString id; /* unique ID */[m [31m- iString name; /* human-readable label */[m [31m- iString sourcePath; /* file where the path was loaded, could be a .fontpack */[m [31m- int flags;[m [31m- int priority;[m [31m- float heightScale[2]; /* overall height scaling; ui, document */[m [31m- float glyphScale[2]; /* ui, document */[m [31m- float vertOffsetScale[2]; /* ui, document */[m [32m+[m[32m iString id; /* unique ID */[m [32m+[m[32m iString name; /* human-readable label */[m [32m+[m[32m iString sourcePath; /* file where the path was loaded, could be a .fontpack */[m [32m+[m[32m int flags;[m [32m+[m[32m int priority;[m [32m+[m[32m float heightScale[2]; /* overall height scaling; ui, document */[m [32m+[m[32m float glyphScale[2]; /* ui, document */[m [32m+[m[32m float vertOffsetScale[2]; /* ui, document */[m const iFontFile *styles[max_FontStyle];[m };[m [m [36m@@ -158,25 +159,26 @@[m [miBool isDisabled_FontPack (const iFontPack *);[m iBool isReadOnly_FontPack (const iFontPack *);[m const iPtrArray * listSpecs_FontPack (const iFontPack *);[m iString * infoText_FontPack (const iFontPack *);[m [31m-const iArray * actions_FontPack (const iFontPack *);[m [32m+[m[32mconst iArray * actions_FontPack (const iFontPack *, iBool showInstalled);[m [m const iString * idFromUrl_FontPack (const iString *url);[m [m /*----------------------------------------------------------------------------------------------*/[m [m [31m-iDeclareType(GmDocument)[m [31m-[m void init_Fonts (const char *userDir);[m void deinit_Fonts (void);[m [m [32m+[m[32mvoid enablePack_Fonts (const iString *packId, iBool enable);[m [32m+[m[32mvoid updateActive_Fonts (void);[m const iFontPack * pack_Fonts (const char *packId);[m const iFontPack * packByPath_Fonts (const iString *path);[m const iFontSpec * findSpec_Fonts (const char *fontId);[m const iPtrArray * listPacks_Fonts (void);[m const iPtrArray * listSpecs_Fonts (iBool (*filterFunc)(const iFontSpec *));[m const iPtrArray * listSpecsByPriority_Fonts (void);[m [31m-const iString * infoPage_Fonts (void);[m [32m+[m[32mconst iString * infoPage_Fonts (iRangecc query);[m void install_Fonts (const iString *fontId, const iBlock *data);[m [32m+[m[32mvoid installFontFile_Fonts (const iString *fileName, const iBlock *data);[m void reload_Fonts (void);[m [m iLocalDef iBool isInstalled_Fonts(const char *packId) {[m [1mdiff --git a/src/gmdocument.c b/src/gmdocument.c[m [1mindex 23ebace3..b0851fec 100644[m [1m--- a/src/gmdocument.c[m [1m+++ b/src/gmdocument.c[m [36m@@ -479,7 +479,7 @@[m [mstatic void commit_RunTypesetter_(iRunTypesetter *d, iGmDocument *doc) {[m [m static const int maxLedeLines_ = 10;[m [m [31m-static int applyAttributes_RunTypesetter_(iRunTypesetter *d, iTextAttrib attrib) {[m [32m+[m[32mstatic void applyAttributes_RunTypesetter_(iRunTypesetter *d, iTextAttrib attrib) {[m /* WARNING: This is duplicated in run_Font_(). Make sure they behave identically. */[m if (attrib.bold) {[m d->run.font = fontWithStyle_Text(d->baseFont, bold_FontStyle);[m [36m@@ -495,7 +495,7 @@[m [mstatic int applyAttributes_RunTypesetter_(iRunTypesetter *d, iTextAttrib attrib)[m else {[m d->run.font = d->baseFont;[m d->run.color = d->baseColor;[m [31m- }[m [32m+[m[32m }[m[41m [m }[m [m static iBool typesetOneLine_RunTypesetter_(iWrapText *wrap, iRangecc wrapRange, iTextAttrib attrib,[m [1mdiff --git a/src/gmrequest.c b/src/gmrequest.c[m [1mindex f7a22e0a..03a6d999 100644[m [1m--- a/src/gmrequest.c[m [1m+++ b/src/gmrequest.c[m [36m@@ -362,7 +362,7 @@[m [mstatic const iBlock *aboutPageSource_(iRangecc path, iRangecc query) {[m return utf8_String(debugInfo_App());[m }[m if (equalCase_Rangecc(path, "fonts")) {[m [31m- return utf8_String(infoPage_Fonts());[m [32m+[m[32m return utf8_String(infoPage_Fonts(query));[m }[m if (equalCase_Rangecc(path, "feeds")) {[m return utf8_String(entryListPage_Feeds());[m [1mdiff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c[m [1mindex 1039fc2b..48ce5b5f 100644[m [1m--- a/src/ui/documentwidget.c[m [1m+++ b/src/ui/documentwidget.c[m [36m@@ -1130,7 +1130,8 @@[m [mstatic void makeFooterButtons_DocumentWidget_(iDocumentWidget *d, const iMenuIte[m d->footerButtons,[m iClob(newKeyMods_LabelWidget([m items[i].label, items[i].key, items[i].kmods, items[i].command)),[m [31m- alignLeft_WidgetFlag | drawKey_WidgetFlag);[m [32m+[m[32m alignLeft_WidgetFlag | drawKey_WidgetFlag | extraPadding_WidgetFlag);[m [32m+[m[32m setPadding1_Widget(as_Widget(button), gap_UI / 2);[m checkIcon_LabelWidget(button);[m setFont_LabelWidget(button, uiContent_FontId);[m }[m [36m@@ -1473,7 +1474,7 @@[m [mstatic void updateDocument_DocumentWidget_(iDocumentWidget *d,[m trim_Rangecc(¶m);[m /* Detect fontpacks even if the server doesn't use the right media type. */[m if (isRequestFinished && equal_Rangecc(param, "application/octet-stream")) {[m [31m- if (detect_FontPack(&d->sourceContent)) {[m [32m+[m[32m if (detect_FontPack(&response->body)) {[m param = range_CStr(mimeType_FontPack);[m }[m }[m [36m@@ -1492,6 +1493,42 @@[m [mstatic void updateDocument_DocumentWidget_(iDocumentWidget *d,[m docFormat = plainText_SourceFormat;[m setRange_String(&d->sourceMime, param);[m }[m [32m+[m[32m else if (isRequestFinished && equal_Rangecc(param, "font/ttf")) {[m [32m+[m[32m clear_String(&str);[m [32m+[m[32m docFormat = gemini_SourceFormat;[m [32m+[m[32m setRange_String(&d->sourceMime, param);[m [32m+[m[32m format_String(&str, "# TrueType Font\n");[m [32m+[m[32m iString *decUrl = collect_String(urlDecode_String(d->mod.url));[m [32m+[m[32m iRangecc name = baseName_Path(decUrl);[m [32m+[m[32m iBool isInstalled = iFalse;[m [32m+[m[32m if (startsWith_String(collect_String(localFilePathFromUrl_String(d->mod.url)),[m [32m+[m[32m cstr_String(dataDir_App()))) {[m [32m+[m[32m isInstalled = iTrue;[m [32m+[m[32m }[m [32m+[m[32m appendCStr_String(&str, "## ");[m [32m+[m[32m appendRange_String(&str, name);[m [32m+[m[32m appendCStr_String(&str, "\n\n");[m [32m+[m[32m appendCStr_String([m [32m+[m[32m &str, cstr_Lang(isInstalled ? "truetype.help.installed" : "truetype.help"));[m [32m+[m[32m appendCStr_String(&str, "\n");[m [32m+[m[32m if (!isInstalled) {[m [32m+[m[32m makeFooterButtons_DocumentWidget_([m [32m+[m[32m d,[m [32m+[m[32m (iMenuItem[]){[m [32m+[m[32m { add_Icon " ${fontpack.install.ttf}",[m [32m+[m[32m SDLK_RETURN,[m [32m+[m[32m 0,[m [32m+[m[32m format_CStr("!fontpack.install ttf:1 name:%s",[m [32m+[m[32m cstr_Rangecc(name)) },[m [32m+[m[32m { folder_Icon " ${fontpack.open.fontsdir}",[m [32m+[m[32m SDLK_d,[m [32m+[m[32m 0,[m [32m+[m[32m format_CStr("!open url:%s/fonts",[m [32m+[m[32m cstrCollect_String(makeFileUrl_String(dataDir_App())))[m [32m+[m[32m }[m [32m+[m[32m }, 2);[m [32m+[m[32m }[m [32m+[m[32m }[m else if (isRequestFinished &&[m (equal_Rangecc(param, "application/zip") ||[m (startsWith_Rangecc(param, "application/") &&[m [36m@@ -1499,32 +1536,39 @@[m [mstatic void updateDocument_DocumentWidget_(iDocumentWidget *d,[m clear_String(&str);[m docFormat = gemini_SourceFormat;[m setRange_String(&d->sourceMime, param);[m [31m- iString *key = collectNew_String();[m [31m- toString_Sym(SDLK_s, KMOD_PRIMARY, key);[m format_String(&str, "# %s\n", zipPageHeading_(param));[m [31m- appendFormat_String(&str,[m [31m- cstr_Lang("doc.archive"),[m [31m- cstr_Rangecc(baseName_Path(d->mod.url)));[m if (equal_Rangecc(param, mimeType_FontPack)) {[m /* Show some information about fontpacks, and set up footer actions. */[m iArchive *zip = iClob(new_Archive());[m [31m- if (openData_Archive(zip, &d->sourceContent)) {[m [32m+[m[32m if (openData_Archive(zip, &response->body)) {[m iFontPack *fp = new_FontPack();[m setUrl_FontPack(fp, d->mod.url);[m setStandalone_FontPack(fp, iTrue);[m if (loadArchive_FontPack(fp, zip)) {[m [31m- appendFormat_String(&str, "\n\n%s",[m [32m+[m[32m appendFormat_String(&str, "## %s\n%s",[m [32m+[m[32m cstr_String(id_FontPack(fp).id),[m cstrCollect_String(infoText_FontPack(fp)));[m }[m [31m- const iArray *actions = actions_FontPack(fp);[m [32m+[m[32m appendCStr_String(&str, "\n");[m [32m+[m[32m appendCStr_String(&str, cstr_Lang("fontpack.help"));[m [32m+[m[32m appendCStr_String(&str, "\n");[m [32m+[m[32m const iArray *actions = actions_FontPack(fp, iTrue);[m makeFooterButtons_DocumentWidget_(d, constData_Array(actions),[m size_Array(actions));[m delete_FontPack(fp);[m }[m }[m [31m- appendCStr_String(&str, "\n\n");[m [32m+[m[32m else {[m [32m+[m[32m appendFormat_String(&str,[m [32m+[m[32m cstr_Lang("doc.archive"),[m [32m+[m[32m cstr_Rangecc(baseName_Path(d->mod.url)));[m [32m+[m[32m appendCStr_String(&str, "\n");[m[41m [m [32m+[m[32m }[m [32m+[m[32m appendCStr_String(&str, "\n");[m iString *localPath = localFilePathFromUrl_String(d->mod.url);[m if (!localPath) {[m [32m+[m[32m iString *key = collectNew_String();[m [32m+[m[32m toString_Sym(SDLK_s, KMOD_PRIMARY, key);[m appendFormat_String(&str, "%s\n\n",[m format_CStr(cstr_Lang("error.unsupported.suggestsave"),[m cstr_String(key),[m [36m@@ -3267,10 +3311,17 @@[m [mstatic iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)[m }[m return iTrue;[m }[m [31m- else if (equalWidget_Command(cmd, w, "fontpack.install")) {[m [31m- const iString *id = idFromUrl_FontPack(d->mod.url);[m [31m- install_Fonts(id, &d->sourceContent);[m [31m- postCommandf_App("open gotoheading:%s url:about:fonts", cstr_String(id));[m [32m+[m[32m else if (equal_Command(cmd, "fontpack.install") && document_App() == d) {[m [32m+[m[32m if (argLabel_Command(cmd, "ttf")) {[m [32m+[m[32m iAssert(!cmp_String(&d->sourceMime, "font/ttf"));[m [32m+[m[32m installFontFile_Fonts(collect_String(suffix_Command(cmd, "name")), &d->sourceContent);[m [32m+[m[32m postCommand_App("open url:about:fonts");[m[41m [m [32m+[m[32m }[m [32m+[m[32m else {[m [32m+[m[32m const iString *id = idFromUrl_FontPack(d->mod.url);[m [32m+[m[32m install_Fonts(id, &d->sourceContent);[m [32m+[m[32m postCommandf_App("open gotoheading:%s url:about:fonts", cstr_String(id));[m [32m+[m[32m }[m return iTrue;[m }[m return iFalse;[m [36m@@ -5199,6 +5250,7 @@[m [mvoid updateSize_DocumentWidget(iDocumentWidget *d) {[m d->drawBufs->flags |= updateSideBuf_DrawBufsFlag;[m updateVisible_DocumentWidget_(d);[m invalidate_DocumentWidget_(d);[m [32m+[m[32m arrange_Widget(d->footerButtons);[m }[m [m #if 0[m [1mdiff --git a/src/ui/text.c b/src/ui/text.c[m [1mindex 4baf60d3..106c55e9 100644[m [1m--- a/src/ui/text.c[m [1m+++ b/src/ui/text.c[m [36m@@ -458,7 +458,7 @@[m [mstatic void initFonts_Text_(iText *d) {[m /* Check if there are auxiliary fonts available and set those up, too. */[m iConstForEach(PtrArray, s, listSpecsByPriority_Fonts()) {[m const iFontSpec *spec = s.ptr;[m [31m- if (spec->flags & auxiliary_FontSpecFlag) {[m [32m+[m[32m if (spec->flags & (auxiliary_FontSpecFlag | user_FontSpecFlag)) {[m const int fontId = size_Array(&d->fonts);[m resize_Array(&d->fonts, fontId + maxVariants_Fonts);[m setupFontVariants_Text_(d, spec, fontId);[m
text/gemini; charset=utf-8
This content has been proxied by September (ba2dc).