aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--docs/design/004-text.md14
-rw-r--r--symbols/symbols-tool.c3
-rw-r--r--text/layout.c110
-rw-r--r--text/layout.h37
-rw-r--r--text/shape.c4
-rw-r--r--text/shape.h3
-rw-r--r--text/test-layout.c3
8 files changed, 131 insertions, 47 deletions
diff --git a/Makefile b/Makefile
index e3ffdd8..b57de0f 100644
--- a/Makefile
+++ b/Makefile
@@ -48,7 +48,7 @@ HARFBUZZ_LIBS ?= $(shell pkg-config --libs harfbuzz)
# Drawing them on the GPU takes OpenGL ES 3.1 through EGL.
GL_LIBS ?= $(shell pkg-config --libs egl glesv2)
SYMBOLS = symbols/symbols.c symbols/symbols-gl.c
-SHAPE = text/shape.c text/syllabic-data.c text/layout.c text/bidi.c text/linebreak.c text/unicode-data.c
+SHAPE = text/shape.c text/syllabic-data.c text/layout.c text/bidi.c text/linebreak.c text/grapheme.c text/unicode-data.c
$(BUILD)/vega-symbols: symbols/symbols-tool.c $(SYMBOLS) symbols/symbols.h symbols/symbols-gl.h $(SHAPE) text/shape-build.c \
text/shape.h text/shape-build.h text/layout.h text/text.h text/syllabic.h | $(BUILD)
$(CC) $(CPPFLAGS) $(CFLAGS) $(FREETYPE_CFLAGS) $(HARFBUZZ_CFLAGS) -Itext symbols/symbols-tool.c $(SYMBOLS) $(SHAPE) \
@@ -58,7 +58,7 @@ TEXT = text/bidi.c text/normalize.c text/linebreak.c text/grapheme.c text/word.c
$(BUILD)/test-text: text/test-text.c $(TEXT) text/text.h | $(BUILD)
$(CC) $(CPPFLAGS) $(CFLAGS) -Itext text/test-text.c $(TEXT) $(LDFLAGS) $(LDLIBS) -o $@
-LAYOUT = text/layout.c text/shape.c text/syllabic-data.c text/bidi.c text/linebreak.c text/unicode-data.c
+LAYOUT = text/layout.c text/shape.c text/syllabic-data.c text/bidi.c text/linebreak.c text/grapheme.c text/unicode-data.c
$(BUILD)/test-layout: text/test-layout.c $(LAYOUT) text/layout.h text/shape.h text/text.h text/syllabic.h symbols/symbols.c \
symbols/symbols.h | $(BUILD)
$(CC) $(CPPFLAGS) $(CFLAGS) -Itext -Isymbols text/test-layout.c $(LAYOUT) symbols/symbols.c $(LDFLAGS) -lm $(LDLIBS) -o $@
diff --git a/docs/design/004-text.md b/docs/design/004-text.md
index 13f9d96..8325109 100644
--- a/docs/design/004-text.md
+++ b/docs/design/004-text.md
@@ -237,12 +237,20 @@ U+180B–180E만 넣고 네 번째 자유 이체 선택자(U+180F)를 빠뜨렸�
[text/layout.c](../../text/layout.c)는 검증한 부품을 UAX #9 3.4절과 UAX #14가 말하는 순서로 잇는다.
1. 문단의 수준(P2–I2). 제거된 문자는 앞 문자의 수준을 받는다.
-2. 문자마다 문자 체계. Common과 Inherited는 앞 문자의 것을 받고, 맨 앞이면 뒤에서 처음 나오는 것을 받는다.
+2. 문자마다 문자 체계. Common과 Inherited는 앞 문자의 것을 받고, 맨 앞이면 뒤에서 처음 나오는 것을 받는다. 그리고 자소
+ 클러스터(UAX #29)마다 글꼴. 클러스터 하나가 두 글꼴로 갈라지지 않는다.
3. 줄 바꿈 기회와, 문단을 한 번 셰이핑해 잰 문자마다의 폭.
4. 줄: 보이는 부분이 아직 들어가는 마지막 기회에서 바꾼다. 줄 끝의 공백은 줄 밖으로 걸린다. 들어가는 기회가 없으면 첫 기회에서
바꾼다. 줄로 다시 셰이핑하면 잰 것보다 넓을 수 있다(줄 끝 글자가 끝 꼴이 되는 아랍 문자). 그러면 앞 기회로 물러난다.
-5. 줄마다: 줄 끝에서 L1을 하고, 수준과 문자 체계가 같은 실행으로 나눠 다시 셰이핑하고(연결과 커닝이 줄과 함께 끝난다),
- 실행들을 L2 순서로 놓고, 글리프를 왼쪽부터 놓는다. 오른쪽에서 왼쪽으로 쓰는 문단의 줄은 오른쪽 끝에 맞춘다.
+5. 줄마다: 줄 끝에서 L1을 하고, 수준과 문자 체계와 글꼴이 같은 실행으로 나눠 다시 셰이핑하고(연결과 커닝이 줄과 함께
+ 끝난다), 실행들을 L2 순서로 놓고, 글리프를 왼쪽부터 놓는다. 오른쪽에서 왼쪽으로 쓰는 문단의 줄은 오른쪽 끝에 맞춘다.
+
+글꼴은 선호 순서의 목록이다(`VegaFonts`). 클러스터는 그 문자를 모두 가진 첫 글꼴로 간다. 기본 무시 문자(ZWJ, 이형 선택자
+등)는 셰이핑이 숨기므로 없어도 된다. Common과 Inherited만으로 된 클러스터(공백, 숫자, 문장 부호)는 앞 클러스터의 글꼴에
+있으면 그 글꼴에 남는다. 그래서 아랍 낱말 사이의 공백이 아랍 글꼴에 남아 실행이 공백마다 끊기지 않는다. 모두 가진 글꼴이
+없으면 첫 문자를 가진 첫 글꼴로, 그것도 없으면 첫 글꼴(의 .notdef)로 간다. "가졌다"는 기본으로 문자표이고, 부르는 쪽이 좁힐 수
+있다. 심볼 파일은 코드가 닿는 글리프만 담으므로, 픽셀 표면은 문자표에 있고 그 글리프가 파일에도 있는 문자만 가졌다고 한다.
+위치는 첫 글꼴의 단위다. 다른 단위(upem)의 글꼴은 실행 안의 펜을 통째로 바꿔 반올림이 쌓이지 않게 한다.
정답인 참조 구현이 없으므로(브라우저마다 다르다), [text/test-layout.c](../../text/test-layout.c)는 부품이 약속하는 것을 확인한다.
무작위 문단(라틴·아랍·히브리 낱말, 부호, 숫자, 괄호, 방향 표시, 줄 구분자)을 무작위 폭으로 짠다. 그리고 다음을 본다.
diff --git a/symbols/symbols-tool.c b/symbols/symbols-tool.c
index 0682c1c..5b8595f 100644
--- a/symbols/symbols-tool.c
+++ b/symbols/symbols-tool.c
@@ -1178,11 +1178,12 @@ static int paragraph(int argc, char **argv) {
VegaShaper *shaper = vega_shaper_new();
VegaGlyphs *glyphs = vega_glyphs_open(&s, (float)(pixels / s.units));
VegaLayout layouts[16] = {0};
+ VegaFonts fonts = {{&shaping}, 1, NULL, NULL};
int paragraphs = argc - 6 < 16 ? argc - 6 : 16, height = 2 * margin;
for (int k = 0; k < paragraphs; ++k) {
uint32_t codes[4096];
size_t count = decode_utf8(argv[6 + k], codes, 4096);
- if (!vega_layout(&layouts[k], shaper, &shaping, codes, count, VEGA_BIDI_AUTO, (int32_t)(width / per_unit)))
+ if (!vega_layout(&layouts[k], shaper, &fonts, codes, count, VEGA_BIDI_AUTO, (int32_t)(width / per_unit)))
fail("cannot lay out");
height += (int)layouts[k].line_count * line_height + line_height / 2;
}
diff --git a/text/layout.c b/text/layout.c
index dd4bd4d..b52de94 100644
--- a/text/layout.c
+++ b/text/layout.c
@@ -1,10 +1,11 @@
/* Paragraphs set into lines (text/layout.h). The parts are the ones Unicode
- * and HarfBuzz check (bidi.c, linebreak.c, shape.c); this puts them
- * together as UAX #9 section 3.4 and UAX #14 describe:
+ * and HarfBuzz check (bidi.c, linebreak.c, grapheme.c, shape.c); this puts
+ * them together as UAX #9 section 3.4 and UAX #14 describe:
* 1. levels for the paragraph (P2 to I2), each removed character at the
* level of the one before it;
* 2. a script for each character, Common and Inherited ones taking the
- * one before them (at the start, the first after);
+ * one before them (at the start, the first after); and a font for each
+ * grapheme cluster (UAX #29), so no cluster is split between fonts;
* 3. break opportunities, and one shaping of the paragraph to measure it;
* 4. lines: the last opportunity where what shows still fits (spaces at a
* line's end hang past it), or the first when none does;
@@ -16,7 +17,7 @@
#include <stdlib.h>
#include <string.h>
-typedef struct { size_t start, end; uint8_t level; VegaScript script; } Run;
+typedef struct { size_t start, end; uint8_t level; VegaScript script; uint8_t font; } Run;
static bool grow(void **array, size_t *allocated, size_t need, size_t size) {
if (need <= *allocated) return true;
@@ -37,47 +38,86 @@ static bool removed(VegaBidiClass c) {
c == VEGA_BIDI_PDF;
}
-/* Shape [r->start, r->end) and add its glyphs at *pen. */
-static bool shape_run(VegaLayout *l, VegaShaper *shaper, const VegaShaping *s, const uint32_t *codes, const Run *r,
+/* v in the units of a font of upem `from` as the first font's (`to`), rounded. */
+static int32_t scaled(int64_t v, uint32_t from, uint32_t to) {
+ if (from == to || !from) return (int32_t)v;
+ int64_t t = v * to;
+ return (int32_t)((t >= 0 ? t + from / 2 : t - from / 2) / from);
+}
+
+/* Shape [r->start, r->end) and add its glyphs at *pen: in the first font's
+ * units, the run's own pen scaled (so rounding does not add up). */
+static bool shape_run(VegaLayout *l, VegaShaper *shaper, const VegaFonts *fonts, const uint32_t *codes, const Run *r,
int32_t *pen) {
+ const VegaShaping *s = fonts->fonts[r->font];
const VegaShaped *g;
long n = vega_shape(shaper, s, codes + r->start, r->end - r->start, vega_script_code(r->script),
r->level & 1 ? VEGA_SHAPE_RTL : VEGA_SHAPE_LTR, &g);
if (n < 0 || !grow((void **)&l->glyphs, &l->glyphs_allocated, l->glyph_count + (size_t)n, sizeof(VegaPlaced)))
return false;
+ uint32_t from = s->upem, to = fonts->fonts[0]->upem;
+ int64_t at = 0;
for (long k = 0; k < n; ++k) {
- l->glyphs[l->glyph_count++] = (VegaPlaced){g[k].glyph, (uint32_t)(r->start + g[k].cluster), *pen + g[k].x_offset,
- g[k].y_offset, g[k].x_advance};
- *pen += g[k].x_advance;
+ int32_t x = scaled(at, from, to);
+ l->glyphs[l->glyph_count++] = (VegaPlaced){g[k].glyph, (uint32_t)(r->start + g[k].cluster),
+ *pen + scaled(at + g[k].x_offset, from, to),
+ scaled(g[k].y_offset, from, to),
+ scaled(at + g[k].x_advance, from, to) - x, r->font};
+ at += g[k].x_advance;
}
+ *pen += scaled(at, from, to);
return true;
}
-/* Runs of one level and one script in [start, end). */
-static size_t itemize(const uint8_t *levels, const VegaScript *scripts, size_t start, size_t end, Run *out) {
+/* Runs of one level, one script and one font in [start, end). */
+static size_t itemize(const uint8_t *levels, const VegaScript *scripts, const uint8_t *fonts, size_t start, size_t end,
+ Run *out) {
size_t n = 0;
for (size_t i = start; i < end; ++i) {
- if (n && out[n - 1].level == levels[i] && out[n - 1].script == scripts[i]) out[n - 1].end = i + 1;
- else out[n++] = (Run){i, i + 1, levels[i], scripts[i]};
+ if (n && out[n - 1].level == levels[i] && out[n - 1].script == scripts[i] && out[n - 1].font == fonts[i])
+ out[n - 1].end = i + 1;
+ else out[n++] = (Run){i, i + 1, levels[i], scripts[i], fonts[i]};
}
return n;
}
-static bool set_line(VegaLayout *l, VegaShaper *shaper, const VegaShaping *s, const uint32_t *codes,
+static bool has(const VegaFonts *fonts, unsigned font, uint32_t code) {
+ if (vega_default_ignorable(code)) return true;
+ return fonts->has ? fonts->has(fonts->context, font, code) : vega_shaping_glyph(fonts->fonts[font], code) != 0;
+}
+
+/* The font for the cluster codes[0, count), after one in previous. */
+static unsigned font_for(const VegaFonts *fonts, const uint32_t *codes, size_t count, unsigned previous) {
+ bool common = true;
+ for (size_t i = 0; i < count && common; ++i) common = vega_script(codes[i]) <= VEGA_SCRIPT_INHERITED;
+ for (unsigned k = common ? 0 : 1; k <= fonts->count; ++k) {
+ unsigned f = k ? k - 1 : previous;
+ bool all = true;
+ for (size_t i = 0; i < count && all; ++i) all = has(fonts, f, codes[i]);
+ if (all) return f;
+ }
+ for (unsigned f = 0; f < fonts->count; ++f)
+ if (has(fonts, f, codes[0])) return f;
+ return common ? previous : 0;
+}
+
+static bool set_line(VegaLayout *l, VegaShaper *shaper, const VegaFonts *fonts, const uint32_t *codes,
const VegaBidiClass *classes, const uint8_t *levels, uint8_t *line_levels, const VegaScript *scripts,
- Run *runs, size_t *order, int paragraph, size_t start, size_t end, int32_t *pen_out,
- int32_t *hang_out);
+ const uint8_t *font_of, Run *runs, size_t *order, int paragraph, size_t start, size_t end,
+ int32_t *pen_out, int32_t *hang_out);
-bool vega_layout(VegaLayout *l, VegaShaper *shaper, const VegaShaping *s, const uint32_t *codes, size_t count,
+bool vega_layout(VegaLayout *l, VegaShaper *shaper, const VegaFonts *fonts, const uint32_t *codes, size_t count,
int direction, int32_t width) {
l->line_count = l->glyph_count = 0;
l->width = 0;
l->level = direction == VEGA_BIDI_RTL;
if (!count) return true;
+ if (!fonts->count || fonts->count > VEGA_LAYOUT_FONTS) return false;
/* The work, in one block: classes, levels (the paragraph's, and a
- * line's), scripts, breaks, each character's advance, runs and their
- * order, and each line's end widths. */
- size_t need = count * (sizeof(VegaBidiClass) + 2 + sizeof(VegaScript) + 1 + sizeof(int32_t) + sizeof(Run) +
+ * line's), scripts, breaks, fonts, grapheme boundaries, each
+ * character's advance, runs and their order, and each line's end
+ * widths. */
+ size_t need = count * (sizeof(VegaBidiClass) + 2 + sizeof(VegaScript) + 1 + 2 + sizeof(int32_t) + sizeof(Run) +
sizeof(size_t) + 2 * sizeof(int32_t)) + 64;
if (!grow(&l->work, &l->work_size, need, 1)) return false;
VegaBidiClass *classes = l->work;
@@ -86,6 +126,7 @@ bool vega_layout(VegaLayout *l, VegaShaper *shaper, const VegaShaping *s, const
size_t *order = (size_t *)(runs + count);
uint8_t *levels = (uint8_t *)(order + count), *line_levels = levels + count, *breaks = line_levels + count;
VegaScript *scripts = breaks + count + 1;
+ uint8_t *font_of = scripts + count, *clusters = font_of + count;
/* 1. Levels. */
for (size_t i = 0; i < count; ++i) classes[i] = vega_bidi_class(codes[i]);
@@ -108,14 +149,25 @@ bool vega_layout(VegaLayout *l, VegaShaper *shaper, const VegaShaping *s, const
if (script > VEGA_SCRIPT_INHERITED) last = script;
scripts[i] = script > VEGA_SCRIPT_INHERITED ? script : last ? last : first;
}
+ memset(font_of, 0, count);
+ if (fonts->count > 1) {
+ vega_graphemes(codes, count, clusters);
+ for (size_t i = 0, font = 0; i < count;) {
+ size_t end = i + 1;
+ while (end < count && !clusters[end]) ++end;
+ font = font_for(fonts, codes + i, end - i, (unsigned)font);
+ memset(font_of + i, (int)font, end - i);
+ i = end;
+ }
+ }
/* 3. Opportunities, and what each character takes. */
if (!vega_line_breaks(codes, count, breaks)) return false;
memset(advance, 0, count * sizeof(*advance));
- size_t run_count = itemize(levels, scripts, 0, count, runs);
+ size_t run_count = itemize(levels, scripts, font_of, 0, count, runs);
for (size_t r = 0; r < run_count; ++r) {
size_t before = l->glyph_count;
int32_t pen = 0;
- if (!shape_run(l, shaper, s, codes, &runs[r], &pen)) return false;
+ if (!shape_run(l, shaper, fonts, codes, &runs[r], &pen)) return false;
for (size_t k = before; k < l->glyph_count; ++k) advance[l->glyphs[k].cluster] += l->glyphs[k].advance;
l->glyph_count = before;
}
@@ -143,8 +195,8 @@ bool vega_layout(VegaLayout *l, VegaShaper *shaper, const VegaShaping *s, const
size_t first = l->glyph_count;
int32_t pen, hang;
for (;;) {
- if (!set_line(l, shaper, s, codes, classes, levels, line_levels, scripts, runs, order, paragraph, start, end,
- &pen, &hang))
+ if (!set_line(l, shaper, fonts, codes, classes, levels, line_levels, scripts, font_of, runs, order, paragraph,
+ start, end, &pen, &hang))
return false;
/* Shaped as a line, it may be wider than measured (a letter
* at its end takes its final form): then end at the opportunity
@@ -177,10 +229,10 @@ bool vega_layout(VegaLayout *l, VegaShaper *shaper, const VegaShaping *s, const
/* Lay out [start, end) as one line: its glyphs added to the layout, how
* far they go (pen) and how much of that is spaces hanging at its end. */
-static bool set_line(VegaLayout *l, VegaShaper *shaper, const VegaShaping *s, const uint32_t *codes,
+static bool set_line(VegaLayout *l, VegaShaper *shaper, const VegaFonts *fonts, const uint32_t *codes,
const VegaBidiClass *classes, const uint8_t *levels, uint8_t *line_levels, const VegaScript *scripts,
- Run *runs, size_t *order, int paragraph, size_t start, size_t end, int32_t *pen_out,
- int32_t *hang_out) {
+ const uint8_t *font_of, Run *runs, size_t *order, int paragraph, size_t start, size_t end,
+ int32_t *pen_out, int32_t *hang_out) {
/* L1 at the line's end: separators, and the whitespace and isolate
* controls before them or at the end, go to the paragraph's level. */
memcpy(line_levels + start, levels + start, end - start);
@@ -192,7 +244,7 @@ static bool set_line(VegaLayout *l, VegaShaper *shaper, const VegaShaping *s, co
} else
trailing = 0;
}
- size_t n = itemize(line_levels, scripts, start, end, runs);
+ size_t n = itemize(line_levels, scripts, font_of, start, end, runs);
/* L2 over the runs: from the highest level to the lowest odd one,
* reverse each stretch of runs at that level or higher. */
uint8_t highest = 0, lowest_odd = 0xff;
@@ -219,7 +271,7 @@ static bool set_line(VegaLayout *l, VegaShaper *shaper, const VegaShaping *s, co
size_t first = l->glyph_count;
int32_t pen = 0;
for (size_t r = 0; r < n; ++r)
- if (!shape_run(l, shaper, s, codes, &runs[order[r]], &pen)) return false;
+ if (!shape_run(l, shaper, fonts, codes, &runs[order[r]], &pen)) return false;
/* The spaces at its end hang: they are not in its width. */
size_t shown = end;
while (shown > start && classes[shown - 1] == VEGA_BIDI_WS) --shown;
diff --git a/text/layout.h b/text/layout.h
index 9282536..f5aeb89 100644
--- a/text/layout.h
+++ b/text/layout.h
@@ -3,14 +3,16 @@
#include "shape.h"
/* A paragraph set into lines (docs/design/004-text.md): its bidi levels
- * (UAX #9), lines broken where UAX #14 allows and they fit (spaces at a
- * line's end hang past it), and each line's runs (one level, one script)
- * shaped and put in visual order. Positions are in the font's units: x
- * from the paragraph's left edge, y up from the line's baseline. */
+ * (UAX #9), a font for each grapheme cluster, lines broken where UAX #14
+ * allows and they fit (spaces at a line's end hang past it), and each
+ * line's runs (one level, one script, one font) shaped and put in visual
+ * order. Positions are in the first font's units: x from the paragraph's
+ * left edge, y up from the line's baseline. */
typedef struct {
- uint32_t glyph; /* the font's: its symbol is VEGA_GLYPH_SYMBOLS + glyph */
+ uint32_t glyph; /* its font's: its symbol is VEGA_GLYPH_SYMBOLS + glyph */
uint32_t cluster; /* the paragraph index of the first character it came from */
int32_t x, y, advance;
+ uint32_t font; /* which of the fonts */
} VegaPlaced;
typedef struct {
size_t first, count; /* its glyphs, left to right */
@@ -29,10 +31,27 @@ typedef struct {
size_t work_size, lines_allocated, glyphs_allocated;
} VegaLayout;
-/* Set count characters in lines of at most width (0: no limit) with the
- * font's shaping; direction is VEGA_BIDI_AUTO, _LTR or _RTL. The layout
- * holds the result until it is set again; false without memory. */
-bool vega_layout(VegaLayout *, VegaShaper *, const VegaShaping *, const uint32_t *codes, size_t count, int direction,
+/* What text is set in: fonts in order of preference, each cluster in the
+ * first that has all its characters (default ignorables aside: shaping
+ * hides them), a cluster of Common and Inherited characters (spaces,
+ * digits, punctuation) in the font before it when that one has them; no
+ * font having them all, the first that has its first character, else the
+ * first font. has says whether a font has a character, NULL whether its
+ * character map does: a font whose glyphs are not all at hand can say it
+ * lacks the others. */
+enum { VEGA_LAYOUT_FONTS = 16 };
+typedef struct {
+ const VegaShaping *fonts[VEGA_LAYOUT_FONTS];
+ unsigned count;
+ bool (*has)(void *context, unsigned font, uint32_t code);
+ void *context;
+} VegaFonts;
+
+/* Set count characters in lines of at most width (0: no limit) in the
+ * fonts; direction is VEGA_BIDI_AUTO, _LTR or _RTL. The layout holds the
+ * result until it is set again; false without memory, or when a run's font
+ * has no plan for its script (nor for Common). */
+bool vega_layout(VegaLayout *, VegaShaper *, const VegaFonts *, const uint32_t *codes, size_t count, int direction,
int32_t width);
void vega_layout_free(VegaLayout *);
#endif
diff --git a/text/shape.c b/text/shape.c
index c9b8c0c..0ea2968 100644
--- a/text/shape.c
+++ b/text/shape.c
@@ -584,7 +584,7 @@ static bool cased_or_other_letter_or_space(unsigned c) {
/* HarfBuzz's Default_Ignorable: Unicode's, less the Hangul fillers and a
* few others that fonts draw. */
-static bool default_ignorable(uint32_t ch) {
+bool vega_default_ignorable(uint32_t ch) {
uint32_t plane = ch >> 16;
if (!plane) {
switch (ch >> 8) {
@@ -651,7 +651,7 @@ static void init_unicode_props(Info *info, uint32_t *scratch) {
unsigned gen = vega_category(u), props = gen;
if (u >= 0x80) {
*scratch |= HAS_NON_ASCII;
- if (default_ignorable(u)) {
+ if (vega_default_ignorable(u)) {
*scratch |= HAS_DEFAULT_IGNORABLES;
props |= UPROPS_IGNORABLE;
if (u == 0x200C) props |= UPROPS_ZWNJ;
diff --git a/text/shape.h b/text/shape.h
index 9381754..cafa9b4 100644
--- a/text/shape.h
+++ b/text/shape.h
@@ -128,6 +128,9 @@ bool vega_shaping_open(VegaShaping *, const unsigned char *bytes, size_t size, c
void vega_shaping_close(VegaShaping *);
/* The font's glyph for a character, or 0. */
uint32_t vega_shaping_glyph(const VegaShaping *, uint32_t code);
+/* Default_Ignorable_Code_Point as HarfBuzz 10.2 has it: what shaping hides
+ * (a zero-width space in its place, or nothing). */
+bool vega_default_ignorable(uint32_t code);
/* A shaped glyph, as HarfBuzz gives it: in visual order (left to right),
* cluster the index of the first character it came from; each glyph is
diff --git a/text/test-layout.c b/text/test-layout.c
index 5282572..420733a 100644
--- a/text/test-layout.c
+++ b/text/test-layout.c
@@ -88,6 +88,7 @@ int main(int argc, char **argv) {
'1', '2', '3', 0x0661, 0x0662, 0x200E, 0x200F, 0x2028, 0x2014, 0x00A0};
VegaShaper *shaper = vega_shaper_new();
VegaLayout layout = {0};
+ VegaFonts fonts = {{&shaping}, 1, NULL, NULL};
size_t lines = 0, glyphs = 0, characters = 0;
double seconds = 0;
static uint32_t codes[2048];
@@ -115,7 +116,7 @@ int main(int argc, char **argv) {
int32_t width = below(5) == 0 ? 0 : (int32_t)(shaping.upem * (1 + below(30)));
struct timespec a, b;
clock_gettime(CLOCK_MONOTONIC, &a);
- if (!vega_layout(&layout, shaper, &shaping, codes, n, direction, width)) {
+ if (!vega_layout(&layout, shaper, &fonts, codes, n, direction, width)) {
fail(k, "no layout");
continue;
}