1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/mathml/nsMathMLmencloseFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,826 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsMathMLmencloseFrame.h" 1.10 +#include "nsPresContext.h" 1.11 +#include "nsRenderingContext.h" 1.12 +#include "nsWhitespaceTokenizer.h" 1.13 + 1.14 +#include "nsDisplayList.h" 1.15 +#include "gfxContext.h" 1.16 +#include "nsMathMLChar.h" 1.17 +#include <algorithm> 1.18 + 1.19 +// 1.20 +// <menclose> -- enclose content with a stretching symbol such 1.21 +// as a long division sign. - implementation 1.22 + 1.23 +// longdiv: 1.24 +// Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis 1.25 +// renders better with current font support. 1.26 +static const char16_t kLongDivChar = ')'; 1.27 + 1.28 +// radical: 'SQUARE ROOT' 1.29 +static const char16_t kRadicalChar = 0x221A; 1.30 + 1.31 +// updiagonalstrike 1.32 +static const uint8_t kArrowHeadSize = 10; 1.33 + 1.34 +nsIFrame* 1.35 +NS_NewMathMLmencloseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.36 +{ 1.37 + return new (aPresShell) nsMathMLmencloseFrame(aContext); 1.38 +} 1.39 + 1.40 +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame) 1.41 + 1.42 +nsMathMLmencloseFrame::nsMathMLmencloseFrame(nsStyleContext* aContext) : 1.43 + nsMathMLContainerFrame(aContext), mNotationsToDraw(0), 1.44 + mLongDivCharIndex(-1), mRadicalCharIndex(-1), mContentWidth(0) 1.45 +{ 1.46 +} 1.47 + 1.48 +nsMathMLmencloseFrame::~nsMathMLmencloseFrame() 1.49 +{ 1.50 +} 1.51 + 1.52 +nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask) 1.53 +{ 1.54 + // Is the char already allocated? 1.55 + if ((mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) || 1.56 + (mask == NOTATION_RADICAL && mRadicalCharIndex >= 0)) 1.57 + return NS_OK; 1.58 + 1.59 + // No need to track the style context given to our MathML chars. 1.60 + // The Style System will use Get/SetAdditionalStyleContext() to keep it 1.61 + // up-to-date if dynamic changes arise. 1.62 + uint32_t i = mMathMLChar.Length(); 1.63 + nsAutoString Char; 1.64 + 1.65 + if (!mMathMLChar.AppendElement()) 1.66 + return NS_ERROR_OUT_OF_MEMORY; 1.67 + 1.68 + if (mask == NOTATION_LONGDIV) { 1.69 + Char.Assign(kLongDivChar); 1.70 + mLongDivCharIndex = i; 1.71 + } else if (mask == NOTATION_RADICAL) { 1.72 + Char.Assign(kRadicalChar); 1.73 + mRadicalCharIndex = i; 1.74 + } 1.75 + 1.76 + nsPresContext *presContext = PresContext(); 1.77 + mMathMLChar[i].SetData(presContext, Char); 1.78 + ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar[i]); 1.79 + 1.80 + return NS_OK; 1.81 +} 1.82 + 1.83 +/* 1.84 + * Add a notation to draw, if the argument is the name of a known notation. 1.85 + * @param aNotation string name of a notation 1.86 + */ 1.87 +nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation) 1.88 +{ 1.89 + nsresult rv; 1.90 + 1.91 + if (aNotation.EqualsLiteral("longdiv")) { 1.92 + rv = AllocateMathMLChar(NOTATION_LONGDIV); 1.93 + NS_ENSURE_SUCCESS(rv, rv); 1.94 + mNotationsToDraw |= NOTATION_LONGDIV; 1.95 + } else if (aNotation.EqualsLiteral("actuarial")) { 1.96 + mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_TOP); 1.97 + } else if (aNotation.EqualsLiteral("radical")) { 1.98 + rv = AllocateMathMLChar(NOTATION_RADICAL); 1.99 + NS_ENSURE_SUCCESS(rv, rv); 1.100 + mNotationsToDraw |= NOTATION_RADICAL; 1.101 + } else if (aNotation.EqualsLiteral("box")) { 1.102 + mNotationsToDraw |= (NOTATION_LEFT | NOTATION_RIGHT | 1.103 + NOTATION_TOP | NOTATION_BOTTOM); 1.104 + } else if (aNotation.EqualsLiteral("roundedbox")) { 1.105 + mNotationsToDraw |= NOTATION_ROUNDEDBOX; 1.106 + } else if (aNotation.EqualsLiteral("circle")) { 1.107 + mNotationsToDraw |= NOTATION_CIRCLE; 1.108 + } else if (aNotation.EqualsLiteral("left")) { 1.109 + mNotationsToDraw |= NOTATION_LEFT; 1.110 + } else if (aNotation.EqualsLiteral("right")) { 1.111 + mNotationsToDraw |= NOTATION_RIGHT; 1.112 + } else if (aNotation.EqualsLiteral("top")) { 1.113 + mNotationsToDraw |= NOTATION_TOP; 1.114 + } else if (aNotation.EqualsLiteral("bottom")) { 1.115 + mNotationsToDraw |= NOTATION_BOTTOM; 1.116 + } else if (aNotation.EqualsLiteral("updiagonalstrike")) { 1.117 + mNotationsToDraw |= NOTATION_UPDIAGONALSTRIKE; 1.118 + } else if (aNotation.EqualsLiteral("updiagonalarrow")) { 1.119 + mNotationsToDraw |= NOTATION_UPDIAGONALARROW; 1.120 + } else if (aNotation.EqualsLiteral("downdiagonalstrike")) { 1.121 + mNotationsToDraw |= NOTATION_DOWNDIAGONALSTRIKE; 1.122 + } else if (aNotation.EqualsLiteral("verticalstrike")) { 1.123 + mNotationsToDraw |= NOTATION_VERTICALSTRIKE; 1.124 + } else if (aNotation.EqualsLiteral("horizontalstrike")) { 1.125 + mNotationsToDraw |= NOTATION_HORIZONTALSTRIKE; 1.126 + } else if (aNotation.EqualsLiteral("madruwb")) { 1.127 + mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_BOTTOM); 1.128 + } 1.129 + 1.130 + return NS_OK; 1.131 +} 1.132 + 1.133 +/* 1.134 + * Initialize the list of notations to draw 1.135 + */ 1.136 +void nsMathMLmencloseFrame::InitNotations() 1.137 +{ 1.138 + mNotationsToDraw = 0; 1.139 + mLongDivCharIndex = mRadicalCharIndex = -1; 1.140 + mMathMLChar.Clear(); 1.141 + 1.142 + nsAutoString value; 1.143 + 1.144 + if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::notation_, value)) { 1.145 + // parse the notation attribute 1.146 + nsWhitespaceTokenizer tokenizer(value); 1.147 + 1.148 + while (tokenizer.hasMoreTokens()) 1.149 + AddNotation(tokenizer.nextToken()); 1.150 + 1.151 + if (IsToDraw(NOTATION_UPDIAGONALARROW)) { 1.152 + // For <menclose notation="updiagonalstrike updiagonalarrow">, if 1.153 + // the two notations are drawn then the strike line may cause the point of 1.154 + // the arrow to be too wide. Hence we will only draw the updiagonalarrow 1.155 + // and the arrow shaft may be thought to be the updiagonalstrike. 1.156 + mNotationsToDraw &= ~NOTATION_UPDIAGONALSTRIKE; 1.157 + } 1.158 + } else { 1.159 + // default: longdiv 1.160 + if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV))) 1.161 + return; 1.162 + mNotationsToDraw = NOTATION_LONGDIV; 1.163 + } 1.164 +} 1.165 + 1.166 +NS_IMETHODIMP 1.167 +nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent) 1.168 +{ 1.169 + // let the base class get the default from our parent 1.170 + nsMathMLContainerFrame::InheritAutomaticData(aParent); 1.171 + 1.172 + mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY; 1.173 + 1.174 + InitNotations(); 1.175 + 1.176 + return NS_OK; 1.177 +} 1.178 + 1.179 +NS_IMETHODIMP 1.180 +nsMathMLmencloseFrame::TransmitAutomaticData() 1.181 +{ 1.182 + if (IsToDraw(NOTATION_RADICAL)) { 1.183 + // The TeXBook (Ch 17. p.141) says that \sqrt is cramped 1.184 + UpdatePresentationDataFromChildAt(0, -1, 1.185 + NS_MATHML_COMPRESSED, 1.186 + NS_MATHML_COMPRESSED); 1.187 + } 1.188 + 1.189 + return NS_OK; 1.190 +} 1.191 + 1.192 +void 1.193 +nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.194 + const nsRect& aDirtyRect, 1.195 + const nsDisplayListSet& aLists) 1.196 +{ 1.197 + ///////////// 1.198 + // paint the menclosed content 1.199 + nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); 1.200 + 1.201 + if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) 1.202 + return; 1.203 + 1.204 + nsRect mencloseRect = nsIFrame::GetRect(); 1.205 + mencloseRect.x = mencloseRect.y = 0; 1.206 + 1.207 + if (IsToDraw(NOTATION_RADICAL)) { 1.208 + mMathMLChar[mRadicalCharIndex].Display(aBuilder, this, aLists, 0); 1.209 + 1.210 + nsRect rect; 1.211 + mMathMLChar[mRadicalCharIndex].GetRect(rect); 1.212 + rect.MoveBy(StyleVisibility()->mDirection ? -mContentWidth : rect.width, 0); 1.213 + rect.SizeTo(mContentWidth, mRuleThickness); 1.214 + DisplayBar(aBuilder, this, rect, aLists); 1.215 + } 1.216 + 1.217 + if (IsToDraw(NOTATION_LONGDIV)) { 1.218 + mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1); 1.219 + 1.220 + nsRect rect; 1.221 + mMathMLChar[mLongDivCharIndex].GetRect(rect); 1.222 + rect.SizeTo(rect.width + mContentWidth, mRuleThickness); 1.223 + DisplayBar(aBuilder, this, rect, aLists); 1.224 + } 1.225 + 1.226 + if (IsToDraw(NOTATION_TOP)) { 1.227 + nsRect rect(0, 0, mencloseRect.width, mRuleThickness); 1.228 + DisplayBar(aBuilder, this, rect, aLists); 1.229 + } 1.230 + 1.231 + if (IsToDraw(NOTATION_BOTTOM)) { 1.232 + nsRect rect(0, mencloseRect.height - mRuleThickness, 1.233 + mencloseRect.width, mRuleThickness); 1.234 + DisplayBar(aBuilder, this, rect, aLists); 1.235 + } 1.236 + 1.237 + if (IsToDraw(NOTATION_LEFT)) { 1.238 + nsRect rect(0, 0, mRuleThickness, mencloseRect.height); 1.239 + DisplayBar(aBuilder, this, rect, aLists); 1.240 + } 1.241 + 1.242 + if (IsToDraw(NOTATION_RIGHT)) { 1.243 + nsRect rect(mencloseRect.width - mRuleThickness, 0, 1.244 + mRuleThickness, mencloseRect.height); 1.245 + DisplayBar(aBuilder, this, rect, aLists); 1.246 + } 1.247 + 1.248 + if (IsToDraw(NOTATION_ROUNDEDBOX)) { 1.249 + DisplayNotation(aBuilder, this, mencloseRect, aLists, 1.250 + mRuleThickness, NOTATION_ROUNDEDBOX); 1.251 + } 1.252 + 1.253 + if (IsToDraw(NOTATION_CIRCLE)) { 1.254 + DisplayNotation(aBuilder, this, mencloseRect, aLists, 1.255 + mRuleThickness, NOTATION_CIRCLE); 1.256 + } 1.257 + 1.258 + if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) { 1.259 + DisplayNotation(aBuilder, this, mencloseRect, aLists, 1.260 + mRuleThickness, NOTATION_UPDIAGONALSTRIKE); 1.261 + } 1.262 + 1.263 + if (IsToDraw(NOTATION_UPDIAGONALARROW)) { 1.264 + DisplayNotation(aBuilder, this, mencloseRect, aLists, 1.265 + mRuleThickness, NOTATION_UPDIAGONALARROW); 1.266 + } 1.267 + 1.268 + if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) { 1.269 + DisplayNotation(aBuilder, this, mencloseRect, aLists, 1.270 + mRuleThickness, NOTATION_DOWNDIAGONALSTRIKE); 1.271 + } 1.272 + 1.273 + if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) { 1.274 + nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2, 1.275 + mencloseRect.width, mRuleThickness); 1.276 + DisplayBar(aBuilder, this, rect, aLists); 1.277 + } 1.278 + 1.279 + if (IsToDraw(NOTATION_VERTICALSTRIKE)) { 1.280 + nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0, 1.281 + mRuleThickness, mencloseRect.height); 1.282 + DisplayBar(aBuilder, this, rect, aLists); 1.283 + } 1.284 +} 1.285 + 1.286 +/* virtual */ nsresult 1.287 +nsMathMLmencloseFrame::MeasureForWidth(nsRenderingContext& aRenderingContext, 1.288 + nsHTMLReflowMetrics& aDesiredSize) 1.289 +{ 1.290 + return PlaceInternal(aRenderingContext, false, aDesiredSize, true); 1.291 +} 1.292 + 1.293 +/* virtual */ nsresult 1.294 +nsMathMLmencloseFrame::Place(nsRenderingContext& aRenderingContext, 1.295 + bool aPlaceOrigin, 1.296 + nsHTMLReflowMetrics& aDesiredSize) 1.297 +{ 1.298 + return PlaceInternal(aRenderingContext, aPlaceOrigin, aDesiredSize, false); 1.299 +} 1.300 + 1.301 +/* virtual */ nsresult 1.302 +nsMathMLmencloseFrame::PlaceInternal(nsRenderingContext& aRenderingContext, 1.303 + bool aPlaceOrigin, 1.304 + nsHTMLReflowMetrics& aDesiredSize, 1.305 + bool aWidthOnly) 1.306 +{ 1.307 + /////////////// 1.308 + // Measure the size of our content using the base class to format like an 1.309 + // inferred mrow. 1.310 + nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode()); 1.311 + nsresult rv = 1.312 + nsMathMLContainerFrame::Place(aRenderingContext, false, baseSize); 1.313 + 1.314 + if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { 1.315 + DidReflowChildren(GetFirstPrincipalChild()); 1.316 + return rv; 1.317 + } 1.318 + 1.319 + nsBoundingMetrics bmBase = baseSize.mBoundingMetrics; 1.320 + nscoord dx_left = 0, dx_right = 0; 1.321 + nsBoundingMetrics bmLongdivChar, bmRadicalChar; 1.322 + nscoord radicalAscent = 0, radicalDescent = 0; 1.323 + nscoord longdivAscent = 0, longdivDescent = 0; 1.324 + nscoord psi = 0; 1.325 + 1.326 + /////////////// 1.327 + // Thickness of bars and font metrics 1.328 + nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); 1.329 + 1.330 + nscoord mEmHeight; 1.331 + nsRefPtr<nsFontMetrics> fm; 1.332 + nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); 1.333 + aRenderingContext.SetFont(fm); 1.334 + GetRuleThickness(aRenderingContext, fm, mRuleThickness); 1.335 + GetEmHeight(fm, mEmHeight); 1.336 + 1.337 + char16_t one = '1'; 1.338 + nsBoundingMetrics bmOne = aRenderingContext.GetBoundingMetrics(&one, 1); 1.339 + 1.340 + /////////////// 1.341 + // General rules: the menclose element takes the size of the enclosed content. 1.342 + // We add a padding when needed. 1.343 + 1.344 + // determine padding & psi 1.345 + nscoord padding = 3 * mRuleThickness; 1.346 + nscoord delta = padding % onePixel; 1.347 + if (delta) 1.348 + padding += onePixel - delta; // round up 1.349 + 1.350 + if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { 1.351 + nscoord phi; 1.352 + // Rule 11, App. G, TeXbook 1.353 + // psi = clearance between rule and content 1.354 + if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) 1.355 + phi = fm->XHeight(); 1.356 + else 1.357 + phi = mRuleThickness; 1.358 + psi = mRuleThickness + phi / 4; 1.359 + 1.360 + delta = psi % onePixel; 1.361 + if (delta) 1.362 + psi += onePixel - delta; // round up 1.363 + } 1.364 + 1.365 + if (mRuleThickness < onePixel) 1.366 + mRuleThickness = onePixel; 1.367 + 1.368 + // Set horizontal parameters 1.369 + if (IsToDraw(NOTATION_ROUNDEDBOX) || 1.370 + IsToDraw(NOTATION_TOP) || 1.371 + IsToDraw(NOTATION_LEFT) || 1.372 + IsToDraw(NOTATION_BOTTOM) || 1.373 + IsToDraw(NOTATION_CIRCLE)) 1.374 + dx_left = padding; 1.375 + 1.376 + if (IsToDraw(NOTATION_ROUNDEDBOX) || 1.377 + IsToDraw(NOTATION_TOP) || 1.378 + IsToDraw(NOTATION_RIGHT) || 1.379 + IsToDraw(NOTATION_BOTTOM) || 1.380 + IsToDraw(NOTATION_CIRCLE)) 1.381 + dx_right = padding; 1.382 + 1.383 + // Set vertical parameters 1.384 + if (IsToDraw(NOTATION_RIGHT) || 1.385 + IsToDraw(NOTATION_LEFT) || 1.386 + IsToDraw(NOTATION_UPDIAGONALSTRIKE) || 1.387 + IsToDraw(NOTATION_UPDIAGONALARROW) || 1.388 + IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || 1.389 + IsToDraw(NOTATION_VERTICALSTRIKE) || 1.390 + IsToDraw(NOTATION_CIRCLE) || 1.391 + IsToDraw(NOTATION_ROUNDEDBOX) || 1.392 + IsToDraw(NOTATION_RADICAL) || 1.393 + IsToDraw(NOTATION_LONGDIV)) { 1.394 + // set a minimal value for the base height 1.395 + bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent); 1.396 + bmBase.descent = std::max(0, bmBase.descent); 1.397 + } 1.398 + 1.399 + mBoundingMetrics.ascent = bmBase.ascent; 1.400 + mBoundingMetrics.descent = bmBase.descent; 1.401 + 1.402 + if (IsToDraw(NOTATION_ROUNDEDBOX) || 1.403 + IsToDraw(NOTATION_TOP) || 1.404 + IsToDraw(NOTATION_LEFT) || 1.405 + IsToDraw(NOTATION_RIGHT) || 1.406 + IsToDraw(NOTATION_CIRCLE)) 1.407 + mBoundingMetrics.ascent += padding; 1.408 + 1.409 + if (IsToDraw(NOTATION_ROUNDEDBOX) || 1.410 + IsToDraw(NOTATION_LEFT) || 1.411 + IsToDraw(NOTATION_RIGHT) || 1.412 + IsToDraw(NOTATION_BOTTOM) || 1.413 + IsToDraw(NOTATION_CIRCLE)) 1.414 + mBoundingMetrics.descent += padding; 1.415 + 1.416 + /////////////// 1.417 + // updiagonal arrow notation. We need enough space at the top right corner to 1.418 + // draw the arrow head. 1.419 + if (IsToDraw(NOTATION_UPDIAGONALARROW)) { 1.420 + // This is an estimate, see nsDisplayNotation::Paint for the exact head size 1.421 + nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness; 1.422 + 1.423 + // We want that the arrow shaft strikes the menclose content and that the 1.424 + // arrow head does not overlap with that content. Hence we add some space 1.425 + // on the right. We don't add space on the top but only ensure that the 1.426 + // ascent is large enough. 1.427 + dx_right = std::max(dx_right, arrowHeadSize); 1.428 + mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize); 1.429 + } 1.430 + 1.431 + /////////////// 1.432 + // circle notation: we don't want the ellipse to overlap the enclosed 1.433 + // content. Hence, we need to increase the size of the bounding box by a 1.434 + // factor of at least sqrt(2). 1.435 + if (IsToDraw(NOTATION_CIRCLE)) { 1.436 + double ratio = (sqrt(2.0) - 1.0) / 2.0; 1.437 + nscoord padding2; 1.438 + 1.439 + // Update horizontal parameters 1.440 + padding2 = ratio * bmBase.width; 1.441 + 1.442 + dx_left = std::max(dx_left, padding2); 1.443 + dx_right = std::max(dx_right, padding2); 1.444 + 1.445 + // Update vertical parameters 1.446 + padding2 = ratio * (bmBase.ascent + bmBase.descent); 1.447 + 1.448 + mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, 1.449 + bmBase.ascent + padding2); 1.450 + mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, 1.451 + bmBase.descent + padding2); 1.452 + } 1.453 + 1.454 + /////////////// 1.455 + // longdiv notation: 1.456 + if (IsToDraw(NOTATION_LONGDIV)) { 1.457 + if (aWidthOnly) { 1.458 + nscoord longdiv_width = mMathMLChar[mLongDivCharIndex]. 1.459 + GetMaxWidth(PresContext(), aRenderingContext); 1.460 + 1.461 + // Update horizontal parameters 1.462 + dx_left = std::max(dx_left, longdiv_width); 1.463 + } else { 1.464 + // Stretch the parenthesis to the appropriate height if it is not 1.465 + // big enough. 1.466 + nsBoundingMetrics contSize = bmBase; 1.467 + contSize.ascent = mRuleThickness; 1.468 + contSize.descent = bmBase.ascent + bmBase.descent + psi; 1.469 + 1.470 + // height(longdiv) should be >= height(base) + psi + mRuleThickness 1.471 + mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aRenderingContext, 1.472 + NS_STRETCH_DIRECTION_VERTICAL, 1.473 + contSize, bmLongdivChar, 1.474 + NS_STRETCH_LARGER, false); 1.475 + mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar); 1.476 + 1.477 + // Update horizontal parameters 1.478 + dx_left = std::max(dx_left, bmLongdivChar.width); 1.479 + 1.480 + // Update vertical parameters 1.481 + longdivAscent = bmBase.ascent + psi + mRuleThickness; 1.482 + longdivDescent = std::max(bmBase.descent, 1.483 + (bmLongdivChar.ascent + bmLongdivChar.descent - 1.484 + longdivAscent)); 1.485 + 1.486 + mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, 1.487 + longdivAscent); 1.488 + mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, 1.489 + longdivDescent); 1.490 + } 1.491 + } 1.492 + 1.493 + /////////////// 1.494 + // radical notation: 1.495 + if (IsToDraw(NOTATION_RADICAL)) { 1.496 + nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left; 1.497 + 1.498 + if (aWidthOnly) { 1.499 + nscoord radical_width = mMathMLChar[mRadicalCharIndex]. 1.500 + GetMaxWidth(PresContext(), aRenderingContext); 1.501 + 1.502 + // Update horizontal parameters 1.503 + *dx_leading = std::max(*dx_leading, radical_width); 1.504 + } else { 1.505 + // Stretch the radical symbol to the appropriate height if it is not 1.506 + // big enough. 1.507 + nsBoundingMetrics contSize = bmBase; 1.508 + contSize.ascent = mRuleThickness; 1.509 + contSize.descent = bmBase.ascent + bmBase.descent + psi; 1.510 + 1.511 + // height(radical) should be >= height(base) + psi + mRuleThickness 1.512 + mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aRenderingContext, 1.513 + NS_STRETCH_DIRECTION_VERTICAL, 1.514 + contSize, bmRadicalChar, 1.515 + NS_STRETCH_LARGER, 1.516 + StyleVisibility()->mDirection); 1.517 + mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar); 1.518 + 1.519 + // Update horizontal parameters 1.520 + *dx_leading = std::max(*dx_leading, bmRadicalChar.width); 1.521 + 1.522 + // Update vertical parameters 1.523 + radicalAscent = bmBase.ascent + psi + mRuleThickness; 1.524 + radicalDescent = std::max(bmBase.descent, 1.525 + (bmRadicalChar.ascent + bmRadicalChar.descent - 1.526 + radicalAscent)); 1.527 + 1.528 + mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, 1.529 + radicalAscent); 1.530 + mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, 1.531 + radicalDescent); 1.532 + } 1.533 + } 1.534 + 1.535 + /////////////// 1.536 + // 1.537 + if (IsToDraw(NOTATION_CIRCLE) || 1.538 + IsToDraw(NOTATION_ROUNDEDBOX) || 1.539 + (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) { 1.540 + // center the menclose around the content (horizontally) 1.541 + dx_left = dx_right = std::max(dx_left, dx_right); 1.542 + } 1.543 + 1.544 + /////////////// 1.545 + // The maximum size is now computed: set the remaining parameters 1.546 + mBoundingMetrics.width = dx_left + bmBase.width + dx_right; 1.547 + 1.548 + mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing); 1.549 + mBoundingMetrics.rightBearing = 1.550 + std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing); 1.551 + 1.552 + aDesiredSize.Width() = mBoundingMetrics.width; 1.553 + 1.554 + aDesiredSize.SetTopAscent(std::max(mBoundingMetrics.ascent, baseSize.TopAscent())); 1.555 + aDesiredSize.Height() = aDesiredSize.TopAscent() + 1.556 + std::max(mBoundingMetrics.descent, baseSize.Height() - baseSize.TopAscent()); 1.557 + 1.558 + if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { 1.559 + // get the leading to be left at the top of the resulting frame 1.560 + // this seems more reliable than using fm->GetLeading() on suspicious 1.561 + // fonts 1.562 + nscoord leading = nscoord(0.2f * mEmHeight); 1.563 + nscoord desiredSizeAscent = aDesiredSize.TopAscent(); 1.564 + nscoord desiredSizeDescent = aDesiredSize.Height() - aDesiredSize.TopAscent(); 1.565 + 1.566 + if (IsToDraw(NOTATION_LONGDIV)) { 1.567 + desiredSizeAscent = std::max(desiredSizeAscent, 1.568 + longdivAscent + leading); 1.569 + desiredSizeDescent = std::max(desiredSizeDescent, 1.570 + longdivDescent + mRuleThickness); 1.571 + } 1.572 + 1.573 + if (IsToDraw(NOTATION_RADICAL)) { 1.574 + desiredSizeAscent = std::max(desiredSizeAscent, 1.575 + radicalAscent + leading); 1.576 + desiredSizeDescent = std::max(desiredSizeDescent, 1.577 + radicalDescent + mRuleThickness); 1.578 + } 1.579 + 1.580 + aDesiredSize.SetTopAscent(desiredSizeAscent); 1.581 + aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent; 1.582 + } 1.583 + 1.584 + if (IsToDraw(NOTATION_CIRCLE) || 1.585 + IsToDraw(NOTATION_ROUNDEDBOX) || 1.586 + (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) { 1.587 + // center the menclose around the content (vertically) 1.588 + nscoord dy = std::max(aDesiredSize.TopAscent() - bmBase.ascent, 1.589 + aDesiredSize.Height() - aDesiredSize.TopAscent() - 1.590 + bmBase.descent); 1.591 + 1.592 + aDesiredSize.SetTopAscent(bmBase.ascent + dy); 1.593 + aDesiredSize.Height() = aDesiredSize.TopAscent() + bmBase.descent + dy; 1.594 + } 1.595 + 1.596 + // Update mBoundingMetrics ascent/descent 1.597 + if (IsToDraw(NOTATION_TOP) || 1.598 + IsToDraw(NOTATION_RIGHT) || 1.599 + IsToDraw(NOTATION_LEFT) || 1.600 + IsToDraw(NOTATION_UPDIAGONALSTRIKE) || 1.601 + IsToDraw(NOTATION_UPDIAGONALARROW) || 1.602 + IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || 1.603 + IsToDraw(NOTATION_VERTICALSTRIKE) || 1.604 + IsToDraw(NOTATION_CIRCLE) || 1.605 + IsToDraw(NOTATION_ROUNDEDBOX)) 1.606 + mBoundingMetrics.ascent = aDesiredSize.TopAscent(); 1.607 + 1.608 + if (IsToDraw(NOTATION_BOTTOM) || 1.609 + IsToDraw(NOTATION_RIGHT) || 1.610 + IsToDraw(NOTATION_LEFT) || 1.611 + IsToDraw(NOTATION_UPDIAGONALSTRIKE) || 1.612 + IsToDraw(NOTATION_UPDIAGONALARROW) || 1.613 + IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || 1.614 + IsToDraw(NOTATION_VERTICALSTRIKE) || 1.615 + IsToDraw(NOTATION_CIRCLE) || 1.616 + IsToDraw(NOTATION_ROUNDEDBOX)) 1.617 + mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.TopAscent(); 1.618 + 1.619 + aDesiredSize.mBoundingMetrics = mBoundingMetrics; 1.620 + 1.621 + mReference.x = 0; 1.622 + mReference.y = aDesiredSize.TopAscent(); 1.623 + 1.624 + if (aPlaceOrigin) { 1.625 + ////////////////// 1.626 + // Set position and size of MathMLChars 1.627 + if (IsToDraw(NOTATION_LONGDIV)) 1.628 + mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left - 1.629 + bmLongdivChar.width, 1.630 + aDesiredSize.TopAscent() - 1.631 + longdivAscent, 1.632 + bmLongdivChar.width, 1.633 + bmLongdivChar.ascent + 1.634 + bmLongdivChar.descent)); 1.635 + 1.636 + if (IsToDraw(NOTATION_RADICAL)) { 1.637 + nscoord dx = (StyleVisibility()->mDirection ? 1.638 + dx_left + bmBase.width : dx_left - bmRadicalChar.width); 1.639 + 1.640 + mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx, 1.641 + aDesiredSize.TopAscent() - 1.642 + radicalAscent, 1.643 + bmRadicalChar.width, 1.644 + bmRadicalChar.ascent + 1.645 + bmRadicalChar.descent)); 1.646 + } 1.647 + 1.648 + mContentWidth = bmBase.width; 1.649 + 1.650 + ////////////////// 1.651 + // Finish reflowing child frames 1.652 + PositionRowChildFrames(dx_left, aDesiredSize.TopAscent()); 1.653 + } 1.654 + 1.655 + return NS_OK; 1.656 +} 1.657 + 1.658 +nscoord 1.659 +nsMathMLmencloseFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize) 1.660 +{ 1.661 + nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize); 1.662 + if (!gap) 1.663 + return 0; 1.664 + 1.665 + // Move the MathML characters 1.666 + nsRect rect; 1.667 + for (uint32_t i = 0; i < mMathMLChar.Length(); i++) { 1.668 + mMathMLChar[i].GetRect(rect); 1.669 + rect.MoveBy(gap, 0); 1.670 + mMathMLChar[i].SetRect(rect); 1.671 + } 1.672 + 1.673 + return gap; 1.674 +} 1.675 + 1.676 +nsresult 1.677 +nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID, 1.678 + nsIAtom* aAttribute, 1.679 + int32_t aModType) 1.680 +{ 1.681 + if (aAttribute == nsGkAtoms::notation_) { 1.682 + InitNotations(); 1.683 + } 1.684 + 1.685 + return nsMathMLContainerFrame:: 1.686 + AttributeChanged(aNameSpaceID, aAttribute, aModType); 1.687 +} 1.688 + 1.689 +////////////////// 1.690 +// the Style System will use these to pass the proper style context to our 1.691 +// MathMLChar 1.692 +nsStyleContext* 1.693 +nsMathMLmencloseFrame::GetAdditionalStyleContext(int32_t aIndex) const 1.694 +{ 1.695 + int32_t len = mMathMLChar.Length(); 1.696 + if (aIndex >= 0 && aIndex < len) 1.697 + return mMathMLChar[aIndex].GetStyleContext(); 1.698 + else 1.699 + return nullptr; 1.700 +} 1.701 + 1.702 +void 1.703 +nsMathMLmencloseFrame::SetAdditionalStyleContext(int32_t aIndex, 1.704 + nsStyleContext* aStyleContext) 1.705 +{ 1.706 + int32_t len = mMathMLChar.Length(); 1.707 + if (aIndex >= 0 && aIndex < len) 1.708 + mMathMLChar[aIndex].SetStyleContext(aStyleContext); 1.709 +} 1.710 + 1.711 +class nsDisplayNotation : public nsDisplayItem 1.712 +{ 1.713 +public: 1.714 + nsDisplayNotation(nsDisplayListBuilder* aBuilder, 1.715 + nsIFrame* aFrame, const nsRect& aRect, 1.716 + nscoord aThickness, nsMencloseNotation aType) 1.717 + : nsDisplayItem(aBuilder, aFrame), mRect(aRect), 1.718 + mThickness(aThickness), mType(aType) { 1.719 + MOZ_COUNT_CTOR(nsDisplayNotation); 1.720 + } 1.721 +#ifdef NS_BUILD_REFCNT_LOGGING 1.722 + virtual ~nsDisplayNotation() { 1.723 + MOZ_COUNT_DTOR(nsDisplayNotation); 1.724 + } 1.725 +#endif 1.726 + 1.727 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.728 + nsRenderingContext* aCtx) MOZ_OVERRIDE; 1.729 + NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION) 1.730 + 1.731 +private: 1.732 + nsRect mRect; 1.733 + nscoord mThickness; 1.734 + nsMencloseNotation mType; 1.735 +}; 1.736 + 1.737 +void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder, 1.738 + nsRenderingContext* aCtx) 1.739 +{ 1.740 + // get the gfxRect 1.741 + nsPresContext* presContext = mFrame->PresContext(); 1.742 + gfxRect rect = presContext->AppUnitsToGfxUnits(mRect + ToReferenceFrame()); 1.743 + 1.744 + // paint the frame with the current text color 1.745 + aCtx->SetColor(mFrame->GetVisitedDependentColor(eCSSProperty_color)); 1.746 + 1.747 + // change line width to mThickness 1.748 + gfxContext *gfxCtx = aCtx->ThebesContext(); 1.749 + gfxFloat e = presContext->AppUnitsToGfxUnits(mThickness); 1.750 + gfxCtx->Save(); 1.751 + gfxCtx->SetLineWidth(e); 1.752 + 1.753 + rect.Deflate(e / 2.0); 1.754 + 1.755 + switch(mType) 1.756 + { 1.757 + case NOTATION_CIRCLE: 1.758 + gfxCtx->NewPath(); 1.759 + gfxCtx->Ellipse(rect.Center(), rect.Size()); 1.760 + gfxCtx->Stroke(); 1.761 + break; 1.762 + 1.763 + case NOTATION_ROUNDEDBOX: 1.764 + gfxCtx->NewPath(); 1.765 + gfxCtx->RoundedRectangle(rect, gfxCornerSizes(3 * e), true); 1.766 + gfxCtx->Stroke(); 1.767 + break; 1.768 + 1.769 + case NOTATION_UPDIAGONALSTRIKE: 1.770 + gfxCtx->NewPath(); 1.771 + gfxCtx->Line(rect.BottomLeft(), rect.TopRight()); 1.772 + gfxCtx->Stroke(); 1.773 + break; 1.774 + 1.775 + case NOTATION_DOWNDIAGONALSTRIKE: 1.776 + gfxCtx->NewPath(); 1.777 + gfxCtx->Line(rect.TopLeft(), rect.BottomRight()); 1.778 + gfxCtx->Stroke(); 1.779 + break; 1.780 + 1.781 + case NOTATION_UPDIAGONALARROW: { 1.782 + // Compute some parameters to draw the updiagonalarrow. The values below 1.783 + // are taken from MathJax's HTML-CSS output. 1.784 + gfxFloat W = rect.Width(); gfxFloat H = rect.Height(); 1.785 + gfxFloat l = sqrt(W*W + H*H); 1.786 + gfxFloat f = gfxFloat(kArrowHeadSize) * e / l; 1.787 + gfxFloat w = W * f; gfxFloat h = H * f; 1.788 + 1.789 + // Draw the arrow shaft 1.790 + gfxCtx->NewPath(); 1.791 + gfxCtx->Line(rect.BottomLeft(), rect.TopRight() + gfxPoint(-.7*w, .7*h)); 1.792 + gfxCtx->Stroke(); 1.793 + 1.794 + // Draw the arrow head 1.795 + gfxCtx->NewPath(); 1.796 + gfxPoint p[] = { 1.797 + rect.TopRight(), 1.798 + rect.TopRight() + gfxPoint(-w -.4*h, std::max(-e / 2.0, h - .4*w)), 1.799 + rect.TopRight() + gfxPoint(-.7*w, .7*h), 1.800 + rect.TopRight() + gfxPoint(std::min(e / 2.0, -w + .4*h), h + .4*w), 1.801 + rect.TopRight() 1.802 + }; 1.803 + gfxCtx->Polygon(p, MOZ_ARRAY_LENGTH(p)); 1.804 + gfxCtx->Fill(); 1.805 + } 1.806 + break; 1.807 + 1.808 + default: 1.809 + NS_NOTREACHED("This notation can not be drawn using nsDisplayNotation"); 1.810 + break; 1.811 + } 1.812 + 1.813 + gfxCtx->Restore(); 1.814 +} 1.815 + 1.816 +void 1.817 +nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder, 1.818 + nsIFrame* aFrame, const nsRect& aRect, 1.819 + const nsDisplayListSet& aLists, 1.820 + nscoord aThickness, 1.821 + nsMencloseNotation aType) 1.822 +{ 1.823 + if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() || 1.824 + aThickness <= 0) 1.825 + return; 1.826 + 1.827 + aLists.Content()->AppendNewToTop(new (aBuilder) 1.828 + nsDisplayNotation(aBuilder, aFrame, aRect, aThickness, aType)); 1.829 +}