Skip to content
Permalink
Browse files
Use Stack* parameters from the OpenType MATH table
https://bugs.webkit.org/show_bug.cgi?id=155714

Patch by Frederic Wang <fwang@igalia.com> on 2016-07-11
Reviewed by Brent Fulgham.

Source/WebCore:

Test: mathml/mathml-in-html5/frac-parameters-2.html

* rendering/mathml/RenderMathMLFraction.cpp:
(WebCore::RenderMathMLFraction::updateFromElement): Set the stack parameters when
the line thickness is zero.
(WebCore::RenderMathMLFraction::layoutBlock): Correctly set the <mfrac> ascent and
the denominator vertical offset when the line thickness is zero.
(WebCore::RenderMathMLFraction::paint): Early return when we actually do not need to
paint any fraction bar.
* rendering/mathml/RenderMathMLFraction.h: Define an isStack helper function and define
members corresponding to stack parameters.

LayoutTests:

We import a test from the MathML in HTML5 test suite to verify Stack* parameters.

* imported/mathml-in-html5/fonts/math/stack-axisheight7000.woff: Added.
* imported/mathml-in-html5/fonts/math/stack-bottomdisplaystyleshiftdown5000.woff: Added.
* imported/mathml-in-html5/fonts/math/stack-bottomshiftdown6000.woff: Added.
* imported/mathml-in-html5/fonts/math/stack-displaystylegapmin4000.woff: Added.
* imported/mathml-in-html5/fonts/math/stack-gapmin8000.woff: Added.
* imported/mathml-in-html5/fonts/math/stack-topdisplaystyleshiftup3000.woff: Added.
* imported/mathml-in-html5/fonts/math/stack-topshiftup9000.woff: Added.
* imported/mathml-in-html5/mathml/presentation-markup/fractions/frac-parameters-2-expected.txt: Added.
* imported/mathml-in-html5/mathml/presentation-markup/fractions/frac-parameters-2.html: Added.

Canonical link: https://commits.webkit.org/177779@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@203073 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
fred-wang committed Jul 11, 2016
1 parent a7ad765 commit 7b12fca64d60a1b889d3be525032ba427a536dab
Showing 13 changed files with 283 additions and 22 deletions.
@@ -1,3 +1,22 @@
2016-07-11 Frederic Wang <fwang@igalia.com>

Use Stack* parameters from the OpenType MATH table
https://bugs.webkit.org/show_bug.cgi?id=155714

Reviewed by Brent Fulgham.

We import a test from the MathML in HTML5 test suite to verify Stack* parameters.

* imported/mathml-in-html5/fonts/math/stack-axisheight7000.woff: Added.
* imported/mathml-in-html5/fonts/math/stack-bottomdisplaystyleshiftdown5000.woff: Added.
* imported/mathml-in-html5/fonts/math/stack-bottomshiftdown6000.woff: Added.
* imported/mathml-in-html5/fonts/math/stack-displaystylegapmin4000.woff: Added.
* imported/mathml-in-html5/fonts/math/stack-gapmin8000.woff: Added.
* imported/mathml-in-html5/fonts/math/stack-topdisplaystyleshiftup3000.woff: Added.
* imported/mathml-in-html5/fonts/math/stack-topshiftup9000.woff: Added.
* imported/mathml-in-html5/mathml/presentation-markup/fractions/frac-parameters-2-expected.txt: Added.
* imported/mathml-in-html5/mathml/presentation-markup/fractions/frac-parameters-2.html: Added.

2016-07-11 Frederic Wang <fwang@igalia.com>

Add support for mathvariants that cannot be emulated via CSS.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,9 @@

PASS AxisHeight
PASS BottomDisplayStyleShiftDown
PASS BottomShiftDown
PASS DisplayStyleGapMin
PASS GapMin
PASS TopDisplayStyleShiftUp
PASS ToShiftUp

