diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /layout/mathml/nsMathMLmmultiscriptsFrame.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'layout/mathml/nsMathMLmmultiscriptsFrame.cpp')
-rw-r--r-- | layout/mathml/nsMathMLmmultiscriptsFrame.cpp | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/layout/mathml/nsMathMLmmultiscriptsFrame.cpp b/layout/mathml/nsMathMLmmultiscriptsFrame.cpp new file mode 100644 index 0000000000..057546a7b1 --- /dev/null +++ b/layout/mathml/nsMathMLmmultiscriptsFrame.cpp @@ -0,0 +1,689 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "nsMathMLmmultiscriptsFrame.h" + +#include "mozilla/PresShell.h" +#include "mozilla/StaticPrefs_mathml.h" +#include "nsPresContext.h" +#include <algorithm> +#include "gfxContext.h" +#include "gfxMathTable.h" +#include "gfxTextRun.h" + +using namespace mozilla; + +// +// <mmultiscripts> -- attach prescripts and tensor indices to a base - +// implementation <msub> -- attach a subscript to a base - implementation +// <msubsup> -- attach a subscript-superscript pair to a base - implementation +// <msup> -- attach a superscript to a base - implementation +// + +nsIFrame* NS_NewMathMLmmultiscriptsFrame(PresShell* aPresShell, + ComputedStyle* aStyle) { + return new (aPresShell) + nsMathMLmmultiscriptsFrame(aStyle, aPresShell->GetPresContext()); +} + +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmmultiscriptsFrame) + +nsMathMLmmultiscriptsFrame::~nsMathMLmmultiscriptsFrame() = default; + +uint8_t nsMathMLmmultiscriptsFrame::ScriptIncrement(nsIFrame* aFrame) { + if (!aFrame) return 0; + if (mFrames.ContainsFrame(aFrame)) { + if (mFrames.FirstChild() == aFrame || + aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) { + return 0; // No script increment for base frames or prescript markers + } + return 1; + } + return 0; // not a child +} + +NS_IMETHODIMP +nsMathMLmmultiscriptsFrame::TransmitAutomaticData() { + // if our base is an embellished operator, let its state bubble to us + mPresentationData.baseFrame = mFrames.FirstChild(); + GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData); + + // The TeXbook (Ch 17. p.141) says the superscript inherits the compression + // while the subscript is compressed. So here we collect subscripts and set + // the compression flag in them. + + int32_t count = 0; + bool isSubScript = !mContent->IsMathMLElement(nsGkAtoms::msup_); + + AutoTArray<nsIFrame*, 8> subScriptFrames; + nsIFrame* childFrame = mFrames.FirstChild(); + while (childFrame) { + if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) { + // mprescripts frame + } else if (0 == count) { + // base frame + } else { + // super/subscript block + if (isSubScript) { + // subscript + subScriptFrames.AppendElement(childFrame); + } else { + // superscript + } + PropagateFrameFlagFor(childFrame, NS_FRAME_MATHML_SCRIPT_DESCENDANT); + isSubScript = !isSubScript; + } + count++; + childFrame = childFrame->GetNextSibling(); + } + for (int32_t i = subScriptFrames.Length() - 1; i >= 0; i--) { + childFrame = subScriptFrames[i]; + PropagatePresentationDataFor(childFrame, NS_MATHML_COMPRESSED, + NS_MATHML_COMPRESSED); + } + + return NS_OK; +} + +/* virtual */ +nsresult nsMathMLmmultiscriptsFrame::Place(DrawTarget* aDrawTarget, + bool aPlaceOrigin, + ReflowOutput& aDesiredSize) { + nscoord subScriptShift = 0; + nscoord supScriptShift = 0; + float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); + + if (!StaticPrefs::mathml_script_shift_attributes_disabled()) { + // subscriptshift + // + // "Specifies the minimum amount to shift the baseline of subscript down; + // the default is for the rendering agent to use its own positioning rules." + // + // values: length + // default: automatic + // + // We use 0 as the default value so unitless values can be ignored. + // As a minimum, negative values can be ignored. + // + nsAutoString value; + if (!mContent->IsMathMLElement(nsGkAtoms::msup_) && + mContent->AsElement()->GetAttr(kNameSpaceID_None, + nsGkAtoms::subscriptshift_, value)) { + mContent->OwnerDoc()->WarnOnceAbout( + dom::DeprecatedOperations::eMathML_DeprecatedScriptShiftAttributes); + ParseNumericValue(value, &subScriptShift, 0, PresContext(), + mComputedStyle, fontSizeInflation); + } + // superscriptshift + // + // "Specifies the minimum amount to shift the baseline of superscript up; + // the default is for the rendering agent to use its own positioning rules." + // + // values: length + // default: automatic + // + // We use 0 as the default value so unitless values can be ignored. + // As a minimum, negative values can be ignored. + // + if (!mContent->IsMathMLElement(nsGkAtoms::msub_) && + mContent->AsElement()->GetAttr(kNameSpaceID_None, + nsGkAtoms::superscriptshift_, value)) { + mContent->OwnerDoc()->WarnOnceAbout( + dom::DeprecatedOperations::eMathML_DeprecatedScriptShiftAttributes); + ParseNumericValue(value, &supScriptShift, 0, PresContext(), + mComputedStyle, fontSizeInflation); + } + } + return PlaceMultiScript(PresContext(), aDrawTarget, aPlaceOrigin, + aDesiredSize, this, subScriptShift, supScriptShift, + fontSizeInflation); +} + +// exported routine that both munderover and mmultiscripts share. +// munderover uses this when movablelimits is set. +nsresult nsMathMLmmultiscriptsFrame::PlaceMultiScript( + nsPresContext* aPresContext, DrawTarget* aDrawTarget, bool aPlaceOrigin, + ReflowOutput& aDesiredSize, nsMathMLContainerFrame* aFrame, + nscoord aUserSubScriptShift, nscoord aUserSupScriptShift, + float aFontSizeInflation) { + nsAtom* tag = aFrame->GetContent()->NodeInfo()->NameAtom(); + + // This function deals with both munderover etc. as well as msubsup etc. + // As the former behaves identically to the later, we treat it as such + // to avoid additional checks later. + if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mover_)) + tag = nsGkAtoms::msup_; + else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munder_)) + tag = nsGkAtoms::msub_; + else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munderover_)) + tag = nsGkAtoms::msubsup_; + + nsBoundingMetrics bmFrame; + + nscoord minShiftFromXHeight, subDrop, supDrop; + + //////////////////////////////////////// + // Initialize super/sub shifts that + // depend only on the current font + //////////////////////////////////////// + + nsIFrame* baseFrame = aFrame->PrincipalChildList().FirstChild(); + + if (!baseFrame) { + if (tag == nsGkAtoms::mmultiscripts_) + aFrame->ReportErrorToConsole("NoBase"); + else + aFrame->ReportChildCountError(); + return aFrame->ReflowError(aDrawTarget, aDesiredSize); + } + + // get x-height (an ex) + const nsStyleFont* font = aFrame->StyleFont(); + RefPtr<nsFontMetrics> fm = + nsLayoutUtils::GetFontMetricsForFrame(baseFrame, aFontSizeInflation); + + nscoord xHeight = fm->XHeight(); + + nscoord oneDevPixel = fm->AppUnitsPerDevPixel(); + gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont(); + // scriptspace from TeX for extra spacing after sup/subscript + nscoord scriptSpace; + if (mathFont) { + scriptSpace = mathFont->MathTable()->Constant( + gfxMathTable::SpaceAfterScript, oneDevPixel); + } else { + // (0.5pt in plain TeX) + scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f); + } + + // Try and read sub and sup drops from the MATH table. + if (mathFont) { + subDrop = mathFont->MathTable()->Constant( + gfxMathTable::SubscriptBaselineDropMin, oneDevPixel); + supDrop = mathFont->MathTable()->Constant( + gfxMathTable::SuperscriptBaselineDropMax, oneDevPixel); + } + + // force the scriptSpace to be at least 1 pixel + nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); + scriptSpace = std::max(onePixel, scriptSpace); + + ///////////////////////////////////// + // first the shift for the subscript + + nscoord subScriptShift; + if (mathFont) { + // Try and get the sub script shift from the MATH table. Note that contrary + // to TeX we only have one parameter. + subScriptShift = mathFont->MathTable()->Constant( + gfxMathTable::SubscriptShiftDown, oneDevPixel); + } else { + // subScriptShift{1,2} + // = minimum amount to shift the subscript down + // = sub{1,2} in TeXbook + // subScriptShift1 = subscriptshift attribute * x-height + nscoord subScriptShift1, subScriptShift2; + // Get subScriptShift{1,2} default from font + GetSubScriptShifts(fm, subScriptShift1, subScriptShift2); + if (tag == nsGkAtoms::msub_) { + subScriptShift = subScriptShift1; + } else { + subScriptShift = std::max(subScriptShift1, subScriptShift2); + } + } + + if (0 < aUserSubScriptShift) { + // the user has set the subscriptshift attribute + subScriptShift = std::max(subScriptShift, aUserSubScriptShift); + } + + ///////////////////////////////////// + // next the shift for the superscript + + nscoord supScriptShift; + nsPresentationData presentationData; + aFrame->GetPresentationData(presentationData); + if (mathFont) { + // Try and get the super script shift from the MATH table. Note that + // contrary to TeX we only have two parameters. + supScriptShift = mathFont->MathTable()->Constant( + NS_MATHML_IS_COMPRESSED(presentationData.flags) + ? gfxMathTable::SuperscriptShiftUpCramped + : gfxMathTable::SuperscriptShiftUp, + oneDevPixel); + } else { + // supScriptShift{1,2,3} + // = minimum amount to shift the supscript up + // = sup{1,2,3} in TeX + // supScriptShift1 = superscriptshift attribute * x-height + // Note that there are THREE values for supscript shifts depending + // on the current style + nscoord supScriptShift1, supScriptShift2, supScriptShift3; + // Set supScriptShift{1,2,3} default from font + GetSupScriptShifts(fm, supScriptShift1, supScriptShift2, supScriptShift3); + + // get sup script shift depending on current script level and display style + // Rule 18c, App. G, TeXbook + if (font->mMathDepth == 0 && + font->mMathStyle == NS_STYLE_MATH_STYLE_NORMAL && + !NS_MATHML_IS_COMPRESSED(presentationData.flags)) { + // Style D in TeXbook + supScriptShift = supScriptShift1; + } else if (NS_MATHML_IS_COMPRESSED(presentationData.flags)) { + // Style C' in TeXbook = D',T',S',SS' + supScriptShift = supScriptShift3; + } else { + // everything else = T,S,SS + supScriptShift = supScriptShift2; + } + } + + if (0 < aUserSupScriptShift) { + // the user has set the supscriptshift attribute + supScriptShift = std::max(supScriptShift, aUserSupScriptShift); + } + + //////////////////////////////////// + // Get the children's sizes + //////////////////////////////////// + + const WritingMode wm(aDesiredSize.GetWritingMode()); + nscoord width = 0, prescriptsWidth = 0, rightBearing = 0; + nscoord minSubScriptShift = 0, minSupScriptShift = 0; + nscoord trySubScriptShift = subScriptShift; + nscoord trySupScriptShift = supScriptShift; + nscoord maxSubScriptShift = subScriptShift; + nscoord maxSupScriptShift = supScriptShift; + ReflowOutput baseSize(wm); + ReflowOutput subScriptSize(wm); + ReflowOutput supScriptSize(wm); + ReflowOutput multiSubSize(wm), multiSupSize(wm); + baseFrame = nullptr; + nsIFrame* subScriptFrame = nullptr; + nsIFrame* supScriptFrame = nullptr; + nsIFrame* prescriptsFrame = nullptr; // frame of <mprescripts/>, if there. + + bool firstPrescriptsPair = false; + nsBoundingMetrics bmBase, bmSubScript, bmSupScript, bmMultiSub, bmMultiSup; + multiSubSize.SetBlockStartAscent(-0x7FFFFFFF); + multiSupSize.SetBlockStartAscent(-0x7FFFFFFF); + bmMultiSub.ascent = bmMultiSup.ascent = -0x7FFFFFFF; + bmMultiSub.descent = bmMultiSup.descent = -0x7FFFFFFF; + nscoord italicCorrection = 0; + + nsBoundingMetrics boundingMetrics; + boundingMetrics.width = 0; + boundingMetrics.ascent = boundingMetrics.descent = -0x7FFFFFFF; + aDesiredSize.Width() = aDesiredSize.Height() = 0; + + int32_t count = 0; + bool foundNoneTag = false; + + // Boolean to determine whether the current child is a subscript. + // Note that only msup starts with a superscript. + bool isSubScript = (tag != nsGkAtoms::msup_); + + nsIFrame* childFrame = aFrame->PrincipalChildList().FirstChild(); + while (childFrame) { + if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) { + if (tag != nsGkAtoms::mmultiscripts_) { + if (aPlaceOrigin) { + aFrame->ReportInvalidChildError(nsGkAtoms::mprescripts_); + } + return aFrame->ReflowError(aDrawTarget, aDesiredSize); + } + if (prescriptsFrame) { + // duplicate <mprescripts/> found + // report an error, encourage people to get their markups in order + if (aPlaceOrigin) { + aFrame->ReportErrorToConsole("DuplicateMprescripts"); + } + return aFrame->ReflowError(aDrawTarget, aDesiredSize); + } + if (!isSubScript) { + if (aPlaceOrigin) { + aFrame->ReportErrorToConsole("SubSupMismatch"); + } + return aFrame->ReflowError(aDrawTarget, aDesiredSize); + } + + prescriptsFrame = childFrame; + firstPrescriptsPair = true; + } else if (0 == count) { + // base + + if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::none)) { + if (tag == nsGkAtoms::mmultiscripts_) { + if (aPlaceOrigin) { + aFrame->ReportErrorToConsole("NoBase"); + } + return aFrame->ReflowError(aDrawTarget, aDesiredSize); + } else { + // A different error message is triggered later for the other tags + foundNoneTag = true; + } + } + baseFrame = childFrame; + GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); + + if (tag != nsGkAtoms::msub_) { + // Apply italics correction if there is the potential for a + // postsupscript. + GetItalicCorrection(bmBase, italicCorrection); + // If italics correction is applied, we always add "a little to spare" + // (see TeXbook Ch.11, p.64), as we estimate the italic creation + // ourselves and it isn't the same as TeX. + italicCorrection += onePixel; + } + + // we update boundingMetrics.{ascent,descent} with that + // of the baseFrame only after processing all the sup/sub pairs + boundingMetrics.width = bmBase.width; + boundingMetrics.rightBearing = bmBase.rightBearing; + boundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten + } else { + // super/subscript block + if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::none)) { + foundNoneTag = true; + } + + if (isSubScript) { + // subscript + subScriptFrame = childFrame; + GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, + bmSubScript); + if (!mathFont) { + // get the subdrop from the subscript font + GetSubDropFromChild(subScriptFrame, subDrop, aFontSizeInflation); + } + + // parameter v, Rule 18a, App. G, TeXbook + minSubScriptShift = bmBase.descent + subDrop; + trySubScriptShift = std::max(minSubScriptShift, subScriptShift); + multiSubSize.SetBlockStartAscent(std::max( + multiSubSize.BlockStartAscent(), subScriptSize.BlockStartAscent())); + bmMultiSub.ascent = std::max(bmMultiSub.ascent, bmSubScript.ascent); + bmMultiSub.descent = std::max(bmMultiSub.descent, bmSubScript.descent); + multiSubSize.Height() = + std::max(multiSubSize.Height(), + subScriptSize.Height() - subScriptSize.BlockStartAscent()); + if (bmSubScript.width) width = bmSubScript.width + scriptSpace; + rightBearing = bmSubScript.rightBearing; + + if (tag == nsGkAtoms::msub_) { + boundingMetrics.rightBearing = boundingMetrics.width + rightBearing; + boundingMetrics.width += width; + + nscoord subscriptTopMax; + if (mathFont) { + subscriptTopMax = mathFont->MathTable()->Constant( + gfxMathTable::SubscriptTopMax, oneDevPixel); + } else { + // get min subscript shift limit from x-height + // = h(x) - 4/5 * sigma_5, Rule 18b, App. G, TeXbook + subscriptTopMax = NSToCoordRound((4.0f / 5.0f) * xHeight); + } + nscoord minShiftFromXHeight = bmSubScript.ascent - subscriptTopMax; + maxSubScriptShift = std::max(trySubScriptShift, minShiftFromXHeight); + + maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift); + trySubScriptShift = subScriptShift; + } + } else { + // supscript + supScriptFrame = childFrame; + GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, + bmSupScript); + if (!mathFont) { + // get the supdrop from the supscript font + GetSupDropFromChild(supScriptFrame, supDrop, aFontSizeInflation); + } + // parameter u, Rule 18a, App. G, TeXbook + minSupScriptShift = bmBase.ascent - supDrop; + nscoord superscriptBottomMin; + if (mathFont) { + superscriptBottomMin = mathFont->MathTable()->Constant( + gfxMathTable::SuperscriptBottomMin, oneDevPixel); + } else { + // get min supscript shift limit from x-height + // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook + superscriptBottomMin = NSToCoordRound((1.0f / 4.0f) * xHeight); + } + minShiftFromXHeight = bmSupScript.descent + superscriptBottomMin; + trySupScriptShift = std::max( + minSupScriptShift, std::max(minShiftFromXHeight, supScriptShift)); + multiSupSize.SetBlockStartAscent(std::max( + multiSupSize.BlockStartAscent(), supScriptSize.BlockStartAscent())); + bmMultiSup.ascent = std::max(bmMultiSup.ascent, bmSupScript.ascent); + bmMultiSup.descent = std::max(bmMultiSup.descent, bmSupScript.descent); + multiSupSize.Height() = + std::max(multiSupSize.Height(), + supScriptSize.Height() - supScriptSize.BlockStartAscent()); + + if (bmSupScript.width) + width = std::max(width, bmSupScript.width + scriptSpace); + + if (!prescriptsFrame) { // we are still looping over base & postscripts + rightBearing = std::max(rightBearing, + italicCorrection + bmSupScript.rightBearing); + boundingMetrics.rightBearing = boundingMetrics.width + rightBearing; + boundingMetrics.width += width; + } else { + prescriptsWidth += width; + if (firstPrescriptsPair) { + firstPrescriptsPair = false; + boundingMetrics.leftBearing = + std::min(bmSubScript.leftBearing, bmSupScript.leftBearing); + } + } + width = rightBearing = 0; + + // negotiate between the various shifts so that + // there is enough gap between the sup and subscripts + // Rule 18e, App. G, TeXbook + if (tag == nsGkAtoms::mmultiscripts_ || tag == nsGkAtoms::msubsup_) { + nscoord subSuperscriptGapMin; + if (mathFont) { + subSuperscriptGapMin = mathFont->MathTable()->Constant( + gfxMathTable::SubSuperscriptGapMin, oneDevPixel); + } else { + nscoord ruleSize; + GetRuleThickness(aDrawTarget, fm, ruleSize); + subSuperscriptGapMin = 4 * ruleSize; + } + nscoord gap = (trySupScriptShift - bmSupScript.descent) - + (bmSubScript.ascent - trySubScriptShift); + if (gap < subSuperscriptGapMin) { + // adjust trySubScriptShift to get a gap of subSuperscriptGapMin + trySubScriptShift += subSuperscriptGapMin - gap; + } + + // next we want to ensure that the bottom of the superscript + // will be > superscriptBottomMaxWithSubscript + nscoord superscriptBottomMaxWithSubscript; + if (mathFont) { + superscriptBottomMaxWithSubscript = mathFont->MathTable()->Constant( + gfxMathTable::SuperscriptBottomMaxWithSubscript, oneDevPixel); + } else { + superscriptBottomMaxWithSubscript = + NSToCoordRound((4.0f / 5.0f) * xHeight); + } + gap = superscriptBottomMaxWithSubscript - + (trySupScriptShift - bmSupScript.descent); + if (gap > 0) { + trySupScriptShift += gap; + trySubScriptShift -= gap; + } + } + + maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift); + maxSupScriptShift = std::max(maxSupScriptShift, trySupScriptShift); + + trySubScriptShift = subScriptShift; + trySupScriptShift = supScriptShift; + } + + isSubScript = !isSubScript; + } + count++; + childFrame = childFrame->GetNextSibling(); + } + + // NoBase error may also have been reported above + if ((count != 2 && (tag == nsGkAtoms::msup_ || tag == nsGkAtoms::msub_)) || + (count != 3 && tag == nsGkAtoms::msubsup_) || !baseFrame || + (foundNoneTag && tag != nsGkAtoms::mmultiscripts_) || + (!isSubScript && tag == nsGkAtoms::mmultiscripts_)) { + // report an error, encourage people to get their markups in order + if (aPlaceOrigin) { + if ((count != 2 && + (tag == nsGkAtoms::msup_ || tag == nsGkAtoms::msub_)) || + (count != 3 && tag == nsGkAtoms::msubsup_)) { + aFrame->ReportChildCountError(); + } else if (foundNoneTag && tag != nsGkAtoms::mmultiscripts_) { + aFrame->ReportInvalidChildError(nsGkAtoms::none); + } else if (!baseFrame) { + aFrame->ReportErrorToConsole("NoBase"); + } else { + aFrame->ReportErrorToConsole("SubSupMismatch"); + } + } + return aFrame->ReflowError(aDrawTarget, aDesiredSize); + } + + // we left out the width of prescripts, so ... + boundingMetrics.rightBearing += prescriptsWidth; + boundingMetrics.width += prescriptsWidth; + + // Zero out the shifts in where a frame isn't present to avoid the potential + // for overflow. + if (!subScriptFrame) maxSubScriptShift = 0; + if (!supScriptFrame) maxSupScriptShift = 0; + + // we left out the base during our bounding box updates, so ... + if (tag == nsGkAtoms::msub_) { + boundingMetrics.ascent = + std::max(bmBase.ascent, bmMultiSub.ascent - maxSubScriptShift); + } else { + boundingMetrics.ascent = + std::max(bmBase.ascent, (bmMultiSup.ascent + maxSupScriptShift)); + } + if (tag == nsGkAtoms::msup_) { + boundingMetrics.descent = + std::max(bmBase.descent, bmMultiSup.descent - maxSupScriptShift); + } else { + boundingMetrics.descent = + std::max(bmBase.descent, (bmMultiSub.descent + maxSubScriptShift)); + } + aFrame->SetBoundingMetrics(boundingMetrics); + + // get the reflow metrics ... + aDesiredSize.SetBlockStartAscent( + std::max(baseSize.BlockStartAscent(), + std::max(multiSubSize.BlockStartAscent() - maxSubScriptShift, + multiSupSize.BlockStartAscent() + maxSupScriptShift))); + aDesiredSize.Height() = + aDesiredSize.BlockStartAscent() + + std::max(baseSize.Height() - baseSize.BlockStartAscent(), + std::max(multiSubSize.Height() + maxSubScriptShift, + multiSupSize.Height() - maxSupScriptShift)); + aDesiredSize.Width() = boundingMetrics.width; + aDesiredSize.mBoundingMetrics = boundingMetrics; + + aFrame->SetReference(nsPoint(0, aDesiredSize.BlockStartAscent())); + + ////////////////// + // Place Children + + // Place prescripts, followed by base, and then postscripts. + // The list of frames is in the order: {base} {postscripts} {prescripts} + // We go over the list in a circular manner, starting at <prescripts/> + + if (aPlaceOrigin) { + nscoord dx = 0, dy = 0; + + // With msub and msup there is only one element and + // subscriptFrame/supScriptFrame have already been set above where + // relevant. In these cases we skip to the reflow part. + if (tag == nsGkAtoms::msub_ || tag == nsGkAtoms::msup_) + count = 1; + else + count = 0; + childFrame = prescriptsFrame; + bool isPreScript = true; + do { + if (!childFrame) { // end of prescripts, + isPreScript = false; + // place the base ... + childFrame = baseFrame; + dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent(); + FinishReflowChild( + baseFrame, aPresContext, baseSize, nullptr, + aFrame->MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx), dy, + ReflowChildFlags::Default); + dx += bmBase.width; + } else if (prescriptsFrame == childFrame) { + // Clear reflow flags of prescripts frame. + prescriptsFrame->DidReflow(aPresContext, nullptr); + } else { + // process each sup/sub pair + if (0 == count) { + subScriptFrame = childFrame; + count = 1; + } else if (1 == count) { + if (tag != nsGkAtoms::msub_) supScriptFrame = childFrame; + count = 0; + + // get the ascent/descent of sup/subscripts stored in their rects + // rect.x = descent, rect.y = ascent + if (subScriptFrame) + GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, + bmSubScript); + if (supScriptFrame) + GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, + bmSupScript); + + width = std::max(subScriptSize.Width(), supScriptSize.Width()); + + if (subScriptFrame) { + nscoord x = dx; + // prescripts should be right aligned + // https://bugzilla.mozilla.org/show_bug.cgi?id=928675 + if (isPreScript) x += width - subScriptSize.Width(); + dy = aDesiredSize.BlockStartAscent() - + subScriptSize.BlockStartAscent() + maxSubScriptShift; + FinishReflowChild(subScriptFrame, aPresContext, subScriptSize, + nullptr, + aFrame->MirrorIfRTL(aDesiredSize.Width(), + subScriptSize.Width(), x), + dy, ReflowChildFlags::Default); + } + + if (supScriptFrame) { + nscoord x = dx; + if (isPreScript) { + x += width - supScriptSize.Width(); + } else { + // post superscripts are shifted by the italic correction value + x += italicCorrection; + } + dy = aDesiredSize.BlockStartAscent() - + supScriptSize.BlockStartAscent() - maxSupScriptShift; + FinishReflowChild(supScriptFrame, aPresContext, supScriptSize, + nullptr, + aFrame->MirrorIfRTL(aDesiredSize.Width(), + supScriptSize.Width(), x), + dy, ReflowChildFlags::Default); + } + dx += width + scriptSpace; + } + } + childFrame = childFrame->GetNextSibling(); + } while (prescriptsFrame != childFrame); + } + + return NS_OK; +} |