├── .gitignore ├── LICENSE ├── Makefile ├── README └── font-config-info.c /.gitignore: -------------------------------------------------------------------------------- 1 | core 2 | font-config-info 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021, Daniel Erat 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBS=fontconfig gio-2.0 gtk+-3.0 x11 2 | 3 | font-config-info: font-config-info.c 4 | gcc -g -Wall -std=c99 font-config-info.c -o font-config-info \ 5 | `pkg-config --cflags ${LIBS}` \ 6 | `pkg-config --libs ${LIBS}` 7 | 8 | all: font-config-info 9 | 10 | clean: 11 | rm -f font-config-info 12 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Moved to https://codeberg.org/derat/font-config-info 2 | -------------------------------------------------------------------------------- /font-config-info.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define NAME_FORMAT "%-20s " 19 | 20 | const char* GetFontconfigResultString(FcResult result) { 21 | switch (result) { 22 | case FcResultMatch: 23 | return "match"; 24 | case FcResultNoMatch: 25 | return "no match"; 26 | case FcResultTypeMismatch: 27 | return "type mismatch"; 28 | case FcResultNoId: 29 | return "no id"; 30 | case FcResultOutOfMemory: 31 | return "out of memory"; 32 | default: 33 | return "unknown"; 34 | } 35 | } 36 | 37 | const char* GetFontconfigHintStyleString(int style) { 38 | switch (style) { 39 | case FC_HINT_NONE: 40 | return "none"; 41 | case FC_HINT_SLIGHT: 42 | return "slight"; 43 | case FC_HINT_MEDIUM: 44 | return "medium"; 45 | case FC_HINT_FULL: 46 | return "full"; 47 | default: 48 | return "invalid"; 49 | } 50 | } 51 | 52 | const char* GetFontconfigRgbaString(int rgba) { 53 | switch (rgba) { 54 | case FC_RGBA_UNKNOWN: 55 | return "unknown"; 56 | case FC_RGBA_RGB: 57 | return "rgb"; 58 | case FC_RGBA_BGR: 59 | return "bgr"; 60 | case FC_RGBA_VRGB: 61 | return "vrgb"; 62 | case FC_RGBA_VBGR: 63 | return "vbgr"; 64 | case FC_RGBA_NONE: 65 | return "none"; 66 | default: 67 | return "invalid"; 68 | } 69 | } 70 | 71 | void PrintGtkBoolSetting(GtkSettings* settings, const char* name) { 72 | gint value = -1; 73 | g_object_get(settings, name, &value, NULL); 74 | printf(NAME_FORMAT "%d (%s)\n", name, value, 75 | (value == 0 ? "no": (value > 0 ? "yes" : "default"))); 76 | } 77 | 78 | void PrintGtkStringSetting(GtkSettings* settings, const char* name) { 79 | gchar* value = NULL; 80 | g_object_get(settings, name, &value, NULL); 81 | printf(NAME_FORMAT "\"%s\"\n", name, (value ? value : "[unset]")); 82 | if (value) 83 | g_free(value); 84 | } 85 | 86 | void PrintGtkSettings() { 87 | printf("GtkSettings:\n"); 88 | GtkSettings* settings = gtk_settings_get_default(); 89 | assert(settings); 90 | PrintGtkStringSetting(settings, "gtk-font-name"); 91 | PrintGtkBoolSetting(settings, "gtk-xft-antialias"); 92 | PrintGtkBoolSetting(settings, "gtk-xft-hinting"); 93 | PrintGtkStringSetting(settings, "gtk-xft-hintstyle"); 94 | PrintGtkStringSetting(settings, "gtk-xft-rgba"); 95 | 96 | // The DPI setting actually contains the real DPI times 1024. 97 | const char kDpiName[] = "gtk-xft-dpi"; 98 | gint dpi = -1; 99 | g_object_get(settings, kDpiName, &dpi, NULL); 100 | if (dpi > 0) 101 | printf(NAME_FORMAT "%d (%0.2f DPI)\n", kDpiName, dpi, dpi / 1024.0); 102 | else 103 | printf(NAME_FORMAT "%d (default)\n", kDpiName, dpi); 104 | 105 | printf("\n"); 106 | } 107 | 108 | void PrintGtkWidgetFontStyleAndSinkRef(GtkWidget* widget) { 109 | GtkStyle* style = gtk_rc_get_style(widget); 110 | PangoFontDescription* font_desc = style->font_desc; 111 | gchar* font_string = font_desc ? 112 | pango_font_description_to_string(font_desc) : NULL; 113 | printf(NAME_FORMAT "\"%s\"\n", G_OBJECT_TYPE_NAME(widget), 114 | font_string ? font_string : "[unset]"); 115 | 116 | if (font_string) 117 | g_free(font_string); 118 | g_object_ref_sink(widget); 119 | } 120 | 121 | void PrintGtkStyles() { 122 | printf("GTK 2.0 styles:\n"); 123 | PrintGtkWidgetFontStyleAndSinkRef(gtk_label_new("foo")); 124 | PrintGtkWidgetFontStyleAndSinkRef(gtk_menu_item_new_with_label("foo")); 125 | PrintGtkWidgetFontStyleAndSinkRef(gtk_toolbar_new()); 126 | printf("\n"); 127 | } 128 | 129 | void PrintGSettingsSetting(GSettings* settings, 130 | const char* key) { 131 | GVariant* variant = g_settings_get_value(settings, key); 132 | if (!variant) { 133 | printf(NAME_FORMAT "[unset]\n", key); 134 | return; 135 | } 136 | 137 | if (g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)) 138 | printf(NAME_FORMAT "\"%s\"\n", key, g_variant_get_string(variant, NULL)); 139 | else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_DOUBLE)) 140 | printf(NAME_FORMAT "%0.2f\n", key, g_variant_get_double(variant)); 141 | else 142 | printf(NAME_FORMAT "[unknown type]\n", key); 143 | 144 | g_variant_unref(variant); 145 | } 146 | 147 | void PrintGnomeSettings() { 148 | const char kSchema[] = "org.gnome.desktop.interface"; 149 | printf("GSettings (%s):\n", kSchema); 150 | 151 | gchar** schemas = NULL; 152 | int found_schema = 0; 153 | 154 | g_settings_schema_source_list_schemas(g_settings_schema_source_get_default(), 155 | TRUE, 156 | &schemas, 157 | NULL); 158 | 159 | for ( ; *schemas; schemas++) { 160 | if (strcmp(kSchema, *schemas) == 0) { 161 | found_schema = 1; 162 | break; 163 | } 164 | } 165 | if (!found_schema) { 166 | printf("schema not found; maybe GNOME isn't present\n\n"); 167 | return; 168 | } 169 | 170 | GSettings* settings = g_settings_new(kSchema); 171 | assert(settings); 172 | PrintGSettingsSetting(settings, "font-name"); 173 | PrintGSettingsSetting(settings, "text-scaling-factor"); 174 | g_object_unref(settings); 175 | printf("\n"); 176 | } 177 | 178 | void PrintXDisplayInfo() { 179 | printf("X11 display info:\n"); 180 | Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); 181 | assert(display); 182 | 183 | const int screen = DefaultScreen(display); 184 | const int width_px = DisplayWidth(display, screen); 185 | const int height_px = DisplayHeight(display, screen); 186 | const int width_mm = DisplayWidthMM(display, screen); 187 | const int height_mm = DisplayHeightMM(display, screen); 188 | const double x_dpi = width_px * 25.4 / width_mm; 189 | const double y_dpi = height_px * 25.4 / height_mm; 190 | 191 | printf(NAME_FORMAT "%dx%d\n", "screen pixels", width_px, height_px); 192 | printf(NAME_FORMAT "%dx%d mm (%.2fx%.2f DPI)\n", "screen size", 193 | width_mm, height_mm, x_dpi, y_dpi); 194 | printf("\n"); 195 | } 196 | 197 | void PrintXResource(XrmDatabase db, const char* name) { 198 | char* type = NULL; 199 | XrmValue value; 200 | if (!XrmGetResource(db, name, "*", &type, &value)) { 201 | printf(NAME_FORMAT "[unset]\n", name); 202 | return; 203 | } 204 | 205 | const size_t kBuffer = 256; 206 | char str[kBuffer]; 207 | size_t size = value.size < kBuffer ? value.size : kBuffer; 208 | strncpy(str, value.addr, size); 209 | str[kBuffer - 1] = '\0'; 210 | printf(NAME_FORMAT "\"%s\"\n", name, str); 211 | } 212 | 213 | void PrintXResources() { 214 | printf("X resources (xrdb):\n"); 215 | Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); 216 | assert(display); 217 | 218 | const char* data = XResourceManagerString(display); 219 | if (!data) { 220 | printf("[failed]\n\n"); 221 | return; 222 | } 223 | 224 | XrmDatabase db = XrmGetStringDatabase(data); 225 | PrintXResource(db, "Xft.antialias"); 226 | PrintXResource(db, "Xft.hinting"); 227 | PrintXResource(db, "Xft.hintstyle"); 228 | PrintXResource(db, "Xft.rgba"); 229 | PrintXResource(db, "Xft.dpi"); 230 | XrmDestroyDatabase(db); 231 | printf("\n"); 232 | } 233 | 234 | void PrintFontconfigString(FcPattern* match, const char* prop) { 235 | FcChar8* value = NULL; 236 | FcResult result = FcResultNoMatch; 237 | if ((result = FcPatternGetString(match, prop, 0, &value)) != FcResultMatch) 238 | printf(NAME_FORMAT "[%s]\n", prop, GetFontconfigResultString(result)); 239 | else 240 | printf(NAME_FORMAT "%s\n", prop, value); 241 | } 242 | 243 | void PrintFontconfigBool(FcPattern* match, const char* prop) { 244 | FcBool value = 0; 245 | FcResult result = FcResultNoMatch; 246 | if ((result = FcPatternGetBool(match, prop, 0, &value)) != FcResultMatch) 247 | printf(NAME_FORMAT "[%s]\n", prop, GetFontconfigResultString(result)); 248 | else 249 | printf(NAME_FORMAT "%d\n", prop, value); 250 | } 251 | 252 | void PrintFontconfigInt(FcPattern* match, 253 | const char* prop, 254 | const char* (*int_to_string_func)(int), 255 | const char* suffix) { 256 | int value = 0; 257 | FcResult result = FcResultNoMatch; 258 | if ((result = FcPatternGetInteger(match, prop, 0, &value)) != FcResultMatch) 259 | printf(NAME_FORMAT "[%s]\n", prop, GetFontconfigResultString(result)); 260 | else if (int_to_string_func) 261 | printf(NAME_FORMAT "%d%s (%s)\n", prop, value, suffix, int_to_string_func(value)); 262 | else 263 | printf(NAME_FORMAT "%d%s\n", prop, value, suffix); 264 | } 265 | 266 | void PrintFontconfigDouble(FcPattern* match, 267 | const char* prop, 268 | const char* suffix) { 269 | double value = 0.0; 270 | FcResult result = FcResultNoMatch; 271 | if ((result = FcPatternGetDouble(match, prop, 0, &value)) != FcResultMatch) 272 | printf(NAME_FORMAT "[%s]\n", prop, GetFontconfigResultString(result)); 273 | else 274 | printf(NAME_FORMAT "%.2f%s\n", prop, value, suffix); 275 | } 276 | 277 | void PrintFontconfigPattern(FcPattern* pattern, 278 | int print_family_and_size) { 279 | if (print_family_and_size) { 280 | PrintFontconfigString(pattern, FC_FAMILY); 281 | PrintFontconfigDouble(pattern, FC_PIXEL_SIZE, " pixels"); 282 | PrintFontconfigInt(pattern, FC_SIZE, NULL, " points"); 283 | } 284 | PrintFontconfigBool(pattern, FC_ANTIALIAS); 285 | PrintFontconfigBool(pattern, FC_HINTING); 286 | PrintFontconfigBool(pattern, FC_AUTOHINT); 287 | PrintFontconfigBool(pattern, FC_EMBEDDED_BITMAP); 288 | PrintFontconfigInt(pattern, FC_HINT_STYLE, GetFontconfigHintStyleString, ""); 289 | PrintFontconfigInt(pattern, FC_RGBA, GetFontconfigRgbaString, ""); 290 | printf("\n"); 291 | } 292 | 293 | void PrintFontconfigMatch(const char* user_desc_string, int bold, int italic) { 294 | PangoFontDescription* desc = NULL; 295 | if (user_desc_string) { 296 | desc = pango_font_description_from_string(user_desc_string); 297 | } else { 298 | GtkWidget* widget = gtk_label_new("foo"); 299 | desc = pango_font_description_copy(gtk_rc_get_style(widget)->font_desc); 300 | g_object_ref_sink(widget); 301 | } 302 | 303 | gchar* desc_string = pango_font_description_to_string(desc); 304 | printf("Fontconfig (%s):\n", desc_string); 305 | g_free(desc_string); 306 | 307 | FcPattern* pattern = FcPatternCreate(); 308 | assert(pattern); 309 | 310 | FcPatternAddString(pattern, FC_FAMILY, 311 | (const FcChar8*) pango_font_description_get_family(desc)); 312 | if (bold) { 313 | FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); 314 | printf(NAME_FORMAT "FC_WEIGHT_BOLD\n", "requested weight"); 315 | } 316 | if (italic) { 317 | FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); 318 | printf(NAME_FORMAT "FC_SLANT_ITALIC\n", "requested slant"); 319 | } 320 | 321 | // Pass either pixel or points depending on what was requested. 322 | if (pango_font_description_get_size_is_absolute(desc)) { 323 | const double pixel_size = 324 | pango_font_description_get_size(desc) / PANGO_SCALE; 325 | FcPatternAddDouble(pattern, FC_PIXEL_SIZE, pixel_size); 326 | printf(NAME_FORMAT "%.2f pixels\n", "requested size", pixel_size); 327 | } else { 328 | const int point_size = pango_font_description_get_size(desc) / PANGO_SCALE; 329 | FcPatternAddInteger(pattern, FC_SIZE, point_size); 330 | printf(NAME_FORMAT "%d points\n", "requested size", point_size); 331 | } 332 | 333 | FcConfigSubstitute(NULL, pattern, FcMatchPattern); 334 | FcDefaultSubstitute(pattern); 335 | FcResult result; 336 | FcPattern* match = FcFontMatch(0, pattern, &result); 337 | assert(match); 338 | 339 | PrintFontconfigPattern(match, 1); 340 | 341 | FcPatternDestroy(pattern); 342 | FcPatternDestroy(match); 343 | pango_font_description_free(desc); 344 | } 345 | 346 | void PrintFontconfigDefaults() { 347 | printf("Fontconfig (default pattern):\n"); 348 | FcPattern* query = FcPatternCreate(); 349 | assert(query); 350 | FcConfigSubstitute(NULL, query, FcMatchPattern); 351 | FcDefaultSubstitute(query); 352 | PrintFontconfigPattern(query, 0); 353 | 354 | printf("Fontconfig (default match):\n"); 355 | FcResult result; 356 | FcPattern* match = FcFontMatch(NULL, query, &result); 357 | assert(match); 358 | PrintFontconfigPattern(match, 1); 359 | 360 | printf("Fontconfig (non-family/size defaults):\n"); 361 | FcPattern* defaults = FcPatternDuplicate(query); 362 | FcPatternDel(defaults, FC_FAMILY); 363 | FcPatternDel(defaults, FC_PIXEL_SIZE); 364 | FcPatternDel(defaults, FC_SIZE); 365 | FcConfigSubstituteWithPat(NULL, defaults, query, FcMatchFont); 366 | PrintFontconfigPattern(defaults, 0); 367 | 368 | FcPatternDestroy(query); 369 | FcPatternDestroy(match); 370 | FcPatternDestroy(defaults); 371 | } 372 | 373 | void PrintXSettings() { 374 | printf("XSETTINGS:\n"); 375 | fflush(NULL); 376 | int retval = system( 377 | "bash -c \"" 378 | "set -o pipefail; " 379 | "dump_xsettings | " 380 | "grep -E '^(Gtk/FontName |Xft/)' | " 381 | "sed -e 's/ */|/' | " 382 | "awk -F '|' '{printf \\\"" NAME_FORMAT "%s\\n\\\", \\$1, \\$2}'" 383 | "\""); 384 | if (WEXITSTATUS(retval) != 0) { 385 | printf("Install dump_xsettings from https://code.google.com/p/xsettingsd/\n" 386 | "to print this information.\n"); 387 | } 388 | printf("\n"); 389 | } 390 | 391 | int main(int argc, char** argv) { 392 | int opt; 393 | int bold = 0, italic = 0; 394 | const char* user_font_desc = NULL; 395 | while ((opt = getopt(argc, argv, "bf:hi")) != -1) { 396 | switch (opt) { 397 | case 'b': 398 | bold = 1; 399 | break; 400 | case 'f': 401 | user_font_desc = optarg; 402 | break; 403 | case 'i': 404 | italic = 1; 405 | break; 406 | default: 407 | fprintf(stderr, 408 | "Usage: %s [options]\n" 409 | "\n" 410 | "Options:\n" 411 | " -b Request bold font from Fontconfig\n" 412 | " -f DESC Specify Pango font description for Fontconfig\n" 413 | " -i Request italic font from Fontconfig\n", 414 | argv[0]); 415 | return 1; 416 | } 417 | } 418 | 419 | time_t now = time(NULL); 420 | printf("Running at %s\n", ctime(&now)); 421 | 422 | gtk_init(&argc, &argv); 423 | PrintGtkSettings(); 424 | PrintGtkStyles(); 425 | PrintGnomeSettings(); 426 | PrintXDisplayInfo(); 427 | PrintXResources(); 428 | PrintXSettings(); 429 | PrintFontconfigMatch(user_font_desc, bold, italic); 430 | PrintFontconfigDefaults(); 431 | return 0; 432 | } 433 | --------------------------------------------------------------------------------