From ab9d30965d298c10e0f1934364b03276067cf8a5 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Wed, 10 Apr 2019 17:21:37 -0700 Subject: [PATCH] Add tt var metrics test cases & bug fixes --- src/hb-ot-font.cc | 4 +- src/hb-ot-glyf-table.hh | 131 +++++++++--------- src/hb-ot-hmtx-table.hh | 4 +- src/hb-ot-var-gvar-table.hh | 4 +- .../SourceSansVariable-Roman.modcomp.ttf | Bin 0 -> 3252 bytes test/api/test-ot-metrics-tt-var.c | 79 ++++++++++- 6 files changed, 147 insertions(+), 75 deletions(-) create mode 100644 test/api/fonts/SourceSansVariable-Roman.modcomp.ttf diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index d03371cc3..9e7f301ea 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -161,7 +161,9 @@ hb_ot_get_glyph_v_origin (hb_font_t *font, if (ot_face->glyf->get_extents (font, glyph, &extents)) { const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; - hb_position_t tsb = vmtx.get_side_bearing (font, glyph); + hb_position_t tsb = vmtx.has_data ()? + vmtx.get_side_bearing (font, glyph): + ((extents.height - font->get_glyph_v_advance (glyph)) / 2); *y = font->em_scale_y (extents.y_bearing + tsb); return true; } diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index 5be1eadf9..186750f7a 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -452,7 +452,7 @@ struct glyf phantoms[PHANTOM_LEFT].x = h_delta; phantoms[PHANTOM_RIGHT].x = h_adv + h_delta; phantoms[PHANTOM_TOP].y = v_orig; - phantoms[PHANTOM_BOTTOM].y = -v_adv + v_orig; + phantoms[PHANTOM_BOTTOM].y = -(int)v_adv + v_orig; } /* for a simple glyph, return contour end points, flags, along with coordinate points @@ -539,39 +539,6 @@ struct glyf read_points (p, points_, checker)); } - /* Note: Recursively calls itself. */ - bool get_var_metrics (hb_codepoint_t glyph, - const int *coords, unsigned int coord_count, - contour_point_vector_t &phantoms /* OUT */, - unsigned int depth=0) const - { - if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return false; - contour_point_vector_t points; - hb_vector_t end_points; - if (unlikely (!get_contour_points (glyph, points, end_points, true/*phantom_only*/))) return false; - hb_array_t phantoms_array = points.sub_array (points.length-PHANTOM_COUNT, PHANTOM_COUNT); - init_phantom_points (glyph, phantoms_array); - if (unlikely (!gvar_accel.apply_deltas_to_points (glyph, coords, coord_count, - points.as_array (), end_points.as_array ()))) return false; - - for (unsigned int i = 0; i < PHANTOM_COUNT; i++) - phantoms[i] = points[points.length - PHANTOM_COUNT + i]; - - CompositeGlyphHeader::Iterator composite; - if (!get_composite (glyph, &composite)) return true; /* simple glyph */ - do - { - if (composite.current->flags & CompositeGlyphHeader::USE_MY_METRICS) - { - if (unlikely (!get_var_metrics (composite.current->glyphIndex, coords, coord_count, - phantoms, depth))) return false; - - composite.current->transform_points (phantoms); - } - } while (composite.move_to_next()); - return true; - } - struct contour_bounds_t { contour_bounds_t () { min.x = min.y = FLT_MAX; max.x = max.y = -FLT_MAX; } @@ -591,7 +558,7 @@ struct glyf }; /* Note: Recursively calls itself. - * all_points does not include phantom points + * all_points includes phantom points */ bool get_points_var (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, @@ -602,8 +569,8 @@ struct glyf contour_point_vector_t points; hb_vector_t end_points; if (unlikely (!get_contour_points (glyph, points, end_points))) return false; - hb_array_t phantoms_array = points.sub_array (points.length-PHANTOM_COUNT, PHANTOM_COUNT); - init_phantom_points (glyph, phantoms_array); + hb_array_t phantoms = points.sub_array (points.length-PHANTOM_COUNT, PHANTOM_COUNT); + init_phantom_points (glyph, phantoms); if (unlikely (!gvar_accel.apply_deltas_to_points (glyph, coords, coord_count, points.as_array (), end_points.as_array ()))) return false; unsigned int comp_index = 0; @@ -611,8 +578,7 @@ struct glyf if (!get_composite (glyph, &composite)) { /* simple glyph */ - if (likely (points.length >= PHANTOM_COUNT)) - all_points.extend (points.sub_array (0, points.length - PHANTOM_COUNT)); + all_points.extend (points.as_array ()); } else { @@ -621,7 +587,13 @@ struct glyf { contour_point_vector_t comp_points; if (unlikely (!get_points_var (composite.current->glyphIndex, coords, coord_count, - comp_points))) return false; + comp_points, depth)) + || comp_points.length < PHANTOM_COUNT) return false; + + /* Copy phantom points from component if USE_MY_METRICS flag set */ + if (composite.current->flags & CompositeGlyphHeader::USE_MY_METRICS) + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; /* Apply component transformation & translation */ composite.current->transform_points (comp_points); @@ -642,58 +614,82 @@ struct glyf comp_points.translate (delta); } } - all_points.extend (comp_points.as_array ()); + + all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT)); comp_index++; } while (composite.move_to_next()); + + all_points.extend (phantoms); } + if (depth == 1) { /* Undocumented rasterizer behavior: * Shift points horizontally by the updated left side bearing */ contour_point_t delta; - delta.init (-points[points.length - PHANTOM_COUNT + PHANTOM_LEFT].x, 0.f); + delta.init (-phantoms[PHANTOM_LEFT].x, 0.f); if (delta.x != 0.f) all_points.translate (delta); } return true; } - bool get_extents_var (hb_codepoint_t glyph, - const int *coords, unsigned int coord_count, - hb_glyph_extents_t *extents) const + bool get_var_extents_and_phantoms (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + hb_glyph_extents_t *extents=nullptr /* OUt */, + contour_point_vector_t *phantoms=nullptr /* OUT */) const { contour_point_vector_t all_points; if (unlikely (!get_points_var (glyph, coords, coord_count, all_points))) return false; - contour_bounds_t bounds; - for (unsigned int i = 0; i < all_points.length; i++) - bounds.add (all_points[i]); + if (extents != nullptr) + { + contour_bounds_t bounds; + for (unsigned int i = 0; i + PHANTOM_COUNT < all_points.length; i++) + bounds.add (all_points[i]); - if (bounds.min.x >= bounds.max.x) - { - extents->width = 0; - extents->x_bearing = 0; + if (bounds.min.x > bounds.max.x) + { + extents->width = 0; + extents->x_bearing = 0; + } + else + { + extents->x_bearing = (int32_t)floorf (bounds.min.x); + extents->width = (int32_t)ceilf (bounds.max.x) - extents->x_bearing; + } + if (bounds.min.y > bounds.max.y) + { + extents->height = 0; + extents->y_bearing = 0; + } + else + { + extents->y_bearing = (int32_t)ceilf (bounds.max.y); + extents->height = (int32_t)floorf (bounds.min.y) - extents->y_bearing; + } } - else + if (phantoms != nullptr) { - extents->x_bearing = (int32_t)floorf (bounds.min.x); - extents->width = (int32_t)ceilf (bounds.max.x) - extents->x_bearing; - } - if (bounds.min.y >= bounds.max.y) - { - extents->height = 0; - extents->y_bearing = 0; - } - else - { - extents->y_bearing = (int32_t)ceilf (bounds.max.y); - extents->height = (int32_t)floorf (bounds.min.y) - extents->y_bearing; + if (unlikely (all_points.length < PHANTOM_COUNT)) return false; + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + (*phantoms)[i] = all_points[all_points.length - PHANTOM_COUNT + i]; } return true; } + bool get_var_metrics (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + contour_point_vector_t &phantoms) const + { return get_var_extents_and_phantoms (glyph, coords, coord_count, nullptr, &phantoms); } + + bool get_extents_var (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + hb_glyph_extents_t *extents) const + { return get_var_extents_and_phantoms (glyph, coords, coord_count, extents); } + public: /* based on FontTools _g_l_y_f.py::trim */ bool remove_padding (unsigned int start_offset, @@ -876,13 +872,14 @@ struct glyf int get_side_bearing_var (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, bool vertical) const { + hb_glyph_extents_t extents; contour_point_vector_t phantoms; phantoms.resize (PHANTOM_COUNT); - if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms))) + if (unlikely (!get_var_extents_and_phantoms (glyph, coords, coord_count, &extents, &phantoms))) return vertical? vmtx_accel.get_side_bearing (glyph): hmtx_accel.get_side_bearing (glyph); - return (int)(vertical? -ceilf (phantoms[PHANTOM_TOP].y): floorf (phantoms[PHANTOM_LEFT].x)); + return vertical? (int)ceilf (phantoms[PHANTOM_TOP].y) - extents.y_bearing: (int)floorf (phantoms[PHANTOM_LEFT].x); } bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh index 9df03f9aa..9a2b842d7 100644 --- a/src/hb-ot-hmtx-table.hh +++ b/src/hb-ot-hmtx-table.hh @@ -221,6 +221,8 @@ struct hmtxvmtx var_table.destroy (); } + bool has_data () const { return table.get () != nullptr; } + int get_side_bearing (hb_codepoint_t glyph) const { if (glyph < num_advances) @@ -274,7 +276,7 @@ struct hmtxvmtx if (font->num_coords) { if (var_table.get_blob () != hb_blob_get_empty ()) - advance += var_table->get_advance_var (glyph, font->coords, font->num_coords); // TODO Optimize?! + advance += roundf (var_table->get_advance_var (glyph, font->coords, font->num_coords)); // TODO Optimize?! else advance = get_advance_var_tt (font, glyph, T::tableTag==HB_OT_TAG_vmtx); } diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 46daf509a..c34c9aed4 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -673,8 +673,8 @@ struct gvar /* apply specified / inferred deltas to points */ for (unsigned int i = 0; i < points.length; i++) { - points[i].x += deltas[i].x; - points[i].y += deltas[i].y; + points[i].x += roundf (deltas[i].x); + points[i].y += roundf (deltas[i].y); } } while (iterator.move_to_next ()); diff --git a/test/api/fonts/SourceSansVariable-Roman.modcomp.ttf b/test/api/fonts/SourceSansVariable-Roman.modcomp.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c75041f5db7b7b8d17200bcc2d1e8ab6dfcad7ab GIT binary patch literal 3252 zcma)8TTEO<82)F@au!aPWueQmrDVI}vTSL&bh(t$2I+?05CMT=(-_OjZAo_*vRx^} zOMseqsU}8^G4Vm))ECp37=36O6Af(|qtzyrhsKAdF=`YWU6It5_50^6mujP(lbQLi z-~9i7=A0Q4L{x~uLR|-1_wBE$Y{@6eejU;s2f8|Y^g!$y_zU1)I?!`C^jhJpizufT ze8a)Mz1>I3-+KuBOW>Ox0>5pcd>44O-_zMs9X$GZvXe;6gYWO@-P@aMT>hTOuz>#} zJP}Szd19Y{zXaYt5DSlsZ8_OQlJTD(hW@$bli-ad_8*B(4d43JHVgd?VERU&??3Ru zb)pSNz?Vjb!h`vdAFZGtg?&Q=0=bo-7o3kT5=%}RAKxy9eX$+Cpf{oq@=V0hrq95FQ8jd5 zhVBK&G5RnYn0sMSgE<=yXMJAi!j?|2QR&^wx0jdJ`~j{w;~-+AFZzOxV--M)5ngz+)+{C z+*XSm8q-0>mwt}kVI`~RCPT@wu6D$fla;bW^E&xqQ=V-l9r9cf>8o;%B3cLMq?PY1 za@i+ba?Ua7Mh@5$b2O_R1d7tt?kuv}llFuqVNc4rgkx~fk$}~ibbwk=pFARKc|${e zoxgl%t<~i!bn0z-sHSXdpv2?#@1HNu+UzPS()(JiTRdd}6p38I&Tq_;19}eKqy+`Q znY6^0vXG%Q>ZV&6+Op0jiv|V9&p0~Oh!0I#SjiCAGITcC#LpQz2lP^gHpq#+seHu- zs*~;vZ6iarXXsq2kbN22z7~(jqbfO_kvqsCKgiJeR3p!2=mN@?nfS#<3gD|X*K(4f zc`|e%RcHZvANxIseY6AJOO>l!BdEPJNMqPrL!f(TicU}xbPThX4pE#2piR)Y8pBFA z2u*+@pp%d{;p?rYlj^CW@G4rB@{B>ZmA1pMZPo>3*=0nZwh{chyiN+If` z-FINEApH}k5=di+hSxEDJK?>L`hcIn%A+c3FRHq>LIkp;$~=zjR&-UW^1Fb-Xvg8t zR>PPl(fQp}Q9XJC#(LU?d0pmDVizWI#)Q z$!FTFoqagg&TKHeC`T`p>ZD6l9lcjZkdoL9aOeLaJJdzkXPQ}r1 zGET+jiGV+`@;g41h~xBGL9r13>jWf%PZV`7d9mY(D)2}p17o)IPdJG3c@8^OMSLVrjzp3I6&X#2qk?PVz}el1-QSPz;CXrz>#)0lI|iwK z6SFC^iS3}lIDo+Otae5Qk8Q!D z_Bc_vaShBm2(o@kpR$Z{%JkuaLq#w9UiA-Geq499g<4q5b`C@=P;Z?K&UYmjwGN3K zWT$cy8`PZwS&3X>$H=+l2?rwO4Dv+*D8wQ*3cNe^7KIAmth`^e ze5X&h&@}J3HpmN#-FhmOqP$|acSlp|PkTuy9->)(jN{FoyrmziD%|9rnc1BBCAD;O zX>oD!_k}l3Pft(3edT6J@5IDJ{HVM=D{%W%m>m1Gw8P_+SS(pD^C(%<9d@P+xhY>{ zMi%=eNAJ(G+SSsg>ocOsXKIR|k!LR1W@M_S>y&lzH+yNR{kO##VKB;ixKiRN^ZWg`Pvu>=8G`;yrKHp?wEWAp86itxi4uy`GVW@)7k8wmEWB&Y_Xk2X T+?YiQ`=|?dfDZi4_@Tc6i^d2h literal 0 HcmV?d00001 diff --git a/test/api/test-ot-metrics-tt-var.c b/test/api/test-ot-metrics-tt-var.c index 3259a7910..2305a95b0 100644 --- a/test/api/test-ot-metrics-tt-var.c +++ b/test/api/test-ot-metrics-tt-var.c @@ -54,9 +54,9 @@ test_extents_tt_var (void) g_assert (result); g_assert_cmpint (extents.x_bearing, ==, 0); - g_assert_cmpint (extents.y_bearing, ==, 875); + g_assert_cmpint (extents.y_bearing, ==, 874); g_assert_cmpint (extents.width, ==, 551); - g_assert_cmpint (extents.height, ==, -875); + g_assert_cmpint (extents.height, ==, -874); hb_font_destroy (font); } @@ -158,9 +158,78 @@ test_advance_tt_var_anchor (void) g_assert (result); g_assert_cmpint (extents.x_bearing, ==, 50); - g_assert_cmpint (extents.y_bearing, ==, 668); + g_assert_cmpint (extents.y_bearing, ==, 667); g_assert_cmpint (extents.width, ==, 593); - g_assert_cmpint (extents.height, ==, -680); + g_assert_cmpint (extents.height, ==, -679); + + hb_font_destroy (font); +} + +static void +test_extents_tt_var_comp (void) +{ + hb_face_t *face = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.modcomp.ttf"); + g_assert (face); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + g_assert (font); + hb_ot_font_set_funcs (font); + + hb_glyph_extents_t extents; + float coords[1] = { 800.0f }; + hb_font_set_var_coords_design (font, coords, 1); + + hb_bool_t result; + result = hb_font_get_glyph_extents (font, 2, &extents); /* Ccedilla, cedilla y-scaled by 0.8, with unscaled component offset */ + g_assert (result); + + g_assert_cmpint (extents.x_bearing, ==, 19); + g_assert_cmpint (extents.y_bearing, ==, 663); + g_assert_cmpint (extents.width, ==, 519); + g_assert_cmpint (extents.height, ==, -895); + + result = hb_font_get_glyph_extents (font, 3, &extents); /* Cacute, acute y-scaled by 0.8, with unscaled component offset (default) */ + g_assert (result); + + g_assert_cmpint (extents.x_bearing, ==, 19); + g_assert_cmpint (extents.y_bearing, ==, 909); + g_assert_cmpint (extents.width, ==, 519); + g_assert_cmpint (extents.height, ==, -921); + + result = hb_font_get_glyph_extents (font, 4, &extents); /* Ccaron, caron y-scaled by 0.8, with scaled component offset */ + g_assert (result); + + g_assert_cmpint (extents.x_bearing, ==, 19); + g_assert_cmpint (extents.y_bearing, ==, 866); + g_assert_cmpint (extents.width, ==, 519); + g_assert_cmpint (extents.height, ==, -878); + + hb_font_destroy (font); +} + +static void +test_advance_tt_var_comp_v (void) +{ + hb_face_t *face = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.modcomp.ttf"); + g_assert (face); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + g_assert (font); + hb_ot_font_set_funcs (font); + + float coords[1] = { 800.0f }; + hb_font_set_var_coords_design (font, coords, 1); + + hb_position_t x, y; + hb_font_get_glyph_advance_for_direction(font, 2, HB_DIRECTION_TTB, &x, &y); /* No VVAR; 'C' in composite Ccedilla determines metrics */ + + g_assert_cmpint (x, ==, 0); + g_assert_cmpint (y, ==, -991); + + hb_font_get_glyph_origin_for_direction(font, 2, HB_DIRECTION_TTB, &x, &y); + + g_assert_cmpint (x, ==, 292); + g_assert_cmpint (y, ==, 1013); hb_font_destroy (font); } @@ -174,6 +243,8 @@ main (int argc, char **argv) hb_test_add (test_advance_tt_var_nohvar); hb_test_add (test_advance_tt_var_hvarvvar); hb_test_add (test_advance_tt_var_anchor); + hb_test_add (test_extents_tt_var_comp); + hb_test_add (test_advance_tt_var_comp_v); return hb_test_run (); }