ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
platform/linux/font.c
Go to the documentation of this file.
1
6#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
8#include <ascii-chat/font.h>
10#ifndef USE_MUSL
11#include <fontconfig/fontconfig.h>
12#endif
13#include <string.h>
14#include <sys/stat.h>
15#include <stddef.h>
16
17// Fallback font name for fontconfig when a requested font is not found.
18// DejaVu Sans Mono ships with the vast majority of distros.
19static const char *k_fallback = "DejaVu Sans Mono";
20
21static bool is_absolute_path(const char *s) {
22 return s && s[0] == '/';
23}
24static bool file_exists(const char *p) {
25 struct stat st;
26 return stat(p, &st) == 0;
27}
28
29#ifndef USE_MUSL
30static asciichat_error_t resolve_via_fontconfig(const char *name, char *out, size_t sz) {
31 FcConfig *fc = FcInitLoadConfigAndFonts();
32 FcPattern *pat = FcNameParse((const FcChar8 *)name);
33 if (!pat)
34 return SET_ERRNO(ERROR_INVALID_PARAM, "platform_font_resolve: bad name '%s'", name);
35
36 FcConfigSubstitute(fc, pat, FcMatchPattern);
37 FcDefaultSubstitute(pat);
38
39 FcResult result;
40 FcPattern *match = FcFontMatch(fc, pat, &result);
41 FcPatternDestroy(pat);
42
43 if (!match || result != FcResultMatch)
44 return SET_ERRNO(ERROR_NOT_FOUND, "platform_font_resolve: no font matching '%s'", name);
45
46 FcChar8 *file = NULL;
47 if (FcPatternGetString(match, FC_FILE, 0, &file) != FcResultMatch || !file) {
48 FcPatternDestroy(match);
49 return SET_ERRNO(ERROR_NOT_FOUND, "platform_font_resolve: fontconfig matched but no file");
50 }
51 snprintf(out, sz, "%s", (const char *)file);
52 log_debug("platform_font_resolve: '%s' → %s", name, out);
53 FcPatternDestroy(match);
54 return ASCIICHAT_OK;
55}
56#endif
57
58asciichat_error_t platform_font_resolve(const char *spec, char *out, size_t out_size, bool *out_is_path,
59 const uint8_t **out_font_data, size_t *out_font_data_size) {
60 // If no font is specified, use the bundled default font (DejaVu Sans Mono) for render-file output.
61 // This ensures render-file always has a suitable fallback font available.
62 const char *eff = (spec && spec[0]) ? spec : "default";
63
64 if (out_font_data)
65 *out_font_data = NULL;
66 if (out_font_data_size)
67 *out_font_data_size = 0;
68
69 if (is_absolute_path(eff)) {
70 snprintf(out, out_size, "%s", eff);
71 *out_is_path = true;
72 return file_exists(out) ? ASCIICHAT_OK : SET_ERRNO(ERROR_NOT_FOUND, "render-font: not found: %s", out);
73 }
74
75 // Check for bundled font names ("matrix" or "default")
76 if (strcmp(eff, "matrix") == 0) {
77 if (out_font_data)
78 *out_font_data = g_font_matrix_resurrected;
79 if (out_font_data_size)
80 *out_font_data_size = g_font_matrix_resurrected_size;
81 snprintf(out, out_size, "matrix");
82 *out_is_path = false;
83 log_info("platform_font_resolve: USING MATRIX FONT - g_font_matrix_resurrected=%p (size=%zu), g_font_default=%p "
84 "(size=%zu)",
87 return ASCIICHAT_OK;
88 }
89
90 if (strcmp(eff, "default") == 0) {
91 if (out_font_data)
92 *out_font_data = g_font_default;
93 if (out_font_data_size)
94 *out_font_data_size = g_font_default_size;
95 snprintf(out, out_size, "default");
96 *out_is_path = false;
97 log_info("platform_font_resolve: USING DEFAULT FONT - g_font_default=%p (size=%zu), g_font_matrix_resurrected=%p "
98 "(size=%zu)",
101 return ASCIICHAT_OK;
102 }
103
104#ifndef USE_MUSL
105 *out_is_path = true;
106 asciichat_error_t err = resolve_via_fontconfig(eff, out, out_size);
107 if (err != ASCIICHAT_OK && strcmp(eff, k_fallback) != 0) {
108 log_warn("platform_font_resolve: '%s' not found, trying '%s'", eff, k_fallback);
109 err = resolve_via_fontconfig(k_fallback, out, out_size);
110 }
111
112 // Fallback chain: system fonts → bundled default font
113 if (err != ASCIICHAT_OK) {
114 log_warn("platform_font_resolve: system font resolution failed, using bundled default font");
115 if (out_font_data)
116 *out_font_data = g_font_default;
117 if (out_font_data_size)
118 *out_font_data_size = g_font_default_size;
119 out[0] = '\0';
120 *out_is_path = false;
121 err = ASCIICHAT_OK;
122 }
123
124 return err;
125#else
126 // musl static builds: fontconfig not available, use bundled default font
127 log_debug("platform_font_resolve: using bundled default font (musl build, no fontconfig)");
128 if (out_font_data)
129 *out_font_data = g_font_default;
130 if (out_font_data_size)
131 *out_font_data_size = g_font_default_size;
132 out[0] = '\0';
133 *out_is_path = false;
134 return ASCIICHAT_OK;
135#endif
136}
137#endif
Font resolution and bundled font data.
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_NOT_FOUND
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
📝 Logging API with multiple log levels and terminal output control
const size_t g_font_matrix_resurrected_size
const unsigned char g_font_matrix_resurrected[]
const size_t g_font_default_size
const unsigned char g_font_default[]
Platform-specific font resolution for render-file output.
asciichat_error_t platform_font_resolve(const char *spec, char *out, size_t out_size, bool *out_is_path, const uint8_t **out_font_data, size_t *out_font_data_size)