diff options
author | Christian Persch <chpe@src.gnome.org> | 2022-09-26 17:16:20 +0200 |
---|---|---|
committer | Christian Persch <chpe@src.gnome.org> | 2022-09-26 17:16:20 +0200 |
commit | 431f5576c714a2022b5d202a04ddebf27e23fbaa (patch) | |
tree | 98536cd7165c87b3cb6e77c34d6cfb0acc08ebe4 | |
parent | 8ef3f6b2f8043d28cbc82520eb094f09333b26ae (diff) | |
download | vte-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.build | 16 | ||||
-rw-r--r-- | src/fontconfig-glue.hh | 29 | ||||
-rw-r--r-- | src/fonts-pangocairo.cc | 112 | ||||
-rw-r--r-- | 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, |