summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-05-25 18:25:40 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-05-25 18:25:51 +0300
commitf53e6d34fbb8d2f2fb54aff8b746b1c5d06fe497 (patch)
treec8cbe09db582b66262dd6472bd55be7b485fdf66 /src
parent54d38100bb49a34f9ad11570e0144dc1ec006858 (diff)
Removed Symbola, replaced with other/new fonts
Symbola's license is not permissive enough, so it has been removed. Added "Smol Emoji" with face emoticons and other UI characters for Lagrange. More symbols provided by Noto Sans Symbols and Noto Sans Symbols 2. The fallback fonts are now Iosevka, which has a pretty good coverage (although monospace), and the user's chosen TrueType font (which could still be a local copy of Symbola). Still a work in progress: some characters are missing. IssueID #283
Diffstat (limited to 'src')
-rw-r--r--src/app.c37
-rw-r--r--src/defs.h2
-rw-r--r--src/gmrequest.c4
-rw-r--r--src/prefs.c5
-rw-r--r--src/prefs.h1
-rw-r--r--src/ui/inputwidget.h5
-rw-r--r--src/ui/keys.c8
-rw-r--r--src/ui/root.c8
-rw-r--r--src/ui/text.c127
-rw-r--r--src/ui/text.h16
-rw-r--r--src/ui/util.c2
11 files changed, 147 insertions, 68 deletions
diff --git a/src/app.c b/src/app.c
index e22ee494..bb3afea6 100644
--- a/src/app.c
+++ b/src/app.c
@@ -204,6 +204,7 @@ static iString *serializePrefs_App_(const iApp *d) {
204 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(d->window)); 204 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(d->window));
205 appendFormat_String(str, "prefs.dialogtab arg:%d\n", d->prefs.dialogTab); 205 appendFormat_String(str, "prefs.dialogtab arg:%d\n", d->prefs.dialogTab);
206 appendFormat_String(str, "font.set arg:%d\n", d->prefs.font); 206 appendFormat_String(str, "font.set arg:%d\n", d->prefs.font);
207 appendFormat_String(str, "font.user path:%s\n", cstr_String(&d->prefs.symbolFontPath));
207 appendFormat_String(str, "headingfont.set arg:%d\n", d->prefs.headingFont); 208 appendFormat_String(str, "headingfont.set arg:%d\n", d->prefs.headingFont);
208 appendFormat_String(str, "zoom.set arg:%d\n", d->prefs.zoomPercent); 209 appendFormat_String(str, "zoom.set arg:%d\n", d->prefs.zoomPercent);
209 appendFormat_String(str, "smoothscroll arg:%d\n", d->prefs.smoothScrolling); 210 appendFormat_String(str, "smoothscroll arg:%d\n", d->prefs.smoothScrolling);
@@ -1555,24 +1556,25 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) {
1555 isSelected_Widget(findChild_Widget(d, "prefs.imageloadscroll"))); 1556 isSelected_Widget(findChild_Widget(d, "prefs.imageloadscroll")));
1556 postCommandf_App("hidetoolbarscroll arg:%d", 1557 postCommandf_App("hidetoolbarscroll arg:%d",
1557 isSelected_Widget(findChild_Widget(d, "prefs.hidetoolbarscroll"))); 1558 isSelected_Widget(findChild_Widget(d, "prefs.hidetoolbarscroll")));
1558 postCommandf_App("ostheme arg:%d", 1559 postCommandf_App("ostheme arg:%d", isSelected_Widget(findChild_Widget(d, "prefs.ostheme")));
1559 isSelected_Widget(findChild_Widget(d, "prefs.ostheme"))); 1560 postCommandf_App("font.user path:%s",
1561 cstrText_InputWidget(findChild_Widget(d, "prefs.userfont")));
1560 postCommandf_App("decodeurls arg:%d", 1562 postCommandf_App("decodeurls arg:%d",
1561 isSelected_Widget(findChild_Widget(d, "prefs.decodeurls"))); 1563 isSelected_Widget(findChild_Widget(d, "prefs.decodeurls")));
1562 postCommandf_App("searchurl address:%s", 1564 postCommandf_App("searchurl address:%s",
1563 cstr_String(text_InputWidget(findChild_Widget(d, "prefs.searchurl")))); 1565 cstrText_InputWidget(findChild_Widget(d, "prefs.searchurl")));
1564 postCommandf_App("cachesize.set arg:%d", 1566 postCommandf_App("cachesize.set arg:%d",
1565 toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); 1567 toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize"))));
1566 postCommandf_App("ca.file path:%s", 1568 postCommandf_App("ca.file path:%s",
1567 cstr_String(text_InputWidget(findChild_Widget(d, "prefs.ca.file")))); 1569 cstrText_InputWidget(findChild_Widget(d, "prefs.ca.file")));
1568 postCommandf_App("ca.path path:%s", 1570 postCommandf_App("ca.path path:%s",
1569 cstr_String(text_InputWidget(findChild_Widget(d, "prefs.ca.path")))); 1571 cstrText_InputWidget(findChild_Widget(d, "prefs.ca.path")));
1570 postCommandf_App("proxy.gemini address:%s", 1572 postCommandf_App("proxy.gemini address:%s",
1571 cstr_String(text_InputWidget(findChild_Widget(d, "prefs.proxy.gemini")))); 1573 cstrText_InputWidget(findChild_Widget(d, "prefs.proxy.gemini")));
1572 postCommandf_App("proxy.gopher address:%s", 1574 postCommandf_App("proxy.gopher address:%s",
1573 cstr_String(text_InputWidget(findChild_Widget(d, "prefs.proxy.gopher")))); 1575 cstrText_InputWidget(findChild_Widget(d, "prefs.proxy.gopher")));
1574 postCommandf_App("proxy.http address:%s", 1576 postCommandf_App("proxy.http address:%s",
1575 cstr_String(text_InputWidget(findChild_Widget(d, "prefs.proxy.http")))); 1577 cstrText_InputWidget(findChild_Widget(d, "prefs.proxy.http")));
1576 const iWidget *tabs = findChild_Widget(d, "prefs.tabs"); 1578 const iWidget *tabs = findChild_Widget(d, "prefs.tabs");
1577 if (tabs) { 1579 if (tabs) {
1578 postCommandf_App("prefs.dialogtab arg:%u", 1580 postCommandf_App("prefs.dialogtab arg:%u",
@@ -1892,6 +1894,22 @@ iBool handleCommand_App(const char *cmd) {
1892 resetFonts_Text(); 1894 resetFonts_Text();
1893 return iTrue; 1895 return iTrue;
1894 } 1896 }
1897 else if (equal_Command(cmd, "font.user")) {
1898 const char *path = suffixPtr_Command(cmd, "path");
1899 if (cmp_String(&d->prefs.symbolFontPath, path)) {
1900 if (!isFrozen) {
1901 setFreezeDraw_Window(get_Window(), iTrue);
1902 }
1903 setCStr_String(&d->prefs.symbolFontPath, path);
1904 loadUserFonts_Text();
1905 resetFonts_Text();
1906 if (!isFrozen) {
1907 postCommand_App("font.changed");
1908 postCommand_App("window.unfreeze");
1909 }
1910 }
1911 return iTrue;
1912 }
1895 else if (equal_Command(cmd, "font.set")) { 1913 else if (equal_Command(cmd, "font.set")) {
1896 if (!isFrozen) { 1914 if (!isFrozen) {
1897 setFreezeDraw_Window(get_Window(), iTrue); 1915 setFreezeDraw_Window(get_Window(), iTrue);
@@ -2332,6 +2350,7 @@ iBool handleCommand_App(const char *cmd) {
2332 setToggle_Widget(findChild_Widget(dlg, "prefs.archive.openindex"), d->prefs.openArchiveIndexPages); 2350 setToggle_Widget(findChild_Widget(dlg, "prefs.archive.openindex"), d->prefs.openArchiveIndexPages);
2333 setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme); 2351 setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme);
2334 setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame); 2352 setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame);
2353 setText_InputWidget(findChild_Widget(dlg, "prefs.userfont"), &d->prefs.symbolFontPath);
2335 updatePrefsPinSplitButtons_(dlg, d->prefs.pinSplit); 2354 updatePrefsPinSplitButtons_(dlg, d->prefs.pinSplit);
2336 updateDropdownSelection_(findChild_Widget(dlg, "prefs.uilang"), cstr_String(&d->prefs.uiLanguage)); 2355 updateDropdownSelection_(findChild_Widget(dlg, "prefs.uilang"), cstr_String(&d->prefs.uiLanguage));
2337 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize); 2356 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize);
diff --git a/src/defs.h b/src/defs.h
index 650f7e65..c19dd50e 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -62,7 +62,7 @@ enum iFileVersion {
62#define home_Icon "\U0001f3e0" 62#define home_Icon "\U0001f3e0"
63#define edit_Icon "\u270e" 63#define edit_Icon "\u270e"
64#define delete_Icon "\u232b" 64#define delete_Icon "\u232b"
65#define copy_Icon "\u2bba" 65#define copy_Icon "\u2398" //"\u2bba"
66#define check_Icon "\u2714" 66#define check_Icon "\u2714"
67#define ballotCheck_Icon "\U0001f5f9" 67#define ballotCheck_Icon "\U0001f5f9"
68#define inbox_Icon "\U0001f4e5" 68#define inbox_Icon "\U0001f4e5"
diff --git a/src/gmrequest.c b/src/gmrequest.c
index 3dd92eec..1325c025 100644
--- a/src/gmrequest.c
+++ b/src/gmrequest.c
@@ -346,6 +346,7 @@ static const iBlock *replaceVariables_(const iBlock *block) {
346 if (equal_Rangecc(name, "APP_VERSION")) { 346 if (equal_Rangecc(name, "APP_VERSION")) {
347 repl = range_CStr(LAGRANGE_APP_VERSION); 347 repl = range_CStr(LAGRANGE_APP_VERSION);
348 } 348 }
349#if 0
349 else if (startsWith_Rangecc(name, "BT:")) { /* block text */ 350 else if (startsWith_Rangecc(name, "BT:")) { /* block text */
350 repl = range_String(collect_String(renderBlockChars_Text( 351 repl = range_String(collect_String(renderBlockChars_Text(
351 &fontFiraSansRegular_Embedded, 352 &fontFiraSansRegular_Embedded,
@@ -356,12 +357,13 @@ static const iBlock *replaceVariables_(const iBlock *block) {
356 } 357 }
357 else if (startsWith_Rangecc(name, "ST:")) { /* shaded text */ 358 else if (startsWith_Rangecc(name, "ST:")) { /* shaded text */
358 repl = range_String(collect_String(renderBlockChars_Text( 359 repl = range_String(collect_String(renderBlockChars_Text(
359 &fontSymbola_Embedded, 360 &fontSmolEmojiRegular_Embedded,
360 20, 361 20,
361 shading_TextBlockMode, 362 shading_TextBlockMode,
362 &(iString){ iBlockLiteral( 363 &(iString){ iBlockLiteral(
363 name.start + 3, size_Range(&name) - 3, size_Range(&name) - 3) }))); 364 name.start + 3, size_Range(&name) - 3, size_Range(&name) - 3) })));
364 } 365 }
366#endif
365 else if (equal_Rangecc(name, "ALT")) { 367 else if (equal_Rangecc(name, "ALT")) {
366#if defined (iPlatformApple) 368#if defined (iPlatformApple)
367 repl = range_CStr("\u2325"); 369 repl = range_CStr("\u2325");
diff --git a/src/prefs.c b/src/prefs.c
index 96fa3c59..b29b33b5 100644
--- a/src/prefs.c
+++ b/src/prefs.c
@@ -68,6 +68,7 @@ void init_Prefs(iPrefs *d) {
68 init_String(&d->httpProxy); 68 init_String(&d->httpProxy);
69 init_String(&d->downloadDir); 69 init_String(&d->downloadDir);
70 init_String(&d->searchUrl); 70 init_String(&d->searchUrl);
71 init_String(&d->symbolFontPath);
71 /* TODO: Add some platform-specific common locations? */ 72 /* TODO: Add some platform-specific common locations? */
72 if (fileExistsCStr_FileInfo("/etc/ssl/cert.pem")) { /* macOS */ 73 if (fileExistsCStr_FileInfo("/etc/ssl/cert.pem")) { /* macOS */
73 setCStr_String(&d->caFile, "/etc/ssl/cert.pem"); 74 setCStr_String(&d->caFile, "/etc/ssl/cert.pem");
@@ -75,9 +76,13 @@ void init_Prefs(iPrefs *d) {
75 if (fileExistsCStr_FileInfo("/etc/ssl/certs")) { 76 if (fileExistsCStr_FileInfo("/etc/ssl/certs")) {
76 setCStr_String(&d->caPath, "/etc/ssl/certs"); 77 setCStr_String(&d->caPath, "/etc/ssl/certs");
77 } 78 }
79#if defined (iPlatformAppleDesktop)
80 setCStr_String(&d->symbolFontPath, "/System/Library/Fonts/Apple Symbols.ttf");
81#endif
78} 82}
79 83
80void deinit_Prefs(iPrefs *d) { 84void deinit_Prefs(iPrefs *d) {
85 deinit_String(&d->symbolFontPath);
81 deinit_String(&d->searchUrl); 86 deinit_String(&d->searchUrl);
82 deinit_String(&d->geminiProxy); 87 deinit_String(&d->geminiProxy);
83 deinit_String(&d->gopherProxy); 88 deinit_String(&d->gopherProxy);
diff --git a/src/prefs.h b/src/prefs.h
index 5c01ebda..7185c8f9 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -70,6 +70,7 @@ struct Impl_Prefs {
70 iString gopherProxy; 70 iString gopherProxy;
71 iString httpProxy; 71 iString httpProxy;
72 /* Style */ 72 /* Style */
73 iString symbolFontPath;
73 enum iTextFont font; 74 enum iTextFont font;
74 enum iTextFont headingFont; 75 enum iTextFont headingFont;
75 iBool monospaceGemini; 76 iBool monospaceGemini;
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h
index cb32a29c..5c39aae0 100644
--- a/src/ui/inputwidget.h
+++ b/src/ui/inputwidget.h
@@ -63,6 +63,11 @@ void setNotifyEdits_InputWidget (iInputWidget *, iBool notifyEdits);
63void setEatEscape_InputWidget (iInputWidget *, iBool eatEscape); 63void setEatEscape_InputWidget (iInputWidget *, iBool eatEscape);
64 64
65const iString * text_InputWidget (const iInputWidget *); 65const iString * text_InputWidget (const iInputWidget *);
66
67iLocalDef const char *cstrText_InputWidget(const iInputWidget *d) {
68 return cstr_String(text_InputWidget(d));
69}
70
66iInputWidgetContentPadding 71iInputWidgetContentPadding
67 contentPadding_InputWidget (const iInputWidget *); 72 contentPadding_InputWidget (const iInputWidget *);
68 73
diff --git a/src/ui/keys.c b/src/ui/keys.c
index 87a5fb88..17cc0e72 100644
--- a/src/ui/keys.c
+++ b/src/ui/keys.c
@@ -311,7 +311,10 @@ static iBinding *findCommand_Keys_(iKeys *d, const char *command) {
311static void updateLookup_Keys_(iKeys *d) { 311static void updateLookup_Keys_(iKeys *d) {
312 clear_PtrSet(&d->lookup); 312 clear_PtrSet(&d->lookup);
313 iConstForEach(Array, i, &d->bindings) { 313 iConstForEach(Array, i, &d->bindings) {
314 insert_PtrSet(&d->lookup, i.value); 314 const iBinding *bind = i.value;
315 if (~bind->flags & noDirectTrigger_BindFlag) {
316 insert_PtrSet(&d->lookup, i.value);
317 }
315 } 318 }
316} 319}
317 320
@@ -442,9 +445,6 @@ iBool processEvent_Keys(const SDL_Event *ev) {
442 if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) { 445 if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) {
443 const iBinding *bind = find_Keys_(d, ev->key.keysym.sym, keyMods_Sym(ev->key.keysym.mod)); 446 const iBinding *bind = find_Keys_(d, ev->key.keysym.sym, keyMods_Sym(ev->key.keysym.mod));
444 if (bind) { 447 if (bind) {
445 if (bind->flags & noDirectTrigger_BindFlag) {
446 return iFalse;
447 }
448 if (ev->type == SDL_KEYUP) { 448 if (ev->type == SDL_KEYUP) {
449 if (bind->flags & argRelease_BindFlag) { 449 if (bind->flags & argRelease_BindFlag) {
450 postCommandf_Root(root, "%s release:1", cstr_String(&bind->command)); 450 postCommandf_Root(root, "%s release:1", cstr_String(&bind->command));
diff --git a/src/ui/root.c b/src/ui/root.c
index 7a409a75..49925856 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -156,10 +156,10 @@ static const char *pageMenuCStr_ = midEllipsis_Icon;
156/* TODO: A preference for these, maybe? */ 156/* TODO: A preference for these, maybe? */
157static const char *stopSeqCStr_[] = { 157static const char *stopSeqCStr_[] = {
158 /* Corners */ 158 /* Corners */
159 uiTextCaution_ColorEscape "\U0000230c", 159 uiTextCaution_ColorEscape "\U0000231c",
160 uiTextCaution_ColorEscape "\U0000230d", 160 uiTextCaution_ColorEscape "\U0000231d",
161 uiTextCaution_ColorEscape "\U0000230f", 161 uiTextCaution_ColorEscape "\U0000231f",
162 uiTextCaution_ColorEscape "\U0000230e", 162 uiTextCaution_ColorEscape "\U0000231e",
163#if 0 163#if 0
164 /* Rotating arrow */ 164 /* Rotating arrow */
165 uiTextCaution_ColorEscape "\U00002b62", 165 uiTextCaution_ColorEscape "\U00002b62",
diff --git a/src/ui/text.c b/src/ui/text.c
index b0c4f557..0e6a6d32 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32 32
33#include <the_Foundation/array.h> 33#include <the_Foundation/array.h>
34#include <the_Foundation/file.h> 34#include <the_Foundation/file.h>
35#include <the_Foundation/fileinfo.h>
35#include <the_Foundation/hash.h> 36#include <the_Foundation/hash.h>
36#include <the_Foundation/math.h> 37#include <the_Foundation/math.h>
37#include <the_Foundation/stringlist.h> 38#include <the_Foundation/stringlist.h>
@@ -124,10 +125,6 @@ struct Impl_Font {
124 iBool isMonospaced; 125 iBool isMonospaced;
125 iBool manualKernOnly; 126 iBool manualKernOnly;
126 enum iFontSize sizeId; /* used to look up different fonts of matching size */ 127 enum iFontSize sizeId; /* used to look up different fonts of matching size */
127// enum iFontId
128// enum iFontId japaneseFont; /* font to use for Japanese glyphs */
129// enum iFontId chineseFont; /* font to use for Simplified Chinese glyphs */
130// enum iFontId koreanFont; /* font to use for Korean glyphs */
131 uint32_t indexTable[128 - 32]; /* quick ASCII lookup */ 128 uint32_t indexTable[128 - 32]; /* quick ASCII lookup */
132}; 129};
133 130
@@ -155,13 +152,12 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale,
155 d->xScale *= floorf(advance) / advance; 152 d->xScale *= floorf(advance) / advance;
156 } 153 }
157 } 154 }
158 d->vertOffset = height * (1.0f - scale) / 2; 155 d->baseline = ascent * d->yScale;
159 d->baseline = ascent * d->yScale; 156 d->vertOffset = height * (1.0f - scale) / 2;
160 d->sizeId = sizeId; 157 if (scale > 1.0f) {
161// d->symbolsFont = symbolsFont; 158 d->vertOffset /= 2; /* Tweak for Noto Sans Symbols */
162// d->japaneseFont = regularJapanese_FontId; 159 }
163// d->chineseFont = regularChinese_FontId; 160 d->sizeId = sizeId;
164// d->koreanFont = regularKorean_FontId;
165 memset(d->indexTable, 0xff, sizeof(d->indexTable)); 161 memset(d->indexTable, 0xff, sizeof(d->indexTable));
166} 162}
167 163
@@ -215,7 +211,8 @@ struct Impl_Text {
215 iRegExp * ansiEscape; 211 iRegExp * ansiEscape;
216}; 212};
217 213
218static iText text_; 214static iText text_;
215static iBlock *userFont_;
219 216
220static void initFonts_Text_(iText *d) { 217static void initFonts_Text_(iText *d) {
221 const float textSize = fontSize_UI * d->contentFontSize; 218 const float textSize = fontSize_UI * d->contentFontSize;
@@ -321,27 +318,31 @@ static void initFonts_Text_(iText *d) {
321 { &fontIosevkaTermExtended_Embedded, smallMonoSize, 1.0f, contentMonoSmall_FontSize }, 318 { &fontIosevkaTermExtended_Embedded, smallMonoSize, 1.0f, contentMonoSmall_FontSize },
322 { &fontIosevkaTermExtended_Embedded, monoSize, 1.0f, contentMono_FontSize }, 319 { &fontIosevkaTermExtended_Embedded, monoSize, 1.0f, contentMono_FontSize },
323 /* extra content fonts */ 320 /* extra content fonts */
324 { &fontSourceSans3Regular_Embedded, textSize, scaling, contentRegular_FontSize }, 321 { &fontSourceSans3Regular_Embedded, textSize, scaling, contentRegular_FontSize },
325 { &fontIosevkaTermExtended_Embedded, textSize, 0.866f, contentRegular_FontSize }, 322// { &fontIosevkaTermExtended_Embedded, textSize, 0.866f, contentRegular_FontSize },
326 /* symbols and scripts */ 323 /* symbols and scripts */
327#define DEFINE_FONT_SET(data) \ 324#define DEFINE_FONT_SET(data, glyphScale) \
328 { &data, uiSize, 1.0f, uiNormal_FontSize }, \ 325 { (data), uiSize, glyphScale, uiNormal_FontSize }, \
329 { &data, uiSize * 1.125f, 1.0f, uiMedium_FontSize }, \ 326 { (data), uiSize * 1.125f, glyphScale, uiMedium_FontSize }, \
330 { &data, uiSize * 1.333f, 1.0f, uiBig_FontSize }, \ 327 { (data), uiSize * 1.333f, glyphScale, uiBig_FontSize }, \
331 { &data, uiSize * 1.666f, 1.0f, uiLarge_FontSize }, \ 328 { (data), uiSize * 1.666f, glyphScale, uiLarge_FontSize }, \
332 { &data, textSize, 1.0f, contentRegular_FontSize }, \ 329 { (data), textSize, glyphScale, contentRegular_FontSize }, \
333 { &data, textSize * 1.200f, 1.0f, contentMedium_FontSize }, \ 330 { (data), textSize * 1.200f, glyphScale, contentMedium_FontSize }, \
334 { &data, textSize * 1.333f, 1.0f, contentBig_FontSize }, \ 331 { (data), textSize * 1.333f, glyphScale, contentBig_FontSize }, \
335 { &data, textSize * 1.666f, 1.0f, contentLarge_FontSize }, \ 332 { (data), textSize * 1.666f, glyphScale, contentLarge_FontSize }, \
336 { &data, textSize * 2.000f, 1.0f, contentHuge_FontSize }, \ 333 { (data), textSize * 2.000f, glyphScale, contentHuge_FontSize }, \
337 { &data, smallMonoSize, 1.0f, contentMonoSmall_FontSize }, \ 334 { (data), smallMonoSize, glyphScale, contentMonoSmall_FontSize }, \
338 { &data, monoSize, 1.0f, contentMono_FontSize } 335 { (data), monoSize, glyphScale, contentMono_FontSize }
339 DEFINE_FONT_SET(fontSymbola_Embedded), 336 DEFINE_FONT_SET(userFont_ ? userFont_ : &fontIosevkaTermExtended_Embedded, 1.0f),
340 DEFINE_FONT_SET(fontNotoEmojiRegular_Embedded), 337 DEFINE_FONT_SET(&fontIosevkaTermExtended_Embedded, 0.866f),
341 DEFINE_FONT_SET(fontNotoSansJPRegular_Embedded), 338 DEFINE_FONT_SET(&fontNotoSansSymbolsRegular_Embedded, 1.45f),
342 DEFINE_FONT_SET(fontNotoSansSCRegular_Embedded), 339 DEFINE_FONT_SET(&fontNotoSansSymbols2Regular_Embedded, 1.45f),
343 DEFINE_FONT_SET(fontNanumGothicRegular_Embedded), /* TODO: should use Noto Sans here, too */ 340 DEFINE_FONT_SET(&fontSmolEmojiRegular_Embedded, 1.0f),
344 DEFINE_FONT_SET(fontNotoSansArabicUIRegular_Embedded), 341 DEFINE_FONT_SET(&fontNotoEmojiRegular_Embedded, 1.0f),
342 DEFINE_FONT_SET(&fontNotoSansJPRegular_Embedded, 1.0f),
343 DEFINE_FONT_SET(&fontNotoSansSCRegular_Embedded, 1.0f),
344 DEFINE_FONT_SET(&fontNanumGothicRegular_Embedded, 1.0f), /* TODO: should use Noto Sans here, too */
345 DEFINE_FONT_SET(&fontNotoSansArabicUIRegular_Embedded, 1.0f),
345 }; 346 };
346 iForIndices(i, fontData) { 347 iForIndices(i, fontData) {
347 iFont *font = &d->fonts[i]; 348 iFont *font = &d->fonts[i];
@@ -401,8 +402,28 @@ static void deinitCache_Text_(iText *d) {
401 SDL_DestroyTexture(d->cache); 402 SDL_DestroyTexture(d->cache);
402} 403}
403 404
405void loadUserFonts_Text(void) {
406 if (userFont_) {
407 delete_Block(userFont_);
408 userFont_ = NULL;
409 }
410 /* Load the system font. */
411 const iPrefs *prefs = prefs_App();
412 if (!isEmpty_String(&prefs->symbolFontPath)) {
413 iFile *f = new_File(&prefs->symbolFontPath);
414 if (open_File(f, readOnly_FileMode)) {
415 userFont_ = readAll_File(f);
416 }
417 else {
418 fprintf(stderr, "[Text] failed to open: %s\n", cstr_String(&prefs->symbolFontPath));
419 }
420 iRelease(f);
421 }
422}
423
404void init_Text(SDL_Renderer *render) { 424void init_Text(SDL_Renderer *render) {
405 iText *d = &text_; 425 iText *d = &text_;
426 loadUserFonts_Text();
406 d->contentFont = nunito_TextFont; 427 d->contentFont = nunito_TextFont;
407 d->headingFont = nunito_TextFont; 428 d->headingFont = nunito_TextFont;
408 d->contentFontSize = contentScale_Text_; 429 d->contentFontSize = contentScale_Text_;
@@ -542,14 +563,27 @@ static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) {
542} 563}
543 564
544iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { 565iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
566 /* Smol Emoji overrides all other fonts. */
567 if (ch != 0x20) {
568 iFont *smol = font_Text_(smolEmoji_FontId + d->sizeId);
569 if (smol != d && (*glyphIndex = glyphIndex_Font_(smol, ch)) != 0) {
570 return smol;
571 }
572 }
545 if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { 573 if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) {
546 return d; 574 return d;
547 } 575 }
548 /* Not defined in current font, try Noto Emoji (for selected characters). */ 576 const int fallbacks[] = {
549 if ((ch >= 0x1f300 && ch < 0x1f600) || (ch >= 0x1f680 && ch <= 0x1f6c5)) { 577 smolEmoji_FontId,
550 iFont *emoji = font_Text_(emoji_FontId + d->sizeId); 578 notoEmoji_FontId,
551 if (emoji != d && (*glyphIndex = glyphIndex_Font_(emoji, ch)) != 0) { 579 symbols2_FontId,
552 return emoji; 580 symbols_FontId
581 };
582 /* First fallback is Smol Emoji. */
583 iForIndices(i, fallbacks) {
584 iFont *fallback = font_Text_(fallbacks[i] + d->sizeId);
585 if (fallback != d && (*glyphIndex = glyphIndex_Font_(fallback, ch)) != 0) {
586 return fallback;
553 } 587 }
554 } 588 }
555 /* Try Simplified Chinese. */ 589 /* Try Simplified Chinese. */
@@ -588,13 +622,18 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
588 return d; 622 return d;
589 } 623 }
590#endif 624#endif
591 /* Fall back to Symbola for anything else. */ 625 /* User's symbols font. */ {
592 iFont *font = font_Text_(symbols_FontId + d->sizeId); 626 iFont *sys = font_Text_(userSymbols_FontId + d->sizeId);
593 *glyphIndex = glyphIndex_Font_(font, ch); 627 if (sys != d && (*glyphIndex = glyphIndex_Font_(sys, ch)) != 0) {
594// if (!*glyphIndex) { 628 return sys;
595// fprintf(stderr, "failed to find %08x (%lc)\n", ch, ch); fflush(stderr); 629 }
596// } 630 }
597 return font; 631// iFont *font = font_Text_(iosevka_FontId + d->sizeId);
632// *glyphIndex = glyphIndex_Font_(font, ch);
633 if (!*glyphIndex) {
634 fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr);
635 }
636 return d;
598} 637}
599 638
600static iGlyph *glyph_Font_(iFont *d, iChar ch) { 639static iGlyph *glyph_Font_(iFont *d, iChar ch) {
diff --git a/src/ui/text.h b/src/ui/text.h
index 044ddd32..1bb60e8b 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -68,11 +68,14 @@ enum iFontId {
68 monospace_FontId, 68 monospace_FontId,
69 /* extra content fonts */ 69 /* extra content fonts */
70 defaultContentSized_FontId, /* UI font but sized to regular_FontId */ 70 defaultContentSized_FontId, /* UI font but sized to regular_FontId */
71 regularMonospace_FontId,
72 /* symbols and scripts */ 71 /* symbols and scripts */
73 symbols_FontId, 72 userSymbols_FontId,
74 emoji_FontId = symbols_FontId + max_FontSize, 73 iosevka_FontId = userSymbols_FontId + max_FontSize,
75 japanese_FontId = emoji_FontId + max_FontSize, 74 symbols_FontId = iosevka_FontId + max_FontSize,
75 symbols2_FontId = symbols_FontId + max_FontSize,
76 smolEmoji_FontId = symbols2_FontId + max_FontSize,
77 notoEmoji_FontId = smolEmoji_FontId + max_FontSize,
78 japanese_FontId = notoEmoji_FontId + max_FontSize,
76 chineseSimplified_FontId = japanese_FontId + max_FontSize, 79 chineseSimplified_FontId = japanese_FontId + max_FontSize,
77 korean_FontId = chineseSimplified_FontId + max_FontSize, 80 korean_FontId = chineseSimplified_FontId + max_FontSize,
78 arabic_FontId = korean_FontId + max_FontSize, 81 arabic_FontId = korean_FontId + max_FontSize,
@@ -91,7 +94,7 @@ enum iFontId {
91 uiInput_FontId = defaultMedium_FontId, 94 uiInput_FontId = defaultMedium_FontId,
92 uiContent_FontId = defaultMedium_FontId, 95 uiContent_FontId = defaultMedium_FontId,
93 uiContentBold_FontId = defaultMediumBold_FontId, 96 uiContentBold_FontId = defaultMediumBold_FontId,
94 uiContentSymbols_FontId = symbols_FontId + uiMedium_FontSize, 97 uiContentSymbols_FontId = symbols_FontId + uiMedium_FontSize,
95 /* Document fonts: */ 98 /* Document fonts: */
96 paragraph_FontId = regular_FontId, 99 paragraph_FontId = regular_FontId,
97 firstParagraph_FontId = medium_FontId, 100 firstParagraph_FontId = medium_FontId,
@@ -102,6 +105,7 @@ enum iFontId {
102 heading2_FontId = largeBold_FontId, 105 heading2_FontId = largeBold_FontId,
103 heading3_FontId = big_FontId, 106 heading3_FontId = big_FontId,
104 banner_FontId = largeLight_FontId, 107 banner_FontId = largeLight_FontId,
108 regularMonospace_FontId = iosevka_FontId + contentRegular_FontSize
105}; 109};
106 110
107iLocalDef iBool isJapanese_FontId(enum iFontId id) { 111iLocalDef iBool isJapanese_FontId(enum iFontId id) {
@@ -124,6 +128,8 @@ extern int gap_Text; /* affected by content font size */
124void init_Text (SDL_Renderer *); 128void init_Text (SDL_Renderer *);
125void deinit_Text (void); 129void deinit_Text (void);
126 130
131void loadUserFonts_Text (void); /* based on Prefs */
132
127void setContentFont_Text (enum iTextFont font); 133void setContentFont_Text (enum iTextFont font);
128void setHeadingFont_Text (enum iTextFont font); 134void setHeadingFont_Text (enum iTextFont font);
129void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */ 135void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */
diff --git a/src/ui/util.c b/src/ui/util.c
index 6c87ba26..977eba9d 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -1525,6 +1525,7 @@ void updatePreferencesLayout_Widget(iWidget *prefs) {
1525 static const char *inputIds[] = { 1525 static const char *inputIds[] = {
1526 "prefs.searchurl", 1526 "prefs.searchurl",
1527 "prefs.downloads", 1527 "prefs.downloads",
1528 "prefs.userfont",
1528 "prefs.ca.file", 1529 "prefs.ca.file",
1529 "prefs.ca.path", 1530 "prefs.ca.path",
1530 "prefs.proxy.gemini", 1531 "prefs.proxy.gemini",
@@ -1786,6 +1787,7 @@ iWidget *makePreferences_Widget(void) {
1786 updateSize_LabelWidget((iLabelWidget *) tog); 1787 updateSize_LabelWidget((iLabelWidget *) tog);
1787 } 1788 }
1788 addChildFlags_Widget(values, iClob(boldLink), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); 1789 addChildFlags_Widget(values, iClob(boldLink), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);
1790 addPrefsInputWithHeading_(headings, values, "prefs.userfont", iClob(new_InputWidget(0)));
1789 } 1791 }
1790 makeTwoColumnHeading_("${heading.prefs.paragraph}", headings, values); 1792 makeTwoColumnHeading_("${heading.prefs.paragraph}", headings, values);
1791 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.linewidth}"))); 1793 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.linewidth}")));