// FIXME: It's cleaner to only call updateFromElement when an attribute has changed. The body of // this method should probably be moved to a private stretchHeightChanged or checkStretchHeight // method. Probably at the same time, addChild/removeChild methods should be made to work for // dynamic DOM changes. void RenderMathMLOperator::updateFromElement() { RenderElement* savedRenderer = element().renderer(); // Destroy our current children destroyLeftoverChildren(); // Since we share a node with our children, destroying our children may set our node's // renderer to 0, so we need to restore it. element().setRenderer(savedRenderer); RefPtr<RenderStyle> newStyle = RenderStyle::create(); newStyle->inheritFrom(style()); newStyle->setDisplay(FLEX); RenderMathMLBlock* container = new RenderMathMLBlock(element()); // This container doesn't offer any useful information to accessibility. container->setIgnoreInAccessibilityTree(true); container->setStyle(newStyle.release()); addChild(container); RenderText* text; if (m_operator) text = new RenderText(document(), String(&m_operator, 1)); else text = new RenderText(document(), element().textContent().replace(hyphenMinus, minusSign).impl()); // If we can't figure out the text, leave it blank. if (text) container->addChild(text); updateStyle(); setNeedsLayoutAndPrefWidthsRecalc(); }
void RenderMathMLUnderOver::stretchToHeight(int height) { RenderObject* base = firstChild(); if (!base) return; // For over or underover, the base is the sibling of the first child if (m_kind != Under) base = base->nextSibling(); if (!base) return; // use the child of the row which is the actual base base = base->firstChild(); if (base && base->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(base); block->stretchToHeight(height); updateBoxModelInfoFromStyle(); setNeedsLayoutAndPrefWidthsRecalc(); markContainingBlocksForLayout(); } }
void RenderMathMLFraction::paint(PaintInfo& info, const LayoutPoint& paintOffset) { RenderMathMLBlock::paint(info, paintOffset); if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground) return; if (!firstChild() ||!m_lineThickness) return; LayoutUnit verticalOffset = 0; // The children are always RenderMathMLBlock instances if (firstChild()->isRenderMathMLBlock()) { int adjustForThickness = m_lineThickness > 1 ? int(m_lineThickness / 2) : 1; if (int(m_lineThickness) % 2 == 1) adjustForThickness++; RenderMathMLBlock* numerator = toRenderMathMLBlock(firstChild()); if (numerator->isRenderMathMLRow()) verticalOffset = numerator->offsetHeight() + adjustForThickness; else verticalOffset = numerator->offsetHeight(); } LayoutPoint adjustedPaintOffset = paintOffset + location(); adjustedPaintOffset.setY(adjustedPaintOffset.y() + verticalOffset); GraphicsContextStateSaver stateSaver(*info.context); info.context->setStrokeThickness(m_lineThickness); info.context->setStrokeStyle(SolidStroke); info.context->setStrokeColor(style()->visitedDependentColor(CSSPropertyColor), ColorSpaceSRGB); info.context->drawLine(adjustedPaintOffset, IntPoint(adjustedPaintOffset.x() + offsetWidth(), adjustedPaintOffset.y())); }
void RenderMathMLSubSup::stretchToHeight(int height) { RenderObject* base = firstChild(); if (!base) return; if (base->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(base); block->stretchToHeight(static_cast<int>(gSubSupStretch * height)); } if (height > 0 && m_kind == SubSup && m_scripts) { RenderObject* script = m_scripts->firstChild(); if (script) { // Calculate the script height without the container margins. RenderObject* top = script; int topHeight = getBoxModelObjectHeight(top->firstChild()); int topAdjust = topHeight / gTopAdjustDivisor; top->style()->setMarginTop(Length(-topAdjust, Fixed)); top->style()->setMarginBottom(Length(height - topHeight + topAdjust, Fixed)); if (top->isBoxModelObject()) { RenderBoxModelObject* topBox = toRenderBoxModelObject(top); topBox->updateBoxModelInfoFromStyle(); } m_scripts->setNeedsLayoutAndPrefWidthsRecalc(); m_scripts->markContainingBlocksForLayout(); } } updateBoxModelInfoFromStyle(); setNeedsLayoutAndPrefWidthsRecalc(); markContainingBlocksForLayout(); }
RenderMathMLBlock* RenderMathMLBlock::createAnonymousMathMLBlock(EDisplay display) { RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), display); RenderMathMLBlock* newBlock = new (renderArena()) RenderMathMLBlock(0); newBlock->setDocumentForAnonymous(document()); newBlock->setStyle(newStyle.release()); return newBlock; }
void RenderMathMLBlock::stretchToHeight(int height) { for (RenderObject* current = firstChild(); current; current = current->nextSibling()) if (current->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(current); block->stretchToHeight(height); } }
void RenderMathMLUnderOver::stretchToHeight(int height) { RenderBoxModelObject* base = this->base(); if (base && base->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(base); block->stretchToHeight(height); setNeedsLayout(true); } }
void RenderMathMLFraction::addChild(RenderObject* child, RenderObject* beforeChild) { RenderMathMLBlock* row = createAnonymousMathMLBlock(); RenderMathMLBlock::addChild(row, beforeChild); row->addChild(child); fixChildStyle(row); updateFromElement(); }
int RenderMathMLRow::baselinePosition(FontBaseline, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const { if (firstChild() && firstChild()->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(firstChild()); if (block->isRenderMathMLOperator()) return block->y() + block->baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode); } return RenderBlock::baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode); }
void RenderMathMLSubSup::addChild(RenderObject* child, RenderObject* beforeChild) { // Note: The RenderMathMLBlock only allows element children to be added. Element* childElement = toElement(child->node()); if (childElement && !childElement->previousElementSibling()) { // Position 1 is always the base of the msub/msup/msubsup. RenderMathMLBlock* wrapper = new (renderArena()) RenderMathMLBlock(node()); RefPtr<RenderStyle> wrapperStyle = RenderStyle::create(); wrapperStyle->inheritFrom(style()); wrapperStyle->setDisplay(INLINE_BLOCK); wrapperStyle->setVerticalAlign(BASELINE); wrapper->setStyle(wrapperStyle.release()); RenderMathMLBlock::addChild(wrapper, firstChild()); wrapper->addChild(child); // Make sure we have a script block for rendering. if (m_kind == SubSup && !m_scripts) { m_scripts = new (renderArena()) RenderMathMLBlock(node()); RefPtr<RenderStyle> scriptsStyle = RenderStyle::create(); scriptsStyle->inheritFrom(style()); scriptsStyle->setDisplay(INLINE_BLOCK); scriptsStyle->setVerticalAlign(TOP); scriptsStyle->setMarginLeft(Length(gSubsupScriptMargin, Fixed)); scriptsStyle->setTextAlign(LEFT); // Set this wrapper's font-size for its line-height & baseline position. scriptsStyle->setBlendedFontSize(static_cast<int>(0.75 * style()->fontSize())); m_scripts->setStyle(scriptsStyle.release()); RenderMathMLBlock::addChild(m_scripts, beforeChild); } } else { if (m_kind == SubSup) { ASSERT(childElement); if (!childElement) return; RenderBlock* script = new (renderArena()) RenderMathMLBlock(node()); RefPtr<RenderStyle> scriptStyle = RenderStyle::create(); scriptStyle->inheritFrom(m_scripts->style()); scriptStyle->setDisplay(BLOCK); script->setStyle(scriptStyle.release()); // The order is always backwards so the first script is the subscript and the superscript // is last. That means the superscript is the first to render vertically. Element* previousSibling = childElement->previousElementSibling(); if (previousSibling && !previousSibling->previousElementSibling()) m_scripts->addChild(script); else m_scripts->addChild(script, m_scripts->firstChild()); script->addChild(child); } else RenderMathMLBlock::addChild(child, beforeChild); } }
void RenderMathMLUnderOver::addChild(RenderObject* child, RenderObject* beforeChild) { RenderMathMLBlock* row = new (renderArena()) RenderMathMLBlock(node()); RefPtr<RenderStyle> rowStyle = makeBlockStyle(); row->setStyle(rowStyle.release()); // look through the children for rendered elements counting the blocks so we know what child // we are adding int blocks = 0; RenderObject* current = this->firstChild(); while (current) { blocks++; current = current->nextSibling(); } switch (blocks) { case 0: // this is the base so just append it RenderBlock::addChild(row, beforeChild); break; case 1: // the under or over // FIXME: text-align: center does not work row->style()->setTextAlign(CENTER); if (m_kind == Over) { // add the over as first RenderBlock::addChild(row, firstChild()); } else { // add the under as last RenderBlock::addChild(row, beforeChild); } break; case 2: // the under or over // FIXME: text-align: center does not work row->style()->setTextAlign(CENTER); if (m_kind == UnderOver) { // add the over as first RenderBlock::addChild(row, firstChild()); } else { // we really shouldn't get here as only munderover should have three children RenderBlock::addChild(row, beforeChild); } break; default: // munderover shouldn't have more than three children. In theory we shouldn't // get here if the MathML is correctly formed, but that isn't a guarantee. // We will treat this as another under element and they'll get something funky. RenderBlock::addChild(row, beforeChild); } row->addChild(child); }
int RenderMathMLUnderOver::nonOperatorHeight() const { int nonOperators = 0; for (RenderObject* current = firstChild(); current; current = current->nextSibling()) { if (current->firstChild() && current->firstChild()->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(current->firstChild()); if (!block->isRenderMathMLOperator()) nonOperators += getOffsetHeight(current); } else nonOperators += getOffsetHeight(current); } return nonOperators; }
LayoutUnit RenderMathMLFraction::baselinePosition(FontBaseline, bool firstLine, LineDirectionMode lineDirection, LinePositionMode linePositionMode) const { if (firstChild() && firstChild()->isRenderMathMLBlock()) { RenderMathMLBlock* numerator = toRenderMathMLBlock(firstChild()); RenderStyle* refStyle = style(); if (previousSibling()) refStyle = previousSibling()->style(); else if (nextSibling()) refStyle = nextSibling()->style(); int shift = int(ceil((refStyle->fontMetrics().xHeight() + 1) / 2)); return numerator->offsetHeight() + shift; } return RenderBlock::baselinePosition(AlphabeticBaseline, firstLine, lineDirection, linePositionMode); }
int RenderMathMLRow::nonOperatorHeight() const { int maxHeight = 0; for (RenderObject* current = firstChild(); current; current = current->nextSibling()) { if (current->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(current); int blockHeight = block->nonOperatorHeight(); // Check to see if this box has a larger height if (blockHeight > maxHeight) maxHeight = blockHeight; } else if (current->isBoxModelObject()) { RenderBoxModelObject* box = toRenderBoxModelObject(current); // Check to see if this box has a larger height if (box->offsetHeight() > maxHeight) maxHeight = box->offsetHeight(); } } return maxHeight; }
void RenderMathMLRow::layout() { int stretchHeightAboveBaseline = 0, stretchDepthBelowBaseline = 0; for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { if (child->needsLayout()) toRenderElement(child)->layout(); if (child->isRenderMathMLBlock()) { // We skip the stretchy operators as they must not be included in the computation of the stretch size. auto renderOperator = toRenderMathMLBlock(child)->unembellishedOperator(); if (renderOperator && renderOperator->hasOperatorFlag(MathMLOperatorDictionary::Stretchy)) continue; } LayoutUnit childHeightAboveBaseline = 0, childDepthBelowBaseline = 0; if (child->isRenderMathMLBlock()) { RenderMathMLBlock* mathmlChild = toRenderMathMLBlock(child); childHeightAboveBaseline = mathmlChild->firstLineBaseline(); if (childHeightAboveBaseline == -1) childHeightAboveBaseline = mathmlChild->logicalHeight(); childDepthBelowBaseline = mathmlChild->logicalHeight() - childHeightAboveBaseline; } else if (child->isRenderMathMLTable()) { RenderMathMLTable* tableChild = toRenderMathMLTable(child); childHeightAboveBaseline = tableChild->firstLineBaseline(); childDepthBelowBaseline = tableChild->logicalHeight() - childHeightAboveBaseline; } else if (child->isBox()) { childHeightAboveBaseline = toRenderBox(child)->logicalHeight(); childDepthBelowBaseline = 0; } stretchHeightAboveBaseline = std::max<LayoutUnit>(stretchHeightAboveBaseline, childHeightAboveBaseline); stretchDepthBelowBaseline = std::max<LayoutUnit>(stretchDepthBelowBaseline, childDepthBelowBaseline); } if (stretchHeightAboveBaseline + stretchDepthBelowBaseline <= 0) stretchHeightAboveBaseline = style().fontSize(); // Set the sizes of (possibly embellished) stretchy operator children. for (auto& child : childrenOfType<RenderMathMLBlock>(*this)) { if (auto renderOperator = child.unembellishedOperator()) renderOperator->stretchTo(stretchHeightAboveBaseline, stretchDepthBelowBaseline); } RenderMathMLBlock::layout(); }
void RenderMathMLBlock::computeChildrenPreferredLogicalHeights() { ASSERT(needsLayout()); // This is ugly, but disable fragmentation when computing the preferred heights. FragmentationDisabler fragmentationDisabler(this); // Ensure a full repaint will happen after layout finishes. setNeedsLayout(true, MarkOnlyThis); RenderView* renderView = view(); bool hadLayoutState = renderView->layoutState(); if (!hadLayoutState) renderView->pushLayoutState(this); { LayoutStateDisabler layoutStateDisabler(renderView); LayoutUnit oldAvailableLogicalWidth = availableLogicalWidth(); setLogicalWidth(cLargeLogicalWidth); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { if (!child->isBox()) continue; // Because our width changed, |child| may need layout. if (child->maxPreferredLogicalWidth() > oldAvailableLogicalWidth) child->setNeedsLayout(true, MarkOnlyThis); RenderMathMLBlock* childMathMLBlock = child->isRenderMathMLBlock() ? toRenderMathMLBlock(child) : 0; if (childMathMLBlock && !childMathMLBlock->isPreferredLogicalHeightDirty()) continue; // Layout our child to compute its preferred logical height. child->layoutIfNeeded(); if (childMathMLBlock) childMathMLBlock->setPreferredLogicalHeight(childMathMLBlock->logicalHeight()); } } if (!hadLayoutState) renderView->popLayoutState(this); }
void RenderMathMLSubSup::addChild(RenderObject* child, RenderObject* beforeChild) { if (firstChild()) { // We already have a base, so this is the super/subscripts being added. if (m_kind == SubSup) { if (!m_scripts) { m_scripts = new (renderArena()) RenderMathMLBlock(node()); RefPtr<RenderStyle> scriptsStyle = RenderStyle::create(); scriptsStyle->inheritFrom(style()); scriptsStyle->setDisplay(INLINE_BLOCK); scriptsStyle->setVerticalAlign(MIDDLE); scriptsStyle->setMarginLeft(Length(gSubsupScriptMargin, Fixed)); scriptsStyle->setTextAlign(LEFT); m_scripts->setStyle(scriptsStyle.release()); RenderMathMLBlock::addChild(m_scripts, beforeChild); } RenderBlock* script = new (renderArena()) RenderMathMLBlock(node()); RefPtr<RenderStyle> scriptStyle = RenderStyle::create(); scriptStyle->inheritFrom(m_scripts->style()); scriptStyle->setDisplay(BLOCK); script->setStyle(scriptStyle.release()); m_scripts->addChild(script, m_scripts->firstChild()); script->addChild(child); } else RenderMathMLBlock::addChild(child, beforeChild); } else { RenderMathMLBlock* wrapper = new (renderArena()) RenderMathMLBlock(node()); RefPtr<RenderStyle> wrapperStyle = RenderStyle::create(); wrapperStyle->inheritFrom(style()); wrapperStyle->setDisplay(INLINE_BLOCK); wrapperStyle->setVerticalAlign(MIDDLE); wrapper->setStyle(wrapperStyle.release()); RenderMathMLBlock::addChild(wrapper, beforeChild); wrapper->addChild(child); } }
void RenderMathMLSubSup::addChild(RenderObject* child, RenderObject* beforeChild) { // Note: The RenderMathMLBlock only allows element children to be added. Element* childElement = toElement(child->node()); if (childElement && !childElement->previousElementSibling()) { // Position 1 is always the base of the msub/msup/msubsup. RenderMathMLBlock* baseWrapper = createAnonymousMathMLBlock(INLINE_BLOCK); RenderMathMLBlock::addChild(baseWrapper, firstChild()); baseWrapper->addChild(child); // Make sure we have a script block for rendering. if (m_kind == SubSup && !m_scripts) { m_scripts = createAnonymousMathMLBlock(INLINE_BLOCK); fixScriptsStyle(); RenderMathMLBlock::addChild(m_scripts, beforeChild); } } else { if (m_kind == SubSup) { ASSERT(childElement); if (!childElement) return; RenderMathMLBlock* script = m_scripts->createAnonymousMathMLBlock(); // The order is always backwards so the first script is the subscript and the superscript // is last. That means the superscript is the first to render vertically. Element* previousSibling = childElement->previousElementSibling(); if (previousSibling && !previousSibling->previousElementSibling()) m_scripts->addChild(script); else m_scripts->addChild(script, m_scripts->firstChild()); script->addChild(child); } else RenderMathMLBlock::addChild(child, beforeChild); } }
void RenderMathMLUnderOver::stretchToHeight(int height) { RenderObject* base = firstChild(); if (!base) return; // For over or underover, the base is the sibling of the first child if (m_kind != Under) base = base->nextSibling(); if (!base) return; // use the child of the row which is the actual base base = base->firstChild(); if (base && base->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(base); block->stretchToHeight(height); setNeedsLayout(true); } }
void RenderMathMLRow::layout() { RenderBlock::layout(); int maxHeight = 0; // Calculate the non-operator max height of the row. for (RenderObject* current = firstChild(); current; current = current->nextSibling()) { if (current->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(current); if (!block->unembellishedOperator() && block->offsetHeight() > maxHeight) maxHeight = block->offsetHeight(); } else if (current->isBoxModelObject()) { RenderBoxModelObject* box = toRenderBoxModelObject(current); // Check to see if this box has a larger height. if (box->pixelSnappedOffsetHeight() > maxHeight) maxHeight = box->pixelSnappedOffsetHeight(); } } if (!maxHeight) maxHeight = style()->fontSize(); // Stretch everything to the same height (blocks can ignore the request). if (maxHeight > 0) { bool didStretch = false; for (RenderObject* current = firstChild(); current; current = current->nextSibling()) { if (current->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(current); block->stretchToHeight(maxHeight); didStretch = true; } } if (didStretch) { setNeedsLayout(true); setPreferredLogicalWidthsDirty(true, false); RenderBlock::layout(); } } }
void RenderMathMLScripts::layout() { RenderMathMLBlock::layout(); if (!m_baseWrapper) return; RenderBox* base = m_baseWrapper->firstChildBox(); if (!base) return; // Our layout rules include: Don't let the superscript go below the "axis" (half x-height above the // baseline), or the subscript above the axis. Also, don't let the superscript's top edge be // below the base's top edge, or the subscript's bottom edge above the base's bottom edge. LayoutUnit baseHeight = base->logicalHeight(); LayoutUnit baseBaseline = base->firstLineBaseline().valueOr(baseHeight); LayoutUnit axis = style().fontMetrics().xHeight() / 2; int fontSize = style().fontSize(); ASSERT(m_baseWrapper->style().hasOneRef()); bool needsSecondLayout = false; LayoutUnit topPadding = 0; LayoutUnit bottomPadding = 0; Element* scriptElement = element(); LayoutUnit superscriptShiftValue = 0; LayoutUnit subscriptShiftValue = 0; if (m_kind == Sub || m_kind == SubSup || m_kind == Multiscripts) parseMathMLLength(scriptElement->fastGetAttribute(MathMLNames::subscriptshiftAttr), subscriptShiftValue, &style(), false); if (m_kind == Super || m_kind == SubSup || m_kind == Multiscripts) parseMathMLLength(scriptElement->fastGetAttribute(MathMLNames::superscriptshiftAttr), superscriptShiftValue, &style(), false); bool isPostScript = true; RenderMathMLBlock* subSupPair = downcast<RenderMathMLBlock>(m_baseWrapper->nextSibling()); for (; subSupPair; subSupPair = downcast<RenderMathMLBlock>(subSupPair->nextSibling())) { // We skip the base and <mprescripts/> elements. if (isPrescript(*subSupPair)) { if (!isPostScript) break; isPostScript = false; continue; } if (RenderBox* superscript = m_kind == Sub ? 0 : subSupPair->lastChildBox()) { LayoutUnit superscriptHeight = superscript->logicalHeight(); LayoutUnit superscriptBaseline = superscript->firstLineBaseline().valueOr(superscriptHeight); LayoutUnit minBaseline = std::max<LayoutUnit>(fontSize / 3 + 1 + superscriptBaseline, superscriptHeight + axis + superscriptShiftValue); topPadding = std::max<LayoutUnit>(topPadding, minBaseline - baseBaseline); } if (RenderBox* subscript = m_kind == Super ? 0 : subSupPair->firstChildBox()) { LayoutUnit subscriptHeight = subscript->logicalHeight(); LayoutUnit subscriptBaseline = subscript->firstLineBaseline().valueOr(subscriptHeight); LayoutUnit baseExtendUnderBaseline = baseHeight - baseBaseline; LayoutUnit subscriptUnderItsBaseline = subscriptHeight - subscriptBaseline; LayoutUnit minExtendUnderBaseline = std::max<LayoutUnit>(fontSize / 5 + 1 + subscriptUnderItsBaseline, subscriptHeight + subscriptShiftValue - axis); bottomPadding = std::max<LayoutUnit>(bottomPadding, minExtendUnderBaseline - baseExtendUnderBaseline); } } Length newPadding(topPadding, Fixed); if (newPadding != m_baseWrapper->style().paddingTop()) { m_baseWrapper->style().setPaddingTop(newPadding); needsSecondLayout = true; } newPadding = Length(bottomPadding, Fixed); if (newPadding != m_baseWrapper->style().paddingBottom()) { m_baseWrapper->style().setPaddingBottom(newPadding); needsSecondLayout = true; } if (!needsSecondLayout) return; setNeedsLayout(MarkOnlyThis); m_baseWrapper->setChildNeedsLayout(MarkOnlyThis); RenderMathMLBlock::layout(); }
void RenderMathMLSubSup::layout() { RenderBlock::layout(); if (m_kind != SubSup || !m_scripts) return; RenderMathMLBlock* baseWrapper = toRenderMathMLBlock(firstChild()); RenderMathMLBlock* superscriptWrapper = toRenderMathMLBlock(m_scripts->firstChild()); RenderMathMLBlock* subscriptWrapper = toRenderMathMLBlock(m_scripts->lastChild()); if (!baseWrapper || !superscriptWrapper || !subscriptWrapper || superscriptWrapper == subscriptWrapper) return; LayoutUnit baseWrapperBaseline = toRenderBox(firstChild())->firstLineBoxBaseline(); LayoutUnit baseBaseline = baseWrapperBaseline - baseWrapper->paddingBefore(); LayoutUnit baseExtendUnderBaseline = baseWrapper->logicalHeight() - baseWrapperBaseline; LayoutUnit axis = style()->fontMetrics().xHeight() / 2; LayoutUnit superscriptHeight = superscriptWrapper->logicalHeight() - superscriptWrapper->paddingAfter(); LayoutUnit subscriptHeight = subscriptWrapper->logicalHeight(); // Our layout rules are: Don't let the superscript go below the "axis" (half x-height above the // baseline), or the subscript above the axis. Also, don't let the superscript's top edge be // below the base's top edge, or the subscript's bottom edge above the base's bottom edge. // // FIXME: Check any subscriptshift or superscriptshift attributes, and maybe use more sophisticated // heuristics from TeX or elsewhere. See https://bugs.webkit.org/show_bug.cgi?id=79274#c5. // Above we did scriptsStyle->setVerticalAlign(TOP) for mscripts' style, so the superscript's // top edge will equal the top edge of the base's padding. LayoutUnit basePaddingTop = superscriptHeight + axis - baseBaseline; // If basePaddingTop is positive, it's indeed the base's padding-top that we need. If it's negative, // then we should instead use its absolute value to pad the bottom of the superscript, to get the // superscript's bottom edge down to the axis. First we compute how much more we need to shift the // subscript down, once its top edge is at the axis. LayoutUnit superPaddingBottom = max<LayoutUnit>(baseExtendUnderBaseline + axis - subscriptHeight, 0); if (basePaddingTop < 0) { superPaddingBottom += -basePaddingTop; basePaddingTop = 0; } setChildNeedsLayout(true, MarkOnlyThis); baseWrapper->style()->setPaddingTop(Length(basePaddingTop, Fixed)); baseWrapper->setNeedsLayout(true, MarkOnlyThis); superscriptWrapper->style()->setPaddingBottom(Length(superPaddingBottom, Fixed)); superscriptWrapper->setNeedsLayout(true, MarkOnlyThis); m_scripts->setNeedsLayout(true, MarkOnlyThis); RenderBlock::layout(); }
void RenderMathMLRow::layout() { RenderBlock::layout(); // Calculate the maximum height of the row without the operators. int maxHeight = nonOperatorHeight(); // Set the maximum height of the row for intermediate layouts. style()->setHeight(Length(maxHeight, Fixed)); // Notify contained operators they may need to re-layout their stretched operators. // We need to keep track of the number of children and operators because a row of // operators needs some special handling. int childCount = 0; int operatorCount = 0; for (RenderObject* current = firstChild(); current; current = current->nextSibling()) { childCount++; if (current->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(current); block->stretchToHeight(maxHeight); if (block->isRenderMathMLOperator()) operatorCount++; } } // Layout the non-operators which have just been stretched. setNeedsLayoutAndPrefWidthsRecalc(); markContainingBlocksForLayout(); RenderBlock::layout(); // Make a second pass with the real height of the operators. int operatorHeight = 0; for (RenderObject* current = firstChild(); current; current = current->nextSibling()) { if (current->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(current); if (!block->hasBase() && !block->isRenderMathMLOperator()) { // Check to see if this box has a larger height. if (block->offsetHeight() > maxHeight) maxHeight = block->offsetHeight(); } if (block->isRenderMathMLOperator()) if (block->offsetHeight() > operatorHeight) operatorHeight = block->offsetHeight(); } else if (current->isBoxModelObject()) { RenderBoxModelObject* box = toRenderBoxModelObject(current); // Check to see if this box has a larger height. if (box->offsetHeight() > maxHeight) maxHeight = box->offsetHeight(); } } if (childCount > 0 && childCount == operatorCount) { // We have only operators and so set the max height to the operator height. maxHeight = operatorHeight; } int stretchHeight = maxHeight; // Stretch the operators again and re-calculate the row height. for (RenderObject* current = firstChild(); current; current = current->nextSibling()) { if (current->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(current); if (block->isRenderMathMLOperator()) { RenderMathMLOperator* mathop = toRenderMathMLOperator(block); mathop->stretchToHeight(stretchHeight); } else { block->stretchToHeight(stretchHeight); RenderBoxModelObject* box = toRenderBoxModelObject(current); // Check to see if this box has a larger height if (box->offsetHeight() > maxHeight) maxHeight = box->offsetHeight(); } } else if (current->isBoxModelObject()) { RenderBoxModelObject* box = toRenderBoxModelObject(current); // Check to see if this box has a larger height if (box->offsetHeight() > maxHeight) maxHeight = box->offsetHeight(); } } // Set the maximum height of the row based on the calculations. style()->setHeight(Length(maxHeight, Fixed)); // Do the final layout by calling our parent's layout again. setNeedsLayoutAndPrefWidthsRecalc(); markContainingBlocksForLayout(); RenderBlock::layout(); }
void RenderMathMLSubSup::layout() { RenderMathMLBlock::layout(); RenderMathMLBlock* baseWrapper = toRenderMathMLBlock(firstChild()); if (!baseWrapper || !m_scripts) return; RenderBox* base = baseWrapper->firstChildBox(); if (!base) return; // Our layout rules include: Don't let the superscript go below the "axis" (half x-height above the // baseline), or the subscript above the axis. Also, don't let the superscript's top edge be // below the base's top edge, or the subscript's bottom edge above the base's bottom edge. // // FIXME: Check any subscriptshift or superscriptshift attributes, and maybe use more sophisticated // heuristics from TeX or elsewhere. See https://bugs.webkit.org/show_bug.cgi?id=79274#c5. LayoutUnit baseHeight = base->logicalHeight(); LayoutUnit baseBaseline = base->firstLineBoxBaseline(); if (baseBaseline == -1) baseBaseline = baseHeight; LayoutUnit axis = style()->fontMetrics().xHeight() / 2; int fontSize = style()->fontSize(); ASSERT(baseWrapper->style()->hasOneRef()); bool needsSecondLayout = false; if (RenderBox* superscript = m_kind == Sub ? 0 : m_scripts->lastChildBox()) { LayoutUnit superscriptHeight = superscript->logicalHeight(); LayoutUnit superscriptBaseline = superscript->firstLineBoxBaseline(); if (superscriptBaseline == -1) superscriptBaseline = superscriptHeight; LayoutUnit minBaseline = max<LayoutUnit>(fontSize / 3 + 1 + superscriptBaseline, superscriptHeight + axis); Length newPadding = Length(max<LayoutUnit>(minBaseline - baseBaseline, 0), Fixed); if (newPadding != baseWrapper->style()->paddingTop()) { baseWrapper->style()->setPaddingTop(newPadding); needsSecondLayout = true; } } if (RenderBox* subscript = m_kind == Super ? 0 : m_scripts->firstChildBox()) { LayoutUnit subscriptHeight = subscript->logicalHeight(); LayoutUnit subscriptBaseline = subscript->firstLineBoxBaseline(); if (subscriptBaseline == -1) subscriptBaseline = subscriptHeight; LayoutUnit baseExtendUnderBaseline = baseHeight - baseBaseline; LayoutUnit subscriptUnderItsBaseline = subscriptHeight - subscriptBaseline; LayoutUnit minExtendUnderBaseline = max<LayoutUnit>(fontSize / 5 + 1 + subscriptUnderItsBaseline, subscriptHeight - axis); Length newPadding = Length(max<LayoutUnit>(minExtendUnderBaseline - baseExtendUnderBaseline, 0), Fixed); if (newPadding != baseWrapper->style()->paddingBottom()) { baseWrapper->style()->setPaddingBottom(newPadding); needsSecondLayout = true; } } if (!needsSecondLayout) return; setNeedsLayout(true, MarkOnlyThis); baseWrapper->setChildNeedsLayout(true, MarkOnlyThis); RenderMathMLBlock::layout(); }
void RenderMathMLRow::layout() { RenderBlock::layout(); LayoutUnit maxHeight = 0; int childCount = 0; int operatorCount = 0; // Calculate the non-operator max height of the row. LayoutUnit operatorHeight = 0; for (RenderObject* current = firstChild(); current; current = current->nextSibling()) { childCount++; if (current->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(current); // Check to see if the non-operator block has a greater height. if (!block->hasBase() && !block->isRenderMathMLOperator() && block->offsetHeight() > maxHeight) maxHeight = block->offsetHeight(); if (block->hasBase() && block->nonOperatorHeight() > maxHeight) maxHeight = block->nonOperatorHeight(); // If the block is an operator, capture the maximum height and increment the count. if (block->isRenderMathMLOperator()) { if (block->offsetHeight() > operatorHeight) operatorHeight = block->offsetHeight(); operatorCount++; } } else if (current->isBoxModelObject()) { RenderBoxModelObject* box = toRenderBoxModelObject(current); // Check to see if this box has a larger height. if (box->offsetHeight() > maxHeight) maxHeight = box->offsetHeight(); } } if (childCount > 0 && childCount == operatorCount) { // We have only operators and so set the max height to the operator height. maxHeight = operatorHeight; } // Stretch everything to the same height (blocks can ignore the request). if (maxHeight > 0) { bool didStretch = false; for (RenderObject* current = firstChild(); current; current = current->nextSibling()) { if (current->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(current); block->stretchToHeight(maxHeight); didStretch = true; } } if (didStretch) { setNeedsLayout(true); setPreferredLogicalWidthsDirty(true, false); RenderBlock::layout(); } } }