dotfiles

personal configuration files and scripts
git clone https://tongong.net/git/dotfiles.git
Log | Files | Refs | README

hb.c (3155B)


      1 #include <stdlib.h>
      2 #include <stdio.h>
      3 #include <math.h>
      4 #include <X11/Xft/Xft.h>
      5 #include <hb.h>
      6 #include <hb-ft.h>
      7 
      8 #include "st.h"
      9 
     10 void hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length);
     11 hb_font_t *hbfindfont(XftFont *match);
     12 
     13 typedef struct {
     14 	XftFont *match;
     15 	hb_font_t *font;
     16 } HbFontMatch;
     17 
     18 static int hbfontslen = 0;
     19 static HbFontMatch *hbfontcache = NULL;
     20 
     21 void
     22 hbunloadfonts()
     23 {
     24 	for (int i = 0; i < hbfontslen; i++) {
     25 		hb_font_destroy(hbfontcache[i].font);
     26 		XftUnlockFace(hbfontcache[i].match);
     27 	}
     28 
     29 	if (hbfontcache != NULL) {
     30 		free(hbfontcache);
     31 		hbfontcache = NULL;
     32 	}
     33 	hbfontslen = 0;
     34 }
     35 
     36 hb_font_t *
     37 hbfindfont(XftFont *match)
     38 {
     39 	for (int i = 0; i < hbfontslen; i++) {
     40 		if (hbfontcache[i].match == match)
     41 			return hbfontcache[i].font;
     42 	}
     43 
     44 	/* Font not found in cache, caching it now. */
     45 	hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 1));
     46 	FT_Face face = XftLockFace(match);
     47 	hb_font_t *font = hb_ft_font_create(face, NULL);
     48 	if (font == NULL)
     49 		die("Failed to load Harfbuzz font.");
     50 
     51 	hbfontcache[hbfontslen].match = match;
     52 	hbfontcache[hbfontslen].font = font;
     53 	hbfontslen += 1;
     54 
     55 	return font;
     56 }
     57 
     58 void
     59 hbtransform(XftGlyphFontSpec *specs, const Glyph *glyphs, size_t len, int x, int y)
     60 {
     61 	int start = 0, length = 1, gstart = 0;
     62 	hb_codepoint_t *codepoints = calloc(len, sizeof(hb_codepoint_t));
     63 
     64 	for (int idx = 1, specidx = 1; idx < len; idx++) {
     65 		if (glyphs[idx].mode & ATTR_WDUMMY) {
     66 			length += 1;
     67 			continue;
     68 		}
     69 
     70 		if (specs[specidx].font != specs[start].font || ATTRCMP(glyphs[gstart], glyphs[idx]) || selected(x + idx, y) != selected(x + gstart, y)) {
     71 			hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length);
     72 
     73 			/* Reset the sequence. */
     74 			length = 1;
     75 			start = specidx;
     76 			gstart = idx;
     77 		} else {
     78 			length += 1;
     79 		}
     80 
     81 		specidx++;
     82 	}
     83 
     84 	/* EOL. */
     85 	hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length);
     86 
     87 	/* Apply the transformation to glyph specs. */
     88 	for (int i = 0, specidx = 0; i < len; i++) {
     89 		if (glyphs[i].mode & ATTR_WDUMMY)
     90 			continue;
     91 
     92 		if (codepoints[i] != specs[specidx].glyph)
     93 			((Glyph *)glyphs)[i].mode |= ATTR_LIGA;
     94 
     95 		specs[specidx++].glyph = codepoints[i];
     96 	}
     97 
     98 	free(codepoints);
     99 }
    100 
    101 void
    102 hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length)
    103 {
    104 	hb_font_t *font = hbfindfont(xfont);
    105 	if (font == NULL)
    106 		return;
    107 
    108 	Rune rune;
    109 	ushort mode = USHRT_MAX;
    110 	hb_buffer_t *buffer = hb_buffer_create();
    111 	hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
    112 
    113 	/* Fill buffer with codepoints. */
    114 	for (int i = start; i < (start+length); i++) {
    115 		rune = string[i].u;
    116 		mode = string[i].mode;
    117 		if (mode & ATTR_WDUMMY)
    118 			rune = 0x0020;
    119 		hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1);
    120 	}
    121 
    122 	/* Shape the segment. */
    123 	hb_shape(font, buffer, NULL, 0);
    124 
    125 	/* Get new glyph info. */
    126 	hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL);
    127 
    128 	/* Write new codepoints. */
    129 	for (int i = 0; i < length; i++) {
    130 		hb_codepoint_t gid = info[i].codepoint;
    131 		codepoints[start+i] = gid;
    132 	}
    133 
    134 	/* Cleanup. */
    135 	hb_buffer_destroy(buffer);
    136 }