[vte/wip/font-measure-fontconfig] fonts: Use fontconfig to get characters to measure




commit 431f5576c714a2022b5d202a04ddebf27e23fbaa
Author: Christian Persch <chpe src gnome org>
Date:   Mon Sep 26 17:16:20 2022 +0200

    fonts: Use fontconfig to get characters to measure
    
    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

 meson.build             |  16 ++++---
 src/fontconfig-glue.hh  |  29 +++++++++++++
 src/fonts-pangocairo.cc | 112 +++++++++++++++++++++++++++++++++++++-----------
 src/meson.build         |   7 ++-
 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,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]