Commit 850068ab authored by Frédéric Wang's avatar Frédéric Wang Committed by Commit Bot

[mathml] Simplification and cleanup in stretchy operator shaper code

This CL improves the stretchy operator shaper code after more testing
in MathML layout. The following changes are performed:

* Add an OUT Metrics parameter to StretchyOperatorShaper::Shape and
  improve this function. As discussed in [1], it is expected that
  LayoutNG algorithms perform both text shaping and measurements,
  and pass the shape result to the painter classes, so it is not
  necessary to have a separate GetMetrics method.

* Remove GetGlyphStretchSize and ToMetrics helper functions since
  they are now only used in only one place.

* Remove the stretch axis direction parameter from the version of
  CreateForStretchyMathOperator that outputs a single glyph since
  MathML only supports horizontal mode and vertical operators
  still correspond to horizontal text.

* Improve code documentation and update tests.

[1] https://chromium-review.googlesource.com/c/chromium/src/+/2051923

Bug: 6606
Change-Id: Icc755adf9460ac59b68e0d904c062296700fd3c0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2144110Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Commit-Queue: Frédéric Wang <fwang@igalia.com>
Cr-Commit-Position: refs/heads/master@{#758495}
parent bbb0fcc8
...@@ -1453,18 +1453,14 @@ scoped_refptr<ShapeResult> ShapeResult::CreateForSpaces(const Font* font, ...@@ -1453,18 +1453,14 @@ scoped_refptr<ShapeResult> ShapeResult::CreateForSpaces(const Font* font,
scoped_refptr<ShapeResult> ShapeResult::CreateForStretchyMathOperator( scoped_refptr<ShapeResult> ShapeResult::CreateForStretchyMathOperator(
const Font* font, const Font* font,
TextDirection direction, TextDirection direction,
OpenTypeMathStretchData::StretchAxis stretch_axis,
Glyph glyph_variant, Glyph glyph_variant,
float stretch_size) { float stretch_size) {
bool is_horizontal_assembly =
stretch_axis == OpenTypeMathStretchData::StretchAxis::Horizontal;
unsigned start_index = 0; unsigned start_index = 0;
unsigned num_characters = 1; unsigned num_characters = 1;
scoped_refptr<ShapeResult> result = scoped_refptr<ShapeResult> result =
ShapeResult::Create(font, start_index, num_characters, direction); ShapeResult::Create(font, start_index, num_characters, direction);
hb_direction_t hb_direction = hb_direction_t hb_direction = HB_DIRECTION_LTR;
is_horizontal_assembly ? HB_DIRECTION_LTR : HB_DIRECTION_TTB;
unsigned glyph_index = 0; unsigned glyph_index = 0;
scoped_refptr<ShapeResult::RunInfo> run = RunInfo::Create( scoped_refptr<ShapeResult::RunInfo> run = RunInfo::Create(
font->PrimaryFont(), hb_direction, CanvasRotationInVertical::kRegular, font->PrimaryFont(), hb_direction, CanvasRotationInVertical::kRegular,
......
...@@ -146,7 +146,6 @@ class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> { ...@@ -146,7 +146,6 @@ class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> {
static scoped_refptr<ShapeResult> CreateForStretchyMathOperator( static scoped_refptr<ShapeResult> CreateForStretchyMathOperator(
const Font*, const Font*,
TextDirection, TextDirection,
OpenTypeMathStretchData::StretchAxis,
Glyph, Glyph,
float stretch_size); float stretch_size);
static scoped_refptr<ShapeResult> CreateForStretchyMathOperator( static scoped_refptr<ShapeResult> CreateForStretchyMathOperator(
......
...@@ -27,18 +27,6 @@ inline float HarfBuzzUnitsToFloat(hb_position_t value) { ...@@ -27,18 +27,6 @@ inline float HarfBuzzUnitsToFloat(hb_position_t value) {
return kFloatToHbRatio * value; return kFloatToHbRatio * value;
} }
inline float GetGlyphStretchSize(
FloatRect bounds,
OpenTypeMathStretchData::StretchAxis stretch_axis) {
return stretch_axis == OpenTypeMathStretchData::StretchAxis::Horizontal
? bounds.Width()
: bounds.Height();
}
inline StretchyOperatorShaper::Metrics ToMetrics(FloatRect bounds) {
return {bounds.Width(), -bounds.Y(), bounds.MaxY()};
}
base::Optional<OpenTypeMathStretchData::AssemblyParameters> base::Optional<OpenTypeMathStretchData::AssemblyParameters>
GetAssemblyParameters(const HarfBuzzFace* harfbuzz_face, GetAssemblyParameters(const HarfBuzzFace* harfbuzz_face,
Glyph base_glyph, Glyph base_glyph,
...@@ -138,57 +126,16 @@ GetAssemblyParameters(const HarfBuzzFace* harfbuzz_face, ...@@ -138,57 +126,16 @@ GetAssemblyParameters(const HarfBuzzFace* harfbuzz_face,
} // namespace } // namespace
StretchyOperatorShaper::Metrics StretchyOperatorShaper::GetMetrics(
const Font* font,
float target_size) const {
const SimpleFontData* primary_font = font->PrimaryFont();
const HarfBuzzFace* harfbuzz_face =
primary_font->PlatformData().GetHarfBuzzFace();
Glyph base_glyph = primary_font->GlyphForCharacter(stretchy_character_);
FloatRect bounds;
// Try different glyph variants.
for (auto& variant : OpenTypeMathSupport::GetGlyphVariantRecords(
harfbuzz_face, base_glyph, stretch_axis_)) {
bounds = primary_font->BoundsForGlyph(variant);
if (GetGlyphStretchSize(bounds, stretch_axis_) >= target_size)
return ToMetrics(bounds);
}
// Try a glyph assembly.
auto params = GetAssemblyParameters(harfbuzz_face, base_glyph, stretch_axis_,
target_size);
if (!params)
return ToMetrics(bounds);
bounds = stretch_axis_ == OpenTypeMathStretchData::StretchAxis::Horizontal
? FloatRect(0, 0, params->stretch_size, 0)
: FloatRect(0, -params->stretch_size, 0, params->stretch_size);
for (auto& part : params->parts) {
// Include dimension of the part, orthogonal to the stretch axis.
auto glyph_bounds = primary_font->BoundsForGlyph(part.glyph);
if (stretch_axis_ == OpenTypeMathStretchData::StretchAxis::Horizontal) {
glyph_bounds.SetX(0);
glyph_bounds.SetWidth(0);
} else {
glyph_bounds.SetY(0);
glyph_bounds.SetHeight(0);
}
bounds.UniteEvenIfEmpty(glyph_bounds);
}
return ToMetrics(bounds);
}
scoped_refptr<ShapeResult> StretchyOperatorShaper::Shape( scoped_refptr<ShapeResult> StretchyOperatorShaper::Shape(
const Font* font, const Font* font,
float target_size) const { float target_size,
Metrics* metrics) const {
const SimpleFontData* primary_font = font->PrimaryFont(); const SimpleFontData* primary_font = font->PrimaryFont();
const HarfBuzzFace* harfbuzz_face = const HarfBuzzFace* harfbuzz_face =
primary_font->PlatformData().GetHarfBuzzFace(); primary_font->PlatformData().GetHarfBuzzFace();
Glyph base_glyph = primary_font->GlyphForCharacter(stretchy_character_); Glyph base_glyph = primary_font->GlyphForCharacter(stretchy_character_);
if (metrics)
*metrics = Metrics();
Glyph glyph_variant; Glyph glyph_variant;
float glyph_variant_stretch_size; float glyph_variant_stretch_size;
...@@ -198,12 +145,18 @@ scoped_refptr<ShapeResult> StretchyOperatorShaper::Shape( ...@@ -198,12 +145,18 @@ scoped_refptr<ShapeResult> StretchyOperatorShaper::Shape(
for (auto& variant : OpenTypeMathSupport::GetGlyphVariantRecords( for (auto& variant : OpenTypeMathSupport::GetGlyphVariantRecords(
harfbuzz_face, base_glyph, stretch_axis_)) { harfbuzz_face, base_glyph, stretch_axis_)) {
glyph_variant = variant; glyph_variant = variant;
auto bounds = primary_font->BoundsForGlyph(glyph_variant); FloatRect bounds = primary_font->BoundsForGlyph(glyph_variant);
glyph_variant_stretch_size = GetGlyphStretchSize(bounds, stretch_axis_); if (metrics) {
*metrics = {primary_font->WidthForGlyph(variant), -bounds.Y(),
bounds.MaxY()};
}
glyph_variant_stretch_size =
stretch_axis_ == OpenTypeMathStretchData::StretchAxis::Horizontal
? bounds.Width()
: bounds.Height();
if (glyph_variant_stretch_size >= target_size) { if (glyph_variant_stretch_size >= target_size) {
return ShapeResult::CreateForStretchyMathOperator( return ShapeResult::CreateForStretchyMathOperator(
font, direction, stretch_axis_, glyph_variant, font, direction, glyph_variant, glyph_variant_stretch_size);
glyph_variant_stretch_size);
} }
} }
...@@ -212,12 +165,31 @@ scoped_refptr<ShapeResult> StretchyOperatorShaper::Shape( ...@@ -212,12 +165,31 @@ scoped_refptr<ShapeResult> StretchyOperatorShaper::Shape(
target_size); target_size);
if (!params) { if (!params) {
return ShapeResult::CreateForStretchyMathOperator( return ShapeResult::CreateForStretchyMathOperator(
font, direction, stretch_axis_, glyph_variant, font, direction, glyph_variant, glyph_variant_stretch_size);
glyph_variant_stretch_size);
} }
return ShapeResult::CreateForStretchyMathOperator( scoped_refptr<ShapeResult> shape_result_for_glyph_assembly =
font, direction, stretch_axis_, std::move(*params)); ShapeResult::CreateForStretchyMathOperator(font, direction, stretch_axis_,
std::move(*params));
if (metrics) {
// The OpenType MATH specification does provide any distinction between
// the advance width and ink width, so the latter is returned here.
FloatRect bounds = shape_result_for_glyph_assembly->ComputeInkBounds();
if (stretch_axis_ == OpenTypeMathStretchData::StretchAxis::Horizontal) {
*metrics = {bounds.Width(), -bounds.Y(), bounds.MaxY()};
} else {
// For assemblies growing in the vertical direction, the distribution of
// height between ascent and descent is not defined by the OpenType MATH
// specification. This code uses MathML Core's convention of
// ascent = height and descent = 0.
// Additionally, ShapeResult::CreateForStretchyMathOperator uses a text
// run that is HB_DIRECTION_TTB in order to stack the parts vertically but
// the actual glyph assembly is still horizontal text, so height and width
// are inverted.
*metrics = {bounds.Height(), bounds.Width(), 0};
}
}
return shape_result_for_glyph_assembly;
} }
} // namespace blink } // namespace blink
...@@ -30,22 +30,20 @@ class PLATFORM_EXPORT StretchyOperatorShaper final { ...@@ -30,22 +30,20 @@ class PLATFORM_EXPORT StretchyOperatorShaper final {
OpenTypeMathStretchData::StretchAxis stretch_axis) OpenTypeMathStretchData::StretchAxis stretch_axis)
: stretchy_character_(stretchy_character), stretch_axis_(stretch_axis) {} : stretchy_character_(stretchy_character), stretch_axis_(stretch_axis) {}
// Returns the metrics of the stretched operator for layout purpose.
// May be called multiple times; font and direction may vary between calls.
// https://mathml-refresh.github.io/mathml-core/#dfn-box-metrics-of-a-stretchy-glyph
struct Metrics { struct Metrics {
float advance; float advance;
float ascent; float ascent;
float descent; float descent;
// TODO(https://crbug.com/1057592): Add italic correction. // TODO(https://crbug.com/1057592): Add italic correction.
}; };
Metrics GetMetrics(const Font*, float target_size) const;
// Shape the stretched operator. The coordinates of the glyph(s) use the same // Shape the stretched operator. The coordinates of the glyph(s) use the same
// origin as the rectangle returned by GetMetrics. // origin as the rectangle assigned to the optional OUT Metrics parameter.
// May be called multiple times; font and direction may vary between calls. // May be called multiple times; font and direction may vary between calls.
// https://mathml-refresh.github.io/mathml-core/#dfn-shape-a-stretchy-glyph // https://mathml-refresh.github.io/mathml-core/#dfn-shape-a-stretchy-glyph
scoped_refptr<ShapeResult> Shape(const Font*, float target_size) const; // https://mathml-refresh.github.io/mathml-core/#dfn-box-metrics-of-a-stretchy-glyph
scoped_refptr<ShapeResult> Shape(const Font*,
float target_size,
Metrics* metrics = nullptr) const;
~StretchyOperatorShaper() = default; ~StretchyOperatorShaper() = default;
......
...@@ -91,7 +91,8 @@ TEST_F(StretchyOperatorShaperTest, GlyphVariants) { ...@@ -91,7 +91,8 @@ TEST_F(StretchyOperatorShaperTest, GlyphVariants) {
// Metrics of horizontal size variants. // Metrics of horizontal size variants.
{ {
auto metrics = horizontal_shaper.GetMetrics(&math, target_size); StretchyOperatorShaper::Metrics metrics;
horizontal_shaper.Shape(&math, target_size, &metrics);
EXPECT_NEAR(metrics.advance, (i + 1) * 1000, kSizeError); EXPECT_NEAR(metrics.advance, (i + 1) * 1000, kSizeError);
EXPECT_NEAR(metrics.ascent, 1000, kSizeError); EXPECT_NEAR(metrics.ascent, 1000, kSizeError);
EXPECT_FLOAT_EQ(metrics.descent, 0); EXPECT_FLOAT_EQ(metrics.descent, 0);
...@@ -100,7 +101,8 @@ TEST_F(StretchyOperatorShaperTest, GlyphVariants) { ...@@ -100,7 +101,8 @@ TEST_F(StretchyOperatorShaperTest, GlyphVariants) {
// Metrics of vertical size variants. // Metrics of vertical size variants.
{ {
auto metrics = vertical_shaper.GetMetrics(&math, target_size); StretchyOperatorShaper::Metrics metrics;
vertical_shaper.Shape(&math, target_size, &metrics);
EXPECT_NEAR(metrics.advance, 1000, kSizeError); EXPECT_NEAR(metrics.advance, 1000, kSizeError);
EXPECT_NEAR(metrics.ascent, (i + 1) * 1000, kSizeError); EXPECT_NEAR(metrics.ascent, (i + 1) * 1000, kSizeError);
EXPECT_FLOAT_EQ(metrics.descent, 0); EXPECT_FLOAT_EQ(metrics.descent, 0);
...@@ -170,7 +172,8 @@ TEST_F(StretchyOperatorShaperTest, GlyphVariants) { ...@@ -170,7 +172,8 @@ TEST_F(StretchyOperatorShaperTest, GlyphVariants) {
// Metrics of horizontal assembly. // Metrics of horizontal assembly.
{ {
auto metrics = horizontal_shaper.GetMetrics(&math, target_size); StretchyOperatorShaper::Metrics metrics;
horizontal_shaper.Shape(&math, target_size, &metrics);
EXPECT_NEAR(metrics.advance, target_size, kSizeError); EXPECT_NEAR(metrics.advance, target_size, kSizeError);
EXPECT_NEAR(metrics.ascent, 1000, kSizeError); EXPECT_NEAR(metrics.ascent, 1000, kSizeError);
EXPECT_FLOAT_EQ(metrics.descent, 0); EXPECT_FLOAT_EQ(metrics.descent, 0);
...@@ -178,7 +181,8 @@ TEST_F(StretchyOperatorShaperTest, GlyphVariants) { ...@@ -178,7 +181,8 @@ TEST_F(StretchyOperatorShaperTest, GlyphVariants) {
// Metrics of vertical assembly. // Metrics of vertical assembly.
{ {
auto metrics = vertical_shaper.GetMetrics(&math, target_size); StretchyOperatorShaper::Metrics metrics;
vertical_shaper.Shape(&math, target_size, &metrics);
EXPECT_NEAR(metrics.advance, 1000, kSizeError); EXPECT_NEAR(metrics.advance, 1000, kSizeError);
EXPECT_NEAR(metrics.ascent, target_size, kSizeError); EXPECT_NEAR(metrics.ascent, target_size, kSizeError);
EXPECT_FLOAT_EQ(metrics.descent, 0); EXPECT_FLOAT_EQ(metrics.descent, 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment