From 2656d756851d97434da7846a5a30202baafb2241 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 8 Jan 2024 15:32:07 +0800 Subject: [PATCH] Properly instruct Italic Arial or BS Mono at small PPEM sizes * src/sfnt.c (sfnt_read_simple_glyph): Correct alignment errors. (PUSH2_UNCHECKED): Don't shift negative signed value to the left. (SLOOP): Permit LOOP to be set to 0, which inhibits the execution of instructions it affects. (sfnt_address_zp2, sfnt_address_zp1, sfnt_address_zp0): Permit X and Y to be NULL. (sfnt_dot_fix_14): Guarantee that the final value is rounded to negative infinity, not zero. (sfnt_project_zp1_zp0_org): New function. (sfnt_interpret_mdrp): Avoid rounding issues by computing original distance from unscaled coordinates, if at all possible. (sfnt_interpret_simple_glyph, sfnt_interpret_compound_glyph_2): Set zone->simple. (all_tests) : Update test. (sfnt_identify_instruction, main): Adjust tests. * src/sfnt.h (struct sfnt_interpreter_zone): New field simple. --- src/sfnt.c | 161 +++++++++++++++++++++++++++++++++++++++++------------ src/sfnt.h | 4 ++ 2 files changed, 128 insertions(+), 37 deletions(-) diff --git a/src/sfnt.c b/src/sfnt.c index 36240f4cdff..b300eb4ba89 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -1937,8 +1937,11 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, simple->instructions comes one word past number_of_contours, because end_pts_of_contours also contains the instruction length. */ - simple->instructions = (uint8_t *) (simple->end_pts_of_contours - + glyph->number_of_contours + 1); + + simple->x_coordinates = (int16_t *) (simple->end_pts_of_contours + + glyph->number_of_contours + 1); + simple->y_coordinates = simple->x_coordinates + number_of_points; + simple->instructions = (uint8_t *) (simple->y_coordinates + number_of_points); simple->flags = simple->instructions + simple->instruction_length; /* Read instructions into the glyph. */ @@ -2022,7 +2025,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, /* Now that the flags have been decoded, start decoding the vectors. */ - simple->x_coordinates = (int16_t *) (simple->flags + number_of_points); vec_start = flags_start; i = 0; x = 0; @@ -2080,7 +2082,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, pointer to the flags for the current vector. */ flags_start = simple->flags; y = 0; - simple->y_coordinates = simple->x_coordinates + i; i = 0; while (i < number_of_points) @@ -6944,7 +6945,7 @@ sfnt_interpret_trap (struct sfnt_interpreter *interpreter, { \ int16_t word; \ \ - word = (((int8_t) high) << 8 | low); \ + word = (((uint8_t) high) << 8 | low); \ PUSH_UNCHECKED (word); \ } \ @@ -7024,14 +7025,18 @@ sfnt_interpret_trap (struct sfnt_interpreter *interpreter, #define SLOOP() \ { \ - uint32_t loop; \ + int32_t loop; \ \ loop = POP (); \ \ - if (!loop) \ - TRAP ("loop set to 0"); \ + if (loop < 0) \ + TRAP ("loop set to invalid value"); \ \ - interpreter->state.loop = loop; \ + /* N.B. loop might be greater than 65535, \ + but no reasonable font should define \ + such values. */ \ + interpreter->state.loop \ + = MIN (65535, loop); \ } #define SMD() \ @@ -8570,8 +8575,11 @@ sfnt_address_zp2 (struct sfnt_interpreter *interpreter, if (number >= interpreter->glyph_zone->num_points) TRAP ("address to ZP2 (glyph zone) out of bounds"); - *x = interpreter->glyph_zone->x_current[number]; - *y = interpreter->glyph_zone->y_current[number]; + if (x && y) + { + *x = interpreter->glyph_zone->x_current[number]; + *y = interpreter->glyph_zone->y_current[number]; + } if (x_org && y_org) { @@ -8618,8 +8626,11 @@ sfnt_address_zp1 (struct sfnt_interpreter *interpreter, if (number >= interpreter->glyph_zone->num_points) TRAP ("address to ZP1 (glyph zone) out of bounds"); - *x = interpreter->glyph_zone->x_current[number]; - *y = interpreter->glyph_zone->y_current[number]; + if (x && y) + { + *x = interpreter->glyph_zone->x_current[number]; + *y = interpreter->glyph_zone->y_current[number]; + } if (x_org && y_org) { @@ -8666,8 +8677,11 @@ sfnt_address_zp0 (struct sfnt_interpreter *interpreter, if (number >= interpreter->glyph_zone->num_points) TRAP ("address to ZP0 (glyph zone) out of bounds"); - *x = interpreter->glyph_zone->x_current[number]; - *y = interpreter->glyph_zone->y_current[number]; + if (x && y) + { + *x = interpreter->glyph_zone->x_current[number]; + *y = interpreter->glyph_zone->y_current[number]; + } if (x_org && y_org) { @@ -10570,6 +10584,7 @@ sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by) return (int32_t) (((uint32_t) hi << 18) | (l >> 14)); #else int64_t xx, yy; + int64_t temp; xx = (int64_t) ax * bx; yy = (int64_t) ay * by; @@ -10578,7 +10593,12 @@ sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by) yy = xx >> 63; xx += 0x2000 + yy; - return (int32_t) (xx / (1 << 14)); + /* TrueType fonts rely on "division" here truncating towards + negative infinity, so compute the arithmetic right shift in place + of division. */ + temp = -(xx < 0); + temp = (temp ^ xx) >> 14 ^ temp; + return (int32_t) (temp); #endif } @@ -11412,6 +11432,63 @@ sfnt_interpret_mirp (struct sfnt_interpreter *interpreter, interpreter->state.rp0 = p; } +/* Return the projection of the two points P1 and P2's original values + along the dual projection vector, with P1 inside ZP0 and P2 inside + ZP1. If this zone is the glyph zone and the outline positions of + those points are directly accessible, project their original + positions and scale the result with rounding, so as to prevent + rounding-introduced inaccuracies. + + The scenario where such inaccuracies are significant is generally + where an Italic glyph is being instructed at small PPEM sizes, + during which a point moved by MDAP[rN] is within 1/64th of a + pixel's distance from a point on the grid, yet the measurements + taken between such a point and the reference point against which + the distance to move is computed is such that the position of the + point after applying their rounded values differs by one grid + coordinate from the font designer's intentions, either exaggerating + or neutralizing the slant of the stem to which it belongs. + + This behavior applies only to MDRP, which see. */ + +static sfnt_f26dot6 +sfnt_project_zp1_zp0_org (struct sfnt_interpreter *interpreter, + uint32_t p1, uint32_t p2) +{ + sfnt_fword x1, y1, x2, y2, projection; + struct sfnt_simple_glyph *simple; + sfnt_f26dot6 org_x1, org_y1, org_x2, org_y2; + + /* Addressing the twilight zone, perhaps only partially. */ + if (!interpreter->state.zp0 + || !interpreter->state.zp1 + /* Not interpreting a glyph. */ + || !interpreter->glyph_zone + /* Not interpreting a simple glyph. */ + || !interpreter->glyph_zone->simple + /* P1 or P2 are phantom points. */ + || p1 >= interpreter->glyph_zone->simple->number_of_points + || p2 >= interpreter->glyph_zone->simple->number_of_points) + goto project_normally; + + simple = interpreter->glyph_zone->simple; + x1 = simple->x_coordinates[p1]; + y1 = simple->y_coordinates[p1]; + x2 = simple->x_coordinates[p2]; + y2 = simple->y_coordinates[p2]; + + /* Compute the projection. */ + projection = DUAL_PROJECT (x1 - x2, y1 - y2); + + /* Return the projection, scaled with rounding. */ + return sfnt_mul_fixed_round (projection, interpreter->scale); + + project_normally: + sfnt_address_zp1 (interpreter, p1, NULL, NULL, &org_x1, &org_y1); + sfnt_address_zp0 (interpreter, p2, NULL, NULL, &org_x2, &org_y2); + return DUAL_PROJECT (org_x1 - org_x2, org_y1 - org_y2); +} + /* Interpret an MDRP instruction with the specified OPCODE in INTERPRETER. Pop a point in ZP1, and move the point until its distance from RP0 in ZP0 is the same as in the original outline. @@ -11428,20 +11505,19 @@ sfnt_interpret_mdrp (struct sfnt_interpreter *interpreter, uint32_t p; sfnt_f26dot6 distance, applied; sfnt_f26dot6 current_projection; - sfnt_f26dot6 x, y, org_x, org_y; - sfnt_f26dot6 rx, ry, org_rx, org_ry; + sfnt_f26dot6 x, y, rx, ry; /* Point number. */ p = POP (); /* Load the points. */ - sfnt_address_zp1 (interpreter, p, &x, &y, &org_x, &org_y); + sfnt_address_zp1 (interpreter, p, &x, &y, NULL, NULL); sfnt_address_zp0 (interpreter, interpreter->state.rp0, - &rx, &ry, &org_rx, &org_ry); + &rx, &ry, NULL, NULL); /* Calculate the distance between P and rp0 prior to hinting. */ - distance = DUAL_PROJECT (org_x - org_rx, - org_y - org_ry); + distance = sfnt_project_zp1_zp0_org (interpreter, p, + interpreter->state.rp0); /* Calculate the distance between P and rp0 as of now in the hinting process. */ @@ -12478,6 +12554,7 @@ sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph, zone->y_current = zone->y_points + zone->num_points; zone->flags = (unsigned char *) (zone->y_current + zone->num_points); + zone->simple = glyph->simple; /* Load x_points and x_current. */ for (i = 0; i < glyph->simple->number_of_points; ++i) @@ -12776,6 +12853,7 @@ sfnt_interpret_compound_glyph_2 (struct sfnt_glyph *glyph, zone->y_current = zone->y_points + zone->num_points; zone->flags = (unsigned char *) (zone->y_current + zone->num_points); + zone->simple = NULL; /* Copy and renumber all contour end points to start from base_index. */ @@ -18459,13 +18537,13 @@ static struct sfnt_interpreter_test all_tests[] = "SLOOP", /* PUSHB[0] 2 SLOOP[] - PUSHB[0] 0 + PUSHW[0] 255 255 (-1) SLOOP[] */ (unsigned char []) { 0xb0, 2, 0x17, - 0xb0, 0, + 0xb8, 255, 255, 0x17, }, - 6, + 7, NULL, sfnt_check_sloop, }, @@ -20258,7 +20336,8 @@ sfnt_identify_instruction (struct sfnt_interpreter *interpreter) return buffer; } - if (exec_fpgm->instructions + if (exec_fpgm + && exec_fpgm->instructions && where >= exec_fpgm->instructions && where < (exec_fpgm->instructions + exec_fpgm->num_instructions)) @@ -20529,6 +20608,13 @@ main (int argc, char **argv) if (!interpreter) abort (); + if (getenv ("SFNT_VERBOSE")) + { + interpreter->run_hook = sfnt_verbose; + interpreter->push_hook = sfnt_push_hook; + interpreter->pop_hook = sfnt_pop_hook; + } + for (i = 0; i < ARRAYELTS (all_tests); ++i) sfnt_run_interpreter_test (&all_tests[i], interpreter); @@ -20631,8 +20717,8 @@ main (int argc, char **argv) return 1; } -#define FANCY_PPEM 14 -#define EASY_PPEM 14 +#define FANCY_PPEM 16 +#define EASY_PPEM 16 interpreter = NULL; head = sfnt_read_head_table (fd, font); @@ -21023,6 +21109,16 @@ main (int argc, char **argv) interpreter = sfnt_make_interpreter (maxp, cvt, head, fvar, FANCY_PPEM, FANCY_PPEM); + + if (getenv ("SFNT_DEBUG")) + interpreter->run_hook = sfnt_run_hook; + else if (getenv ("SFNT_VERBOSE")) + { + interpreter->run_hook = sfnt_verbose; + interpreter->push_hook = sfnt_push_hook; + interpreter->pop_hook = sfnt_pop_hook; + } + state = interpreter->state; if (instance && gvar) @@ -21236,15 +21332,6 @@ main (int argc, char **argv) if (interpreter) { - if (getenv ("SFNT_DEBUG")) - interpreter->run_hook = sfnt_run_hook; - else if (getenv ("SFNT_VERBOSE")) - { - interpreter->run_hook = sfnt_verbose; - interpreter->push_hook = sfnt_push_hook; - interpreter->pop_hook = sfnt_pop_hook; - } - if (!sfnt_lookup_glyph_metrics (code, &metrics, hmtx, hhea, maxp)) { diff --git a/src/sfnt.h b/src/sfnt.h index 2b92f9f540a..5b01270e8ce 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -1759,6 +1759,10 @@ struct sfnt_interpreter_zone /* Pointer to the flags associated with this data. */ unsigned char *flags; + + /* If this structure was produced from a simple glyph, pointer to + the simple glyph itself. NULL otherwise. */ + struct sfnt_simple_glyph *simple; }; enum -- 2.39.2