pastry
truetype font renderer
demo.c
5102 bytes raw
1#include <stdio.h>
2#include <string.h>
3#include <X11/Xlib.h>
4#include <X11/extensions/Xrender.h>
5
6#include <stdint.h>
7#include "pastry.h"
8
9#include <hb.h>
10#include <hb-ot.h>
11
12static int add_glyph(Display *dpy, GlyphSet glyphset, PASTRY *pastry, PASTRY_Glyph glyph)
13{
14#define ABORT(glyph, m) do { fprintf(stderr, "glyph %lu %s\n", (unsigned long)(glyph), m); return -1; } while (0)
15
16 PASTRY_GMetrics mtx;
17 if (pastry_gmetrics(pastry, glyph, &mtx) < 0)
18 ABORT(glyph, "bad glyph metrics");
19
20 PASTRY_Image img = {
21 .width = (mtx.minWidth + 3) & ~3,
22 .height = mtx.minHeight,
23 };
24 char pixels[img.width * img.height];
25 img.pixels = pixels;
26 if (pastry_render(pastry, glyph, img) < 0)
27 ABORT(glyph, "not rendered");
28
29 XGlyphInfo info = {
30 .x = (short) -mtx.leftSideBearing,
31 .y = (short) -mtx.yOffset,
32 .width = (unsigned short) img.width,
33 .height = (unsigned short) img.height,
34 .xOff = (short) mtx.advanceWidth,
35 .yOff = 0
36 };
37 Glyph g = (Glyph) glyph;
38 XRenderAddGlyphs(dpy, glyphset, &g, &info, 1,
39 img.pixels, (int) (img.width * img.height));
40
41 return 0;
42}
43
44int main(void)
45{
46#define END(m) do { fprintf(stderr, "%s\n", m); return 1; } while (0)
47
48 Display *dpy = XOpenDisplay(NULL);
49 if (dpy == NULL)
50 END("Can't open X display");
51 unsigned int s = 2, screen = (unsigned int)DefaultScreen(dpy);
52
53 Window win = XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, 600*s, 440*s, 0,
54 DefaultDepth(dpy, screen), InputOutput, CopyFromParent, 0, NULL);
55 XSelectInput(dpy, win, ExposureMask);
56 Atom wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
57 XSetWMProtocols(dpy, win, &wmDeleteWindow, 1);
58 XMapRaised(dpy, win);
59
60 XRenderPictFormat *fmt = XRenderFindVisualFormat(dpy, DefaultVisual(dpy, screen));
61 Picture pic = XRenderCreatePicture(dpy, win, fmt, 0, NULL);
62 XRenderColor fg = { 0, 0, 0, 0xFFFF }, bg = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
63
64 Pixmap fgpix = XCreatePixmap(dpy, win, 1, 1, 24);
65 XRenderPictureAttributes attr = { .repeat = True };
66 fmt = XRenderFindStandardFormat(dpy, PictStandardRGB24);
67 Picture fgpic = XRenderCreatePicture(dpy, fgpix, fmt, CPRepeat, &attr);
68 XRenderFillRectangle(dpy, PictOpSrc, fgpic, &fg, 0, 0, 1, 1);
69
70 fmt = XRenderFindStandardFormat(dpy, PictStandardA8);
71 GlyphSet glyphset = XRenderCreateGlyphSet(dpy, fmt);
72
73 PASTRY pastry = {
74 .xScale = 16*s,
75 .yScale = 16*s,
76 .flags = PASTRY_DOWNWARD_Y,
77 };
78 pastry.font = pastry_loadfile("firago-extended.ttf");
79 if (pastry.font == NULL)
80 END("failed to load font");
81
82 // harfbuzz does the shaping; pastry just draws the glyph ids it hands us
83 hb_blob_t *blob = hb_blob_create_from_file_or_fail("firago-extended.ttf");
84 if (blob == NULL)
85 END("harfbuzz failed to load font");
86 hb_face_t *face = hb_face_create(blob, 0);
87 hb_font_t *font = hb_font_create(face);
88 hb_ot_font_set_funcs(font);
89 hb_buffer_t *buffer = hb_buffer_create();
90
91 XEvent event;
92 while (!XNextEvent(dpy, &event)) {
93 if (event.type == Expose) {
94 XRenderFillRectangle(dpy, PictOpOver, pic, &bg, 0, 0, (unsigned int)event.xexpose.width, (unsigned int)event.xexpose.height);
95
96 FILE *file = fopen("text.utf8", "r");
97 if (file == NULL)
98 END("cannot open input text");
99
100 PASTRY_LMetrics lmtx;
101 pastry_lmetrics(&pastry, &lmtx);
102 int y = 20 + (int)lmtx.ascender + (int)lmtx.lineGap;
103
104 char text[256];
105 while (fgets(text, sizeof(text), file)) {
106 size_t len = strlen(text);
107 if (len > 0 && text[len - 1] == '\n')
108 text[--len] = 0;
109
110 hb_buffer_clear_contents(buffer);
111 hb_buffer_add_utf8(buffer, text, (int)len, 0, (int)len);
112 hb_buffer_guess_segment_properties(buffer);
113 hb_shape(font, buffer, NULL, 0);
114
115 unsigned int count = 0;
116 hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &count);
117 for (unsigned int i = 0; i < count; i++)
118 add_glyph(dpy, glyphset, &pastry, (PASTRY_Glyph) info[i].codepoint);
119
120 unsigned int glyphs[256];
121 for (unsigned int i = 0; i < count && i < 256; i++)
122 glyphs[i] = info[i].codepoint;
123 XRenderCompositeString32(dpy, PictOpOver, fgpic, pic, NULL, glyphset, 0, 0, 20, y, glyphs, (int)count);
124
125 y += 2 * ((int)lmtx.ascender + (int)lmtx.descender + (int)lmtx.lineGap);
126 }
127
128 fclose(file);
129 } else if (event.type == ClientMessage) {
130 if ((Atom) event.xclient.data.l[0] == wmDeleteWindow)
131 break;
132 }
133 }
134
135 hb_buffer_destroy(buffer);
136 hb_font_destroy(font);
137 hb_face_destroy(face);
138 hb_blob_destroy(blob);
139
140 pastry_freefont(pastry.font);
141 XCloseDisplay(dpy);
142 return 0;
143}