@@ -0,0 +1,175 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Stack parameters</title>
<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2">
<meta name="assert" content="Element mfrac correctly uses the stack parameters from the MATH table.">
<script src="../../../../../resources/testharness.js"></script>
<script src="../../../../../resources/testharnessreport.js"></script>
<style>
math, mspace {
font-size: 10px;
}
@font-face {
font-family: axisheight7000;
src: url("../../../fonts/math/stack-axisheight7000.woff");
}
@font-face {
font-family: bottomdisplaystyleshiftdown5000;
src: url("../../../fonts/math/stack-bottomdisplaystyleshiftdown5000.woff");
}
@font-face {
font-family: bottomshiftdown6000;
src: url("../../../fonts/math/stack-bottomshiftdown6000.woff");
}
@font-face {
font-family: displaystylegapmin4000;
src: url("../../../fonts/math/stack-displaystylegapmin4000.woff");
}
@font-face {
font-family: gapmin8000;
src: url("../../../fonts/math/stack-gapmin8000.woff");
}
@font-face {
font-family: topdisplaystyleshiftup3000;
src: url("../../../fonts/math/stack-topdisplaystyleshiftup3000.woff");
}
@font-face {
font-family: topshiftup9000;
src: url("../../../fonts/math/stack-topshiftup9000.woff");
}
</style>
<script>
var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
var epsilon = 1;

function getBox(aId) {
return document.getElementById(aId).getBoundingClientRect();
}

setup({ explicit_done: true });
window.addEventListener("load", function() {
document.fonts.ready.then(function() {
window.setTimeout(runTests, 250);
});
});

function runTests() {
test(function() {
var v = 7000 * emToPx;
assert_approx_equals(getBox("ref0001").top - getBox("num0001").bottom,
v, epsilon, "mfrac: axis height");
}, "AxisHeight");

test(function() {
var v = 5000 * emToPx;
assert_approx_equals(getBox("den0002").top - getBox("ref0002").bottom,
v, epsilon, "mfrac: denominator shift");
}, "BottomDisplayStyleShiftDown");

test(function() {
var v = 6000 * emToPx;
assert_approx_equals(getBox("den0003").top - getBox("ref0003").bottom,
v, epsilon, "mfrac: denominator shift");
}, "BottomShiftDown");

test(function() {
var v = 4000 * emToPx;
assert_approx_equals(getBox("den0004").top - getBox("num0004").bottom,
v, epsilon, "mfrac: gap");
}, "DisplayStyleGapMin");

test(function() {
var v = 8000 * emToPx;
assert_approx_equals(getBox("den0005").top - getBox("num0005").bottom,
v, epsilon, "mfrac: gap");
}, "GapMin");

test(function() {
var v = 3000 * emToPx;
assert_approx_equals(getBox("ref0006").top - getBox("num0006").bottom,
v, epsilon, "mfrac: numerator shift");
}, "TopDisplayStyleShiftUp");

test(function() {
var v = 9000 * emToPx;
assert_approx_equals(getBox("ref0007").top - getBox("num0007").bottom,
v, epsilon, "mfrac: numerator shift");
}, "ToShiftUp");

done();
}
</script>
</head>
<body>
<p>
<math style="font-family: axisheight7000;">
<mspace id="ref0001" depth="1em" width="3em" mathbackground="green"/>
<mfrac linethickness="0px">
<mspace width="3em" height="1em" id="num0001" mathbackground="blue"/>
<mspace width="3em"/>
</mfrac>
</math>
</p>
<hr/>
<p>
<math display="block" style="font-family: bottomdisplaystyleshiftdown5000;">
<mspace id="ref0002" width="3em" height="1em" mathbackground="green"/>
<mfrac linethickness="0px">
<mspace width="3em"/>
<mspace width="3em" depth="1em" id="den0002" mathbackground="blue"/>
</mfrac>
</math>
</p>
<hr/>
<p>
<math style="font-family: bottomshiftdown6000;">
<mspace id="ref0003" width="3em" height="1em" mathbackground="green"/>
<mfrac linethickness="0px">
<mspace width="3em"/>
<mspace width="3em" depth="1em" id="den0003" mathbackground="blue"/>
</mfrac>
</math>
</p>
<hr/>
<p>
<math display="block" style="font-family: displaystylegapmin4000;">
<mfrac linethickness="0px">
<mspace width="3em" height="1em" id="num0004" mathbackground="blue"/>
<mspace width="3em" depth="1em" id="den0004" mathbackground="green"/>
</mfrac>
</math>
</p>
<hr/>
<p>
<math style="font-family: gapmin8000;">
<mfrac linethickness="0px">
<mspace width="3em" height="1em" id="num0005" mathbackground="blue"/>
<mspace width="3em" depth="1em" id="den0005" mathbackground="green"/>
</mfrac>
</math>
</p>
<hr/>
<p>
<math display="block" style="font-family: topdisplaystyleshiftup3000;">
<mspace id="ref0006" width="3em" depth="1em" mathbackground="green"/>
<mfrac linethickness="0px">
<mspace width="3em" height="1em" id="num0006" mathbackground="blue"/>
<mspace width="3em"/>
</mfrac>
</math>
</p>
<hr/>
<p>
<math style="font-family: topshiftup9000;">
<mspace id="ref0007" width="3em" depth="1em" mathbackground="green"/>
<mfrac linethickness="0px">
<mspace width="3em" height="1em" id="num0007" mathbackground="blue"/>
<mspace width="3em"/>
</mfrac>
</math>
</p>
<hr/>
</body>
</html>
@@ -1,3 +1,22 @@
2016-07-11 Frederic Wang <fwang@igalia.com>

