summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Persch <chpe@src.gnome.org>2022-09-26 17:16:20 +0200
committerChristian Persch <chpe@src.gnome.org>2022-09-26 17:16:20 +0200
commit431f5576c714a2022b5d202a04ddebf27e23fbaa (patch)
tree98536cd7165c87b3cb6e77c34d6cfb0acc08ebe4
parent8ef3f6b2f8043d28cbc82520eb094f09333b26ae (diff)
downloadvte-wip/font-measure-fontconfig.tar.gz
fonts: Use fontconfig to get characters to measurewip/font-measure-fontconfig
This was intended to improve GNOME/kgx#200 when the font falls back to a non-monospace font for some glyphs in the user's main language, but it turns out some of the tamil characters have wcwidth 1 but the glyphs are about width 3 times the width of latin characters. Ref: https://gitlab.gnome.org/GNOME/console/-/issues/200
-rw-r--r--meson.build16
-rw-r--r--src/fontconfig-glue.hh29
-rw-r--r--src/fonts-pangocairo.cc112
-rw-r--r--src/meson.build7
4 files changed, 131 insertions, 33 deletions
diff --git a/meson.build b/meson.build
index f36f93d5..4df1ecc0 100644
--- a/meson.build
+++ b/meson.build
@@ -46,6 +46,7 @@ gtk4_min_req_version = '4.0'
gtk4_max_allowed_version = '4.0'
fribidi_req_version = '1.0.0'
+fontconfig_req_version = '2.0'
gio_req_version = '2.52.0'
glib_req_version = '2.52.0'
glib_min_req_version = '2.52'
@@ -612,13 +613,14 @@ endforeach
# Dependencies
-gio_dep = dependency('gio-2.0', version: '>=' + gio_req_version)
-glib_dep = dependency('glib-2.0', version: '>=' + glib_req_version)
-gobject_dep = dependency('gobject-2.0')
-pango_dep = dependency('pango', version: '>=' + pango_req_version)
-pcre2_dep = dependency('libpcre2-8', version: '>=' + pcre2_req_version)
-pthreads_dep = dependency('threads')
-zlib_dep = dependency('zlib')
+fontconfig_dep = dependency('fontconfig', version: '>=' + fontconfig_req_version)
+gio_dep = dependency('gio-2.0', version: '>=' + gio_req_version)
+glib_dep = dependency('glib-2.0', version: '>=' + glib_req_version)
+gobject_dep = dependency('gobject-2.0')
+pango_dep = dependency('pango', version: '>=' + pango_req_version)
+pcre2_dep = dependency('libpcre2-8', version: '>=' + pcre2_req_version)
+pthreads_dep = dependency('threads')
+zlib_dep = dependency('zlib')
if get_option('fribidi')
fribidi_dep = dependency('fribidi', version: '>=' + fribidi_req_version)
diff --git a/src/fontconfig-glue.hh b/src/fontconfig-glue.hh
new file mode 100644
index 00000000..bda9e410
--- /dev/null
+++ b/src/fontconfig-glue.hh
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2020 Christian Persch
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <fontconfig/fontconfig.h>
+
+#include "std-glue.hh"
+
+namespace vte {
+
+VTE_DECLARE_FREEABLE(FcChar8, FcStrFree);
+VTE_DECLARE_FREEABLE(FcCharSet, FcCharSetDestroy);
+
+} // namespace vte
diff --git a/src/fonts-pangocairo.cc b/src/fonts-pangocairo.cc
index 8d0cb2a3..c348af3a 100644
--- a/src/fonts-pangocairo.cc
+++ b/src/fonts-pangocairo.cc
@@ -23,16 +23,7 @@
#include "debug.h"
#include "vtedefines.hh"
-/* Have a space between letters to make sure ligatures aren't used when caching the glyphs: bug 793391. */
-#define VTE_DRAW_SINGLE_WIDE_CHARACTERS \
- " ! \" # $ % & ' ( ) * + , - . / " \
- "0 1 2 3 4 5 6 7 8 9 " \
- ": ; < = > ? @ " \
- "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z " \
- "[ \\ ] ^ _ ` " \
- "a b c d e f g h i j k l m n o p q r s t u v w x y z " \
- "{ | } ~ " \
- ""
+#include "fontconfig-glue.hh"
static inline bool
_vte_double_equal(double a,
@@ -167,29 +158,100 @@ FontInfo::cache_ascii()
#endif
}
+static int
+unichar_width(unsigned c)
+{
+ if (c < 0x80)
+ return 1;
+ if (g_unichar_iszerowidth(c)) [[unlikely]]
+ return 0;
+ if (g_unichar_iswide(c))
+ return 2;
+ if (g_unichar_iswide_cjk(c))
+ return 2;
+ return 1;
+}
+
void
FontInfo::measure_font()
{
- PangoRectangle logical;
+ auto charset = vte::take_freeable(FcCharSetCreate());
+ // Directly measure U+0020..U+003F which aren't included in the orthographies
+ for (auto c = FcChar32{0x20}; c < FcChar32{0x40}; ++c)
+ FcCharSetAddChar(charset.get(), c);
+
+ // Create the union of the orthographies of the user languages
+ auto const langs = g_get_language_names();
+ for (auto i = 0; langs[i]; ++i) {
+ auto const nlang = vte::take_freeable(FcLangNormalize(reinterpret_cast<FcChar8 const*>(langs[i])));
+ if (!nlang)
+ continue;
+
+ auto const lcharset = FcLangGetCharSet(nlang.get());
+ if (!lcharset)
+ continue;
+
+ if (!FcCharSetMerge(charset.get(), lcharset, nullptr))
+ continue;
+ }
- /* Measure U+0021..U+007E individually instead of all together and then
- * averaging. For monospace fonts, the results should be the same, but
- * if the user (by design, or trough mis-configuration) uses a proportional
- * font, the latter method will greatly underestimate the required width,
- * leading to unreadable, overlapping characters.
- * https://gitlab.gnome.org/GNOME/vte/issues/138
- */
+
+ // Measure the characters individually instead of all together and then
+ // averaging. For monospace fonts, the results should be the same, but
+ // if the user (by design, or trough mis-configuration) uses a proportional
+ // font, the latter method will greatly underestimate the required width,
+ // leading to unreadable, overlapping characters.
+ // https://gitlab.gnome.org/GNOME/vte/issues/138
+
+ auto const n = FcCharSetCount(charset.get());
+ auto str = std::string{};
+ str.reserve(n * (4 + 1) + 1);
+
+ PangoRectangle logical;
auto max_width = 1;
auto max_height = 1;
- for (char c = 0x21; c < 0x7f; ++c) {
- pango_layout_set_text(m_layout.get(), &c, 1);
- pango_layout_get_extents(m_layout.get(), nullptr, &logical);
- max_width = std::max(max_width, PANGO_PIXELS_CEIL(logical.width));
- max_height = std::max(max_height, PANGO_PIXELS_CEIL(logical.height));
+
+ FcChar32 map[FC_CHARSET_MAP_SIZE];
+ auto next = FcChar32{};
+ for (auto base = FcCharSetFirstPage(charset.get(), map, &next);
+ base != FC_CHARSET_DONE;
+ base = FcCharSetNextPage(charset.get(), map, &next)) {
+
+ for (auto i = 0u; i < FC_CHARSET_MAP_SIZE; ++i) {
+ auto const v = map[i];
+ if (!v)
+ continue;
+
+ auto const ci = base + (i << 5);
+ for (auto j = 0u, mask = 1u; j < 32; ++j, mask <<= 1) {
+ if (!(v & mask))
+ continue;
+
+ auto const c = ci + j;
+ auto const cw = unichar_width(c);
+ if (c <= 0) [[unlikely]]
+ continue;
+
+ char utf8[7];
+ auto const len = g_unichar_to_utf8(c, utf8);
+
+ pango_layout_set_text(m_layout.get(), utf8, len);
+ pango_layout_get_extents(m_layout.get(), nullptr, &logical);
+
+ auto const width = PANGO_PIXELS_CEIL(logical.width);
+ max_width = std::max(max_width, cw > 1 ? (width + 1) / 2 : width);
+ max_height = std::max(max_height, PANGO_PIXELS_CEIL(logical.height));
+
+ // g_print("Character U+%04X %s charwidth %d width %d\n", c, utf8, cw, width);
+
+ str.append(utf8, len);
+ str.push_back(' ');
+ }
+ }
}
- /* Use the sample text to get the baseline */
- pango_layout_set_text(m_layout.get(), VTE_DRAW_SINGLE_WIDE_CHARACTERS, -1);
+ /* Use the combined orthography text to get the baseline */
+ pango_layout_set_text(m_layout.get(), str.c_str(), str.size());
pango_layout_get_extents(m_layout.get(), nullptr, &logical);
/* We don't do CEIL for width since we are averaging;
* rounding is more accurate */
diff --git a/src/meson.build b/src/meson.build
index b3f934a2..6a8ca42c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -28,6 +28,10 @@ debug_sources = files(
'debug.h',
)
+fontconfig_glue_sources = files(
+ 'fontconfig-glue.hh',
+)
+
glib_glue_sources = files(
'glib-glue.cc',
'glib-glue.hh',
@@ -167,7 +171,7 @@ vte_glue_sources = files(
'vte-glue.hh',
)
-libvte_common_sources = config_sources + debug_sources + glib_glue_sources + gtk_glue_sources + libc_glue_sources + modes_sources + pango_glue_sources + parser_sources + pastify_sources + pcre2_glue_sources + pty_sources + refptr_sources + regex_sources + std_glue_sources + utf8_sources + vte_glue_sources + files(
+libvte_common_sources = config_sources + debug_sources + fontconfig_glue_sources + glib_glue_sources + gtk_glue_sources + libc_glue_sources + modes_sources + pango_glue_sources + parser_sources + pastify_sources + pcre2_glue_sources + pty_sources + refptr_sources + regex_sources + std_glue_sources + utf8_sources + vte_glue_sources + files(
'attr.hh',
'bidi.cc',
'bidi.hh',
@@ -281,6 +285,7 @@ libvte_common_public_deps = [
]
libvte_common_deps = libvte_common_public_deps + [
+ fontconfig_dep,
fribidi_dep,
gnutls_dep,
icu_dep,