aboutsummaryrefslogtreecommitdiffstats
path: root/symbols/symbols-tool.c
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-28 13:03:50 +0100
committereunhhu <[email protected]>2026-09-28 13:03:50 +0100
commit202e6bf4677565c0cfbef5340119d0f9674bf17d (patch)
treede23a6974e74784813556e51b52c0719b699b220 /symbols/symbols-tool.c
parent0d2e04f1c9d05a598deb443ba067ef61e0b9cab3 (diff)
downloadvega-202e6bf4677565c0cfbef5340119d0f9674bf17d.tar.gz
vega-202e6bf4677565c0cfbef5340119d0f9674bf17d.zip
Set paragraphs into lines: bidi, script runs, shaping, line breaking, visual order
text/layout.c puts the checked parts together: a paragraph's levels, scripts resolved for Common and Inherited characters, break opportunities, one shaping to measure, lines broken at the last opportunity that fits (trailing spaces hang, and a line that shapes wider than measured backs off), then per line L1, runs of one level and one script shaped again, the runs in L2's order and their glyphs placed. test-layout checks 30,000 random mixed-script paragraphs: lines cover the text, mandatory breaks end lines, lines fit when they can, and glyphs come in the order UAX #9 gives the characters. vega-symbols paragraph draws a layout through the symbol file's glyphs.
Diffstat (limited to 'symbols/symbols-tool.c')
-rw-r--r--symbols/symbols-tool.c85
1 files changed, 85 insertions, 0 deletions
diff --git a/symbols/symbols-tool.c b/symbols/symbols-tool.c
index 765dd5c..0682c1c 100644
--- a/symbols/symbols-tool.c
+++ b/symbols/symbols-tool.c
@@ -8,6 +8,7 @@
* vega-symbols info OUT.vsym
* vega-symbols render OUT.vsym CODE PIXELS OUT.pgm
* vega-symbols gpu OUT.vsym --sizes 12,16,32,64 [--frames N] */
+#include "layout.h"
#include "shape-build.h"
#include "symbols-gl.h"
#include "symbols.h"
@@ -1149,7 +1150,91 @@ static int shape_one(int argc, char **argv) {
return 0;
}
+/* UTF-8 (valid) to code points; how many. */
+static size_t decode_utf8(const char *text, uint32_t *out, size_t room) {
+ size_t n = 0;
+ for (const unsigned char *p = (const unsigned char *)text; *p && n < room;) {
+ uint32_t c = *p;
+ int more = c >= 0xF0 ? 3 : c >= 0xE0 ? 2 : c >= 0xC0 ? 1 : 0;
+ c &= more == 3 ? 0x07 : more == 2 ? 0x0F : more == 1 ? 0x1F : 0x7F;
+ for (++p; more-- > 0 && *p; ++p) c = c << 6 | (*p & 0x3F);
+ out[n++] = c;
+ }
+ return n;
+}
+
+/* Set text in lines at most width pixels wide and draw it: vega-symbols
+ * paragraph OUT.vsym PIXELS WIDTH OUT.pgm TEXT... (the texts are
+ * paragraphs, one after another). */
+static int paragraph(int argc, char **argv) {
+ if (argc < 7) fail("usage: vega-symbols paragraph OUT.vsym PIXELS WIDTH OUT.pgm TEXT...");
+ char *bytes, error[128];
+ VegaSymbols s = open_symbols(argv[2], &bytes);
+ VegaShaping shaping;
+ if (!s.shaping || !vega_shaping_open(&shaping, s.shaping, s.shaping_size, error, sizeof(error))) fail("no shaping");
+ double pixels = atof(argv[3]), per_unit = pixels / shaping.upem;
+ int width = atoi(argv[4]), margin = (int)pixels, line_height = (int)ceil((s.ascent - s.descent) * pixels / s.units);
+ int ascent = (int)ceil(s.ascent * pixels / s.units);
+ VegaShaper *shaper = vega_shaper_new();
+ VegaGlyphs *glyphs = vega_glyphs_open(&s, (float)(pixels / s.units));
+ VegaLayout layouts[16] = {0};
+ 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)))
+ fail("cannot lay out");
+ height += (int)layouts[k].line_count * line_height + line_height / 2;
+ }
+ int total_width = width + 2 * margin;
+ unsigned char *image = malloc((size_t)total_width * (size_t)height);
+ memset(image, 255, (size_t)total_width * (size_t)height);
+ int top = margin;
+ for (int k = 0; k < paragraphs; ++k) {
+ const VegaLayout *l = &layouts[k];
+ for (size_t i = 0; i < l->line_count; ++i, top += line_height) {
+ const VegaLine *line = &l->lines[i];
+ int baseline = top + ascent;
+ for (size_t g = line->first; g < line->first + line->count; ++g) {
+ const VegaPlaced *placed = &l->glyphs[g];
+ const VegaBitmap *b = vega_glyph(glyphs, VEGA_GLYPH_SYMBOLS + placed->glyph);
+ if (!b) continue;
+ int pen_x = margin + (int)lround(placed->x * per_unit), pen_y = baseline - (int)lround(placed->y * per_unit);
+ for (int y = 0; y < b->place.height; ++y)
+ for (int x = 0; x < b->place.width; ++x) {
+ int px = pen_x + b->place.left + x, py = pen_y - b->place.top + y;
+ if (px < 0 || py < 0 || px >= total_width || py >= height) continue;
+ unsigned a = b->pixels[(size_t)y * (size_t)b->place.width + (size_t)x];
+ unsigned char *out = &image[(size_t)py * (size_t)total_width + (size_t)px];
+ *out = (unsigned char)(*out * (255 - a) / 255);
+ }
+ }
+ }
+ top += line_height / 2;
+ }
+ FILE *f = fopen(argv[5], "wb");
+ if (!f) fail("cannot write the image");
+ fprintf(f, "P5\n%d %d\n255\n", total_width, height);
+ fwrite(image, 1, (size_t)total_width * (size_t)height, f);
+ fclose(f);
+ size_t lines = 0, glyph_count = 0;
+ for (int k = 0; k < paragraphs; ++k) {
+ lines += layouts[k].line_count;
+ glyph_count += layouts[k].glyph_count;
+ vega_layout_free(&layouts[k]);
+ }
+ printf("{\"paragraphs\":%d,\"lines\":%zu,\"glyphs\":%zu,\"width\":%d,\"height\":%d}\n", paragraphs, lines,
+ glyph_count, total_width, height);
+ free(image);
+ vega_glyphs_close(glyphs);
+ vega_shaper_free(shaper);
+ vega_shaping_close(&shaping);
+ free(bytes);
+ return 0;
+}
+
int main(int argc, char **argv) {
+ if (argc > 1 && !strcmp(argv[1], "paragraph")) return paragraph(argc, argv);
if (argc > 1 && !strcmp(argv[1], "shape")) return shape_one(argc, argv);
if (argc > 1 && !strcmp(argv[1], "build")) return build(argc, argv);
if (argc > 1 && !strcmp(argv[1], "shape-check")) return shape_check(argc, argv);