Use Stack* parameters from the OpenType MATH table
https://bugs.webkit.org/show_bug.cgi?id=155714

Reviewed by Brent Fulgham.

Test: mathml/mathml-in-html5/frac-parameters-2.html

* rendering/mathml/RenderMathMLFraction.cpp:
(WebCore::RenderMathMLFraction::updateFromElement): Set the stack parameters when
the line thickness is zero.
(WebCore::RenderMathMLFraction::layoutBlock): Correctly set the <mfrac> ascent and
the denominator vertical offset when the line thickness is zero.
(WebCore::RenderMathMLFraction::paint): Early return when we actually do not need to
paint any fraction bar.
* rendering/mathml/RenderMathMLFraction.h: Define an isStack helper function and define
members corresponding to stack parameters.

2016-07-11 Frederic Wang <fwang@igalia.com>

Add support for mathvariants that cannot be emulated via CSS.
@@ -110,19 +110,32 @@ void RenderMathMLFraction::updateFromElement()
}

// We now know whether we should layout as a normal fraction or as a stack (fraction without bar) and so determine the relevant constants.
// FIXME: If m_lineThickness == 0, we should read Stack* parameters. See http://wkb.ug/122297
bool display = mathMLStyle()->displayStyle();
if (mathData) {
m_numeratorGapMin = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionNumDisplayStyleGapMin : OpenTypeMathData::FractionNumeratorGapMin);
m_denominatorGapMin = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionDenomDisplayStyleGapMin : OpenTypeMathData::FractionDenominatorGapMin);
m_numeratorMinShiftUp = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionNumeratorDisplayStyleShiftUp : OpenTypeMathData::FractionNumeratorShiftUp);
m_denominatorMinShiftDown = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionDenominatorDisplayStyleShiftDown : OpenTypeMathData::FractionDenominatorShiftDown);
if (isStack()) {
if (mathData) {
m_gapMin = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::StackDisplayStyleGapMin : OpenTypeMathData::StackGapMin);
m_topShiftUp = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::StackTopDisplayStyleShiftUp : OpenTypeMathData::StackTopShiftUp);
m_bottomShiftDown = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::StackBottomDisplayStyleShiftDown : OpenTypeMathData::StackBottomShiftDown);
} else {
// We use the values suggested in the MATH table specification.
m_gapMin = m_denominatorGapMin = display ? 7 * ruleThicknessFallback() : 3 * ruleThicknessFallback();

// The MATH table specification does not suggest any values for shifts, so we leave them at zero.
m_topShiftUp = m_bottomShiftDown = 0;
}
} else {
// The MATH table specification suggests default rule thickness or (in displaystyle) 3 times default rule thickness for the gaps.
m_numeratorGapMin = m_denominatorGapMin = display ? 3 * ruleThicknessFallback() : ruleThicknessFallback();

// The MATH table specification does not suggest any values for shifts, so we leave them at zero.
m_numeratorMinShiftUp = m_denominatorMinShiftDown = 0;
if (mathData) {
m_numeratorGapMin = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionNumDisplayStyleGapMin : OpenTypeMathData::FractionNumeratorGapMin);
m_denominatorGapMin = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionDenomDisplayStyleGapMin : OpenTypeMathData::FractionDenominatorGapMin);
m_numeratorMinShiftUp = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionNumeratorDisplayStyleShiftUp : OpenTypeMathData::FractionNumeratorShiftUp);
m_denominatorMinShiftDown = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionDenominatorDisplayStyleShiftDown : OpenTypeMathData::FractionDenominatorShiftDown);
} else {
// The MATH table specification suggests default rule thickness or (in displaystyle) 3 times default rule thickness for the gaps.
m_numeratorGapMin = m_denominatorGapMin = display ? 3 * ruleThicknessFallback() : ruleThicknessFallback();

// The MATH table specification does not suggest any values for shifts, so we leave them at zero.
m_numeratorMinShiftUp = m_denominatorMinShiftDown = 0;
}
}

