diff --git a/po/en.po b/po/en.po

index 0d5707b1..cc6ea545 100644

--- a/po/en.po

+++ b/po/en.po

@@ -1921,6 +1921,9 @@ msgstr "Enable "%s""

msgid "fontpack.disable"

msgstr "Disable "%s""



+msgid "fontpack.export"

+msgstr "View fontpack.ini template"

+

#, c-format

msgid "fontpack.install"

msgstr "Install "%s""

@@ -1943,3 +1946,21 @@ msgstr "Do you really want to permanently delete\nthe fontpack "%s"?"

msgid "dlg.fontpack.delete"

msgstr "Delete Fontpack"



+msgid "fontpack.help"

+msgstr "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."

+

+msgid "fontpack.install.ttf"

+msgstr "Install TrueType Font"

+

+msgid "fontpack.open.fontsdir"

+msgstr "Open User Fonts Directory"

+

+msgid "fontpack.open.aboutfonts"

+msgstr "Show Installed Fonts"

+

+msgid "truetype.help"

+msgstr "Lagrange attempts to load all individual TrueType files that are copied to the user fonts directory."

+

+msgid "truetype.help.installed"

+msgstr "This font is installed in the user fonts directory."

+

diff --git a/res/lang/de.bin b/res/lang/de.bin

index cf3250a5..e6b1bf85 100644

Binary files a/res/lang/de.bin and b/res/lang/de.bin differ

diff --git a/res/lang/en.bin b/res/lang/en.bin

index ffdd4226..2ef67f55 100644

Binary files a/res/lang/en.bin and b/res/lang/en.bin differ

diff --git a/res/lang/eo.bin b/res/lang/eo.bin

index 8ff3294e..89ec6fea 100644

Binary files a/res/lang/eo.bin and b/res/lang/eo.bin differ

diff --git a/res/lang/es.bin b/res/lang/es.bin

index 810fe574..701f0478 100644

Binary files a/res/lang/es.bin and b/res/lang/es.bin differ

diff --git a/res/lang/es_MX.bin b/res/lang/es_MX.bin

index 98234c64..3013590a 100644

Binary files a/res/lang/es_MX.bin and b/res/lang/es_MX.bin differ

diff --git a/res/lang/fi.bin b/res/lang/fi.bin

index 8d307700..edc0df2b 100644

Binary files a/res/lang/fi.bin and b/res/lang/fi.bin differ

diff --git a/res/lang/fr.bin b/res/lang/fr.bin

index 313e4043..c4b482de 100644

Binary files a/res/lang/fr.bin and b/res/lang/fr.bin differ

diff --git a/res/lang/gl.bin b/res/lang/gl.bin

index cee70fde..6b1ca49a 100644

Binary files a/res/lang/gl.bin and b/res/lang/gl.bin differ

diff --git a/res/lang/ia.bin b/res/lang/ia.bin

index e7e4a2ea..7f677aab 100644

Binary files a/res/lang/ia.bin and b/res/lang/ia.bin differ

diff --git a/res/lang/ie.bin b/res/lang/ie.bin

index b29318ae..6488b20f 100644

Binary files a/res/lang/ie.bin and b/res/lang/ie.bin differ

diff --git a/res/lang/isv.bin b/res/lang/isv.bin

index 80bc275f..0f783c31 100644

Binary files a/res/lang/isv.bin and b/res/lang/isv.bin differ

diff --git a/res/lang/pl.bin b/res/lang/pl.bin

index 208e9e3c..e2d8bc6a 100644

Binary files a/res/lang/pl.bin and b/res/lang/pl.bin differ

diff --git a/res/lang/ru.bin b/res/lang/ru.bin

index 2a8554a5..a0f75c6c 100644

Binary files a/res/lang/ru.bin and b/res/lang/ru.bin differ

diff --git a/res/lang/sk.bin b/res/lang/sk.bin

index f5172ada..91bd1358 100644

Binary files a/res/lang/sk.bin and b/res/lang/sk.bin differ

diff --git a/res/lang/sr.bin b/res/lang/sr.bin

index 14ca2240..55a289bd 100644

Binary files a/res/lang/sr.bin and b/res/lang/sr.bin differ

diff --git a/res/lang/tok.bin b/res/lang/tok.bin

index fad37087..938f85d4 100644

Binary files a/res/lang/tok.bin and b/res/lang/tok.bin differ

diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin

index 2f8e6beb..c6beace1 100644

Binary files a/res/lang/zh_Hans.bin and b/res/lang/zh_Hans.bin differ

diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin

index 2b11cbd2..20128ba3 100644

Binary files a/res/lang/zh_Hant.bin and b/res/lang/zh_Hant.bin differ

diff --git a/src/app.c b/src/app.c

index 485c2495..46856e22 100644

--- a/src/app.c

+++ b/src/app.c

@@ -836,6 +836,7 @@ static void init_App_(iApp *d, int argc, char **argv) {

 loadPalette_Color(dataDir_App_());

 setThemePalette_Color(d->prefs.theme); /* default UI colors */

 loadPrefs_App_(d);

+ updateActive_Fonts();

 load_Keys(dataDir_App_());

 /* See if the user wants to override the window size. */ {

     iCommandLineArg *arg = iClob(checkArgument_CommandLine(&d->args, windowWidth_CommandLineOption));

@@ -3069,13 +3070,7 @@ iBool handleCommand_App(const char *cmd) {

 }

 else if (equal_Command(cmd, "fontpack.enable")) {

     const iString *packId = collect_String(suffix_Command(cmd, "id"));

- if (arg_Command(cmd)) {

- remove_StringSet(d->prefs.disabledFontPacks, packId);

- }

- else {

- insert_StringSet(d->prefs.disabledFontPacks, packId);

- }

- resetFonts_App();

+ enablePack_Fonts(packId, arg_Command(cmd));

     postCommand_App("navigate.reload");

     return iTrue;

 }

diff --git a/src/fontpack.c b/src/fontpack.c

index 226392bc..f22ab8dc 100644

--- a/src/fontpack.c

+++ b/src/fontpack.c

@@ -90,6 +90,14 @@ static void load_FontFile_(iFontFile *d, const iBlock *data) {

#endif

}



+static iBool detectMonospace_FontFile_(const iFontFile *d) {

+ int em, i, period;

+ stbtt_GetCodepointHMetrics(&d->stbInfo, 'M', &em, NULL);

+ stbtt_GetCodepointHMetrics(&d->stbInfo, 'i', &i, NULL);

+ stbtt_GetCodepointHMetrics(&d->stbInfo, '.', &period, NULL);

+ return em == i && em == period;

+}

+

static void unload_FontFile_(iFontFile *d) {

#if defined(LAGRANGE_ENABLE_HARFBUZZ)

 /* HarfBuzz objects. */

@@ -300,6 +308,8 @@ static iBlock *readFile_FontPack_(const iFontPack *d, const iString *path) {

 return data;

}



+static const char *styles_[max_FontStyle] = { "regular", "italic", "light", "semibold", "bold" };

+

void handleIniKeyValue_FontPack_(void *context, const iString *table, const iString *key,

                              const iTomlValue *value) {

 iFontPack *d = context;

@@ -359,9 +369,8 @@ void handleIniKeyValue_FontPack_(void *context, const iString *table, const iStr

                  ((int) number_TomlValue(value)) & 1);

 }

 else if (value->type == string_TomlType) {

- const char *styles[max_FontStyle] = { "regular", "italic", "light", "semibold", "bold" };

- iForIndices(i, styles) {

- if (!cmp_String(key, styles[i]) && !d->loadSpec->styles[i]) {

+ iForIndices(i, styles_) {

+ if (!cmp_String(key, styles_[i]) && !d->loadSpec->styles[i]) {

             iFontFile *ff = NULL;

             iString *fontFileId = concat_Path(d->loadPath, value->value.string);

             if (!(ff = findFile_Fonts_(&fonts_, fontFileId))) {

@@ -441,6 +450,7 @@ void setLoadPath_FontPack(iFontPack *d, const iString *path) {

 /* Pack ID is based on the file name. */

 setRange_String(&d->id, baseName_Path(path));

 setRange_String(&d->id, withoutExtension_Path(&d->id));

+ replace_String(&d->id, " ", "-");

}



const iString *idFromUrl_FontPack(const iString *url) {

@@ -449,6 +459,7 @@ const iString *idFromUrl_FontPack(const iString *url) {

 init_Url(&parts, url);

 setRange_String(id, baseName_Path(collectNewRange_String(parts.path)));

 setRange_String(id, withoutExtension_Path(id));

+ replace_String(id, " ", "-");

 return collect_String(id);

}



@@ -592,6 +603,49 @@ void init_Fonts(const char *userDir) {

     }

     iRelease(f);

 }

+ /* Individual TrueType files in the user fonts directory. */ {

+ iForEach(DirFileInfo, entry, iClob(new_DirFileInfo(userFontsDirectory_Fonts_(d)))) {

+ const iString *entryPath = path_FileInfo(entry.value);

+ if (endsWithCase_String(entryPath, ".ttf")) {

+ iFile *f = new_File(entryPath);

+ iFontFile *font = NULL;

+ if (open_File(f, readOnly_FileMode)) {

+ iBlock *data = readAll_File(f);

+ font = new_FontFile();

+ load_FontFile_(font, data);

+ set_String(&font->id, entryPath);

+ pushBack_ObjectList(fonts_.files, font); /* centralized ownership */

+ iRelease(font);

+ delete_Block(data);

+ }

+ iRelease(f);

+ if (!font) {

+ fprintf(stderr, "[fonts] failed to load: %s\n", cstr_String(entryPath));

+ continue;

+ }

+ iFontPack *pack = new_FontPack();

+ setStandalone_FontPack(pack, iTrue); 

+ iFontSpec *spec = new_FontSpec();

+ spec->flags |= user_FontSpecFlag;

+ if (detectMonospace_FontFile_(font)) {

+ spec->flags |= monospace_FontSpecFlag;

+ }

+ setRange_String(&spec->id, baseName_Path(collect_String(lower_String(&font->id))));

+ setRange_String(&spec->id, withoutExtension_Path(&spec->id)); 

+ replace_String(&spec->id, " ", "-");

+ setRange_String(&spec->name, baseName_Path(&font->id));

+ setRange_String(&spec->name, withoutExtension_Path(&spec->name));

+ set_String(&spec->sourcePath, entryPath);

+ iForIndices(j, spec->styles) {

+ spec->styles[j] = ref_Object(font);

+ }

+ pushBack_PtrArray(&pack->fonts, spec);

+ set_String(&pack->id, &spec->id);

+ pack->loadPath = copy_String(entryPath);

+ pushBack_PtrArray(&d->packs, pack);

+ }

+ }

+ } 

 sortSpecs_Fonts_(d);

}



@@ -679,7 +733,7 @@ iString *infoText_FontPack(const iFontPack *d) {

 return str;

}



-const iArray *actions_FontPack(const iFontPack *d) {

+const iArray *actions_FontPack(const iFontPack *d, iBool showInstalled) {

 iArray           *items     = new_Array(sizeof(iMenuItem));

 const iFontPackId fp        = id_FontPack(d);

 const char       *fpId      = cstr_String(fp.id);

@@ -687,33 +741,44 @@ const iArray *actions_FontPack(const iFontPack *d) {

 const iBool       isEnabled = !isDisabled_FontPack(d);

 if (isInstalled_Fonts(fpId)) {

     if (d->version > installed->version) {

- pushBack_Array(items, &(iMenuItem){

- format_Lang(add_Icon " ${fontpack.upgrade}", fpId, d->version),

- SDLK_RETURN, 0, "fontpack.install"

- });

+ pushBack_Array(

+ items,

+ &(iMenuItem){ format_Lang(add_Icon " ${fontpack.upgrade}", fpId, d->version),

+ SDLK_RETURN,

+ 0,

+ "fontpack.install" });

     }

- pushBack_Array(items, &(iMenuItem){

- format_Lang(isEnabled ? close_Icon " ${fontpack.disable}"

- : leftArrowhead_Icon " ${fontpack.enable}", fpId), 0, 0,

- format_CStr("fontpack.enable arg:%d id:%s", !isEnabled, fpId) });

- if (!d->isReadOnly && installed->loadPath && d->loadPath &&

+ pushBack_Array(

+ items,

+ &(iMenuItem){ format_Lang(isEnabled ? close_Icon " ${fontpack.disable}"

+ : leftArrowhead_Icon " ${fontpack.enable}",

+ fpId),

+ 0,

+ 0,

+ format_CStr("fontpack.enable arg:%d id:%s", !isEnabled, fpId) });

+ if (!d->isReadOnly && !d->isStandalone && installed->loadPath && d->loadPath &&

         !cmpString_String(installed->loadPath, d->loadPath)) {

- pushBack_Array(items, &(iMenuItem){

- format_Lang(delete_Icon " ${fontpack.delete}", fpId), 0, 0,

- format_CStr("fontpack.delete id:%s", fpId) });

+ pushBack_Array(items,

+ &(iMenuItem){ format_Lang(delete_Icon " ${fontpack.delete}", fpId),

+ 0,

+ 0,

+ format_CStr("fontpack.delete id:%s", fpId) });

     }

 }

 else if (d->isStandalone) {

- pushBack_Array(items, &(iMenuItem){

- format_Lang(add_Icon " ${fontpack.install}", fpId),

- SDLK_RETURN, 0, "fontpack.install"

- });

- pushBack_Array(items, &(iMenuItem){

- download_Icon " " saveToDownloads_Label,

- 0,

- 0,

- "document.save"

- });

+ pushBack_Array(items,

+ &(iMenuItem){ format_Lang(add_Icon " ${fontpack.install}", fpId),

+ SDLK_RETURN,

+ 0,

+ "fontpack.install" });

+ pushBack_Array(

+ items, &(iMenuItem){ download_Icon " " saveToDownloads_Label, 0, 0, "document.save" });

+ }

+ if (showInstalled) {

+ pushBack_Array(

+ items,

+ &(iMenuItem){

+ fontpack_Icon " ${fontpack.open.aboutfonts}", 0, 0, "!open url:about:fonts" });

 }

 return collect_Array(items);

}

@@ -722,8 +787,64 @@ iBool isDisabled_FontPack(const iFontPack *d) {

 return contains_StringSet(prefs_App()->disabledFontPacks, &d->id);

}



-const iString *infoPage_Fonts(void) {

+const iPtrArray *disabledSpecs_Fonts_(const iFonts *d) {

+ iPtrArray *list = collectNew_PtrArray();

+ iConstForEach(PtrArray, i, &d->packs) {

+ const iFontPack *pack = i.ptr;

+ if (isDisabled_FontPack(pack)) {

+ iConstForEach(PtrArray, j, &pack->fonts) {

+ pushBack_PtrArray(list, j.ptr);

+ }

+ }

+ }

+ return list;

+}

+

+static const char *boolStr_(int value) {

+ return value ? "true" : "false";

+}

+

+static const iString *exportFontPackIni_Fonts_(const iFonts *d, const iRangecc packId) {

+ iString *str = collectNew_String();

+ const iFontPack *pack = pack_Fonts(cstr_Rangecc(packId));

+ if (!pack) {

+ appendFormat_String(str, "Fontpack "%s" not found.\n", cstr_Rangecc(packId));

+ return str;

+ }

+ const iFontPackId fp = id_FontPack(pack);

+ appendCStr_String(str, "To create a fontpack, add this fontpack.ini into a ZIP archive whose "

+ "name has the .fontpack file extension.\n```Fontpack configuration\n");

+ appendFormat_String(str, "version = %d\n", fp.version);

+ iConstForEach(PtrArray, i, &pack->fonts) {

+ const iFontSpec *spec = i.ptr;

+ appendFormat_String(str, "\n[%s]\n", cstr_String(&spec->id));

+ appendFormat_String(str, "name = "%s"\n", cstrCollect_String(quote_String(&spec->name, iFalse)));

+ appendFormat_String(str, "priority = %d\n", spec->priority);

+ appendFormat_String(str, "override = %s\n", boolStr_(spec->flags & override_FontSpecFlag));

+ appendFormat_String(str, "monospace = %s\n", boolStr_(spec->flags & monospace_FontSpecFlag));

+ appendFormat_String(str, "auxiliary = %s\n", boolStr_(spec->flags & auxiliary_FontSpecFlag));

+ appendFormat_String(str, "allowspace = %s\n", boolStr_(spec->flags & allowSpacePunct_FontSpecFlag));

+ for (int j = 0; j < 2; ++j) {

+ const char *scope = (j == 0 ? "ui" : "doc");

+ appendFormat_String(str, "%s.height = %.3f\n", scope, spec->heightScale[j]);

+ appendFormat_String(str, "%s.glyphscale = %.3f\n", scope, spec->glyphScale[j]);

+ appendFormat_String(str, "%s.voffset = %.3f\n", scope, spec->vertOffsetScale[j]);

+ }

+ iForIndices(j, styles_) {

+ appendFormat_String(str, "%s = "%s"\n", styles_[j],

+ cstrCollect_String(quote_String(&spec->sourcePath, iFalse)));

+ }

+ }

+ appendCStr_String(str, "```\n");

+ return str;

+}

+

+const iString *infoPage_Fonts(iRangecc query) {

 iFonts *d = &fonts_;

+ if (!isEmpty_Range(&query)) {

+ query.start++; /* skip the ? */

+ return exportFontPackIni_Fonts_(d, query);

+ }

 iString *str = collectNewCStr_String("# ${heading.fontpack.meta}\n"

      "=> gemini://skyjake.fi/fonts  Download more fonts\n"

      "=> about:command?!open%20newtab:1%20gotoheading:1%20url:about:help  Using fonts in Lagrange\n"

@@ -734,7 +855,7 @@ const iString *infoPage_Fonts(void) {

 iString *currentSourcePath = collectNew_String();

 for (int group = 0; group < 2; group++) {

     iBool isFirst = iTrue;

- iConstForEach(PtrArray, i, specsByPack) {

+ iConstForEach(PtrArray, i, group == 0 ? specsByPack : disabledSpecs_Fonts_(d)) {

         const iFontSpec *spec = i.ptr;

         if (isEmpty_String(&spec->sourcePath)) {

             continue; /* built-in font */

@@ -753,15 +874,22 @@ const iString *infoPage_Fonts(void) {

                         isFirst = iFalse;

                     }

                     const iString *packId = id_FontPack(pack).id;

- appendFormat_String(str, "### %s\n=> %s ${fontpack.meta.viewfile}\n",

+ appendFormat_String(str, "### %s\n",

                                         isEmpty_String(packId) ? "fonts.ini" :

- cstr_String(packId),

- cstrCollect_String(makeFileUrl_String(&spec->sourcePath)));

+ cstr_String(packId));

                     append_String(str, collect_String(infoText_FontPack(pack)));

- iConstForEach(Array, a, actions_FontPack(pack)) {

+ appendFormat_String(str, "=> %s ${fontpack.meta.viewfile}\n",

+ cstrCollect_String(makeFileUrl_String(&spec->sourcePath)));

+ if (pack->isStandalone) {

+ appendFormat_String(str, "=> about:fonts?%s ${fontpack.export}\n",

+ cstr_String(packId));

+ }

+ iConstForEach(Array, a, actions_FontPack(pack, iFalse)) {

                         const iMenuItem *item = a.value;

- appendFormat_String(str, "=> about:command?%s %s\n",

- cstr_String(withSpacesEncoded_String(collectNewCStr_String(item->command))),

+ appendFormat_String(str,

+ "=> about:command?%s %s\n",

+ cstr_String(withSpacesEncoded_String(

+ collectNewCStr_String(item->command))),

                                             item->label);

                     }

                 }

@@ -824,5 +952,32 @@ void install_Fonts(const iString *packId, const iBlock *data) {

 reload_Fonts();

}



+void installFontFile_Fonts(const iString *fileName, const iBlock *data) {

+ iFonts *d = &fonts_;

+ iFile *f = new_File(collect_String(concat_Path(userFontsDirectory_Fonts_(d), fileName)));

+ if (open_File(f, writeOnly_FileMode)) {

+ write_File(f, data);

+ }

+ iRelease(f);

+ reload_Fonts();

+}

+

+void enablePack_Fonts(const iString *packId, iBool enable) {

+ iFonts *d = &fonts_;

+ if (enable) {

+ remove_StringSet(prefs_App()->disabledFontPacks, packId);

+ }

+ else {

+ insert_StringSet(prefs_App()->disabledFontPacks, packId);

+ }

+ updateActive_Fonts();

+ resetFonts_App();

+ invalidate_Window(get_MainWindow());

+}

+

+void updateActive_Fonts(void) {

+ sortSpecs_Fonts_(&fonts_);

+}

+

iDefineClass(FontFile)



diff --git a/src/fontpack.h b/src/fontpack.h

index fb8d757e..5d592822 100644

--- a/src/fontpack.h

+++ b/src/fontpack.h

@@ -110,22 +110,23 @@ iDeclareType(FontSpec)

iDeclareTypeConstruction(FontSpec)



enum iFontSpecFlags {

- override_FontSpecFlag = iBit(1),

- monospace_FontSpecFlag = iBit(2), /* can be used in preformatted content */

- auxiliary_FontSpecFlag = iBit(3), /* only used for looking up glyphs missing from other fonts */

- allowSpacePunct_FontSpecFlag = iBit(4), /* space/punctuation glyphs from this auxiliary font can be used */

+ user_FontSpecFlag = iBit(1), /* user's standalone font, can be used for anything */

+ override_FontSpecFlag = iBit(2),

+ monospace_FontSpecFlag = iBit(3), /* can be used in preformatted content */

+ auxiliary_FontSpecFlag = iBit(4), /* only used for looking up glyphs missing from other fonts */

+ allowSpacePunct_FontSpecFlag = iBit(5), /* space/punctuation glyphs from this auxiliary font can be used */

 fixNunitoKerning_FontSpecFlag = iBit(31), /* manual hardcoded kerning tweaks for Nunito */

};



struct Impl_FontSpec {

- iString id; /* unique ID */

- iString name; /* human-readable label */

- iString sourcePath; /* file where the path was loaded, could be a .fontpack */

- int flags;

- int priority;

- float heightScale[2]; /* overall height scaling; ui, document */

- float glyphScale[2]; /* ui, document */

- float vertOffsetScale[2]; /* ui, document */

+ iString id; /* unique ID */

+ iString name; /* human-readable label */

+ iString sourcePath; /* file where the path was loaded, could be a .fontpack */

+ int flags;

+ int priority;

+ float heightScale[2]; /* overall height scaling; ui, document */

+ float glyphScale[2]; /* ui, document */

+ float vertOffsetScale[2]; /* ui, document */

 const iFontFile *styles[max_FontStyle];

};



@@ -158,25 +159,26 @@ iBool isDisabled_FontPack (const iFontPack *);

iBool isReadOnly_FontPack (const iFontPack *);

const iPtrArray * listSpecs_FontPack (const iFontPack *);

iString * infoText_FontPack (const iFontPack *);

-const iArray * actions_FontPack (const iFontPack *);

+const iArray * actions_FontPack (const iFontPack *, iBool showInstalled);



const iString * idFromUrl_FontPack (const iString *url);



/----------------------------------------------------------------------------------------------/



-iDeclareType(GmDocument)

-

void init_Fonts (const char *userDir);

void deinit_Fonts (void);



+void enablePack_Fonts (const iString *packId, iBool enable);

+void updateActive_Fonts (void);

const iFontPack * pack_Fonts (const char *packId);

const iFontPack * packByPath_Fonts (const iString *path);

const iFontSpec * findSpec_Fonts (const char *fontId);

const iPtrArray * listPacks_Fonts (void);

const iPtrArray * listSpecs_Fonts (iBool (*filterFunc)(const iFontSpec *));

const iPtrArray * listSpecsByPriority_Fonts (void);

-const iString * infoPage_Fonts (void);

+const iString * infoPage_Fonts (iRangecc query);

void install_Fonts (const iString *fontId, const iBlock *data);

+void installFontFile_Fonts (const iString *fileName, const iBlock *data);

void reload_Fonts (void);



iLocalDef iBool isInstalled_Fonts(const char *packId) {

diff --git a/src/gmdocument.c b/src/gmdocument.c

index 23ebace3..b0851fec 100644

--- a/src/gmdocument.c

+++ b/src/gmdocument.c

@@ -479,7 +479,7 @@ static void commit_RunTypesetter_(iRunTypesetter *d, iGmDocument *doc) {



static const int maxLedeLines_ = 10;



-static int applyAttributes_RunTypesetter_(iRunTypesetter *d, iTextAttrib attrib) {

+static void applyAttributes_RunTypesetter_(iRunTypesetter *d, iTextAttrib attrib) {

 /* WARNING: This is duplicated in run_Font_(). Make sure they behave identically. */

 if (attrib.bold) {

     d->run.font = fontWithStyle_Text(d->baseFont, bold_FontStyle);

@@ -495,7 +495,7 @@ static int applyAttributes_RunTypesetter_(iRunTypesetter *d, iTextAttrib attrib)

 else {

     d->run.font  = d->baseFont;

     d->run.color = d->baseColor;

- }

+ } 

}



static iBool typesetOneLine_RunTypesetter_(iWrapText *wrap, iRangecc wrapRange, iTextAttrib attrib,

diff --git a/src/gmrequest.c b/src/gmrequest.c

index f7a22e0a..03a6d999 100644

--- a/src/gmrequest.c

+++ b/src/gmrequest.c

@@ -362,7 +362,7 @@ static const iBlock *aboutPageSource_(iRangecc path, iRangecc query) {

     return utf8_String(debugInfo_App());

 }

 if (equalCase_Rangecc(path, "fonts")) {

- return utf8_String(infoPage_Fonts());

+ return utf8_String(infoPage_Fonts(query));

 }

 if (equalCase_Rangecc(path, "feeds")) {

     return utf8_String(entryListPage_Feeds());

diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c

index 1039fc2b..48ce5b5f 100644

--- a/src/ui/documentwidget.c

+++ b/src/ui/documentwidget.c

@@ -1130,7 +1130,8 @@ static void makeFooterButtons_DocumentWidget_(iDocumentWidget *d, const iMenuIte

         d->footerButtons,

         iClob(newKeyMods_LabelWidget(

             items[i].label, items[i].key, items[i].kmods, items[i].command)),

- alignLeft_WidgetFlag | drawKey_WidgetFlag);

+ alignLeft_WidgetFlag | drawKey_WidgetFlag | extraPadding_WidgetFlag);

+ setPadding1_Widget(as_Widget(button), gap_UI / 2);

     checkIcon_LabelWidget(button);

     setFont_LabelWidget(button, uiContent_FontId);

 }

@@ -1473,7 +1474,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d,

             trim_Rangecc(&param);

             /* Detect fontpacks even if the server doesn't use the right media type. */

             if (isRequestFinished && equal_Rangecc(param, "application/octet-stream")) {

- if (detect_FontPack(&d->sourceContent)) {

+ if (detect_FontPack(&response->body)) {

                     param = range_CStr(mimeType_FontPack);

                 }

             }

@@ -1492,6 +1493,42 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d,

                 docFormat = plainText_SourceFormat;

                 setRange_String(&d->sourceMime, param);

             }

+ else if (isRequestFinished && equal_Rangecc(param, "font/ttf")) {

+ clear_String(&str);

+ docFormat = gemini_SourceFormat;

+ setRange_String(&d->sourceMime, param);

+ format_String(&str, "# TrueType Font\n");

+ iString *decUrl = collect_String(urlDecode_String(d->mod.url));

+ iRangecc name = baseName_Path(decUrl);

+ iBool isInstalled = iFalse;

+ if (startsWith_String(collect_String(localFilePathFromUrl_String(d->mod.url)),

+ cstr_String(dataDir_App()))) {

+ isInstalled = iTrue;

+ }

+ appendCStr_String(&str, "## ");

+ appendRange_String(&str, name);

+ appendCStr_String(&str, "\n\n");

+ appendCStr_String(

+ &str, cstr_Lang(isInstalled ? "truetype.help.installed" : "truetype.help"));

+ appendCStr_String(&str, "\n");

+ if (!isInstalled) {

+ makeFooterButtons_DocumentWidget_(

+ d,

+ (iMenuItem[]){

+ { add_Icon " ${fontpack.install.ttf}",

+ SDLK_RETURN,

+ 0,

+ format_CStr("!fontpack.install ttf:1 name:%s",

+ cstr_Rangecc(name)) },

+ { folder_Icon " ${fontpack.open.fontsdir}",

+ SDLK_d,

+ 0,

+ format_CStr("!open url:%s/fonts",

+ cstrCollect_String(makeFileUrl_String(dataDir_App())))

+ }

+ }, 2);

+ }

+ }

             else if (isRequestFinished &&

                      (equal_Rangecc(param, "application/zip") ||

                      (startsWith_Rangecc(param, "application/") &&

@@ -1499,32 +1536,39 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d,

                 clear_String(&str);

                 docFormat = gemini_SourceFormat;

                 setRange_String(&d->sourceMime, param);

- iString *key = collectNew_String();

- toString_Sym(SDLK_s, KMOD_PRIMARY, key);

                 format_String(&str, "# %s\n", zipPageHeading_(param));

- appendFormat_String(&str,

- cstr_Lang("doc.archive"),

- cstr_Rangecc(baseName_Path(d->mod.url)));

                 if (equal_Rangecc(param, mimeType_FontPack)) {

                     /* Show some information about fontpacks, and set up footer actions. */

                     iArchive *zip = iClob(new_Archive());

- if (openData_Archive(zip, &d->sourceContent)) {

+ if (openData_Archive(zip, &response->body)) {

                         iFontPack *fp = new_FontPack();

                         setUrl_FontPack(fp, d->mod.url);

                         setStandalone_FontPack(fp, iTrue);

                         if (loadArchive_FontPack(fp, zip)) {

- appendFormat_String(&str, "\n\n%s",

+ appendFormat_String(&str, "## %s\n%s",

+ cstr_String(id_FontPack(fp).id),

                                                 cstrCollect_String(infoText_FontPack(fp)));

                         }

- const iArray *actions = actions_FontPack(fp);

+ appendCStr_String(&str, "\n");

+ appendCStr_String(&str, cstr_Lang("fontpack.help"));

+ appendCStr_String(&str, "\n");

+ const iArray *actions = actions_FontPack(fp, iTrue);

                         makeFooterButtons_DocumentWidget_(d, constData_Array(actions),

                                                           size_Array(actions));

                         delete_FontPack(fp);

                     }

                 }

- appendCStr_String(&str, "\n\n");

+ else {

+ appendFormat_String(&str,

+ cstr_Lang("doc.archive"),

+ cstr_Rangecc(baseName_Path(d->mod.url)));

+ appendCStr_String(&str, "\n"); 

+ }

+ appendCStr_String(&str, "\n");

                 iString *localPath = localFilePathFromUrl_String(d->mod.url);

                 if (!localPath) {

+ iString *key = collectNew_String();

+ toString_Sym(SDLK_s, KMOD_PRIMARY, key);

                     appendFormat_String(&str, "%s\n\n",

                                         format_CStr(cstr_Lang("error.unsupported.suggestsave"),

                                                     cstr_String(key),

@@ -3267,10 +3311,17 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)

     }

     return iTrue;

 }

- else if (equalWidget_Command(cmd, w, "fontpack.install")) {

- const iString *id = idFromUrl_FontPack(d->mod.url);

- install_Fonts(id, &d->sourceContent);

- postCommandf_App("open gotoheading:%s url:about:fonts", cstr_String(id));

+ else if (equal_Command(cmd, "fontpack.install") && document_App() == d) {

+ if (argLabel_Command(cmd, "ttf")) {

+ iAssert(!cmp_String(&d->sourceMime, "font/ttf"));

+ installFontFile_Fonts(collect_String(suffix_Command(cmd, "name")), &d->sourceContent);

+ postCommand_App("open url:about:fonts"); 

+ }

+ else {

+ const iString *id = idFromUrl_FontPack(d->mod.url);

+ install_Fonts(id, &d->sourceContent);

+ postCommandf_App("open gotoheading:%s url:about:fonts", cstr_String(id));

+ }

     return iTrue;

 }

 return iFalse;

@@ -5199,6 +5250,7 @@ void updateSize_DocumentWidget(iDocumentWidget *d) {

 d->drawBufs->flags |= updateSideBuf_DrawBufsFlag;

 updateVisible_DocumentWidget_(d);

 invalidate_DocumentWidget_(d);

+ arrange_Widget(d->footerButtons);

}



#if 0

diff --git a/src/ui/text.c b/src/ui/text.c

index 4baf60d3..106c55e9 100644

--- a/src/ui/text.c

+++ b/src/ui/text.c

@@ -458,7 +458,7 @@ static void initFonts_Text_(iText *d) {

 /* Check if there are auxiliary fonts available and set those up, too. */

 iConstForEach(PtrArray, s, listSpecsByPriority_Fonts()) {

     const iFontSpec *spec = s.ptr;

- if (spec->flags & auxiliary_FontSpecFlag) {

+ if (spec->flags & (auxiliary_FontSpecFlag | user_FontSpecFlag)) {

         const int fontId = size_Array(&d->fonts);

         resize_Array(&d->fonts, fontId + maxVariants_Fonts);

         setupFontVariants_Text_(d, spec, fontId);

Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.8/pcdiff/57cbcc6e864abd368bde93154b3580147936201c
Status Code
Success (20)
Meta
text/plain
Capsule Response Time
29.027283 milliseconds
Gemini-to-HTML Time
9.960314 milliseconds

This content has been proxied by September (ba2dc).