aboutsummaryrefslogtreecommitdiffstats
path: root/text/shape.h
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-28 12:48:45 +0100
committereunhhu <[email protected]>2026-09-28 12:48:45 +0100
commitc38380464d29c4502c98d996236ccf890392fe86 (patch)
tree2c510e29207427fd105f5bd87425deb5b01b208e /text/shape.h
parent16d84b2530e154795a825fbf2b7e028e2f209b71 (diff)
downloadvega-c38380464d29c4502c98d996236ccf890392fe86.tar.gz
vega-c38380464d29c4502c98d996236ccf890392fe86.zip
Shape text as HarfBuzz does, from a symbol file, checked against HarfBuzz
The build tool compiles a font's shaping plans the way HarfBuzz's planner does (features, stages, masks, lookups per script and direction) and carries them with the font's character map, advances and GDEF, GSUB and GPOS tables in a section of the symbol file (VEGASYM4); glyphs shaping reaches become symbols of their own. A small engine ports HarfBuzz 10's default, Arabic and Hebrew shaping step for step and reads the tables in place, every read bounds-checked. The build refuses a file whose shaping differs from HarfBuzz's on random runs: DejaVu Sans agrees on 600,000 runs in six scripts both ways, and every installed font the tool accepts agrees too, at 1.05-1.18 times HarfBuzz's time.
Diffstat (limited to 'text/shape.h')
-rw-r--r--text/shape.h101
1 files changed, 101 insertions, 0 deletions
diff --git a/text/shape.h b/text/shape.h
new file mode 100644
index 0000000..5fe7c4e
--- /dev/null
+++ b/text/shape.h
@@ -0,0 +1,101 @@
+#ifndef VEGA_SHAPE_H
+#define VEGA_SHAPE_H
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/* Shaping (docs/design/004-text.md): a run of characters (one script, one
+ * direction) to glyphs and their positions, as HarfBuzz 10 shapes it with
+ * its default, Arabic and Hebrew shapers, glyph for glyph and unit for unit.
+ *
+ * What a font's shaping needs travels in a symbol file (its shaping
+ * section), written by the build tool:
+ * - plans, compiled from the font: per script and direction, which
+ * lookups run, in what order, on which glyphs (the feature masks);
+ * - the font's character map and advances;
+ * - its OpenType layout tables (GDEF, GSUB, GPOS) as they are: they are
+ * read in place, every read checked against the table's end, so a
+ * damaged table shapes wrong but never reads outside it.
+ * Positions are in the font's units (VegaShaping.upem to an em).
+ *
+ * The section, little-endian:
+ * "VSHP", u32 size, u16 upem, u16 glyph_count,
+ * u32 cmap_count, u32 cmap_offset (u32 code, u32 glyph; by code),
+ * u32 advances_offset (glyph_count u16),
+ * u32 offset, u32 size of GDEF, of GSUB and of GPOS (0 0 when absent),
+ * u32 plan_count, u32 plans_offset (VEGA_PLAN_BYTES each),
+ * u32 step_count, u32 steps_offset (8 bytes each)
+ * Offsets are from the section's start. A plan:
+ * u32 script (ISO 15924 as a tag: 'Latn'), u8 direction, u8 native
+ * direction (of its script; 0 when it has none), u8 shaper, u8 flags,
+ * u32 global mask, u32 masks[VEGA_PLAN_MASKS], u32 first GSUB step,
+ * u32 GSUB steps, u32 first GPOS step, u32 GPOS steps.
+ * A step, a lookup to run: u16 lookup index, u8 flags, u8 0, u32 mask. */
+enum { VEGA_SHAPING_HEADER = 64, VEGA_PLAN_BYTES = 72, VEGA_STEP_BYTES = 8, VEGA_PLAN_MASKS = 11 };
+enum { VEGA_SHAPE_LTR = 1, VEGA_SHAPE_RTL = 2 };
+enum { VEGA_SHAPER_DEFAULT, VEGA_SHAPER_ARABIC, VEGA_SHAPER_HEBREW };
+/* Plan flags. */
+enum { VEGA_PLAN_GPOS = 1, VEGA_PLAN_ZERO_MARKS = 2, VEGA_PLAN_GUESS_CLASSES = 4, VEGA_PLAN_GPOS_MARK = 8,
+ VEGA_PLAN_FRACTIONS = 16 };
+/* The masks a plan names, for the features set glyph by glyph. */
+enum { VEGA_MASK_ISOL, VEGA_MASK_FINA, VEGA_MASK_FIN2, VEGA_MASK_FIN3, VEGA_MASK_MEDI, VEGA_MASK_MED2,
+ VEGA_MASK_INIT, VEGA_MASK_RTLM, VEGA_MASK_FRAC, VEGA_MASK_NUMR, VEGA_MASK_DNOM };
+/* Step flags. */
+enum { VEGA_STEP_AUTO_ZWNJ = 1, VEGA_STEP_AUTO_ZWJ = 2, VEGA_STEP_RANDOM = 4, VEGA_STEP_PER_SYLLABLE = 8 };
+
+typedef struct { const unsigned char *p; uint32_t size; } VegaTable;
+/* Which glyphs may be in a set, as three 64-bit patterns of glyph bits
+ * (HarfBuzz's set digest): a glyph whose bits are not all there is not. */
+typedef struct { uint64_t mask[3]; } VegaDigest;
+typedef struct {
+ uint16_t type; /* with extensions resolved */
+ uint32_t at;
+ VegaDigest digest; /* the glyphs it starts from */
+} VegaSubtable;
+typedef struct {
+ uint16_t type, flags;
+ uint32_t props; /* flags, and the mark filtering set in the high half */
+ uint32_t first, count; /* subtables */
+ bool reverse;
+ VegaDigest digest; /* the glyphs its subtables start from */
+} VegaLookup;
+typedef struct {
+ const unsigned char *bytes;
+ size_t size;
+ uint16_t upem, glyph_count;
+ uint32_t cmap_count, plan_count;
+ const unsigned char *cmap, *advances, *plans, *steps;
+ VegaTable gdef, gsub, gpos;
+ uint32_t glyph_classes, attach_classes, mark_sets; /* in GDEF; 0 when absent */
+ VegaLookup *lookups[2]; /* GSUB, GPOS */
+ uint32_t lookup_count[2];
+ VegaSubtable *subtables;
+ uint16_t *bmp[256]; /* the character map's glyphs below U+10000, by page */
+} VegaShaping;
+
+/* Open a shaping section (kept, not copied); false with why. */
+bool vega_shaping_open(VegaShaping *, const unsigned char *bytes, size_t size, char *error, size_t error_size);
+void vega_shaping_close(VegaShaping *);
+/* The font's glyph for a character, or 0. */
+uint32_t vega_shaping_glyph(const VegaShaping *, 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
+ * drawn at the pen plus its offset, then the pen moves by its advance. */
+typedef struct {
+ uint32_t glyph, cluster;
+ int32_t x_advance, y_advance, x_offset, y_offset;
+} VegaShaped;
+
+/* The buffers shaping works in, kept from one run to the next. */
+typedef struct VegaShaper VegaShaper;
+VegaShaper *vega_shaper_new(void);
+void vega_shaper_free(VegaShaper *);
+/* Shape a run of count characters of script (an ISO 15924 tag, as
+ * vega_script_code gives it) in direction (VEGA_SHAPE_LTR or _RTL) by the
+ * plan compiled for them (or for Common, 'Zyyy', when there is none). The
+ * glyphs (as many as returned) are the shaper's until its next run; -1
+ * when there is no plan or no memory. */
+long vega_shape(VegaShaper *, const VegaShaping *, const uint32_t *codes, size_t count, uint32_t script, int direction,
+ const VegaShaped **out);
+#endif