summaryrefslogtreecommitdiff
path: root/layout/mathml/nsMathMLmencloseFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/mathml/nsMathMLmencloseFrame.cpp')
-rw-r--r--layout/mathml/nsMathMLmencloseFrame.cpp868
1 files changed, 868 insertions, 0 deletions
diff --git a/layout/mathml/nsMathMLmencloseFrame.cpp b/layout/mathml/nsMathMLmencloseFrame.cpp
new file mode 100644
index 0000000000..64277a92e9
--- /dev/null
+++ b/layout/mathml/nsMathMLmencloseFrame.cpp
@@ -0,0 +1,868 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMathMLmencloseFrame.h"
+
+#include "gfx2DGlue.h"
+#include "gfxUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/PathHelpers.h"
+#include "nsPresContext.h"
+#include "nsRenderingContext.h"
+#include "nsWhitespaceTokenizer.h"
+
+#include "nsDisplayList.h"
+#include "gfxContext.h"
+#include "nsMathMLChar.h"
+#include <algorithm>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+//
+// <menclose> -- enclose content with a stretching symbol such
+// as a long division sign. - implementation
+
+// longdiv:
+// Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis
+// renders better with current font support.
+static const char16_t kLongDivChar = ')';
+
+// radical: 'SQUARE ROOT'
+static const char16_t kRadicalChar = 0x221A;
+
+// updiagonalstrike
+static const uint8_t kArrowHeadSize = 10;
+
+// phasorangle
+static const uint8_t kPhasorangleWidth = 8;
+
+nsIFrame*
+NS_NewMathMLmencloseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsMathMLmencloseFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame)
+
+nsMathMLmencloseFrame::nsMathMLmencloseFrame(nsStyleContext* aContext) :
+ nsMathMLContainerFrame(aContext), mNotationsToDraw(0),
+ mLongDivCharIndex(-1), mRadicalCharIndex(-1), mContentWidth(0)
+{
+}
+
+nsMathMLmencloseFrame::~nsMathMLmencloseFrame()
+{
+}
+
+nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask)
+{
+ // Is the char already allocated?
+ if ((mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) ||
+ (mask == NOTATION_RADICAL && mRadicalCharIndex >= 0))
+ return NS_OK;
+
+ // No need to track the style context given to our MathML chars.
+ // The Style System will use Get/SetAdditionalStyleContext() to keep it
+ // up-to-date if dynamic changes arise.
+ uint32_t i = mMathMLChar.Length();
+ nsAutoString Char;
+
+ if (!mMathMLChar.AppendElement())
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (mask == NOTATION_LONGDIV) {
+ Char.Assign(kLongDivChar);
+ mLongDivCharIndex = i;
+ } else if (mask == NOTATION_RADICAL) {
+ Char.Assign(kRadicalChar);
+ mRadicalCharIndex = i;
+ }
+
+ nsPresContext *presContext = PresContext();
+ mMathMLChar[i].SetData(Char);
+ ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar[i]);
+
+ return NS_OK;
+}
+
+/*
+ * Add a notation to draw, if the argument is the name of a known notation.
+ * @param aNotation string name of a notation
+ */
+nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation)
+{
+ nsresult rv;
+
+ if (aNotation.EqualsLiteral("longdiv")) {
+ rv = AllocateMathMLChar(NOTATION_LONGDIV);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mNotationsToDraw |= NOTATION_LONGDIV;
+ } else if (aNotation.EqualsLiteral("actuarial")) {
+ mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_TOP);
+ } else if (aNotation.EqualsLiteral("radical")) {
+ rv = AllocateMathMLChar(NOTATION_RADICAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mNotationsToDraw |= NOTATION_RADICAL;
+ } else if (aNotation.EqualsLiteral("box")) {
+ mNotationsToDraw |= (NOTATION_LEFT | NOTATION_RIGHT |
+ NOTATION_TOP | NOTATION_BOTTOM);
+ } else if (aNotation.EqualsLiteral("roundedbox")) {
+ mNotationsToDraw |= NOTATION_ROUNDEDBOX;
+ } else if (aNotation.EqualsLiteral("circle")) {
+ mNotationsToDraw |= NOTATION_CIRCLE;
+ } else if (aNotation.EqualsLiteral("left")) {
+ mNotationsToDraw |= NOTATION_LEFT;
+ } else if (aNotation.EqualsLiteral("right")) {
+ mNotationsToDraw |= NOTATION_RIGHT;
+ } else if (aNotation.EqualsLiteral("top")) {
+ mNotationsToDraw |= NOTATION_TOP;
+ } else if (aNotation.EqualsLiteral("bottom")) {
+ mNotationsToDraw |= NOTATION_BOTTOM;
+ } else if (aNotation.EqualsLiteral("updiagonalstrike")) {
+ mNotationsToDraw |= NOTATION_UPDIAGONALSTRIKE;
+ } else if (aNotation.EqualsLiteral("updiagonalarrow")) {
+ mNotationsToDraw |= NOTATION_UPDIAGONALARROW;
+ } else if (aNotation.EqualsLiteral("downdiagonalstrike")) {
+ mNotationsToDraw |= NOTATION_DOWNDIAGONALSTRIKE;
+ } else if (aNotation.EqualsLiteral("verticalstrike")) {
+ mNotationsToDraw |= NOTATION_VERTICALSTRIKE;
+ } else if (aNotation.EqualsLiteral("horizontalstrike")) {
+ mNotationsToDraw |= NOTATION_HORIZONTALSTRIKE;
+ } else if (aNotation.EqualsLiteral("madruwb")) {
+ mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_BOTTOM);
+ } else if (aNotation.EqualsLiteral("phasorangle")) {
+ mNotationsToDraw |= (NOTATION_BOTTOM | NOTATION_PHASORANGLE);
+ }
+
+ return NS_OK;
+}
+
+/*
+ * Initialize the list of notations to draw
+ */
+void nsMathMLmencloseFrame::InitNotations()
+{
+ mNotationsToDraw = 0;
+ mLongDivCharIndex = mRadicalCharIndex = -1;
+ mMathMLChar.Clear();
+
+ nsAutoString value;
+
+ if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::notation_, value)) {
+ // parse the notation attribute
+ nsWhitespaceTokenizer tokenizer(value);
+
+ while (tokenizer.hasMoreTokens())
+ AddNotation(tokenizer.nextToken());
+
+ if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
+ // For <menclose notation="updiagonalstrike updiagonalarrow">, if
+ // the two notations are drawn then the strike line may cause the point of
+ // the arrow to be too wide. Hence we will only draw the updiagonalarrow
+ // and the arrow shaft may be thought to be the updiagonalstrike.
+ mNotationsToDraw &= ~NOTATION_UPDIAGONALSTRIKE;
+ }
+ } else {
+ // default: longdiv
+ if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV)))
+ return;
+ mNotationsToDraw = NOTATION_LONGDIV;
+ }
+}
+
+NS_IMETHODIMP
+nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent)
+{
+ // let the base class get the default from our parent
+ nsMathMLContainerFrame::InheritAutomaticData(aParent);
+
+ mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
+
+ InitNotations();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMathMLmencloseFrame::TransmitAutomaticData()
+{
+ if (IsToDraw(NOTATION_RADICAL)) {
+ // The TeXBook (Ch 17. p.141) says that \sqrt is cramped
+ UpdatePresentationDataFromChildAt(0, -1,
+ NS_MATHML_COMPRESSED,
+ NS_MATHML_COMPRESSED);
+ }
+
+ return NS_OK;
+}
+
+void
+nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ /////////////
+ // paint the menclosed content
+ nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
+
+ if (NS_MATHML_HAS_ERROR(mPresentationData.flags))
+ return;
+
+ nsRect mencloseRect = nsIFrame::GetRect();
+ mencloseRect.x = mencloseRect.y = 0;
+
+ if (IsToDraw(NOTATION_RADICAL)) {
+ mMathMLChar[mRadicalCharIndex].Display(aBuilder, this, aLists, 0);
+
+ nsRect rect;
+ mMathMLChar[mRadicalCharIndex].GetRect(rect);
+ rect.MoveBy(StyleVisibility()->mDirection ? -mContentWidth : rect.width, 0);
+ rect.SizeTo(mContentWidth, mRadicalRuleThickness);
+ DisplayBar(aBuilder, this, rect, aLists);
+ }
+
+ if (IsToDraw(NOTATION_PHASORANGLE)) {
+ DisplayNotation(aBuilder, this, mencloseRect, aLists,
+ mRuleThickness, NOTATION_PHASORANGLE);
+ }
+
+ if (IsToDraw(NOTATION_LONGDIV)) {
+ mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1);
+
+ nsRect rect;
+ mMathMLChar[mLongDivCharIndex].GetRect(rect);
+ rect.SizeTo(rect.width + mContentWidth, mRuleThickness);
+ DisplayBar(aBuilder, this, rect, aLists);
+ }
+
+ if (IsToDraw(NOTATION_TOP)) {
+ nsRect rect(0, 0, mencloseRect.width, mRuleThickness);
+ DisplayBar(aBuilder, this, rect, aLists);
+ }
+
+ if (IsToDraw(NOTATION_BOTTOM)) {
+ nsRect rect(0, mencloseRect.height - mRuleThickness,
+ mencloseRect.width, mRuleThickness);
+ DisplayBar(aBuilder, this, rect, aLists);
+ }
+
+ if (IsToDraw(NOTATION_LEFT)) {
+ nsRect rect(0, 0, mRuleThickness, mencloseRect.height);
+ DisplayBar(aBuilder, this, rect, aLists);
+ }
+
+ if (IsToDraw(NOTATION_RIGHT)) {
+ nsRect rect(mencloseRect.width - mRuleThickness, 0,
+ mRuleThickness, mencloseRect.height);
+ DisplayBar(aBuilder, this, rect, aLists);
+ }
+
+ if (IsToDraw(NOTATION_ROUNDEDBOX)) {
+ DisplayNotation(aBuilder, this, mencloseRect, aLists,
+ mRuleThickness, NOTATION_ROUNDEDBOX);
+ }
+
+ if (IsToDraw(NOTATION_CIRCLE)) {
+ DisplayNotation(aBuilder, this, mencloseRect, aLists,
+ mRuleThickness, NOTATION_CIRCLE);
+ }
+
+ if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) {
+ DisplayNotation(aBuilder, this, mencloseRect, aLists,
+ mRuleThickness, NOTATION_UPDIAGONALSTRIKE);
+ }
+
+ if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
+ DisplayNotation(aBuilder, this, mencloseRect, aLists,
+ mRuleThickness, NOTATION_UPDIAGONALARROW);
+ }
+
+ if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) {
+ DisplayNotation(aBuilder, this, mencloseRect, aLists,
+ mRuleThickness, NOTATION_DOWNDIAGONALSTRIKE);
+ }
+
+ if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) {
+ nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2,
+ mencloseRect.width, mRuleThickness);
+ DisplayBar(aBuilder, this, rect, aLists);
+ }
+
+ if (IsToDraw(NOTATION_VERTICALSTRIKE)) {
+ nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0,
+ mRuleThickness, mencloseRect.height);
+ DisplayBar(aBuilder, this, rect, aLists);
+ }
+}
+
+/* virtual */ nsresult
+nsMathMLmencloseFrame::MeasureForWidth(DrawTarget* aDrawTarget,
+ ReflowOutput& aDesiredSize)
+{
+ return PlaceInternal(aDrawTarget, false, aDesiredSize, true);
+}
+
+/* virtual */ nsresult
+nsMathMLmencloseFrame::Place(DrawTarget* aDrawTarget,
+ bool aPlaceOrigin,
+ ReflowOutput& aDesiredSize)
+{
+ return PlaceInternal(aDrawTarget, aPlaceOrigin, aDesiredSize, false);
+}
+
+/* virtual */ nsresult
+nsMathMLmencloseFrame::PlaceInternal(DrawTarget* aDrawTarget,
+ bool aPlaceOrigin,
+ ReflowOutput& aDesiredSize,
+ bool aWidthOnly)
+{
+ ///////////////
+ // Measure the size of our content using the base class to format like an
+ // inferred mrow.
+ ReflowOutput baseSize(aDesiredSize.GetWritingMode());
+ nsresult rv =
+ nsMathMLContainerFrame::Place(aDrawTarget, false, baseSize);
+
+ if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
+ DidReflowChildren(PrincipalChildList().FirstChild());
+ return rv;
+ }
+
+ nsBoundingMetrics bmBase = baseSize.mBoundingMetrics;
+ nscoord dx_left = 0, dx_right = 0;
+ nsBoundingMetrics bmLongdivChar, bmRadicalChar;
+ nscoord radicalAscent = 0, radicalDescent = 0;
+ nscoord longdivAscent = 0, longdivDescent = 0;
+ nscoord psi = 0;
+ nscoord leading = 0;
+
+ ///////////////
+ // Thickness of bars and font metrics
+ nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
+
+ float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
+ GetRuleThickness(aDrawTarget, fm, mRuleThickness);
+ if (mRuleThickness < onePixel) {
+ mRuleThickness = onePixel;
+ }
+
+ char16_t one = '1';
+ nsBoundingMetrics bmOne =
+ nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, aDrawTarget);
+
+ ///////////////
+ // General rules: the menclose element takes the size of the enclosed content.
+ // We add a padding when needed.
+
+ // determine padding & psi
+ nscoord padding = 3 * mRuleThickness;
+ nscoord delta = padding % onePixel;
+ if (delta)
+ padding += onePixel - delta; // round up
+
+ if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
+ GetRadicalParameters(fm, StyleFont()->mMathDisplay ==
+ NS_MATHML_DISPLAYSTYLE_BLOCK,
+ mRadicalRuleThickness, leading, psi);
+
+ // make sure that the rule appears on on screen
+ if (mRadicalRuleThickness < onePixel) {
+ mRadicalRuleThickness = onePixel;
+ }
+
+ // adjust clearance psi to get an exact number of pixels -- this
+ // gives a nicer & uniform look on stacked radicals (bug 130282)
+ delta = psi % onePixel;
+ if (delta) {
+ psi += onePixel - delta; // round up
+ }
+ }
+
+ // Set horizontal parameters
+ if (IsToDraw(NOTATION_ROUNDEDBOX) ||
+ IsToDraw(NOTATION_TOP) ||
+ IsToDraw(NOTATION_LEFT) ||
+ IsToDraw(NOTATION_BOTTOM) ||
+ IsToDraw(NOTATION_CIRCLE))
+ dx_left = padding;
+
+ if (IsToDraw(NOTATION_ROUNDEDBOX) ||
+ IsToDraw(NOTATION_TOP) ||
+ IsToDraw(NOTATION_RIGHT) ||
+ IsToDraw(NOTATION_BOTTOM) ||
+ IsToDraw(NOTATION_CIRCLE))
+ dx_right = padding;
+
+ // Set vertical parameters
+ if (IsToDraw(NOTATION_RIGHT) ||
+ IsToDraw(NOTATION_LEFT) ||
+ IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
+ IsToDraw(NOTATION_UPDIAGONALARROW) ||
+ IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
+ IsToDraw(NOTATION_VERTICALSTRIKE) ||
+ IsToDraw(NOTATION_CIRCLE) ||
+ IsToDraw(NOTATION_ROUNDEDBOX) ||
+ IsToDraw(NOTATION_RADICAL) ||
+ IsToDraw(NOTATION_LONGDIV) ||
+ IsToDraw(NOTATION_PHASORANGLE)) {
+ // set a minimal value for the base height
+ bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent);
+ bmBase.descent = std::max(0, bmBase.descent);
+ }
+
+ mBoundingMetrics.ascent = bmBase.ascent;
+ mBoundingMetrics.descent = bmBase.descent;
+
+ if (IsToDraw(NOTATION_ROUNDEDBOX) ||
+ IsToDraw(NOTATION_TOP) ||
+ IsToDraw(NOTATION_LEFT) ||
+ IsToDraw(NOTATION_RIGHT) ||
+ IsToDraw(NOTATION_CIRCLE))
+ mBoundingMetrics.ascent += padding;
+
+ if (IsToDraw(NOTATION_ROUNDEDBOX) ||
+ IsToDraw(NOTATION_LEFT) ||
+ IsToDraw(NOTATION_RIGHT) ||
+ IsToDraw(NOTATION_BOTTOM) ||
+ IsToDraw(NOTATION_CIRCLE))
+ mBoundingMetrics.descent += padding;
+
+ ///////////////
+ // phasorangle notation
+ if (IsToDraw(NOTATION_PHASORANGLE)) {
+ nscoord phasorangleWidth = kPhasorangleWidth * mRuleThickness;
+ // Update horizontal parameters
+ dx_left = std::max(dx_left, phasorangleWidth);
+ }
+
+ ///////////////
+ // updiagonal arrow notation. We need enough space at the top right corner to
+ // draw the arrow head.
+ if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
+ // This is an estimate, see nsDisplayNotation::Paint for the exact head size
+ nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness;
+
+ // We want that the arrow shaft strikes the menclose content and that the
+ // arrow head does not overlap with that content. Hence we add some space
+ // on the right. We don't add space on the top but only ensure that the
+ // ascent is large enough.
+ dx_right = std::max(dx_right, arrowHeadSize);
+ mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize);
+ }
+
+ ///////////////
+ // circle notation: we don't want the ellipse to overlap the enclosed
+ // content. Hence, we need to increase the size of the bounding box by a
+ // factor of at least sqrt(2).
+ if (IsToDraw(NOTATION_CIRCLE)) {
+ double ratio = (sqrt(2.0) - 1.0) / 2.0;
+ nscoord padding2;
+
+ // Update horizontal parameters
+ padding2 = ratio * bmBase.width;
+
+ dx_left = std::max(dx_left, padding2);
+ dx_right = std::max(dx_right, padding2);
+
+ // Update vertical parameters
+ padding2 = ratio * (bmBase.ascent + bmBase.descent);
+
+ mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
+ bmBase.ascent + padding2);
+ mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
+ bmBase.descent + padding2);
+ }
+
+ ///////////////
+ // longdiv notation:
+ if (IsToDraw(NOTATION_LONGDIV)) {
+ if (aWidthOnly) {
+ nscoord longdiv_width = mMathMLChar[mLongDivCharIndex].
+ GetMaxWidth(PresContext(), aDrawTarget, fontSizeInflation);
+
+ // Update horizontal parameters
+ dx_left = std::max(dx_left, longdiv_width);
+ } else {
+ // Stretch the parenthesis to the appropriate height if it is not
+ // big enough.
+ nsBoundingMetrics contSize = bmBase;
+ contSize.ascent = mRuleThickness;
+ contSize.descent = bmBase.ascent + bmBase.descent + psi;
+
+ // height(longdiv) should be >= height(base) + psi + mRuleThickness
+ mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aDrawTarget,
+ fontSizeInflation,
+ NS_STRETCH_DIRECTION_VERTICAL,
+ contSize, bmLongdivChar,
+ NS_STRETCH_LARGER, false);
+ mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar);
+
+ // Update horizontal parameters
+ dx_left = std::max(dx_left, bmLongdivChar.width);
+
+ // Update vertical parameters
+ longdivAscent = bmBase.ascent + psi + mRuleThickness;
+ longdivDescent = std::max(bmBase.descent,
+ (bmLongdivChar.ascent + bmLongdivChar.descent -
+ longdivAscent));
+
+ mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
+ longdivAscent);
+ mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
+ longdivDescent);
+ }
+ }
+
+ ///////////////
+ // radical notation:
+ if (IsToDraw(NOTATION_RADICAL)) {
+ nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left;
+
+ if (aWidthOnly) {
+ nscoord radical_width = mMathMLChar[mRadicalCharIndex].
+ GetMaxWidth(PresContext(), aDrawTarget, fontSizeInflation);
+
+ // Update horizontal parameters
+ *dx_leading = std::max(*dx_leading, radical_width);
+ } else {
+ // Stretch the radical symbol to the appropriate height if it is not
+ // big enough.
+ nsBoundingMetrics contSize = bmBase;
+ contSize.ascent = mRadicalRuleThickness;
+ contSize.descent = bmBase.ascent + bmBase.descent + psi;
+
+ // height(radical) should be >= height(base) + psi + mRadicalRuleThickness
+ mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aDrawTarget,
+ fontSizeInflation,
+ NS_STRETCH_DIRECTION_VERTICAL,
+ contSize, bmRadicalChar,
+ NS_STRETCH_LARGER,
+ StyleVisibility()->mDirection);
+ mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar);
+
+ // Update horizontal parameters
+ *dx_leading = std::max(*dx_leading, bmRadicalChar.width);
+
+ // Update vertical parameters
+ radicalAscent = bmBase.ascent + psi + mRadicalRuleThickness;
+ radicalDescent = std::max(bmBase.descent,
+ (bmRadicalChar.ascent + bmRadicalChar.descent -
+ radicalAscent));
+
+ mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
+ radicalAscent);
+ mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
+ radicalDescent);
+ }
+ }
+
+ ///////////////
+ //
+ if (IsToDraw(NOTATION_CIRCLE) ||
+ IsToDraw(NOTATION_ROUNDEDBOX) ||
+ (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) {
+ // center the menclose around the content (horizontally)
+ dx_left = dx_right = std::max(dx_left, dx_right);
+ }
+
+ ///////////////
+ // The maximum size is now computed: set the remaining parameters
+ mBoundingMetrics.width = dx_left + bmBase.width + dx_right;
+
+ mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing);
+ mBoundingMetrics.rightBearing =
+ std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing);
+
+ aDesiredSize.Width() = mBoundingMetrics.width;
+
+ aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent,
+ baseSize.BlockStartAscent()));
+ aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
+ std::max(mBoundingMetrics.descent,
+ baseSize.Height() - baseSize.BlockStartAscent());
+
+ if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
+ nscoord desiredSizeAscent = aDesiredSize.BlockStartAscent();
+ nscoord desiredSizeDescent = aDesiredSize.Height() -
+ aDesiredSize.BlockStartAscent();
+
+ if (IsToDraw(NOTATION_LONGDIV)) {
+ desiredSizeAscent = std::max(desiredSizeAscent,
+ longdivAscent + leading);
+ desiredSizeDescent = std::max(desiredSizeDescent,
+ longdivDescent + mRuleThickness);
+ }
+
+ if (IsToDraw(NOTATION_RADICAL)) {
+ desiredSizeAscent = std::max(desiredSizeAscent,
+ radicalAscent + leading);
+ desiredSizeDescent = std::max(desiredSizeDescent,
+ radicalDescent + mRadicalRuleThickness);
+ }
+
+ aDesiredSize.SetBlockStartAscent(desiredSizeAscent);
+ aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent;
+ }
+
+ if (IsToDraw(NOTATION_CIRCLE) ||
+ IsToDraw(NOTATION_ROUNDEDBOX) ||
+ (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) {
+ // center the menclose around the content (vertically)
+ nscoord dy = std::max(aDesiredSize.BlockStartAscent() - bmBase.ascent,
+ aDesiredSize.Height() -
+ aDesiredSize.BlockStartAscent() - bmBase.descent);
+
+ aDesiredSize.SetBlockStartAscent(bmBase.ascent + dy);
+ aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + bmBase.descent + dy;
+ }
+
+ // Update mBoundingMetrics ascent/descent
+ if (IsToDraw(NOTATION_TOP) ||
+ IsToDraw(NOTATION_RIGHT) ||
+ IsToDraw(NOTATION_LEFT) ||
+ IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
+ IsToDraw(NOTATION_UPDIAGONALARROW) ||
+ IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
+ IsToDraw(NOTATION_VERTICALSTRIKE) ||
+ IsToDraw(NOTATION_CIRCLE) ||
+ IsToDraw(NOTATION_ROUNDEDBOX))
+ mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent();
+
+ if (IsToDraw(NOTATION_BOTTOM) ||
+ IsToDraw(NOTATION_RIGHT) ||
+ IsToDraw(NOTATION_LEFT) ||
+ IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
+ IsToDraw(NOTATION_UPDIAGONALARROW) ||
+ IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
+ IsToDraw(NOTATION_VERTICALSTRIKE) ||
+ IsToDraw(NOTATION_CIRCLE) ||
+ IsToDraw(NOTATION_ROUNDEDBOX))
+ mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
+
+ // phasorangle notation:
+ // move up from the bottom by the angled line height
+ if (IsToDraw(NOTATION_PHASORANGLE))
+ mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, 2 * kPhasorangleWidth * mRuleThickness - mBoundingMetrics.descent);
+
+ aDesiredSize.mBoundingMetrics = mBoundingMetrics;
+
+ mReference.x = 0;
+ mReference.y = aDesiredSize.BlockStartAscent();
+
+ if (aPlaceOrigin) {
+ //////////////////
+ // Set position and size of MathMLChars
+ if (IsToDraw(NOTATION_LONGDIV))
+ mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left -
+ bmLongdivChar.width,
+ aDesiredSize.BlockStartAscent() -
+ longdivAscent,
+ bmLongdivChar.width,
+ bmLongdivChar.ascent +
+ bmLongdivChar.descent));
+
+ if (IsToDraw(NOTATION_RADICAL)) {
+ nscoord dx = (StyleVisibility()->mDirection ?
+ dx_left + bmBase.width : dx_left - bmRadicalChar.width);
+
+ mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx,
+ aDesiredSize.BlockStartAscent() -
+ radicalAscent,
+ bmRadicalChar.width,
+ bmRadicalChar.ascent +
+ bmRadicalChar.descent));
+ }
+
+ mContentWidth = bmBase.width;
+
+ //////////////////
+ // Finish reflowing child frames
+ PositionRowChildFrames(dx_left, aDesiredSize.BlockStartAscent());
+ }
+
+ return NS_OK;
+}
+
+nscoord
+nsMathMLmencloseFrame::FixInterFrameSpacing(ReflowOutput& aDesiredSize)
+{
+ nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
+ if (!gap)
+ return 0;
+
+ // Move the MathML characters
+ nsRect rect;
+ for (uint32_t i = 0; i < mMathMLChar.Length(); i++) {
+ mMathMLChar[i].GetRect(rect);
+ rect.MoveBy(gap, 0);
+ mMathMLChar[i].SetRect(rect);
+ }
+
+ return gap;
+}
+
+nsresult
+nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType)
+{
+ if (aAttribute == nsGkAtoms::notation_) {
+ InitNotations();
+ }
+
+ return nsMathMLContainerFrame::
+ AttributeChanged(aNameSpaceID, aAttribute, aModType);
+}
+
+//////////////////
+// the Style System will use these to pass the proper style context to our
+// MathMLChar
+nsStyleContext*
+nsMathMLmencloseFrame::GetAdditionalStyleContext(int32_t aIndex) const
+{
+ int32_t len = mMathMLChar.Length();
+ if (aIndex >= 0 && aIndex < len)
+ return mMathMLChar[aIndex].GetStyleContext();
+ else
+ return nullptr;
+}
+
+void
+nsMathMLmencloseFrame::SetAdditionalStyleContext(int32_t aIndex,
+ nsStyleContext* aStyleContext)
+{
+ int32_t len = mMathMLChar.Length();
+ if (aIndex >= 0 && aIndex < len)
+ mMathMLChar[aIndex].SetStyleContext(aStyleContext);
+}
+
+class nsDisplayNotation : public nsDisplayItem
+{
+public:
+ nsDisplayNotation(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aFrame, const nsRect& aRect,
+ nscoord aThickness, nsMencloseNotation aType)
+ : nsDisplayItem(aBuilder, aFrame), mRect(aRect),
+ mThickness(aThickness), mType(aType) {
+ MOZ_COUNT_CTOR(nsDisplayNotation);
+ }
+#ifdef NS_BUILD_REFCNT_LOGGING
+ virtual ~nsDisplayNotation() {
+ MOZ_COUNT_DTOR(nsDisplayNotation);
+ }
+#endif
+
+ virtual void Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx) override;
+ NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION)
+
+private:
+ nsRect mRect;
+ nscoord mThickness;
+ nsMencloseNotation mType;
+};
+
+void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder,
+ nsRenderingContext* aCtx)
+{
+ DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
+ nsPresContext* presContext = mFrame->PresContext();
+
+ Float strokeWidth = presContext->AppUnitsToGfxUnits(mThickness);
+
+ Rect rect = NSRectToRect(mRect + ToReferenceFrame(),
+ presContext->AppUnitsPerDevPixel());
+ rect.Deflate(strokeWidth / 2.f);
+
+ ColorPattern color(ToDeviceColor(
+ mFrame->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color)));
+
+ StrokeOptions strokeOptions(strokeWidth);
+
+ switch(mType)
+ {
+ case NOTATION_CIRCLE: {
+ RefPtr<Path> ellipse =
+ MakePathForEllipse(aDrawTarget, rect.Center(), rect.Size());
+ aDrawTarget.Stroke(ellipse, color, strokeOptions);
+ return;
+ }
+ case NOTATION_ROUNDEDBOX: {
+ Float radius = 3 * strokeWidth;
+ RectCornerRadii radii(radius, radius);
+ RefPtr<Path> roundedRect =
+ MakePathForRoundedRect(aDrawTarget, rect, radii, true);
+ aDrawTarget.Stroke(roundedRect, color, strokeOptions);
+ return;
+ }
+ case NOTATION_UPDIAGONALSTRIKE: {
+ aDrawTarget.StrokeLine(rect.BottomLeft(), rect.TopRight(),
+ color, strokeOptions);
+ return;
+ }
+ case NOTATION_DOWNDIAGONALSTRIKE: {
+ aDrawTarget.StrokeLine(rect.TopLeft(), rect.BottomRight(),
+ color, strokeOptions);
+ return;
+ }
+ case NOTATION_UPDIAGONALARROW: {
+ // Compute some parameters to draw the updiagonalarrow. The values below
+ // are taken from MathJax's HTML-CSS output.
+ Float W = rect.Width(); gfxFloat H = rect.Height();
+ Float l = sqrt(W*W + H*H);
+ Float f = Float(kArrowHeadSize) * strokeWidth / l;
+ Float w = W * f; gfxFloat h = H * f;
+
+ // Draw the arrow shaft
+ aDrawTarget.StrokeLine(rect.BottomLeft(),
+ rect.TopRight() + Point(-.7*w, .7*h),
+ color, strokeOptions);
+
+ // Draw the arrow head
+ RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
+ builder->MoveTo(rect.TopRight());
+ builder->LineTo(rect.TopRight() + Point(-w -.4*h, std::max(-strokeWidth / 2.0, h - .4*w)));
+ builder->LineTo(rect.TopRight() + Point(-.7*w, .7*h));
+ builder->LineTo(rect.TopRight() + Point(std::min(strokeWidth / 2.0, -w + .4*h), h + .4*w));
+ builder->Close();
+ RefPtr<Path> path = builder->Finish();
+ aDrawTarget.Fill(path, color);
+ return;
+ }
+ case NOTATION_PHASORANGLE: {
+ // Compute some parameters to draw the angled line,
+ // that uses a slope of 2 (angle = tan^-1(2)).
+ // H = w * tan(angle) = w * 2
+ Float w = Float(kPhasorangleWidth) * strokeWidth;
+ Float H = 2 * w;
+
+ // Draw the angled line
+ aDrawTarget.StrokeLine(rect.BottomLeft(),
+ rect.BottomLeft() + Point(w, -H),
+ color, strokeOptions);
+ return;
+ }
+ default:
+ NS_NOTREACHED("This notation can not be drawn using nsDisplayNotation");
+ }
+}
+
+void
+nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aFrame, const nsRect& aRect,
+ const nsDisplayListSet& aLists,
+ nscoord aThickness,
+ nsMencloseNotation aType)
+{
+ if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() ||
+ aThickness <= 0)
+ return;
+
+ aLists.Content()->AppendNewToTop(new (aBuilder)
+ nsDisplayNotation(aBuilder, aFrame, aRect, aThickness, aType));
+}