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 }