// Parse alignment attributes.
@@ -202,12 +215,28 @@ void RenderMathMLFraction::layoutBlock(bool relayoutChildren, LayoutUnit)
numerator().setLocation(numeratorLocation);

LayoutUnit numeratorAscent = ascentForChild(numerator());
verticalOffset += std::max(numerator().logicalHeight() + m_numeratorGapMin + m_lineThickness / 2, numeratorAscent + m_numeratorMinShiftUp); // This is the middle of the fraction bar.
m_ascent = verticalOffset + mathAxisHeight();

LayoutUnit numeratorDescent = numerator().logicalHeight() - numeratorAscent;
LayoutUnit denominatorAscent = ascentForChild(denominator());
LayoutUnit denominatorDescent = denominator().logicalHeight() - denominatorAscent;
verticalOffset += std::max(m_lineThickness / 2 + m_denominatorGapMin, m_denominatorMinShiftDown - denominatorAscent);
if (isStack()) {
LayoutUnit topShiftUp = m_topShiftUp;
LayoutUnit bottomShiftDown = m_bottomShiftDown;
LayoutUnit gap = topShiftUp - numeratorDescent + bottomShiftDown - denominatorAscent;
if (gap < m_gapMin) {
// If the gap is not large enough, we increase the shifts by the same value.
LayoutUnit delta = (m_gapMin - gap) / 2;
topShiftUp += delta;
bottomShiftDown += delta;
}
verticalOffset += numeratorAscent + topShiftUp; // This is the middle of the stack gap.
m_ascent = verticalOffset + mathAxisHeight();
verticalOffset += bottomShiftDown - denominatorAscent;
} else {
verticalOffset += std::max(numerator().logicalHeight() + m_numeratorGapMin + m_lineThickness / 2, numeratorAscent + m_numeratorMinShiftUp); // This is the middle of the fraction bar.
m_ascent = verticalOffset + mathAxisHeight();
verticalOffset += std::max(m_lineThickness / 2 + m_denominatorGapMin, m_denominatorMinShiftDown - denominatorAscent);
}

LayoutPoint denominatorLocation(horizontalOffset(denominator(), m_denominatorAlign), verticalOffset);
denominator().setLocation(denominatorLocation);

@@ -220,7 +249,7 @@ void RenderMathMLFraction::layoutBlock(bool relayoutChildren, LayoutUnit)
void RenderMathMLFraction::paint(PaintInfo& info, const LayoutPoint& paintOffset)
{
RenderMathMLBlock::paint(info, paintOffset);
if (info.context().paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE || !isValid())
if (info.context().paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE || !isValid() || isStack())
return;

IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset + location() + LayoutPoint(0, m_ascent - mathAxisHeight()));
@@ -57,6 +57,7 @@ class RenderMathMLFraction final : public RenderMathMLBlock {
RenderMathMLOperator* unembellishedOperator() final;
void styleDidChange(StyleDifference, const RenderStyle* oldStyle) final;

bool isStack() const { return !m_lineThickness; }
bool isValid() const;
RenderBox& numerator() const;
RenderBox& denominator() const;
@@ -69,14 +70,23 @@ class RenderMathMLFraction final : public RenderMathMLBlock {
LayoutUnit horizontalOffset(RenderBox&, FractionAlignment);

LayoutUnit m_ascent;
LayoutUnit m_defaultLineThickness = 1;
LayoutUnit m_defaultLineThickness { 1 };
LayoutUnit m_lineThickness;
LayoutUnit m_numeratorGapMin;
union {
LayoutUnit m_numeratorGapMin;
LayoutUnit m_gapMin;
};
LayoutUnit m_denominatorGapMin;
LayoutUnit m_numeratorMinShiftUp;
LayoutUnit m_denominatorMinShiftDown;
FractionAlignment m_numeratorAlign = FractionAlignmentCenter;
FractionAlignment m_denominatorAlign = FractionAlignmentCenter;
union {
LayoutUnit m_numeratorMinShiftUp;
LayoutUnit m_topShiftUp;
};
union {
LayoutUnit m_denominatorMinShiftDown;
LayoutUnit m_bottomShiftDown;
};
FractionAlignment m_numeratorAlign { FractionAlignmentCenter };
FractionAlignment m_denominatorAlign { FractionAlignmentCenter };
};

} // namespace WebCore

0 comments on commit 7b12fca

Please sign in to comment.