From 9aea075f5fd6e1d6b7f6d7fe35de8f3da752c3e7 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 23 Dec 2023 11:22:21 +0800 Subject: [PATCH] Respect glyph metrics modified by instruction code * src/sfnt.c (sfnt_read_glyph): Clear advance and origin distortion returning an empty glyph. (sfnt_build_instructed_outline): New parameter *ADVANCE_WIDTH, in which the glyph's advance width is saved. (sfnt_interpret_compound_glyph_1): Refine commentary. (sfnt_verbose, main): Adjust tests. * src/sfnt.h: Update prototypes correspondingly. * src/sfntfont.c (sfntfont_get_glyph_outline): If an instructed outline is available, derive the advance and lbearing from the measurements within. (sfntfont_probe_widths): Call sfntfont_measure_pcm to establish average widths. (sfntfont_open): Do so after instruction code initialization completes. (sfntfont_measure_pcm): Revise commentary. --- src/sfnt.c | 64 ++++++++++++++++++++++++-------- src/sfnt.h | 2 +- src/sfntfont.c | 99 ++++++++++++++++++++++++++++++++------------------ 3 files changed, 114 insertions(+), 51 deletions(-) diff --git a/src/sfnt.c b/src/sfnt.c index 1397e341aa8..6698c9c27df 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -2421,6 +2421,8 @@ sfnt_read_glyph (sfnt_glyph glyph_code, glyph.ymin = 0; glyph.xmax = 0; glyph.ymax = 0; + glyph.advance_distortion = 0; + glyph.origin_distortion = 0; glyph.simple = xmalloc (sizeof *glyph.simple); glyph.compound = NULL; memset (glyph.simple, 0, sizeof *glyph.simple); @@ -12202,15 +12204,18 @@ sfnt_decompose_instructed_outline (struct sfnt_instructed_outline *outline, /* Decompose and build an outline for the specified instructed outline INSTRUCTED. Return the outline data with a refcount of 0 upon - success, or NULL upon failure. + success, and the advance width of the instructed glyph in + *ADVANCE_WIDTH, or NULL upon failure. This function is not reentrant. */ TEST_STATIC struct sfnt_glyph_outline * -sfnt_build_instructed_outline (struct sfnt_instructed_outline *instructed) +sfnt_build_instructed_outline (struct sfnt_instructed_outline *instructed, + sfnt_fixed *advance_width) { struct sfnt_glyph_outline *outline; int rc; + sfnt_f26dot6 x1, x2; memset (&build_outline_context, 0, sizeof build_outline_context); @@ -12247,10 +12252,23 @@ sfnt_build_instructed_outline (struct sfnt_instructed_outline *instructed) instructed. */ if (instructed->num_points > 1) - outline->origin - = instructed->x_points[instructed->num_points - 2]; + { + x1 = instructed->x_points[instructed->num_points - 2]; + x2 = instructed->x_points[instructed->num_points - 1]; + + /* Convert the origin point to a 16.16 fixed point number. */ + outline->origin = x1 * 1024; + + /* Do the same for the advance width. */ + *advance_width = (x2 - x1) * 1024; + } else - outline->origin = 0; + { + /* Phantom points are absent from this outline, which is + impossible. */ + *advance_width = 0; + outline->origin = 0; + } if (rc) { @@ -13133,8 +13151,8 @@ sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph, } /* Run the program for the entire compound glyph, if any. CONTEXT - should not contain phantom points by this point, so append its - own. */ + should not contain phantom points by this point, so append the + points for this glyph as a whole. */ /* Compute phantom points. */ sfnt_compute_phantom_points (glyph, metrics, interpreter->scale, @@ -20216,6 +20234,7 @@ sfnt_verbose (struct sfnt_interpreter *interpreter) unsigned char opcode; const char *name; static unsigned int instructions; + sfnt_fixed advance; /* Build a temporary outline containing the values of the interpreter's glyph zone. */ @@ -20229,7 +20248,7 @@ sfnt_verbose (struct sfnt_interpreter *interpreter) temp.y_points = interpreter->glyph_zone->y_current; temp.flags = interpreter->glyph_zone->flags; - outline = sfnt_build_instructed_outline (&temp); + outline = sfnt_build_instructed_outline (&temp, &advance); if (!outline) return; @@ -20444,6 +20463,7 @@ main (int argc, char **argv) struct sfnt_instance *instance; struct sfnt_blend blend; struct sfnt_metrics_distortion distortion; + sfnt_fixed advance; if (argc < 2) return 1; @@ -20559,8 +20579,8 @@ main (int argc, char **argv) return 1; } -#define FANCY_PPEM 14 -#define EASY_PPEM 14 +#define FANCY_PPEM 12 +#define EASY_PPEM 12 interpreter = NULL; head = sfnt_read_head_table (fd, font); @@ -20787,6 +20807,8 @@ main (int argc, char **argv) if (instance && gvar) sfnt_vary_simple_glyph (&blend, code, glyph, &distortion); + else + memset (&distortion, 0, sizeof distortion); if (sfnt_lookup_glyph_metrics (code, -1, &metrics, @@ -20804,7 +20826,10 @@ main (int argc, char **argv) exit (5); } - outline = sfnt_build_instructed_outline (value); + outline = sfnt_build_instructed_outline (value, &advance); + advances[i] = (advance / 65536); + + fprintf (stderr, "advance: %d\n", advances[i]); if (!outline) exit (6); @@ -20819,8 +20844,6 @@ main (int argc, char **argv) xfree (outline); rasters[i] = raster; - advances[i] = (sfnt_mul_fixed (metrics.advance, scale) - + sfnt_mul_fixed (distortion.advance, scale)); } sfnt_x_raster (rasters, advances, length, hhea, scale); @@ -21085,7 +21108,7 @@ main (int argc, char **argv) fprintf (stderr, "outline origin, rbearing: %" PRIi32" %"PRIi32"\n", outline->origin, - outline->ymax - outline->origin); + outline->xmax - outline->origin); sfnt_test_max = outline->ymax - outline->ymin; for (i = 0; i < outline->outline_used; i++) @@ -21199,9 +21222,20 @@ main (int argc, char **argv) printf ("rasterizing instructed outline\n"); if (outline) xfree (outline); - outline = sfnt_build_instructed_outline (value); + outline + = sfnt_build_instructed_outline (value, + &advance); xfree (value); +#define LB outline->xmin - outline->origin +#define RB outline->xmax - outline->origin + printf ("instructed advance, lb, rb: %g %g %g\n", + sfnt_coerce_fixed (advance), + sfnt_coerce_fixed (LB), + sfnt_coerce_fixed (RB)); +#undef LB +#undef RB + if (outline) { raster diff --git a/src/sfnt.h b/src/sfnt.h index 2ae47ad30ce..7baed372212 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -2102,7 +2102,7 @@ extern const char *sfnt_interpret_control_value_program (PROTOTYPE); #undef PROTOTYPE -#define PROTOTYPE struct sfnt_instructed_outline * +#define PROTOTYPE struct sfnt_instructed_outline *, sfnt_fixed * extern struct sfnt_glyph_outline *sfnt_build_instructed_outline (PROTOTYPE); diff --git a/src/sfntfont.c b/src/sfntfont.c index cecdbeafb8d..078fe6083a6 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -2182,6 +2182,7 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, const char *error; struct sfnt_glyph_metrics temp; struct sfnt_metrics_distortion distortion; + sfnt_fixed advance; start = cache->next; distortion.advance = 0; @@ -2284,23 +2285,52 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, if (!error) { - outline = sfnt_build_instructed_outline (value); + /* Now record the advance with that measured from the + phantom points within the instructed glyph outline, and + subsequently replace it once metrics are scaled. */ + + outline = sfnt_build_instructed_outline (value, + &advance); xfree (value); + + if (outline) + { + /* Save the new advance width. */ + temp.advance = advance; + + /* Finally, adjust the left side bearing of the glyph + metrics by the origin point of the outline, should a + transformation have been applied by either + instruction code or glyph variation. The left side + bearing is the distance from the origin point to the + left most point on the X axis. */ + temp.lbearing = outline->xmin - outline->origin; + } } } if (!outline) - outline = sfnt_build_glyph_outline (glyph, scale, - &temp, - sfntfont_get_glyph, - sfntfont_free_glyph, - sfntfont_get_metrics, - &dcontext); - - /* At this point, the glyph metrics are unscaled. Scale them up. - If INTERPRETER is set, use the scale placed within. */ - - sfnt_scale_metrics (&temp, scale); + { + outline = sfnt_build_glyph_outline (glyph, scale, + &temp, + sfntfont_get_glyph, + sfntfont_free_glyph, + sfntfont_get_metrics, + &dcontext); + + /* At this point, the glyph metrics are unscaled. Scale them + up. If INTERPRETER is set, use the scale placed within. */ + sfnt_scale_metrics (&temp, scale); + + /* Finally, adjust the left side bearing of the glyph metrics by + the origin point of the outline, should a transformation have + been applied by either instruction code or glyph variation. + The left side bearing is the distance from the origin point + to the left most point on the X axis. */ + + if (index != -1) + temp.lbearing = outline->xmin - outline->origin; + } fail: @@ -2309,13 +2339,6 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, if (!outline) return NULL; - if (index != -1) - /* Finally, adjust the left side bearing of the glyph metrics by - the origin point of the outline, should a distortion have been - applied. The left side bearing is the distance from the origin - point to the left most point on the X axis. */ - temp.lbearing = outline->xmin - outline->origin; - start = xmalloc (sizeof *start); start->glyph = glyph_code; start->outline = outline; @@ -2625,16 +2648,23 @@ sfntfont_lookup_glyph (struct sfnt_font_info *font_info, int c) return glyph; } +static int sfntfont_measure_pcm (struct sfnt_font_info *, sfnt_glyph, + struct font_metrics *); + /* Probe and set FONT_INFO->font.average_width, FONT_INFO->font.space_width, and FONT_INFO->font.min_width - according to the tables contained therein. */ + according to the tables contained therein. + + As this function generates outlines for all glyphs, outlines for + all ASCII characters will be entered into the outline cache as + well. */ static void sfntfont_probe_widths (struct sfnt_font_info *font_info) { int i, num_characters, total_width; sfnt_glyph glyph; - struct sfnt_glyph_metrics metrics; + struct font_metrics pcm; num_characters = 0; total_width = 0; @@ -2653,29 +2683,27 @@ sfntfont_probe_widths (struct sfnt_font_info *font_info) if (!glyph) continue; - /* Now look up the metrics of this glyph. */ - if (sfnt_lookup_glyph_metrics (glyph, font_info->font.pixel_size, - &metrics, font_info->hmtx, - font_info->hhea, font_info->head, - font_info->maxp)) + /* Now look up the metrics of this glyph. Data from the metrics + table doesn't fit the bill, since variations and instruction + code is not applied to it. */ + if (sfntfont_measure_pcm (font_info, glyph, &pcm)) continue; /* Increase the number of characters. */ num_characters++; /* Add the advance to total_width. */ - total_width += SFNT_CEIL_FIXED (metrics.advance) / 65536; + total_width += pcm.width; /* Update min_width if it hasn't been set yet or is wider. */ if (font_info->font.min_width == 1 - || font_info->font.min_width > metrics.advance / 65536) - font_info->font.min_width = metrics.advance / 65536; + || font_info->font.min_width > pcm.width) + font_info->font.min_width = pcm.width; /* If i is the space character, set the space width. Make sure to round this up. */ if (i == 32) - font_info->font.space_width - = SFNT_CEIL_FIXED (metrics.advance) / 65536; + font_info->font.space_width = pcm.width; } /* Now, if characters were found, set average_width. */ @@ -3263,9 +3291,6 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, ASET (font_object, FONT_ADSTYLE_INDEX, Qnil); - /* Find out the minimum, maximum and average widths. */ - sfntfont_probe_widths (font_info); - /* Clear various offsets. */ font_info->font.baseline_offset = 0; font_info->font.relative_compose = 0; @@ -3355,6 +3380,10 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, } cancel_blend: + + /* Find out the minimum, maximum and average widths. */ + sfntfont_probe_widths (font_info); + /* Calculate the xfld name. */ font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil, Qt); @@ -3468,7 +3497,7 @@ sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, if (!outline) return 1; - /* Round the left side bearing downwards. */ + /* Round the left side bearing down. */ pcm->lbearing = SFNT_FLOOR_FIXED (metrics.lbearing) / 65536; pcm->rbearing = SFNT_CEIL_FIXED (outline->xmax) / 65536; -- 2.39.2