diff --git a/third_party/blink/renderer/platform/fonts/opentype/open_type_math_test_fonts.h b/third_party/blink/renderer/platform/fonts/opentype/open_type_math_test_fonts.h index 06e2a68c2711b..e2eff45250242 100644 --- a/third_party/blink/renderer/platform/fonts/opentype/open_type_math_test_fonts.h +++ b/third_party/blink/renderer/platform/fonts/opentype/open_type_math_test_fonts.h @@ -20,10 +20,15 @@ class Font; // contains the following data for horizontal (respectively vertical) operators: // - Glyph variants: h0, h1, h2, h3 (respectively v0, v1, v2, v3). // - Glyph parts: non-extender h2 and extender h1 (respectively v2 and v1). +// stretchy.woff and stretchy-centered-on-baseline.woff contain similar stretchy +// constructions for horizontal and vertical arrows only. For the latter, the +// glyphs are centered on the baseline. // For details, see createSizeVariants() and createStretchy() from // third_party/blink/web_tests/external/wpt/mathml/tools/operator-dictionary.py const UChar32 kLeftBraceCodePoint = '{'; const UChar32 kOverBraceCodePoint = 0x23DE; +const UChar32 kVerticalArrow = 0x295C; +const UChar32 kHorizontalArrow = 0x295A; PLATFORM_EXPORT void retrieveGlyphForStretchyOperators( const blink::Font operatorsWoff, Vector& verticalGlyphs, diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc index cf3f32a8fb326..e8214585316c8 100644 --- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc +++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc @@ -1630,6 +1630,10 @@ scoped_refptr ShapeResult::CreateForStretchyMathOperator( if (!repetition_count) continue; DCHECK(part_index < assembly_parameters.glyph_count); + float glyph_ink_ascent; + if (!is_horizontal_assembly) { + glyph_ink_ascent = -font->PrimaryFont()->BoundsForGlyph(part.glyph).y(); + } for (unsigned repetition_index = 0; repetition_index < repetition_count; repetition_index++) { unsigned glyph_index = @@ -1644,7 +1648,7 @@ scoped_refptr ShapeResult::CreateForStretchyMathOperator( full_advance}; if (!is_horizontal_assembly) { GlyphOffset glyph_offset( - 0, -assembly_parameters.stretch_size + part.full_advance); + 0, -assembly_parameters.stretch_size + glyph_ink_ascent); run->glyph_data_.SetOffsetAt(glyph_index, glyph_offset); result->has_vertical_offsets_ |= (glyph_offset.y() != 0); } diff --git a/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper_test.cc b/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper_test.cc index 37e88a35f359c..d228a449e68bc 100644 --- a/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper_test.cc +++ b/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper_test.cc @@ -51,15 +51,16 @@ class StretchyOperatorShaperTest : public FontTestBase { // See blink/web_tests/external/wpt/mathml/tools/operator-dictionary.py and // blink/renderer/platform/fonts/opentype/open_type_math_test_fonts.h. TEST_F(StretchyOperatorShaperTest, GlyphVariants) { - Font math = CreateMathFont("operators.woff"); + Font math = CreateMathFont("stretchy.woff"); StretchyOperatorShaper vertical_shaper( - kLeftBraceCodePoint, OpenTypeMathStretchData::StretchAxis::Vertical); + kVerticalArrow, OpenTypeMathStretchData::StretchAxis::Vertical); StretchyOperatorShaper horizontal_shaper( - kOverBraceCodePoint, OpenTypeMathStretchData::StretchAxis::Horizontal); + kHorizontalArrow, OpenTypeMathStretchData::StretchAxis::Horizontal); - auto left_brace = math.PrimaryFont()->GlyphForCharacter(kLeftBraceCodePoint); - auto over_brace = math.PrimaryFont()->GlyphForCharacter(kOverBraceCodePoint); + auto vertical_arrow = math.PrimaryFont()->GlyphForCharacter(kVerticalArrow); + auto horizontal_arrow = + math.PrimaryFont()->GlyphForCharacter(kHorizontalArrow); // Calculate glyph indices of stretchy operator's parts. Vector v, h; @@ -68,9 +69,9 @@ TEST_F(StretchyOperatorShaperTest, GlyphVariants) { // Stretch operators to target sizes (in font units) 125, 250, 375, 500, 625, // 750, 875, 1000, 1125, ..., 3750, 3875, 4000. // - // Shaper tries glyphs over_brace/left_brace, h0/v0, h1/v1, h2/v2, h3/v3 of - // respective sizes 1000, 1000, 2000, 3000 and 4000. It returns the smallest - // glyph larger than the target size. + // Shaper tries glyphs vertical_arrow/horizontal_arrow, h0/v0, h1/v1, h2/v2, + // h3/v3 of respective sizes 1000, 1000, 2000, 3000 and 4000. It returns the + // smallest glyph larger than the target size. const unsigned size_count = 4; const unsigned subdivision = 8; for (unsigned i = 0; i < size_count; i++) { @@ -106,7 +107,7 @@ TEST_F(StretchyOperatorShaperTest, GlyphVariants) { horizontal_shaper.Shape(&math, target_size); EXPECT_EQ(TestInfo(result)->NumberOfRunsForTesting(), 1u); EXPECT_EQ(TestInfo(result)->RunInfoForTesting(0).NumGlyphs(), 1u); - Glyph expected_variant = i ? h[0] + 2 * i : over_brace; + Glyph expected_variant = i ? h[0] + 2 * i : horizontal_arrow; EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, 0), expected_variant); EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, 0), (i + 1) * 1000, kSizeError); @@ -118,7 +119,7 @@ TEST_F(StretchyOperatorShaperTest, GlyphVariants) { vertical_shaper.Shape(&math, target_size); EXPECT_EQ(TestInfo(result)->NumberOfRunsForTesting(), 1u); EXPECT_EQ(TestInfo(result)->RunInfoForTesting(0).NumGlyphs(), 1u); - Glyph expected_variant = i ? v[0] + 2 * i : left_brace; + Glyph expected_variant = i ? v[0] + 2 * i : vertical_arrow; EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, 0), expected_variant); EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, 0), (i + 1) * 1000, kSizeError); @@ -258,6 +259,88 @@ TEST_F(StretchyOperatorShaperTest, GlyphVariants) { } } +// This test performs similar checks for shaping glyph assemblies to the ones of +// StretchyOperatorShaperTest.GlyphVariants, but the glyphs involved have their +// ink ascents equal to their ink descents. The glyphs used and their advances +// should remain exactly the same. Horizontal assemblies now use the ink +// ascent/descent of the glyphs but vertical assemblies should be normalized to +// a zero ink descent (see crbug.com/1409380). +TEST_F(StretchyOperatorShaperTest, GlyphVariantsCenteredOnBaseline) { + Font math = CreateMathFont("stretchy-centered-on-baseline.woff"); + + StretchyOperatorShaper vertical_shaper( + kVerticalArrow, OpenTypeMathStretchData::StretchAxis::Vertical); + StretchyOperatorShaper horizontal_shaper( + kHorizontalArrow, OpenTypeMathStretchData::StretchAxis::Horizontal); + + // Calculate glyph indices of stretchy operator's parts. + Vector v, h; + retrieveGlyphForStretchyOperators(math, v, h); + + unsigned repetition_count = 5; + float overlap = 750; + float target_size = 3000 + repetition_count * (2000 - overlap); + + // Metrics of horizontal assembly. + { + StretchyOperatorShaper::Metrics metrics; + horizontal_shaper.Shape(&math, target_size, &metrics); + EXPECT_NEAR(metrics.advance, target_size, kSizeError); + EXPECT_NEAR(metrics.ascent, 500, kSizeError); + EXPECT_FLOAT_EQ(metrics.descent, 500); + } + + // Metrics of vertical assembly. + { + StretchyOperatorShaper::Metrics metrics; + vertical_shaper.Shape(&math, target_size, &metrics); + EXPECT_NEAR(metrics.advance, 1000, kSizeError); + EXPECT_NEAR(metrics.ascent, target_size, kSizeError); + EXPECT_FLOAT_EQ(metrics.descent, 0); + } + + // Shaping of horizontal assembly. + // From left to right: h2, h1, h1, h1, ... + { + scoped_refptr result = + horizontal_shaper.Shape(&math, target_size); + + EXPECT_EQ(TestInfo(result)->NumberOfRunsForTesting(), 1u); + EXPECT_EQ(TestInfo(result)->RunInfoForTesting(0).NumGlyphs(), + repetition_count + 1); + EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, 0), h[2]); + EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, 0), 3000 - overlap, + kSizeError); + for (unsigned i = 0; i < repetition_count - 1; i++) { + EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, i + 1), h[1]); + EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, i + 1), 2000 - overlap, + kSizeError); + } + EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, repetition_count), h[1]); + EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, repetition_count), 2000, + kSizeError); + } + + // Shaping of vertical assembly. + // From bottom to top: v2, v1, v1, v1, ... + { + scoped_refptr result = + vertical_shaper.Shape(&math, target_size); + + EXPECT_EQ(TestInfo(result)->NumberOfRunsForTesting(), 1u); + EXPECT_EQ(TestInfo(result)->RunInfoForTesting(0).NumGlyphs(), + repetition_count + 1); + for (unsigned i = 0; i < repetition_count; i++) { + EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, i), v[1]); + EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, i), 2000 - overlap, + kSizeError); + } + EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, repetition_count), v[2]); + EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, repetition_count), 3000, + kSizeError); + } +} + // See blink/web_tests/external/wpt/mathml/tools/operator-dictionary.py and // blink/renderer/platform/fonts/opentype/open_type_math_test_fonts.h. TEST_F(StretchyOperatorShaperTest, NonBMPCodePoint) { diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stretchy-centered-on-baseline.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stretchy-centered-on-baseline.woff new file mode 100644 index 0000000000000..fd753edf7611a Binary files /dev/null and b/third_party/blink/web_tests/external/wpt/fonts/math/stretchy-centered-on-baseline.woff differ diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stretchy.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stretchy.woff index fc1b75c948d46..eb67181e1ea24 100644 Binary files a/third_party/blink/web_tests/external/wpt/fonts/math/stretchy.woff and b/third_party/blink/web_tests/external/wpt/fonts/math/stretchy.woff differ diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/painting-stretchy-operator-001-ref.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/painting-stretchy-operator-001-ref.html new file mode 100644 index 0000000000000..0c7642a20ac6e --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/painting-stretchy-operator-001-ref.html @@ -0,0 +1,22 @@ + + + + +Painting of vertical assembly (reference) + + +

