nsMathMLmmultiscriptsFrame.cpp (28708B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "nsMathMLmmultiscriptsFrame.h" 8 9 #include <algorithm> 10 11 #include "gfxContext.h" 12 #include "gfxMathTable.h" 13 #include "gfxTextRun.h" 14 #include "mozilla/PresShell.h" 15 #include "mozilla/StaticPrefs_mathml.h" 16 #include "mozilla/dom/Document.h" 17 #include "mozilla/dom/Element.h" 18 #include "nsLayoutUtils.h" 19 #include "nsPresContext.h" 20 21 using namespace mozilla; 22 23 // 24 // <mmultiscripts> -- attach prescripts and tensor indices to a base - 25 // implementation <msub> -- attach a subscript to a base - implementation 26 // <msubsup> -- attach a subscript-superscript pair to a base - implementation 27 // <msup> -- attach a superscript to a base - implementation 28 // 29 30 nsIFrame* NS_NewMathMLmmultiscriptsFrame(PresShell* aPresShell, 31 ComputedStyle* aStyle) { 32 return new (aPresShell) 33 nsMathMLmmultiscriptsFrame(aStyle, aPresShell->GetPresContext()); 34 } 35 36 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmmultiscriptsFrame) 37 38 nsMathMLmmultiscriptsFrame::~nsMathMLmmultiscriptsFrame() = default; 39 40 uint8_t nsMathMLmmultiscriptsFrame::ScriptIncrement(nsIFrame* aFrame) { 41 if (!aFrame) { 42 return 0; 43 } 44 if (mFrames.ContainsFrame(aFrame)) { 45 if (mFrames.FirstChild() == aFrame || 46 aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts)) { 47 return 0; // No script increment for base frames or prescript markers 48 } 49 return 1; 50 } 51 return 0; // not a child 52 } 53 54 NS_IMETHODIMP 55 nsMathMLmmultiscriptsFrame::TransmitAutomaticData() { 56 // if our base is an embellished operator, let its state bubble to us 57 mPresentationData.baseFrame = mFrames.FirstChild(); 58 GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData); 59 60 // The TeXbook (Ch 17. p.141) says the superscript inherits the compression 61 // while the subscript is compressed. So here we collect subscripts and set 62 // the compression flag in them. 63 64 int32_t count = 0; 65 bool isSubScript = !mContent->IsMathMLElement(nsGkAtoms::msup); 66 67 AutoTArray<nsIFrame*, 8> subScriptFrames; 68 nsIFrame* childFrame = mFrames.FirstChild(); 69 while (childFrame) { 70 if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts)) { 71 // mprescripts frame 72 } else if (0 == count) { 73 // base frame 74 } else { 75 // super/subscript block 76 if (isSubScript) { 77 // subscript 78 subScriptFrames.AppendElement(childFrame); 79 } else { 80 // superscript 81 } 82 PropagateFrameFlagFor(childFrame, NS_FRAME_MATHML_SCRIPT_DESCENDANT); 83 isSubScript = !isSubScript; 84 } 85 count++; 86 childFrame = childFrame->GetNextSibling(); 87 } 88 if (!StaticPrefs::mathml_math_shift_enabled()) { 89 for (int32_t i = subScriptFrames.Length() - 1; i >= 0; i--) { 90 childFrame = subScriptFrames[i]; 91 PropagatePresentationDataFor(childFrame, 92 MathMLPresentationFlag::Compressed, 93 MathMLPresentationFlag::Compressed); 94 } 95 } 96 97 return NS_OK; 98 } 99 100 /* virtual */ 101 void nsMathMLmmultiscriptsFrame::Place(DrawTarget* aDrawTarget, 102 const PlaceFlags& aFlags, 103 ReflowOutput& aDesiredSize) { 104 nscoord subScriptShift = 0; 105 nscoord supScriptShift = 0; 106 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); 107 108 return PlaceMultiScript(PresContext(), aDrawTarget, aFlags, aDesiredSize, 109 this, subScriptShift, supScriptShift, 110 fontSizeInflation); 111 } 112 113 // exported routine that both munderover and mmultiscripts share. 114 // munderover uses this when movablelimits is set. 115 void nsMathMLmmultiscriptsFrame::PlaceMultiScript( 116 nsPresContext* aPresContext, DrawTarget* aDrawTarget, 117 const PlaceFlags& aFlags, ReflowOutput& aDesiredSize, 118 nsMathMLContainerFrame* aFrame, nscoord aUserSubScriptShift, 119 nscoord aUserSupScriptShift, float aFontSizeInflation) { 120 nsAtom* tag = aFrame->GetContent()->NodeInfo()->NameAtom(); 121 122 // This function deals with both munderover etc. as well as msubsup etc. 123 // As the former behaves identically to the later, we treat it as such 124 // to avoid additional checks later. 125 if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mover)) { 126 tag = nsGkAtoms::msup; 127 } else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munder)) { 128 tag = nsGkAtoms::msub; 129 } else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munderover)) { 130 tag = nsGkAtoms::msubsup; 131 } 132 133 nsBoundingMetrics bmFrame; 134 135 nscoord minShiftFromXHeight, subDrop, supDrop; 136 137 //////////////////////////////////////// 138 // Initialize super/sub shifts that 139 // depend only on the current font 140 //////////////////////////////////////// 141 142 nsIFrame* baseFrame = aFrame->PrincipalChildList().FirstChild(); 143 144 if (!baseFrame) { 145 if (tag == nsGkAtoms::mmultiscripts) { 146 aFrame->ReportErrorToConsole("NoBase"); 147 } else { 148 aFrame->ReportChildCountError(); 149 } 150 return aFrame->PlaceAsMrow(aDrawTarget, aFlags, aDesiredSize); 151 } 152 153 // get x-height (an ex) 154 const nsStyleFont* font = aFrame->StyleFont(); 155 RefPtr<nsFontMetrics> fm = 156 nsLayoutUtils::GetFontMetricsForFrame(baseFrame, aFontSizeInflation); 157 158 nscoord xHeight = fm->XHeight(); 159 160 nscoord oneDevPixel = fm->AppUnitsPerDevPixel(); 161 RefPtr<gfxFont> mathFont = fm->GetThebesFontGroup()->GetFirstMathFont(); 162 // scriptspace from TeX for extra spacing after sup/subscript 163 nscoord scriptSpace; 164 if (mathFont) { 165 scriptSpace = mathFont->MathTable()->Constant( 166 gfxMathTable::SpaceAfterScript, oneDevPixel); 167 } else { 168 // (0.5pt in plain TeX) 169 scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f); 170 } 171 172 // Try and read sub and sup drops from the MATH table. 173 if (mathFont) { 174 subDrop = mathFont->MathTable()->Constant( 175 gfxMathTable::SubscriptBaselineDropMin, oneDevPixel); 176 supDrop = mathFont->MathTable()->Constant( 177 gfxMathTable::SuperscriptBaselineDropMax, oneDevPixel); 178 } 179 180 // force the scriptSpace to be at least 1 pixel 181 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); 182 scriptSpace = std::max(onePixel, scriptSpace); 183 184 ///////////////////////////////////// 185 // first the shift for the subscript 186 187 nscoord subScriptShift; 188 if (mathFont) { 189 // Try and get the sub script shift from the MATH table. Note that contrary 190 // to TeX we only have one parameter. 191 subScriptShift = mathFont->MathTable()->Constant( 192 gfxMathTable::SubscriptShiftDown, oneDevPixel); 193 } else { 194 // subScriptShift{1,2} 195 // = minimum amount to shift the subscript down 196 // = sub{1,2} in TeXbook 197 // subScriptShift1 = subscriptshift attribute * x-height 198 nscoord subScriptShift1, subScriptShift2; 199 // Get subScriptShift{1,2} default from font 200 GetSubScriptShifts(fm, subScriptShift1, subScriptShift2); 201 if (tag == nsGkAtoms::msub) { 202 subScriptShift = subScriptShift1; 203 } else { 204 subScriptShift = std::max(subScriptShift1, subScriptShift2); 205 } 206 } 207 208 if (0 < aUserSubScriptShift) { 209 // the user has set the subscriptshift attribute 210 subScriptShift = std::max(subScriptShift, aUserSubScriptShift); 211 } 212 213 ///////////////////////////////////// 214 // next the shift for the superscript 215 216 nscoord supScriptShift; 217 nsPresentationData presentationData; 218 aFrame->GetPresentationData(presentationData); 219 bool compressed = 220 StaticPrefs::mathml_math_shift_enabled() 221 ? font->mMathShift == StyleMathShift::Compact 222 : presentationData.flags.contains(MathMLPresentationFlag::Compressed); 223 if (mathFont) { 224 // Try and get the super script shift from the MATH table. Note that 225 // contrary to TeX we only have two parameters. 226 supScriptShift = mathFont->MathTable()->Constant( 227 compressed ? gfxMathTable::SuperscriptShiftUpCramped 228 : gfxMathTable::SuperscriptShiftUp, 229 oneDevPixel); 230 } else { 231 // supScriptShift{1,2,3} 232 // = minimum amount to shift the supscript up 233 // = sup{1,2,3} in TeX 234 // supScriptShift1 = superscriptshift attribute * x-height 235 // Note that there are THREE values for supscript shifts depending 236 // on the current style 237 nscoord supScriptShift1, supScriptShift2, supScriptShift3; 238 // Set supScriptShift{1,2,3} default from font 239 GetSupScriptShifts(fm, supScriptShift1, supScriptShift2, supScriptShift3); 240 241 // get sup script shift depending on current script level and display style 242 // Rule 18c, App. G, TeXbook 243 if (font->mMathDepth == 0 && font->mMathStyle == StyleMathStyle::Normal && 244 !compressed) { 245 // Style D in TeXbook 246 supScriptShift = supScriptShift1; 247 } else if (compressed) { 248 // Style C' in TeXbook = D',T',S',SS' 249 supScriptShift = supScriptShift3; 250 } else { 251 // everything else = T,S,SS 252 supScriptShift = supScriptShift2; 253 } 254 } 255 256 if (0 < aUserSupScriptShift) { 257 // the user has set the supscriptshift attribute 258 supScriptShift = std::max(supScriptShift, aUserSupScriptShift); 259 } 260 261 //////////////////////////////////// 262 // Get the children's sizes 263 //////////////////////////////////// 264 265 const WritingMode wm(aDesiredSize.GetWritingMode()); 266 nscoord width = 0, prescriptsWidth = 0, rightBearing = 0; 267 nscoord minSubScriptShift = 0, minSupScriptShift = 0; 268 nscoord trySubScriptShift = subScriptShift; 269 nscoord trySupScriptShift = supScriptShift; 270 nscoord maxSubScriptShift = subScriptShift; 271 nscoord maxSupScriptShift = supScriptShift; 272 ReflowOutput baseSize(wm); 273 ReflowOutput subScriptSize(wm); 274 ReflowOutput supScriptSize(wm); 275 ReflowOutput multiSubSize(wm), multiSupSize(wm); 276 baseFrame = nullptr; 277 nsIFrame* subScriptFrame = nullptr; 278 nsIFrame* supScriptFrame = nullptr; 279 nsIFrame* prescriptsFrame = nullptr; // frame of <mprescripts/>, if there. 280 281 bool firstPrescriptsPair = false; 282 nsBoundingMetrics bmBase, bmSubScript, bmSupScript, bmMultiSub, bmMultiSup; 283 nsMargin baseMargin, subScriptMargin, supScriptMargin; 284 multiSubSize.SetBlockStartAscent(-0x7FFFFFFF); 285 multiSupSize.SetBlockStartAscent(-0x7FFFFFFF); 286 bmMultiSub.ascent = bmMultiSup.ascent = -0x7FFFFFFF; 287 bmMultiSub.descent = bmMultiSup.descent = -0x7FFFFFFF; 288 nscoord italicCorrection = 0; 289 290 nsBoundingMetrics boundingMetrics; 291 boundingMetrics.width = 0; 292 boundingMetrics.ascent = boundingMetrics.descent = -0x7FFFFFFF; 293 aDesiredSize.Width() = aDesiredSize.Height() = 0; 294 295 int32_t count = 0; 296 297 // Boolean to determine whether the current child is a subscript. 298 // Note that only msup starts with a superscript. 299 bool isSubScript = (tag != nsGkAtoms::msup); 300 301 nsIFrame* childFrame = aFrame->PrincipalChildList().FirstChild(); 302 while (childFrame) { 303 if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts)) { 304 if (tag != nsGkAtoms::mmultiscripts) { 305 if (!aFlags.contains(PlaceFlag::MeasureOnly)) { 306 aFrame->ReportInvalidChildError(nsGkAtoms::mprescripts); 307 } 308 return aFrame->PlaceAsMrow(aDrawTarget, aFlags, aDesiredSize); 309 } 310 if (prescriptsFrame) { 311 // duplicate <mprescripts/> found 312 // report an error, encourage people to get their markups in order 313 if (!aFlags.contains(PlaceFlag::MeasureOnly)) { 314 aFrame->ReportErrorToConsole("DuplicateMprescripts"); 315 } 316 return aFrame->PlaceAsMrow(aDrawTarget, aFlags, aDesiredSize); 317 } 318 if (!isSubScript) { 319 if (!aFlags.contains(PlaceFlag::MeasureOnly)) { 320 aFrame->ReportErrorToConsole("SubSupMismatch"); 321 } 322 return aFrame->PlaceAsMrow(aDrawTarget, aFlags, aDesiredSize); 323 } 324 325 prescriptsFrame = childFrame; 326 firstPrescriptsPair = true; 327 } else if (0 == count) { 328 // base 329 baseFrame = childFrame; 330 GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); 331 baseMargin = GetMarginForPlace(aFlags, baseFrame); 332 333 if (tag != nsGkAtoms::msub) { 334 // Apply italics correction if there is the potential for a 335 // postsupscript. 336 GetItalicCorrection(bmBase, italicCorrection); 337 // If italics correction is applied, we always add "a little to spare" 338 // (see TeXbook Ch.11, p.64), as we estimate the italic creation 339 // ourselves and it isn't the same as TeX. 340 italicCorrection += onePixel; 341 } 342 343 // we update boundingMetrics.{ascent,descent} with that 344 // of the baseFrame only after processing all the sup/sub pairs 345 boundingMetrics.width = bmBase.width + baseMargin.LeftRight(); 346 boundingMetrics.rightBearing = 347 bmBase.rightBearing + baseMargin.LeftRight(); 348 boundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten 349 } else { 350 // super/subscript block 351 if (isSubScript) { 352 // subscript 353 subScriptFrame = childFrame; 354 GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, 355 bmSubScript); 356 subScriptMargin = GetMarginForPlace(aFlags, subScriptFrame); 357 358 if (!mathFont) { 359 // get the subdrop from the subscript font 360 GetSubDropFromChild(subScriptFrame, subDrop, aFontSizeInflation); 361 } 362 363 // parameter v, Rule 18a, App. G, TeXbook 364 minSubScriptShift = bmBase.descent + baseMargin.bottom + subDrop; 365 trySubScriptShift = std::max(minSubScriptShift, subScriptShift); 366 multiSubSize.SetBlockStartAscent( 367 std::max(multiSubSize.BlockStartAscent(), 368 subScriptSize.BlockStartAscent() + subScriptMargin.top)); 369 bmMultiSub.ascent = std::max(bmMultiSub.ascent, 370 bmSubScript.ascent + subScriptMargin.top); 371 bmMultiSub.descent = std::max( 372 bmMultiSub.descent, bmSubScript.descent + subScriptMargin.bottom); 373 multiSubSize.Height() = 374 std::max(multiSubSize.Height(), 375 subScriptSize.Height() - subScriptSize.BlockStartAscent() + 376 subScriptMargin.bottom); 377 if (bmSubScript.width) { 378 width = bmSubScript.width + subScriptMargin.LeftRight() + scriptSpace; 379 } 380 rightBearing = bmSubScript.rightBearing + subScriptMargin.LeftRight(); 381 382 if (tag == nsGkAtoms::msub) { 383 boundingMetrics.rightBearing = boundingMetrics.width + rightBearing; 384 boundingMetrics.width += width; 385 386 nscoord subscriptTopMax; 387 if (mathFont) { 388 subscriptTopMax = mathFont->MathTable()->Constant( 389 gfxMathTable::SubscriptTopMax, oneDevPixel); 390 } else { 391 // get min subscript shift limit from x-height 392 // = h(x) - 4/5 * sigma_5, Rule 18b, App. G, TeXbook 393 subscriptTopMax = NSToCoordRound((4.0f / 5.0f) * xHeight); 394 } 395 nscoord minShiftFromXHeight = 396 bmSubScript.ascent + subScriptMargin.top - subscriptTopMax; 397 maxSubScriptShift = std::max(trySubScriptShift, minShiftFromXHeight); 398 399 maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift); 400 trySubScriptShift = subScriptShift; 401 } 402 } else { 403 // supscript 404 supScriptFrame = childFrame; 405 GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, 406 bmSupScript); 407 supScriptMargin = GetMarginForPlace(aFlags, supScriptFrame); 408 if (!mathFont) { 409 // get the supdrop from the supscript font 410 GetSupDropFromChild(supScriptFrame, supDrop, aFontSizeInflation); 411 } 412 // parameter u, Rule 18a, App. G, TeXbook 413 minSupScriptShift = bmBase.ascent + baseMargin.top - supDrop; 414 nscoord superscriptBottomMin; 415 if (mathFont) { 416 superscriptBottomMin = mathFont->MathTable()->Constant( 417 gfxMathTable::SuperscriptBottomMin, oneDevPixel); 418 } else { 419 // get min supscript shift limit from x-height 420 // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook 421 superscriptBottomMin = NSToCoordRound((1.0f / 4.0f) * xHeight); 422 } 423 minShiftFromXHeight = 424 bmSupScript.descent + supScriptMargin.bottom + superscriptBottomMin; 425 trySupScriptShift = std::max( 426 minSupScriptShift, std::max(minShiftFromXHeight, supScriptShift)); 427 multiSupSize.SetBlockStartAscent( 428 std::max(multiSupSize.BlockStartAscent(), 429 supScriptSize.BlockStartAscent() + supScriptMargin.top)); 430 bmMultiSup.ascent = std::max(bmMultiSup.ascent, 431 bmSupScript.ascent + supScriptMargin.top); 432 bmMultiSup.descent = std::max( 433 bmMultiSup.descent, bmSupScript.descent + supScriptMargin.bottom); 434 multiSupSize.Height() = 435 std::max(multiSupSize.Height(), 436 supScriptSize.Height() - supScriptSize.BlockStartAscent() + 437 supScriptMargin.bottom); 438 439 if (bmSupScript.width) { 440 width = 441 std::max(width, bmSupScript.width + supScriptMargin.LeftRight() + 442 scriptSpace); 443 } 444 445 if (!prescriptsFrame) { // we are still looping over base & postscripts 446 rightBearing = std::max(rightBearing, 447 italicCorrection + bmSupScript.rightBearing + 448 supScriptMargin.LeftRight()); 449 boundingMetrics.rightBearing = boundingMetrics.width + rightBearing; 450 boundingMetrics.width += width; 451 } else { 452 prescriptsWidth += width; 453 if (firstPrescriptsPair) { 454 firstPrescriptsPair = false; 455 boundingMetrics.leftBearing = 456 std::min(bmSubScript.leftBearing, bmSupScript.leftBearing); 457 } 458 } 459 width = rightBearing = 0; 460 461 // negotiate between the various shifts so that 462 // there is enough gap between the sup and subscripts 463 // Rule 18e, App. G, TeXbook 464 if (tag == nsGkAtoms::mmultiscripts || tag == nsGkAtoms::msubsup) { 465 nscoord subSuperscriptGapMin; 466 if (mathFont) { 467 subSuperscriptGapMin = mathFont->MathTable()->Constant( 468 gfxMathTable::SubSuperscriptGapMin, oneDevPixel); 469 } else { 470 nscoord ruleSize; 471 GetRuleThickness(aDrawTarget, fm, ruleSize); 472 subSuperscriptGapMin = 4 * ruleSize; 473 } 474 nscoord gap = 475 (trySupScriptShift - bmSupScript.descent - 476 supScriptMargin.bottom) - 477 (subScriptMargin.top + bmSubScript.ascent - trySubScriptShift); 478 if (gap < subSuperscriptGapMin) { 479 // adjust trySubScriptShift to get a gap of subSuperscriptGapMin 480 trySubScriptShift += subSuperscriptGapMin - gap; 481 } 482 483 // next we want to ensure that the bottom of the superscript 484 // will be > superscriptBottomMaxWithSubscript 485 nscoord superscriptBottomMaxWithSubscript; 486 if (mathFont) { 487 superscriptBottomMaxWithSubscript = mathFont->MathTable()->Constant( 488 gfxMathTable::SuperscriptBottomMaxWithSubscript, oneDevPixel); 489 } else { 490 superscriptBottomMaxWithSubscript = 491 NSToCoordRound((4.0f / 5.0f) * xHeight); 492 } 493 gap = superscriptBottomMaxWithSubscript - 494 (trySupScriptShift - bmSupScript.descent - 495 supScriptMargin.bottom); 496 if (gap > 0) { 497 trySupScriptShift += gap; 498 trySubScriptShift -= gap; 499 } 500 } 501 502 maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift); 503 maxSupScriptShift = std::max(maxSupScriptShift, trySupScriptShift); 504 505 trySubScriptShift = subScriptShift; 506 trySupScriptShift = supScriptShift; 507 } 508 509 isSubScript = !isSubScript; 510 } 511 count++; 512 childFrame = childFrame->GetNextSibling(); 513 } 514 515 // NoBase error may also have been reported above 516 if ((count != 2 && (tag == nsGkAtoms::msup || tag == nsGkAtoms::msub)) || 517 (count != 3 && tag == nsGkAtoms::msubsup) || !baseFrame || 518 (!isSubScript && tag == nsGkAtoms::mmultiscripts)) { 519 // report an error, encourage people to get their markups in order 520 if (!aFlags.contains(PlaceFlag::MeasureOnly)) { 521 if ((count != 2 && (tag == nsGkAtoms::msup || tag == nsGkAtoms::msub)) || 522 (count != 3 && tag == nsGkAtoms::msubsup)) { 523 aFrame->ReportChildCountError(); 524 } else if (!baseFrame) { 525 aFrame->ReportErrorToConsole("NoBase"); 526 } else { 527 aFrame->ReportErrorToConsole("SubSupMismatch"); 528 } 529 } 530 return aFrame->PlaceAsMrow(aDrawTarget, aFlags, aDesiredSize); 531 } 532 533 // we left out the width of prescripts, so ... 534 boundingMetrics.rightBearing += prescriptsWidth; 535 boundingMetrics.width += prescriptsWidth; 536 537 // Zero out the shifts in where a frame isn't present to avoid the potential 538 // for overflow. 539 if (!subScriptFrame) { 540 maxSubScriptShift = 0; 541 } 542 if (!supScriptFrame) { 543 maxSupScriptShift = 0; 544 } 545 546 // we left out the base during our bounding box updates, so ... 547 if (tag == nsGkAtoms::msub) { 548 boundingMetrics.ascent = std::max(bmBase.ascent + baseMargin.top, 549 bmMultiSub.ascent - maxSubScriptShift); 550 } else { 551 boundingMetrics.ascent = std::max(bmBase.ascent + baseMargin.top, 552 (bmMultiSup.ascent + maxSupScriptShift)); 553 } 554 if (tag == nsGkAtoms::msup) { 555 boundingMetrics.descent = std::max(bmBase.descent + baseMargin.bottom, 556 bmMultiSup.descent - maxSupScriptShift); 557 } else { 558 boundingMetrics.descent = 559 std::max(bmBase.descent + baseMargin.bottom, 560 (bmMultiSub.descent + maxSubScriptShift)); 561 } 562 563 // get the reflow metrics ... 564 aDesiredSize.SetBlockStartAscent( 565 std::max(baseSize.BlockStartAscent() + baseMargin.top, 566 std::max(multiSubSize.BlockStartAscent() - maxSubScriptShift, 567 multiSupSize.BlockStartAscent() + maxSupScriptShift))); 568 aDesiredSize.Height() = 569 aDesiredSize.BlockStartAscent() + 570 std::max( 571 baseSize.Height() - baseSize.BlockStartAscent() + baseMargin.bottom, 572 std::max(multiSubSize.Height() + maxSubScriptShift, 573 multiSupSize.Height() - maxSupScriptShift)); 574 aDesiredSize.Width() = boundingMetrics.width; 575 aDesiredSize.mBoundingMetrics = boundingMetrics; 576 577 // Apply width/height to math content box. 578 auto sizes = aFrame->GetWidthAndHeightForPlaceAdjustment(aFlags); 579 aFrame->ApplyAdjustmentForWidthAndHeight(aFlags, sizes, aDesiredSize, 580 boundingMetrics); 581 582 // Add padding+border. 583 auto borderPadding = aFrame->GetBorderPaddingForPlace(aFlags); 584 InflateReflowAndBoundingMetrics(borderPadding, aDesiredSize, boundingMetrics); 585 586 aFrame->SetBoundingMetrics(boundingMetrics); 587 aFrame->SetReference(nsPoint(0, aDesiredSize.BlockStartAscent())); 588 589 ////////////////// 590 // Place Children 591 592 // Place prescripts, followed by base, and then postscripts. 593 // The list of frames is in the order: {base} {postscripts} {prescripts} 594 // We go over the list in a circular manner, starting at <prescripts/> 595 596 if (!aFlags.contains(PlaceFlag::MeasureOnly)) { 597 const bool isRTL = 598 aFrame->StyleVisibility()->mDirection == StyleDirection::Rtl; 599 nscoord dx = isRTL ? borderPadding.right : borderPadding.left; 600 nscoord dy = 0; 601 602 // With msub and msup there is only one element and 603 // subscriptFrame/supScriptFrame have already been set above where 604 // relevant. In these cases we skip to the reflow part. 605 if (tag == nsGkAtoms::msub || tag == nsGkAtoms::msup) { 606 count = 1; 607 } else { 608 count = 0; 609 } 610 childFrame = prescriptsFrame; 611 bool isPreScript = true; 612 do { 613 if (!childFrame) { // end of prescripts, 614 isPreScript = false; 615 // place the base ... 616 childFrame = baseFrame; 617 dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent(); 618 baseMargin = GetMarginForPlace(aFlags, baseFrame); 619 nscoord dx_base = dx + (isRTL ? baseMargin.right : baseMargin.left); 620 FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr, 621 aFrame->MirrorIfRTL(aDesiredSize.Width(), 622 baseSize.Width(), dx_base), 623 dy, ReflowChildFlags::Default); 624 if (prescriptsFrame) { 625 // place the <mprescripts/> 626 ReflowOutput prescriptsSize(wm); 627 nsBoundingMetrics unusedBm; 628 GetReflowAndBoundingMetricsFor(prescriptsFrame, prescriptsSize, 629 unusedBm); 630 nsMargin prescriptsMargin = 631 GetMarginForPlace(aFlags, prescriptsFrame); 632 nscoord dx_prescripts = 633 dx + (isRTL ? prescriptsMargin.right : prescriptsMargin.left); 634 dy = aDesiredSize.BlockStartAscent() - 635 prescriptsSize.BlockStartAscent(); 636 FinishReflowChild( 637 prescriptsFrame, aPresContext, prescriptsSize, nullptr, 638 aFrame->MirrorIfRTL(aDesiredSize.Width(), prescriptsSize.Width(), 639 dx_prescripts), 640 dy, ReflowChildFlags::Default); 641 } 642 dx += bmBase.width + baseMargin.LeftRight(); 643 } else if (childFrame != prescriptsFrame) { 644 // process each sup/sub pair 645 if (0 == count) { 646 subScriptFrame = childFrame; 647 count = 1; 648 } else if (1 == count) { 649 if (tag != nsGkAtoms::msub) { 650 supScriptFrame = childFrame; 651 } 652 count = 0; 653 654 // get the ascent/descent of sup/subscripts stored in their rects 655 // rect.x = descent, rect.y = ascent 656 if (subScriptFrame) { 657 GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, 658 bmSubScript); 659 subScriptMargin = GetMarginForPlace(aFlags, subScriptFrame); 660 } 661 if (supScriptFrame) { 662 GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, 663 bmSupScript); 664 supScriptMargin = GetMarginForPlace(aFlags, supScriptFrame); 665 } 666 667 width = std::max(subScriptSize.Width() + subScriptMargin.LeftRight(), 668 supScriptSize.Width() + supScriptMargin.LeftRight()); 669 670 if (subScriptFrame) { 671 nscoord x = 672 dx + (isRTL ? subScriptMargin.right : subScriptMargin.left); 673 // prescripts should be right aligned 674 // https://bugzilla.mozilla.org/show_bug.cgi?id=928675 675 if (isPreScript) { 676 x += width - subScriptSize.Width() - subScriptMargin.LeftRight(); 677 } 678 dy = aDesiredSize.BlockStartAscent() - 679 subScriptSize.BlockStartAscent() + maxSubScriptShift; 680 FinishReflowChild(subScriptFrame, aPresContext, subScriptSize, 681 nullptr, 682 aFrame->MirrorIfRTL(aDesiredSize.Width(), 683 subScriptSize.Width(), x), 684 dy, ReflowChildFlags::Default); 685 } 686 687 if (supScriptFrame) { 688 nscoord x = 689 dx + (isRTL ? supScriptMargin.right : supScriptMargin.left); 690 if (isPreScript) { 691 x += width - supScriptSize.Width() - supScriptMargin.LeftRight(); 692 } else { 693 // post superscripts are shifted by the italic correction value 694 x += italicCorrection; 695 } 696 dy = aDesiredSize.BlockStartAscent() - 697 supScriptSize.BlockStartAscent() - maxSupScriptShift; 698 FinishReflowChild(supScriptFrame, aPresContext, supScriptSize, 699 nullptr, 700 aFrame->MirrorIfRTL(aDesiredSize.Width(), 701 supScriptSize.Width(), x), 702 dy, ReflowChildFlags::Default); 703 } 704 dx += width + scriptSpace; 705 } 706 } 707 childFrame = childFrame->GetNextSibling(); 708 } while (prescriptsFrame != childFrame); 709 } 710 }