1# -*- coding: utf-8 -*- 2# vim:et sts=4 sw=4 3# 4# ibus-typing-booster - A completion input method for IBus 5# 6# Copyright (c) 2015-2016 Mike FABIAN <mfabian@redhat.com> 7# 8# This program is free software: you can redistribute it and/or modify 9# it under the terms of the GNU General Public License as published by 10# the Free Software Foundation, either version 3 of the License, or 11# (at your option) any later version. 12# 13# This program is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU General Public License for more details. 17# 18# You should have received a copy of the GNU General Public License 19# along with this program. If not, see <http://www.gnu.org/licenses/> 20 21'''A module to find out which fonts are used by pango to render a string 22''' 23 24import sys 25import ctypes 26 27class glib__GSList(ctypes.Structure): 28 pass 29glib__GSList._fields_ = [ 30 ('data', ctypes.c_void_p), 31 ('next', ctypes.POINTER(glib__GSList)), 32] 33class libgtk3__GtkWidget(ctypes.Structure): 34 pass 35class libpango__PangoAttribute(ctypes.Structure): 36 pass 37class libpango__PangoAttrList(ctypes.Structure): 38 pass 39class libpango__PangoContext(ctypes.Structure): 40 pass 41class libpango__PangoLayout(ctypes.Structure): 42 pass 43class libpango__PangoFontDescription(ctypes.Structure): 44 pass 45class libpango__PangoLayoutLine(ctypes.Structure): 46 pass 47libpango__PangoLayoutLine._fields_ = [ 48 ('layout', ctypes.POINTER(libpango__PangoLayout)), 49 ('start_index', ctypes.c_int), # start of line as byte index into layout->text 50 ('length', ctypes.c_int), # length of line in bytes 51 ('runs', ctypes.POINTER(glib__GSList)), 52 ('is_paragraph_start', ctypes.c_uint), # TRUE if this is the first line of the paragraph 53 ('resolved_dir', ctypes.c_uint), # Resolved PangoDirection of line 54] 55class libpango__PangoGlyphString(ctypes.Structure): 56 pass 57class libpango__PangoEngineShape(ctypes.Structure): 58 pass 59class libpango__PangoEngineLang(ctypes.Structure): 60 pass 61class libpango__PangoFont(ctypes.Structure): 62 pass 63class libpango__PangoLanguage(ctypes.Structure): 64 pass 65class libpango__PangoAnalysis(ctypes.Structure): 66 _fields_ = [ 67 ('shape_engine', ctypes.POINTER(libpango__PangoEngineShape)), 68 ('lang_engine', ctypes.POINTER(libpango__PangoEngineLang)), 69 ('font', ctypes.POINTER(libpango__PangoFont)), 70 ('level', ctypes.c_uint8), 71 ('gravity', ctypes.c_uint8), 72 ('flags', ctypes.c_uint8), 73 ('script', ctypes.c_uint8), 74 ('language', ctypes.POINTER(libpango__PangoLanguage)), 75 ('extra_attrs', ctypes.POINTER(glib__GSList)), 76 ] 77class libpango__PangoItem(ctypes.Structure): 78 pass 79libpango__PangoItem._fields_ = [ 80 ('offset', ctypes.c_int), 81 ('length', ctypes.c_int), 82 ('num_chars', ctypes.c_int), 83 ('analysis', libpango__PangoAnalysis), 84] 85class libpango__PangoGlyphItem(ctypes.Structure): 86 pass 87libpango__PangoGlyphItem._fields_ = [ 88 ('item', ctypes.POINTER(libpango__PangoItem)), 89 ('glyphs', ctypes.POINTER(libpango__PangoGlyphString)), 90] 91 92libglib__lib = None 93libgtk3__lib = None 94libpango__lib = None 95libglib__g_slist_length = None 96libglib__g_slist_nth_data = None 97libgtk3__gtk_init = None 98libgtk3__gtk_label_new = None 99libgtk3__gtk_widget_get_pango_context = None 100libpango__pango_layout_new = None 101libpango__pango_font_description_from_string = None 102libpango__pango_layout_set_font_description = None 103libpango__pango_layout_set_text = None 104libpango__pango_attr_list_new = None 105libpango__pango_attr_list_unref = None 106libpango__pango_attr_fallback_new = None 107libpango__pango_attribute_destroy = None 108libpango__pango_attr_list_insert = None 109libpango__pango_layout_set_attributes = None 110libpango__pango_layout_get_line_readonly = None 111libpango__pango_font_describe = None 112libpango__pango_font_description_get_family = None 113 114def get_fonts_used_for_text(font, text, fallback=True): 115 '''Return a list of fonts which were really used to render a text 116 117 :param font: The font requested to render the text in 118 :type font: String 119 :param text: The text to render 120 :type text: String 121 :param fallback: Whether to enable font fallback. If disabled, then 122 glyphs will only be used from the closest matching 123 font on the system. No fallback will be done to other 124 fonts on the system that might contain the glyphs needed 125 for the text. 126 :type fallback: Boolean 127 :rtype: List of strings 128 129 Examples: 130 131 >>> get_fonts_used_for_text('DejaVu Sans Mono', ' ') 132 [('', 'Noto Color Emoji'), (' ', 'DejaVu Sans Mono')] 133 134 >>> get_fonts_used_for_text('DejaVu Sans', '日本語 नमस्ते') 135 [('日本語', 'Droid Sans'), (' ', 'DejaVu Sans'), ('नमस्ते', 'Droid Sans')] 136 137 >>> get_fonts_used_for_text('DejaVu Sans', '日本語 ️') 138 [('日本語', 'Droid Sans'), (' ', 'DejaVu Sans'), ('️', 'Noto Color Emoji')] 139 ''' 140 fonts_used = [] 141 label = libgtk3__gtk_label_new(ctypes.c_char_p(b'')) 142 pango_context_p = libgtk3__gtk_widget_get_pango_context(label) 143 pango_layout_p = libpango__pango_layout_new(pango_context_p) 144 pango_font_description_p = libpango__pango_font_description_from_string( 145 ctypes.c_char_p(font.encode('UTF-8', errors='replace'))) 146 libpango__pango_layout_set_font_description( 147 pango_layout_p, pango_font_description_p) 148 pango_attr_list_p = libpango__pango_attr_list_new() 149 pango_attr_p = libpango__pango_attr_fallback_new( 150 ctypes.c_bool(fallback)) 151 libpango__pango_attr_list_insert( 152 pango_attr_list_p, pango_attr_p) 153 libpango__pango_layout_set_attributes( 154 pango_layout_p, pango_attr_list_p) 155 text_utf8 = text.encode('UTF-8', errors='replace') 156 libpango__pango_layout_set_text( 157 pango_layout_p, 158 ctypes.c_char_p(text_utf8), 159 ctypes.c_int(-1)) 160 pango_layout_line_p = libpango__pango_layout_get_line_readonly( 161 pango_layout_p, ctypes.c_int(0)) 162 gs_list = pango_layout_line_p.contents.runs.contents 163 number_of_runs = libglib__g_slist_length(gs_list) 164 for index in range(0, number_of_runs): 165 gpointer = libglib__g_slist_nth_data(gs_list, ctypes.c_uint(index)) 166 pango_glyph_item = ctypes.cast( 167 gpointer, 168 ctypes.POINTER(libpango__PangoGlyphItem)).contents 169 pango_item_p = pango_glyph_item.item 170 offset = pango_item_p.contents.offset 171 length = pango_item_p.contents.length 172 num_chars = pango_item_p.contents.num_chars 173 pango_analysis = pango_item_p.contents.analysis 174 pango_font_p = pango_analysis.font 175 font_description_used = libpango__pango_font_describe(pango_font_p) 176 run_text = text_utf8[offset:offset + length].decode( 177 'UTF-8', errors='replace') 178 run_family = libpango__pango_font_description_get_family( 179 font_description_used).decode('UTF-8', errors='replace') 180 fonts_used.append((run_text, run_family)) 181 libpango__pango_attr_list_unref(pango_attr_list_p) 182 libpango__pango_attribute_destroy(pango_attr_p) 183 return fonts_used 184 185def _init(): 186 global libglib__lib 187 libglib__lib = ctypes.CDLL('libglib-2.0.so.0', mode=ctypes.RTLD_GLOBAL) 188 global libgtk3__lib 189 libgtk3__lib = ctypes.CDLL('libgtk-3.so.0', mode=ctypes.RTLD_GLOBAL) 190 global libpango__lib 191 libpango__lib = ctypes.CDLL('libpango-1.0.so.0', mode=ctypes.RTLD_GLOBAL) 192 global libglib__g_slist_length 193 libglib__g_slist_length = libglib__lib.g_slist_length 194 libglib__g_slist_length.argtypes = [ 195 ctypes.POINTER(glib__GSList)] 196 libglib__g_slist_length.restype = ctypes.c_uint 197 global libglib__g_slist_nth_data 198 libglib__g_slist_nth_data = libglib__lib.g_slist_nth_data 199 libglib__g_slist_nth_data.argtypes = [ 200 ctypes.POINTER(glib__GSList), ctypes.c_uint] 201 libglib__g_slist_nth_data.restype = ctypes.c_void_p 202 global libgtk3__gtk_init 203 libgtk3__gtk_init = libgtk3__lib.gtk_init 204 libgtk3__gtk_init.argtypes = [ 205 ctypes.POINTER(ctypes.c_int), 206 ctypes.POINTER(ctypes.POINTER(ctypes.c_char_p))] 207 global libgtk3__gtk_label_new 208 libgtk3__gtk_label_new = libgtk3__lib.gtk_label_new 209 libgtk3__gtk_label_new.argtypes = [ctypes.c_char_p] 210 libgtk3__gtk_label_new.restype = ctypes.POINTER( 211 libgtk3__GtkWidget) 212 global libgtk3__gtk_widget_get_pango_context 213 libgtk3__gtk_widget_get_pango_context = libgtk3__lib.gtk_widget_get_pango_context 214 libgtk3__gtk_widget_get_pango_context.argtypes = [ 215 ctypes.POINTER(libgtk3__GtkWidget)] 216 libgtk3__gtk_widget_get_pango_context.restype = ctypes.POINTER( 217 libpango__PangoContext) 218 global libpango__pango_layout_new 219 libpango__pango_layout_new = libpango__lib.pango_layout_new 220 libpango__pango_layout_new.argtypes = [ 221 ctypes.POINTER(libpango__PangoContext)] 222 libpango__pango_layout_new.restype = ctypes.POINTER( 223 libpango__PangoLayout) 224 global libpango__pango_font_description_from_string 225 libpango__pango_font_description_from_string = libpango__lib.pango_font_description_from_string 226 libpango__pango_font_description_from_string.argtypes = [ctypes.c_char_p] 227 libpango__pango_font_description_from_string.restype = ctypes.POINTER( 228 libpango__PangoFontDescription) 229 global libpango__pango_layout_set_font_description 230 libpango__pango_layout_set_font_description = libpango__lib.pango_layout_set_font_description 231 libpango__pango_layout_set_font_description.argtypes = [ 232 ctypes.POINTER(libpango__PangoLayout), 233 ctypes.POINTER(libpango__PangoFontDescription)] 234 global libpango__pango_layout_set_text 235 libpango__pango_layout_set_text = libpango__lib.pango_layout_set_text 236 libpango__pango_layout_set_text.argtypes = [ 237 ctypes.POINTER(libpango__PangoLayout), ctypes.c_char_p, ctypes.c_int] 238 global libpango__pango_attr_list_new 239 libpango__pango_attr_list_new = libpango__lib.pango_attr_list_new 240 libpango__pango_attr_list_new.argtypes = [] 241 libpango__pango_attr_list_new.restype = ctypes.POINTER( 242 libpango__PangoAttrList) 243 global libpango__pango_attr_list_unref 244 libpango__pango_attr_list_unref = libpango__lib.pango_attr_list_unref 245 libpango__pango_attr_list_unref.argtypes = [ 246 ctypes.POINTER(libpango__PangoAttrList)] 247 global libpango__pango_attr_fallback_new 248 libpango__pango_attr_fallback_new = libpango__lib.pango_attr_fallback_new 249 libpango__pango_attr_fallback_new.argtypes = [ 250 ctypes.c_bool] 251 libpango__pango_attr_fallback_new.restype = ctypes.POINTER( 252 libpango__PangoAttribute) 253 global libpango__pango_attribute_destroy 254 libpango__pango_attribute_destroy = libpango__lib.pango_attribute_destroy 255 libpango__pango_attribute_destroy.argtypes = [ 256 ctypes.POINTER(libpango__PangoAttribute)] 257 global libpango__pango_attr_list_insert 258 libpango__pango_attr_list_insert = libpango__lib.pango_attr_list_insert 259 libpango__pango_attr_list_insert.argtypes = [ 260 ctypes.POINTER(libpango__PangoAttrList), 261 ctypes.POINTER(libpango__PangoAttribute)] 262 global libpango__pango_layout_set_attributes 263 libpango__pango_layout_set_attributes = libpango__lib.pango_layout_set_attributes 264 libpango__pango_layout_set_attributes.argtypes = [ 265 ctypes.POINTER(libpango__PangoLayout), 266 ctypes.POINTER(libpango__PangoAttrList)] 267 global libpango__pango_layout_get_line_readonly 268 libpango__pango_layout_get_line_readonly = libpango__lib.pango_layout_get_line_readonly 269 libpango__pango_layout_get_line_readonly.argtypes = [ 270 ctypes.POINTER(libpango__PangoLayout), ctypes.c_int] 271 libpango__pango_layout_get_line_readonly.restype = ctypes.POINTER( 272 libpango__PangoLayoutLine) 273 global libpango__pango_font_describe 274 libpango__pango_font_describe = libpango__lib.pango_font_describe 275 libpango__pango_font_describe.argtypes = [ 276 ctypes.POINTER(libpango__PangoFont)] 277 libpango__pango_font_describe.restype = ctypes.POINTER( 278 libpango__PangoFontDescription) 279 global libpango__pango_font_description_get_family 280 libpango__pango_font_description_get_family = libpango__lib.pango_font_description_get_family 281 libpango__pango_font_description_get_family.argtypes = [ 282 ctypes.POINTER(libpango__PangoFontDescription)] 283 libpango__pango_font_description_get_family.restype = ctypes.c_char_p 284 libgtk3__gtk_init( 285 ctypes.byref(ctypes.c_int(0)), 286 ctypes.byref(ctypes.pointer(ctypes.c_char_p(b'')))) 287 288def _del(): 289 '''Cleanup''' 290 pass 291 292class __ModuleInitializer: 293 def __init__(self): 294 _init() 295 296 def __del__(self): 297 return 298 299__module_init = __ModuleInitializer() 300 301if __name__ == "__main__": 302 import doctest 303 (FAILED, _ATTEMPTED) = doctest.testmod() 304 sys.exit(FAILED) 305