This test passes if you see a green rectangle and no red.

+
+
+ diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/painting-stretchy-operator-001.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/painting-stretchy-operator-001.html new file mode 100644 index 0000000000000..2a9578badc047 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/painting-stretchy-operator-001.html @@ -0,0 +1,73 @@ + + + + +Painting of vertical assembly + + + + + + + + +

This test passes if you see a green rectangle and no red.

+
+ + + + + + + + + + +
+ diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/stretchy-centered-on-baseline.py b/third_party/blink/web_tests/external/wpt/mathml/tools/stretchy-centered-on-baseline.py new file mode 100755 index 0000000000000..fa75bafcf0eb6 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/mathml/tools/stretchy-centered-on-baseline.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +from utils import mathfont +import fontforge + +# Create a WOFF font with glyphs for all the operator strings. +font = mathfont.create("stretchy-centered-on-baseline", "Copyright (c) 2023 Igalia S.L.") + +# Set parameters for stretchy tests. +font.math.MinConnectorOverlap = mathfont.em // 2 + +# Make sure that underover parameters don't add extra spacing. +font.math.LowerLimitBaselineDropMin = 0 +font.math.LowerLimitGapMin = 0 +font.math.StretchStackBottomShiftDown = 0 +font.math.StretchStackGapAboveMin = 0 +font.math.UnderbarVerticalGap = 0 +font.math.UnderbarExtraDescender = 0 +font.math.UpperLimitBaselineRiseMin = 0 +font.math.UpperLimitGapMin = 0 +font.math.StretchStackTopShiftUp = 0 +font.math.StretchStackGapBelowMin = 0 +font.math.OverbarVerticalGap = 0 +font.math.AccentBaseHeight = 0 +font.math.OverbarExtraAscender = 0 + +# These two characters will be stretchable in both directions. +horizontalArrow = 0x295A # LEFTWARDS HARPOON WITH BARB UP FROM BAR +verticalArrow = 0x295C # UPWARDS HARPOON WITH BARB RIGHT FROM BAR + +mathfont.createSizeVariants(font, aUsePUA = True, aCenterOnBaseline = True) + +# Add stretchy vertical and horizontal constructions for the horizontal arrow. +mathfont.createSquareGlyph(font, horizontalArrow) +mathfont.createStretchy(font, horizontalArrow, True) +mathfont.createStretchy(font, horizontalArrow, False) + +# Add stretchy vertical and horizontal constructions for the vertical arrow. +mathfont.createSquareGlyph(font, verticalArrow) +mathfont.createStretchy(font, verticalArrow, True) +mathfont.createStretchy(font, verticalArrow, False) + +mathfont.save(font) diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/stretchy.py b/third_party/blink/web_tests/external/wpt/mathml/tools/stretchy.py index b93dbcee74d01..1d1096261cb63 100755 --- a/third_party/blink/web_tests/external/wpt/mathml/tools/stretchy.py +++ b/third_party/blink/web_tests/external/wpt/mathml/tools/stretchy.py @@ -28,7 +28,7 @@ horizontalArrow = 0x295A # LEFTWARDS HARPOON WITH BARB UP FROM BAR verticalArrow = 0x295C # UPWARDS HARPOON WITH BARB RIGHT FROM BAR -mathfont.createSizeVariants(font) +mathfont.createSizeVariants(font, aUsePUA = True, aCenterOnBaseline = False) # Add stretchy vertical and horizontal constructions for the horizontal arrow. mathfont.createSquareGlyph(font, horizontalArrow) diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/utils/mathfont.py b/third_party/blink/web_tests/external/wpt/mathml/tools/utils/mathfont.py index d030c9f2ce077..79772a24cb5e8 100644 --- a/third_party/blink/web_tests/external/wpt/mathml/tools/utils/mathfont.py +++ b/third_party/blink/web_tests/external/wpt/mathml/tools/utils/mathfont.py @@ -171,18 +171,24 @@ def createGlyphFromValue(aFont, aCodePoint): g.width = 5 * em / 2 g.stroke("circular", em / 10, "square", "miter", "cleanup") -def createSizeVariants(aFont, aUsePUA = False): +def createSizeVariants(aFont, aUsePUA = False, aCenterOnBaseline = False): if aUsePUA: codePoint = PUA_startCodePoint else: codePoint = -1 for size in (0, 1, 2, 3): g = aFont.createChar(codePoint, "v%d" % size) - drawRectangleGlyph(g, em, (size + 1) * em, 0) + if aCenterOnBaseline: + drawRectangleGlyph(g, em, (size + 1) * em / 2, (size + 1) * em / 2) + else: + drawRectangleGlyph(g, em, (size + 1) * em, 0) if aUsePUA: codePoint += 1 g = aFont.createChar(codePoint, "h%d" % size) - drawRectangleGlyph(g, (size + 1) * em, em, 0) + if aCenterOnBaseline: + drawRectangleGlyph(g, (size + 1) * em, em/2, em/2) + else: + drawRectangleGlyph(g, (size + 1) * em, em, 0) if aUsePUA: codePoint += 1