URI:
       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 }