text1.c (9574B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <stdint.h> 4 #include <X11/Xlib.h> 5 #include <X11/Xutil.h> 6 #include "stb_truetype.h" 7 #include <harfbuzz/hb.h> 8 #include <harfbuzz/hb-ot.h> 9 #include <limits.h> 10 11 /* These unicode routines are taken from 12 * https://github.com/JeffBezanson/cutef8 */ 13 14 /* is c the start of a utf8 sequence? */ 15 #define isutf(c) (((c)&0xC0)!=0x80) 16 17 static const uint32_t offsetsFromUTF8[6] = { 18 0x00000000UL, 0x00003080UL, 0x000E2080UL, 19 0x03C82080UL, 0xFA082080UL, 0x82082080UL 20 }; 21 22 /* next character without NUL character terminator */ 23 uint32_t u8_nextmemchar(const char *s, size_t *i) 24 { 25 uint32_t ch = 0; 26 size_t sz = 0; 27 do { 28 ch <<= 6; 29 ch += (unsigned char)s[(*i)++]; 30 sz++; 31 } while (!isutf(s[*i])); 32 ch -= offsetsFromUTF8[sz-1]; 33 34 return ch; 35 } 36 37 38 #define STB_TRUETYPE_IMPLEMENTATION 39 #include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */ 40 41 stbtt_fontinfo ltk_load_font(const char *path) 42 { 43 FILE *f; 44 long len; 45 char *contents; 46 stbtt_fontinfo info; 47 f = fopen(path, "rb"); 48 fseek(f, 0, SEEK_END); 49 len = ftell(f); 50 fseek(f, 0, SEEK_SET); 51 contents = malloc(len + 1); 52 fread(contents, 1, len, f); 53 contents[len] = '\0'; 54 fclose(f); 55 if (!stbtt_InitFont(&info, contents, 0)) 56 { 57 fprintf(stderr, "Failed to load font %s\n", path); 58 exit(1); 59 } 60 return info; 61 } 62 63 char *ltk_load_file(const char *path, unsigned long *len) 64 { 65 FILE *f; 66 char *contents; 67 f = fopen(path, "rb"); 68 fseek(f, 0, SEEK_END); 69 *len = ftell(f); 70 fseek(f, 0, SEEK_SET); 71 contents = malloc(*len + 1); 72 fread(contents, 1, *len, f); 73 contents[*len] = '\0'; 74 fclose(f); 75 return contents; 76 } 77 78 int ltk_text_width(uint8_t *text, stbtt_fontinfo fontinfo, int height) 79 { 80 float scale = stbtt_ScaleForMappingEmToPixels(&fontinfo, height); 81 size_t i = 0; 82 int length = strlen(text); 83 if (length < 1) return 0; 84 int temp_x; 85 int kern_advance; 86 int width = 0; 87 uint32_t char1; 88 uint32_t char2; 89 char1 = u8_nextmemchar(text, &i); 90 while(i <= length) 91 { 92 stbtt_GetCodepointHMetrics(&fontinfo, char1, &temp_x, 0); 93 width += temp_x * scale; 94 95 char2 = u8_nextmemchar(text, &i); 96 if (!char2) break; 97 kern_advance = stbtt_GetCodepointKernAdvance(&fontinfo, char1, char2); 98 width += kern_advance * scale; 99 char1 = char2; 100 } 101 102 return width; 103 } 104 105 unsigned long ltk_blend_pixel(Display *display, Colormap colormap, XColor fg, XColor bg, double a) 106 { 107 XColor blended; 108 if (a == 1.0) 109 return fg.pixel; 110 else if (a == 0.0) 111 return bg.pixel; 112 blended.red = (int)((fg.red - bg.red) * a + bg.red); 113 blended.green = (int)((fg.green - bg.green) * a + bg.green); 114 blended.blue = (int)((fg.blue - bg.blue) * a + bg.blue); 115 XAllocColor(display, colormap, &blended); 116 117 return blended.pixel; 118 } 119 120 Pixmap ltk_render_text( 121 Display *display, 122 Window window, 123 GC gc, 124 uint8_t *text, 125 stbtt_fontinfo fontinfo, 126 int height, 127 XColor fg, 128 XColor bg, 129 const char *font_, 130 Colormap colormap, 131 int *w_total, 132 int *h_total 133 ) 134 { 135 hb_blob_t *blob; 136 hb_face_t *face; 137 size_t filelen = 0; 138 char *filedata = ltk_load_file(font_, &filelen); 139 blob = hb_blob_create(filedata, filelen, HB_MEMORY_MODE_READONLY, NULL, NULL); 140 face = hb_face_create(blob, 0); 141 hb_blob_destroy(blob); 142 hb_font_t *hbfont = hb_font_create(face); 143 hb_face_destroy(face); 144 hb_ot_font_set_funcs(hbfont); 145 146 hb_buffer_t *buf; 147 hb_glyph_info_t *ginf; 148 hb_glyph_position_t *gpos; 149 unsigned int len = 0; 150 151 buf = hb_buffer_create(); 152 hb_buffer_set_flags(buf, HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT); 153 hb_buffer_add_utf8(buf, text, strlen(text), 0, strlen(text)); 154 hb_buffer_guess_segment_properties(buf); 155 hb_shape(hbfont, buf, NULL, 0); 156 157 ginf = hb_buffer_get_glyph_infos(buf, &len); 158 gpos = hb_buffer_get_glyph_positions(buf, &len); 159 160 XWindowAttributes attrs; 161 XGetWindowAttributes(display, window, &attrs); 162 int depth = attrs.depth; 163 164 int width = ltk_text_width(text, fontinfo, height) + 200; 165 float scale = stbtt_ScaleForMappingEmToPixels(&fontinfo, height); 166 167 int ascent, descent, line_gap; 168 stbtt_GetFontVMetrics(&fontinfo, &ascent, &descent, &line_gap); 169 ascent *= scale; 170 descent *= scale; 171 172 Pixmap rendered = XCreatePixmap(display, window, width, height + 200, depth); 173 int length = strlen(text); 174 if (length < 1) 175 { 176 printf("WARNING: ltk_render_text: length of text is less than 1.\n"); 177 return XCreatePixmap(display, window, 0, 0, depth); 178 } 179 int ax, x = 0, y = 0, x1, y1, x2, y2, byte_offset, kern_advance; 180 double x_abs = 0, y_abs = 0; 181 unsigned char *b; 182 int w, h, xoff, yoff; 183 int x_min = INT_MAX, x_max = INT_MIN, y_min = INT_MAX, y_max = INT_MIN; 184 int x_min1, x_max1, y_min1, y_max1; 185 for (int i = 0; i < len; i++) { 186 hb_glyph_info_t *gi = &ginf[i]; 187 hb_glyph_position_t *gp = &gpos[i]; 188 stbtt_GetGlyphBitmapBox(&fontinfo, gi->codepoint, scale, scale, &x1, &y1, &x2, &y2); 189 x_min1 = (int)(x_abs + x1 + gp->x_offset * scale); 190 y_min1 = (int)(y_abs + y1 - gp->y_offset * scale); 191 x_max1 = x_min1 + (x2 - x1); 192 y_max1 = y_min1 + (y2 - y1); 193 if (x_min1 < x_min) x_min = x_min1; 194 if (y_min1 < y_min) y_min = y_min1; 195 if (x_max1 > x_max) x_max = x_max1; 196 if (y_max1 > y_max) y_max = y_max1; 197 x_abs += (gp->x_advance * scale); 198 y_abs -= (gp->y_advance * scale); 199 } 200 x_abs = -x_min; 201 y_abs = -y_min; 202 *w_total = x_max - x_min; 203 *h_total = y_max - y_min; 204 /* FIXME: calloc checks for integer overflow, right? */ 205 /* FIXME: check if null returned */ 206 unsigned char *bitmap = calloc(*w_total * *h_total, sizeof(char)); 207 for (int i = 0; i < len; i++) { 208 hb_glyph_info_t *gi = &ginf[i]; 209 hb_glyph_position_t *gp = &gpos[i]; 210 stbtt_GetGlyphBitmapBox(&fontinfo, gi->codepoint, scale, scale, &x1, &y1, &x2, &y2); 211 b = stbtt_GetGlyphBitmap(&fontinfo, scale, scale, gi->codepoint, &w, &h, &xoff, &yoff); 212 213 //x = (int)(x_abs + xoff + gp->x_offset * scale); 214 //y = (int)(y_abs + yoff - gp->y_offset * scale); 215 x = (int)(x_abs + x1 + gp->x_offset * scale); 216 y = (int)(y_abs + y1 - gp->y_offset * scale); 217 for (int i = 0; i < h; i++) 218 { 219 for (int j = 0; j < w; j++) 220 { 221 byte_offset = (y + i) * *w_total + x + j; 222 bitmap[byte_offset] = bitmap[byte_offset] + b[i * w + j]; 223 /* This *should* always be false, right? */ 224 if (bitmap[byte_offset] > 255) bitmap[byte_offset] = 255; 225 } 226 } 227 free(b); 228 229 x_abs += (gp->x_advance * scale); 230 y_abs -= (gp->y_advance * scale); 231 } 232 233 XSetForeground(display, gc, bg.pixel); 234 for (int i = 0; i < *h_total; i++) 235 { 236 for (int j = 0; j < *w_total; j++) 237 { 238 /* Yay! Magic! */ 239 XSetForeground(display, gc, ltk_blend_pixel(display, colormap, fg, bg, (bitmap[i * *w_total + j] / 255.0))); 240 XDrawPoint(display, rendered, gc, j, i); 241 } 242 } 243 XSetForeground(display, gc, bg.pixel); 244 245 /* TODO: separate this into a separate function so that one function only creates 246 * the bitmap and other functions to turn that into a pixmap with solid color 247 * background, image background, etc. 248 */ 249 return rendered; 250 } 251 252 int main(int argc, char *argv[]) 253 { 254 Display *display; 255 int screen; 256 Window window; 257 GC gc; 258 259 unsigned long black, white; 260 Colormap colormap; 261 display = XOpenDisplay((char *)0); 262 screen = DefaultScreen(display); 263 colormap = DefaultColormap(display, screen); 264 black = BlackPixel(display, screen); 265 white = WhitePixel(display, screen); 266 window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, 1366, 512, 0, black, white); 267 XSetStandardProperties(display, window, "Random Window", NULL, None, NULL, 0, NULL); 268 XSelectInput(display, window, ExposureMask|ButtonPressMask|KeyPressMask); 269 gc = XCreateGC(display, window, 0, 0); 270 XSetBackground(display, gc, black); 271 XSetForeground(display, gc, black); 272 XClearWindow(display, window); 273 XMapRaised(display, window); 274 XColor c1, c2; 275 XParseColor(display, colormap, "#FFFFFF", &c1); 276 XParseColor(display, colormap, "#FF0000", &c2); 277 XAllocColor(display, colormap, &c1); 278 XAllocColor(display, colormap, &c2); 279 280 // stbtt_fontinfo fontinfo = ltk_load_font("GentiumPlus-R.ttf"); 281 // stbtt_fontinfo fontinfo = ltk_load_font("NotoNastaliqUrdu-Regular.ttf"); 282 stbtt_fontinfo fontinfo = ltk_load_font("Awami_beta3.ttf"); 283 int width = ltk_text_width("ہمارے بارے میںsdfasdfdsfasf", fontinfo, 200); 284 int w, h; 285 // Pixmap pix = ltk_render_text(display, window, gc, "ہمارے بارے میں", fontinfo, 32, c1, c2, "NotoNastaliqUrdu-Regular.ttf", colormap, &w, &h); 286 Pixmap pix = ltk_render_text(display, window, gc, "ہمارے بارے میں", fontinfo, 32, c1, c2, "Awami_beta3.ttf", colormap, &w, &h); 287 // Pixmap pix = ltk_render_text(display, window, gc, "Bob", fontinfo, 100, c1, c2, "GentiumPlus-R.ttf", colormap); 288 XCopyArea(display, pix, window, gc, 0, 0, w, h, 0, 0); 289 290 XEvent event; 291 KeySym key; 292 char text[255]; 293 294 while(1) 295 { 296 XNextEvent(display, &event); 297 if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1) 298 { 299 XCopyArea(display, pix, window, gc, 0, 0, width, 300, 0, 0); 300 if (text[0] == 'q') 301 { 302 XFreeGC(display, gc); 303 XFreeColormap(display, colormap); 304 XDestroyWindow(display, window); 305 XCloseDisplay(display); 306 exit(0); 307 } 308 } 309 } 310 311 return 0; 312 }