text_common.c - ltkx - GUI toolkit for X11 (old)
HTML git clone git://lumidify.org/ltkx.git (fast, but not encrypted)
HTML git clone https://lumidify.org/git/ltkx.git (encrypted, but very slow)
HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltkx.git (over tor)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
text_common.c (8381B)
---
1 /*
2 * This file is part of the Lumidify ToolKit (LTK)
3 * Copyright (c) 2017, 2018, 2020 lumidify <nobody@lumidify.org>
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdint.h>
27 #include <limits.h>
28 #include <X11/Xlib.h>
29 #include <X11/Xutil.h>
30 #include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
31 #include <fontconfig/fontconfig.h>
32 #include "khash.h"
33 #include <fribidi.h>
34 #include <harfbuzz/hb.h>
35 #include <harfbuzz/hb-ot.h>
36 #include "text_common.h"
37 #include "ltk.h"
38
39 /* These unicode routines are taken from
40 * https://github.com/JeffBezanson/cutef8 */
41
42 /* is c the start of a utf8 sequence? */
43 #define isutf(c) (((c)&0xC0)!=0x80)
44
45 static const uint32_t offsetsFromUTF8[6] = {
46 0x00000000UL, 0x00003080UL, 0x000E2080UL,
47 0x03C82080UL, 0xFA082080UL, 0x82082080UL
48 };
49
50 /* next character without NUL character terminator */
51 uint32_t u8_nextmemchar(const char *s, size_t *i)
52 {
53 uint32_t ch = 0;
54 size_t sz = 0;
55 do {
56 ch <<= 6;
57 ch += (unsigned char)s[(*i)++];
58 sz++;
59 } while (!isutf(s[*i]));
60 ch -= offsetsFromUTF8[sz-1];
61
62 return ch;
63 }
64
65 /* number of characters in NUL-terminated string */
66 size_t u8_strlen(const char *s)
67 {
68 size_t count = 0;
69 size_t i = 0, lasti;
70
71 while (1) {
72 lasti = i;
73 while (s[i] > 0)
74 i++;
75 count += (i-lasti);
76 if (s[i++]==0) break;
77 (void)(isutf(s[++i]) || isutf(s[++i]) || ++i);
78 count++;
79 }
80 return count;
81 }
82
83 size_t u8_wc_toutf8(char *dest, uint32_t ch)
84 {
85 if (ch < 0x80) {
86 dest[0] = (char)ch;
87 return 1;
88 }
89 if (ch < 0x800) {
90 dest[0] = (ch>>6) | 0xC0;
91 dest[1] = (ch & 0x3F) | 0x80;
92 return 2;
93 }
94 if (ch < 0x10000) {
95 dest[0] = (ch>>12) | 0xE0;
96 dest[1] = ((ch>>6) & 0x3F) | 0x80;
97 dest[2] = (ch & 0x3F) | 0x80;
98 return 3;
99 }
100 if (ch < 0x110000) {
101 dest[0] = (ch>>18) | 0xF0;
102 dest[1] = ((ch>>12) & 0x3F) | 0x80;
103 dest[2] = ((ch>>6) & 0x3F) | 0x80;
104 dest[3] = (ch & 0x3F) | 0x80;
105 return 4;
106 }
107 return 0;
108 }
109
110 LtkTextManager *
111 ltk_init_text(char *font_name)
112 {
113 LtkTextManager *tm = malloc(sizeof(LtkTextManager));
114 if (!tm) {
115 (void)printf("Memory exhausted when trying to create text manager.");
116 exit(1);
117 }
118 tm->font_paths = kh_init(fontid);
119 tm->font_cache = kh_init(fontstruct);
120 tm->glyph_cache = kh_init(glyphcache);
121 /* FIXME: THIS REALLY SHOULD NOT BE UINT16_T! IT GETS MESSY WITH BIT-SHIFTING */
122 tm->font_id_cur = 1;
123 ltk_load_default_font(tm, font_name);
124
125 return tm;
126 }
127
128 void
129 ltk_destroy_text_manager(LtkTextManager *tm)
130 {
131 int k;
132
133 kh_destroy(fontid, tm->font_paths);
134
135 for (k = kh_begin(tm->font_cache); k != kh_end(tm->font_cache); k++) {
136 if (kh_exist(tm->font_cache, k)) {
137 ltk_destroy_font(kh_value(tm->font_cache, k));
138 }
139 }
140 kh_destroy(fontstruct, tm->font_cache);
141
142 for (k = kh_begin(tm->glyph_cache); k != kh_end(tm->glyph_cache); k++) {
143 if (kh_exist(tm->glyph_cache, k)) {
144 ltk_destroy_glyph_cache(kh_value(tm->glyph_cache, k));
145 }
146 }
147 kh_destroy(glyphcache, tm->glyph_cache);
148
149 free(tm);
150 }
151
152 LtkGlyphInfo *
153 ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale)
154 {
155 LtkGlyphInfo *glyph = malloc(sizeof(LtkGlyphInfo));
156 if (!glyph) {
157 (void)printf("Out of memory!\n");
158 exit(1);
159 }
160
161 glyph->id = id;
162 glyph->refs = 0;
163 glyph->alphamap = stbtt_GetGlyphBitmap(
164 &font->info, scale, scale, id, &glyph->w,
165 &glyph->h, &glyph->xoff, &glyph->yoff
166 );
167
168 return glyph;
169 }
170
171 void
172 ltk_destroy_glyph_info(LtkGlyphInfo *gi)
173 {
174 free(gi->alphamap);
175 free(gi);
176 }
177
178 LtkGlyphInfo *
179 ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache)
180 {
181 int ret;
182 khint_t k;
183 LtkGlyphInfo *glyph;
184 k = kh_get(glyphinfo, cache, id);
185 if (k == kh_end(cache)) {
186 glyph = ltk_create_glyph_info(font, id, scale);
187 /* FIXME: error checking with ret */
188 k = kh_put(glyphinfo, cache, id, &ret);
189 kh_value(cache, k) = glyph;
190 } else {
191 glyph = kh_value(cache, k);
192 }
193
194 return glyph;
195 }
196
197 khint_t
198 ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size)
199 {
200 khash_t(glyphinfo) *cache = kh_init(glyphinfo);
201 int ret;
202 khint_t k;
203 /* I guess I can just ignore ret for now */
204 k = kh_put(glyphcache, tm->glyph_cache, font_id << 16 + font_size, &ret);
205 kh_value(tm->glyph_cache, k) = cache;
206
207 return k;
208 }
209
210 void
211 ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache)
212 {
213 int k;
214 for (k = kh_begin(cache); k != kh_end(cache); k++) {
215 if (kh_exist(cache, k)) {
216 ltk_destroy_glyph_info(kh_value(cache, k));
217 }
218 }
219 kh_destroy(glyphinfo, cache);
220 }
221
222 void
223 ltk_load_default_font(LtkTextManager *tm, char *name)
224 {
225 FcPattern *match;
226 FcResult result;
227 char *file;
228 int index;
229 uint16_t font;
230
231 tm->fcpattern = FcNameParse(name);
232 FcPatternAddString(tm->fcpattern, FC_FONTFORMAT, "truetype");
233 FcConfigSubstitute(NULL, tm->fcpattern, FcMatchPattern);
234 FcDefaultSubstitute(tm->fcpattern);
235 match = FcFontMatch(NULL, tm->fcpattern, &result);
236
237 FcPatternGetString (match, FC_FILE, 0, (FcChar8 **) &file);
238 /* FIXME: Why is index never used? This is the index within the font file,
239 so it might be important, although I'm not sure if stb_truetype even
240 supports it */
241 FcPatternGetInteger (match, FC_INDEX, 0, &index);
242
243 tm->default_font = ltk_get_font(tm, file);
244
245 FcPatternDestroy (match);
246 }
247
248 LtkFont *
249 ltk_create_font(char *path, uint16_t id)
250 {
251 long len;
252 LtkFont *font = malloc(sizeof(LtkFont));
253 if (!font) {
254 (void)fprintf(stderr, "Out of memory!\n");
255 exit(1);
256 }
257 char *contents = ltk_read_file(path, &len);
258 if (!stbtt_InitFont(&font->info, contents, 0))
259 {
260 (void)fprintf(stderr, "Failed to load font %s\n", path);
261 exit(1);
262 }
263 /* FIXME: make use of the destroy function (last argument to hb_blob_create - see hb-blob.cc in harfbuzz source) */
264 hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL);
265 hb_face_t *face = hb_face_create(blob, 0);
266 /* FIXME: need to use destroy function in order for the original file data to be freed? */
267 hb_blob_destroy(blob);
268 font->hb = hb_font_create(face);
269 hb_face_destroy(face);
270 hb_ot_font_set_funcs(font->hb);
271 font->id = id;
272 font->refs = 0;
273
274 return font;
275 }
276
277 void
278 ltk_destroy_font(LtkFont *font)
279 {
280 free(font->info.data);
281 hb_font_destroy(font->hb);
282 free(font);
283 }
284
285 uint16_t
286 ltk_load_font(LtkTextManager *tm, char *path)
287 {
288 LtkFont *font = ltk_create_font(path, tm->font_id_cur++);
289 int ret;
290 khint_t k;
291 /* FIXME: does kh_destroy also free these copied strings properly? */
292 char *key = strdup(path);
293 k = kh_put(fontid, tm->font_paths, key, &ret);
294 kh_value(tm->font_paths, k) = font->id;
295 k = kh_put(fontstruct, tm->font_cache, (khint_t) font->id, &ret);
296 kh_value(tm->font_cache, k) = font;
297 k = kh_get(fontid, tm->font_paths, path);
298
299 return font->id;
300 }
301
302 uint16_t
303 ltk_get_font(LtkTextManager *tm, char *path)
304 {
305 int ret;
306 khint_t k;
307 uint16_t id;
308 k = kh_get(fontid, tm->font_paths, path);
309 if (k == kh_end(tm->font_paths)) {
310 id = ltk_load_font(tm, path);
311 } else {
312 id = kh_value(tm->font_paths, k);
313 }
314
315 return id;
316 }
317
318 void
319 ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache)
320 {
321 int k;
322 if (--glyph->info->refs < 1) {
323 k = kh_get(glyphinfo, cache, glyph->info->id);
324 kh_del(glyphinfo, cache, k);
325 ltk_destroy_glyph_info(glyph->info);
326 }
327 free(glyph);
328 }