ttf.c: glyph outline extraction, simple and composite (item 4.3.7a)
ttf_glyph_outline() decodes simple-glyph flag/coordinate runs and recursively resolves composite components into a caller-supplied point/ contour-end buffer, in Q48.16. Composite scale/rotation/skew transforms are rejected with TTF_ERR_UNSUPPORTED rather than mis-rendered, since the shared q48_mul/q48_div are unsigned-only; translation-only composites (the only kind the v1 glyph repertoire uses) apply cleanly via q48_add. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5f6cc054d4
commit
70b6918279
@@ -55,6 +55,100 @@ static void check_eq_i(const char *what, uint32_t cp, long got, long want) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Outline expectations (4.3.7a), independently computed by a from-scratch
|
||||
* Python outline decoder (tools/../../tmp .../ttf_outline_ref.py, not
|
||||
* committed, reproducible from FABRIC.md's 4.3.7a completion note) that
|
||||
* shares no code with ttf.c. '.' and '0' are simple glyphs; 'A' is simple
|
||||
* with two contours; 'a' is a translation-only composite (2 components). */
|
||||
typedef struct {
|
||||
int32_t x, y;
|
||||
int on_curve;
|
||||
} exp_point_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t codepoint;
|
||||
uint32_t glyph;
|
||||
const uint16_t *contour_ends;
|
||||
uint32_t contour_count;
|
||||
const exp_point_t *points;
|
||||
uint32_t point_count;
|
||||
} outline_expect_t;
|
||||
|
||||
static const uint16_t DOT_ENDS[] = {11};
|
||||
static const exp_point_t DOT_PTS[] = {
|
||||
{300, -10, 1}, {263, -10, 0}, {218, 34, 0}, {218, 69, 1}, {218, 106, 0}, {263, 152, 0},
|
||||
{300, 152, 1}, {337, 152, 0}, {382, 106, 0}, {382, 69, 1}, {382, 34, 0}, {337, -10, 0},
|
||||
};
|
||||
|
||||
static const uint16_t A_ENDS[] = {7, 16};
|
||||
static const exp_point_t A_PTS[] = {
|
||||
{50, 0, 1}, {240, 730, 1}, {361, 730, 1}, {550, 0, 1}, {459, 0, 1}, {411, 194, 1},
|
||||
{190, 194, 1}, {142, 0, 1}, {208, 270, 1}, {392, 270, 1}, {336, 495, 1}, {320, 559, 0},
|
||||
{302, 645, 0}, {300, 658, 1}, {298, 645, 0}, {280, 559, 0}, {264, 496, 1},
|
||||
};
|
||||
|
||||
static const uint16_t ALOWER_ENDS[] = {27, 38, 42};
|
||||
static const exp_point_t ALOWER_PTS[] = {
|
||||
{252, -10, 1}, {167, -10, 0}, {67, 85, 0}, {67, 162, 1}, {67, 213, 0}, {113, 289, 0},
|
||||
{195, 332, 0}, {248, 332, 1}, {418, 332, 1}, {418, 375, 1}, {418, 482, 0}, {301, 482, 1},
|
||||
{249, 482, 0}, {185, 444, 0}, {183, 410, 1}, {93, 410, 1}, {98, 475, 0}, {209, 560, 0},
|
||||
{301, 560, 1}, {401, 560, 0}, {508, 464, 0}, {508, 378, 1}, {508, 0, 1}, {419, 0, 1},
|
||||
{419, 100, 1}, {417, 100, 1}, {409, 49, 0}, {323, -10, 0}, {274, 66, 1}, {340, 66, 0},
|
||||
{418, 130, 0}, {418, 185, 1}, {418, 262, 1}, {258, 262, 1}, {214, 262, 0}, {159, 209, 0},
|
||||
{159, 165, 1}, {159, 119, 0}, {220, 66, 0}, {247, 645, 1}, {353, 785, 1}, {450, 785, 1},
|
||||
{339, 645, 1},
|
||||
};
|
||||
|
||||
static const outline_expect_t OUTLINE_EXPECTED[] = {
|
||||
{0x002E, 908, DOT_ENDS, 1, DOT_PTS, 12},
|
||||
{0x0041, 1, A_ENDS, 2, A_PTS, 17},
|
||||
{0x0061, 199, ALOWER_ENDS, 3, ALOWER_PTS, 43},
|
||||
};
|
||||
|
||||
static int32_t unscale(q48_16_t q) {
|
||||
return (int32_t) (((int64_t) q) >> 16);
|
||||
}
|
||||
|
||||
static void test_outlines(const ttf_font_t *font) {
|
||||
ttf_point_t point_buf[600];
|
||||
uint16_t contour_buf[16];
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(OUTLINE_EXPECTED) / sizeof(OUTLINE_EXPECTED[0]); i++) {
|
||||
const outline_expect_t *e = &OUTLINE_EXPECTED[i];
|
||||
ttf_outline_t outline;
|
||||
int rc;
|
||||
uint32_t j;
|
||||
|
||||
outline.points = point_buf;
|
||||
outline.max_points = sizeof(point_buf) / sizeof(point_buf[0]);
|
||||
outline.contour_ends = contour_buf;
|
||||
outline.max_contours = sizeof(contour_buf) / sizeof(contour_buf[0]);
|
||||
|
||||
rc = ttf_glyph_outline(font, e->glyph, &outline);
|
||||
if (rc != TTF_OK) {
|
||||
printf(" FAIL U+%04X ttf_glyph_outline rc=%d\n", e->codepoint, rc);
|
||||
failures++;
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("U+%04X outline: points=%u (want %u) contours=%u (want %u)\n",
|
||||
e->codepoint, outline.point_count, e->point_count,
|
||||
outline.contour_count, e->contour_count);
|
||||
check_eq_i("point_count", e->codepoint, outline.point_count, e->point_count);
|
||||
check_eq_i("contour_count", e->codepoint, outline.contour_count, e->contour_count);
|
||||
|
||||
for (j = 0; j < e->contour_count && j < outline.contour_count; j++) {
|
||||
check_eq_i("contour_end", e->codepoint, outline.contour_ends[j], e->contour_ends[j]);
|
||||
}
|
||||
for (j = 0; j < e->point_count && j < outline.point_count; j++) {
|
||||
check_eq_i("point.x", e->codepoint, unscale(outline.points[j].x), e->points[j].x);
|
||||
check_eq_i("point.y", e->codepoint, unscale(outline.points[j].y), e->points[j].y);
|
||||
check_eq_i("point.on_curve", e->codepoint, outline.points[j].on_curve, e->points[j].on_curve);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *fp;
|
||||
long size;
|
||||
@@ -115,6 +209,8 @@ int main(int argc, char **argv) {
|
||||
check_eq_i("glyf_length", e->codepoint, hdr.glyf_length, e->expect_glyf_length);
|
||||
}
|
||||
|
||||
test_outlines(&font);
|
||||
|
||||
free(buf);
|
||||
|
||||
if (failures) {
|
||||
|
||||
Reference in New Issue
Block a user