From ef07b24ad3431496372d3cfd2884b0609b940e27 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sat, 12 Dec 2020 15:37:20 +0200 Subject: New monospace font; flexible mono spacing Iosevka replaces Fira Mono as the monospace font. Emojis are now allowed to be wider than normal monospace glyphs. However, if whitespace permits, the original monospacing is restored. IssueID #73 --- CMakeLists.txt | 2 +- res/about/license.gmi | 3 ++- res/about/version.gmi | 5 ++-- res/fonts/FiraMono-Regular.ttf | Bin 174632 -> 0 bytes res/fonts/IosevkaTerm-Extended.ttf | Bin 0 -> 1503504 bytes src/ui/text.c | 51 ++++++++++++++++++++++++------------- src/ui/text.h | 21 +++++++-------- 7 files changed, 51 insertions(+), 31 deletions(-) delete mode 100644 res/fonts/FiraMono-Regular.ttf create mode 100644 res/fonts/IosevkaTerm-Extended.ttf diff --git a/CMakeLists.txt b/CMakeLists.txt index 995432cb..394f1b6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,7 @@ set (EMBED_RESOURCES res/fonts/Tinos-Regular.ttf res/fonts/Tinos-Italic.ttf res/fonts/Tinos-Bold.ttf - res/fonts/FiraMono-Regular.ttf + res/fonts/IosevkaTerm-Extended.ttf res/fonts/FiraSans-Bold.ttf res/fonts/FiraSans-Italic.ttf res/fonts/FiraSans-Light.ttf diff --git a/res/about/license.gmi b/res/about/license.gmi index 0ea0897a..d5ffe3c4 100644 --- a/res/about/license.gmi +++ b/res/about/license.gmi @@ -100,7 +100,8 @@ The libunistring library is covered by the GNU Lesser General Public License (LG This application uses fonts licensed under the Open Font License. -=> https://github.com/mozilla/Fira/blob/master/LICENSE Fira Sans, Fira Mono +=> https://github.com/mozilla/Fira/blob/master/LICENSE Fira Sans +=> https://typeof.net/Iosevka/ Iosevka => https://github.com/googlefonts/literata/blob/master/OFL.txt Literata => https://github.com/google/fonts/blob/master/ofl/nanumgothic/OFL.txt Nanum Gothic => https://www.google.com/get/noto/help/cjk/ Noto Sans CJK JP diff --git a/res/about/version.gmi b/res/about/version.gmi index 42475e13..1932baa1 100644 --- a/res/about/version.gmi +++ b/res/about/version.gmi @@ -8,9 +8,9 @@ ## 0.13 * Support for Internationalized Domain Names (IDN) in network requests. -* IDNs show up in decoded form in the UI. +* UI shows decoded IDNs. * Percent-encoded Unicode characters in URL paths are decoded for the UI, and encoded in outgoing requests. -* Added option to disable decoding of percent-encoded paths. +* Added option to disable decoding of percent-encoded paths for the UI. * Quick search via URL bar shows entries from subscribed feeds. * Added keybindings for zooming. * Improved usability of page content searching (${CTRL+}F, Escape). @@ -19,6 +19,7 @@ * Fixed handling of Unicode joiners and modifiers (by ignoring them, since we lack the glyphs). * Fixed a layout issue with sidebars where the bottommost content line was occasionally not visible. * Fixed exit when a hook program didn't read its input. +* Fixed use of variable-width fonts in input fields. * Fixed crash when using an identity (with LibreSSL on OpenBSD). ## 0.12.1 diff --git a/res/fonts/FiraMono-Regular.ttf b/res/fonts/FiraMono-Regular.ttf deleted file mode 100644 index 59e1e1a1..00000000 Binary files a/res/fonts/FiraMono-Regular.ttf and /dev/null differ diff --git a/res/fonts/IosevkaTerm-Extended.ttf b/res/fonts/IosevkaTerm-Extended.ttf new file mode 100644 index 00000000..16989a85 Binary files /dev/null and b/res/fonts/IosevkaTerm-Extended.ttf differ diff --git a/src/ui/text.c b/src/ui/text.c index 4229217c..ec240c75 100644 --- a/src/ui/text.c +++ b/src/ui/text.c @@ -120,10 +120,10 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale, d->xScale *= floorf(advance) / advance; } } - d->vertOffset = height * (1.0f - scale) / 2; + d->vertOffset = height * (1.0f - scale) / 2; int ascent; stbtt_GetFontVMetrics(&d->font, &ascent, NULL, NULL); - d->baseline = ceil(ascent * d->yScale); + d->baseline = /*ceil*/(ascent * d->yScale); d->symbolsFont = symbolsFont; d->japaneseFont = regularJapanese_FontId; d->koreanFont = regularKorean_FontId; @@ -176,8 +176,8 @@ static iText text_; static void initFonts_Text_(iText *d) { const float textSize = fontSize_UI * d->contentFontSize; - const float monoSize = fontSize_UI * d->contentFontSize / contentScale_Text_ * 0.866f; - const float smallMonoSize = monoSize * 0.866f; + const float monoSize = textSize * 0.71f; //fontSize_UI * d->contentFontSize / contentScale_Text_ * 1.0f; //0.866f; + const float smallMonoSize = monoSize * 0.8f; const iBlock *regularFont = &fontNunitoRegular_Embedded; const iBlock *italicFont = &fontNunitoLightItalic_Embedded; const iBlock *h12Font = &fontNunitoExtraBold_Embedded; @@ -226,12 +226,12 @@ static void initFonts_Text_(iText *d) { { &fontSourceSansProRegular_Embedded, fontSize_UI, 1.0f, defaultSymbols_FontId }, { &fontSourceSansProRegular_Embedded, fontSize_UI * 1.125f, 1.0f, defaultMediumSymbols_FontId }, { &fontSourceSansProRegular_Embedded, fontSize_UI * 1.666f, 1.0f, defaultLargeSymbols_FontId }, - { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f, 1.0f, defaultSymbols_FontId }, + { &fontIosevkaTermExtended_Embedded, fontSize_UI * 0.866f, 1.0f, defaultSymbols_FontId }, { &fontSourceSansProRegular_Embedded, textSize, scaling, symbols_FontId }, /* content fonts */ { regularFont, textSize, scaling, symbols_FontId }, - { &fontFiraMonoRegular_Embedded, monoSize, 1.0f, monospaceSymbols_FontId }, - { &fontFiraMonoRegular_Embedded, smallMonoSize, 1.0f, monospaceSmallSymbols_FontId }, + { &fontIosevkaTermExtended_Embedded, monoSize, 1.0f, monospaceSymbols_FontId }, + { &fontIosevkaTermExtended_Embedded, smallMonoSize, 1.0f, monospaceSmallSymbols_FontId }, { regularFont, textSize * 1.200f, scaling, mediumSymbols_FontId }, { h3Font, textSize * 1.333f, h123Scaling, bigSymbols_FontId }, { italicFont, textSize, scaling, symbols_FontId }, @@ -239,7 +239,7 @@ static void initFonts_Text_(iText *d) { { h12Font, textSize * 2.000f, h123Scaling, hugeSymbols_FontId }, { lightFont, textSize * 1.666f, lightScaling, largeSymbols_FontId }, /* monospace content fonts */ - { &fontFiraMonoRegular_Embedded, textSize, 0.8f, symbols_FontId }, + { &fontIosevkaTermExtended_Embedded, textSize, 0.866f, symbols_FontId }, /* symbol fonts */ { &fontSymbola_Embedded, fontSize_UI, 1.0f, defaultSymbols_FontId }, { &fontSymbola_Embedded, fontSize_UI * 1.125f, 1.0f, defaultMediumSymbols_FontId }, @@ -288,7 +288,7 @@ static void initFonts_Text_(iText *d) { fontData[i].size, fontData[i].scaling, fontData[i].symbolsFont, - fontData[i].ttf == &fontFiraMonoRegular_Embedded); + fontData[i].ttf == &fontIosevkaTermExtended_Embedded); if (i == default_FontId || i == defaultMedium_FontId) { font->manualKernOnly = iTrue; } @@ -636,6 +636,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe iRect bounds = zero_Rect(); const iInt2 orig = pos; float xpos = pos.x; + float xposExtend = pos.x; /* allows wide glyphs to use more space; restored by whitespace */ float xposMax = xpos; float monoAdvance = 0; iAssert(xposLimit == 0 || isMeasuring_(mode)); @@ -668,6 +669,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe } } iChar ch = nextChar_(&chPos, text.end); + iBool isEmoji = isEmoji_Char(ch); if (ch == 0x200d) { /* zero-width joiner */ /* We don't have the composited Emojis. */ if (isEmoji_Char(prevCh)) { @@ -676,9 +678,19 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe ch = nextChar_(&chPos, text.end); } } +#if 0 + iChar nextCh = 0; { + /* TODO: Since we're peeking ahead, should use this on the next loop iteration. */ + const char *ncp = chPos; + nextCh = nextChar_(&ncp, text.end); + } + /* VS15: Peek ahead and treat as Emoji font. */ + if (nextCh == emojiVariationSelector_Char) { + isEmoji = iTrue; + } +#endif if (isVariationSelector_Char(ch)) { - /* TODO: VS15: Should peek ahead for this and prefer the Emoji font. */ - ch = nextChar_(&chPos, text.end); /* just ignore */ + ch = nextChar_(&chPos, text.end); /* skip it */ } /* Special instructions. */ { if (ch == 0xad) { /* soft hyphen */ @@ -703,7 +715,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe } } if (ch == '\n') { - xpos = pos.x; + xpos = xposExtend = pos.x; pos.y += d->height; prevCh = ch; continue; @@ -711,6 +723,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe if (ch == '\t') { const int tabStopWidth = d->height * 8; xpos = pos.x + ((int) ((xpos - pos.x) / tabStopWidth) + 1) * tabStopWidth; + xposExtend = iMax(xposExtend, xpos); prevCh = 0; continue; } @@ -728,7 +741,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe } } const iGlyph *glyph = glyph_Font_(d, ch); - int x1 = xpos; + int x1 = iMax(xpos, xposExtend); const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0; int x2 = x1 + glyph->rect[hoff].size.x; /* Out of the allotted space? */ @@ -765,11 +778,13 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe bounds.size.x = iMax(bounds.size.x, x2 - orig.x); bounds.size.y = iMax(bounds.size.y, pos.y + glyph->font->height - orig.y); } + /* Symbols and emojis are NOT monospaced, so must conform when the primary font + is monospaced. Except with Japanese script, that's larger than the normal monospace. */ const iBool useMonoAdvance = monoAdvance > 0 && !isJapanese_FontId(fontId_Text_(glyph->font)); const float advance = (useMonoAdvance ? monoAdvance : glyph->advance); if (!isMeasuring_(mode)) { - if (useMonoAdvance && dst.w > advance && glyph->font != d) { + if (useMonoAdvance && dst.w > advance && glyph->font != d && !isEmoji) { /* Glyphs from a different font may need recentering to look better. */ dst.x -= (dst.w - advance) / 2; } @@ -784,10 +799,12 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe } SDL_RenderCopy(text_.render, text_.cache, &src, &dst); } - /* Symbols and emojis are NOT monospaced, so must conform when the primary font - is monospaced. Except with Japanese script, that's larger than the normal monospace. */ xpos += advance; - xposMax = iMax(xposMax, xpos); + if (!isSpace_Char(ch)) { + xposExtend += isEmoji ? glyph->advance : advance; + } + xposExtend = iMax(xposExtend, xpos); + xposMax = iMax(xposMax, xposExtend); if (continueFrom_out && ((mode & noWrapFlag_RunMode) || isWrapBoundary_(prevCh, ch))) { lastWordEnd = chPos; } diff --git a/src/ui/text.h b/src/ui/text.h index 6728bb62..5867a84b 100644 --- a/src/ui/text.h +++ b/src/ui/text.h @@ -127,7 +127,8 @@ iLocalDef iBool isDefaultIgnorable_Char(iChar c) { c == 0xfeff; } iLocalDef iBool isEmoji_Char(iChar c) { - return (c >= 0x1f300 && c < 0x1f700) || (c >= 0x1f900 && c <= 0x1f9ff); + return (c >= 0x1f300 && c < 0x1f700) || (c >= 0x1f7e0 && c <= 0x1f7eb) || + (c >= 0x1f900 && c <= 0x1f9ff); } #define emojiVariationSelector_Char ((iChar) 0xfe0f) @@ -141,21 +142,21 @@ enum iTextFont { extern int gap_Text; /* affected by content font size */ -void init_Text (SDL_Renderer *); -void deinit_Text (void); +void init_Text (SDL_Renderer *); +void deinit_Text (void); void setContentFont_Text (enum iTextFont font); void setHeadingFont_Text (enum iTextFont font); void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */ void resetFonts_Text (void); -int lineHeight_Text (int fontId); -iInt2 measure_Text (int fontId, const char *text); -iInt2 measureRange_Text (int fontId, iRangecc text); -iRect visualBounds_Text (int fontId, iRangecc text); -iInt2 advance_Text (int fontId, const char *text); -iInt2 advanceN_Text (int fontId, const char *text, size_t n); /* `n` in characters */ -iInt2 advanceRange_Text (int fontId, iRangecc text); +int lineHeight_Text (int fontId); +iInt2 measure_Text (int fontId, const char *text); +iInt2 measureRange_Text (int fontId, iRangecc text); +iRect visualBounds_Text (int fontId, iRangecc text); +iInt2 advance_Text (int fontId, const char *text); +iInt2 advanceN_Text (int fontId, const char *text, size_t n); /* `n` in characters */ +iInt2 advanceRange_Text (int fontId, iRangecc text); iInt2 advanceWrapRange_Text (int fontId, int maxWidth, iRangecc text); iInt2 tryAdvance_Text (int fontId, iRangecc text, int width, const char **endPos); -- cgit v1.2.3