1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/mathml/nsMathMLmrootFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,413 @@ 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 "nsMathMLmrootFrame.h" 1.10 +#include "nsPresContext.h" 1.11 +#include "nsRenderingContext.h" 1.12 +#include <algorithm> 1.13 + 1.14 +// 1.15 +// <msqrt> and <mroot> -- form a radical - implementation 1.16 +// 1.17 + 1.18 +//NOTE: 1.19 +// The code assumes that TeX fonts are picked. 1.20 +// There is no fall-back to draw the branches of the sqrt explicitly 1.21 +// in the case where TeX fonts are not there. In general, there are no 1.22 +// fall-back(s) in MathML when some (freely-downloadable) fonts are missing. 1.23 +// Otherwise, this will add much work and unnecessary complexity to the core 1.24 +// MathML engine. Assuming that authors have the free fonts is part of the 1.25 +// deal. We are not responsible for cases of misconfigurations out there. 1.26 + 1.27 +// additional style context to be used by our MathMLChar. 1.28 +#define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0 1.29 + 1.30 +static const char16_t kSqrChar = char16_t(0x221A); 1.31 + 1.32 +nsIFrame* 1.33 +NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.34 +{ 1.35 + return new (aPresShell) nsMathMLmrootFrame(aContext); 1.36 +} 1.37 + 1.38 +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame) 1.39 + 1.40 +nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext* aContext) : 1.41 + nsMathMLContainerFrame(aContext), 1.42 + mSqrChar(), 1.43 + mBarRect() 1.44 +{ 1.45 +} 1.46 + 1.47 +nsMathMLmrootFrame::~nsMathMLmrootFrame() 1.48 +{ 1.49 +} 1.50 + 1.51 +void 1.52 +nsMathMLmrootFrame::Init(nsIContent* aContent, 1.53 + nsIFrame* aParent, 1.54 + nsIFrame* aPrevInFlow) 1.55 +{ 1.56 + nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow); 1.57 + 1.58 + nsPresContext *presContext = PresContext(); 1.59 + 1.60 + // No need to track the style context given to our MathML char. 1.61 + // The Style System will use Get/SetAdditionalStyleContext() to keep it 1.62 + // up-to-date if dynamic changes arise. 1.63 + nsAutoString sqrChar; sqrChar.Assign(kSqrChar); 1.64 + mSqrChar.SetData(presContext, sqrChar); 1.65 + ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mSqrChar); 1.66 +} 1.67 + 1.68 +NS_IMETHODIMP 1.69 +nsMathMLmrootFrame::TransmitAutomaticData() 1.70 +{ 1.71 + // 1. The REC says: 1.72 + // The <mroot> element increments scriptlevel by 2, and sets displaystyle to 1.73 + // "false", within index, but leaves both attributes unchanged within base. 1.74 + // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed 1.75 + UpdatePresentationDataFromChildAt(1, 1, 1.76 + NS_MATHML_COMPRESSED, 1.77 + NS_MATHML_COMPRESSED); 1.78 + UpdatePresentationDataFromChildAt(0, 0, 1.79 + NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED); 1.80 + 1.81 + PropagateFrameFlagFor(mFrames.LastChild(), 1.82 + NS_FRAME_MATHML_SCRIPT_DESCENDANT); 1.83 + 1.84 + return NS_OK; 1.85 +} 1.86 + 1.87 +void 1.88 +nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.89 + const nsRect& aDirtyRect, 1.90 + const nsDisplayListSet& aLists) 1.91 +{ 1.92 + ///////////// 1.93 + // paint the content we are square-rooting 1.94 + nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); 1.95 + 1.96 + ///////////// 1.97 + // paint the sqrt symbol 1.98 + if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) { 1.99 + mSqrChar.Display(aBuilder, this, aLists, 0); 1.100 + 1.101 + DisplayBar(aBuilder, this, mBarRect, aLists); 1.102 + 1.103 +#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX) 1.104 + // for visual debug 1.105 + nsRect rect; 1.106 + mSqrChar.GetRect(rect); 1.107 + nsBoundingMetrics bm; 1.108 + mSqrChar.GetBoundingMetrics(bm); 1.109 + DisplayBoundingMetrics(aBuilder, this, rect.TopLeft(), bm, aLists); 1.110 +#endif 1.111 + } 1.112 +} 1.113 + 1.114 +static void 1.115 +GetRadicalXOffsets(nscoord aIndexWidth, nscoord aSqrWidth, 1.116 + nsFontMetrics* aFontMetrics, 1.117 + nscoord* aIndexOffset, nscoord* aSqrOffset) 1.118 +{ 1.119 + // The index is tucked in closer to the radical while making sure 1.120 + // that the kern does not make the index and radical collide 1.121 + nscoord dxIndex, dxSqr; 1.122 + nscoord xHeight = aFontMetrics->XHeight(); 1.123 + nscoord indexRadicalKern = NSToCoordRound(1.35f * xHeight); 1.124 + if (indexRadicalKern > aIndexWidth) { 1.125 + dxIndex = indexRadicalKern - aIndexWidth; 1.126 + dxSqr = 0; 1.127 + } 1.128 + else { 1.129 + dxIndex = 0; 1.130 + dxSqr = aIndexWidth - indexRadicalKern; 1.131 + } 1.132 + // avoid collision by leaving a minimum space between index and radical 1.133 + nscoord minimumClearance = aSqrWidth/2; 1.134 + if (dxIndex + aIndexWidth + minimumClearance > dxSqr + aSqrWidth) { 1.135 + if (aIndexWidth + minimumClearance < aSqrWidth) { 1.136 + dxIndex = aSqrWidth - (aIndexWidth + minimumClearance); 1.137 + dxSqr = 0; 1.138 + } 1.139 + else { 1.140 + dxIndex = 0; 1.141 + dxSqr = (aIndexWidth + minimumClearance) - aSqrWidth; 1.142 + } 1.143 + } 1.144 + 1.145 + if (aIndexOffset) 1.146 + *aIndexOffset = dxIndex; 1.147 + if (aSqrOffset) 1.148 + *aSqrOffset = dxSqr; 1.149 +} 1.150 + 1.151 +nsresult 1.152 +nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext, 1.153 + nsHTMLReflowMetrics& aDesiredSize, 1.154 + const nsHTMLReflowState& aReflowState, 1.155 + nsReflowStatus& aStatus) 1.156 +{ 1.157 + nsresult rv = NS_OK; 1.158 + nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE); 1.159 + nsReflowStatus childStatus; 1.160 + 1.161 + aDesiredSize.Width() = aDesiredSize.Height() = 0; 1.162 + aDesiredSize.SetTopAscent(0); 1.163 + 1.164 + nsBoundingMetrics bmSqr, bmBase, bmIndex; 1.165 + nsRenderingContext& renderingContext = *aReflowState.rendContext; 1.166 + 1.167 + ////////////////// 1.168 + // Reflow Children 1.169 + 1.170 + int32_t count = 0; 1.171 + nsIFrame* baseFrame = nullptr; 1.172 + nsIFrame* indexFrame = nullptr; 1.173 + nsHTMLReflowMetrics baseSize(aReflowState); 1.174 + nsHTMLReflowMetrics indexSize(aReflowState); 1.175 + nsIFrame* childFrame = mFrames.FirstChild(); 1.176 + while (childFrame) { 1.177 + // ask our children to compute their bounding metrics 1.178 + nsHTMLReflowMetrics childDesiredSize(aReflowState, 1.179 + aDesiredSize.mFlags 1.180 + | NS_REFLOW_CALC_BOUNDING_METRICS); 1.181 + nsHTMLReflowState childReflowState(aPresContext, aReflowState, 1.182 + childFrame, availSize); 1.183 + rv = ReflowChild(childFrame, aPresContext, 1.184 + childDesiredSize, childReflowState, childStatus); 1.185 + //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status"); 1.186 + if (NS_FAILED(rv)) { 1.187 + // Call DidReflow() for the child frames we successfully did reflow. 1.188 + DidReflowChildren(mFrames.FirstChild(), childFrame); 1.189 + return rv; 1.190 + } 1.191 + if (0 == count) { 1.192 + // base 1.193 + baseFrame = childFrame; 1.194 + baseSize = childDesiredSize; 1.195 + bmBase = childDesiredSize.mBoundingMetrics; 1.196 + } 1.197 + else if (1 == count) { 1.198 + // index 1.199 + indexFrame = childFrame; 1.200 + indexSize = childDesiredSize; 1.201 + bmIndex = childDesiredSize.mBoundingMetrics; 1.202 + } 1.203 + count++; 1.204 + childFrame = childFrame->GetNextSibling(); 1.205 + } 1.206 + if (2 != count) { 1.207 + // report an error, encourage people to get their markups in order 1.208 + ReportChildCountError(); 1.209 + rv = ReflowError(renderingContext, aDesiredSize); 1.210 + aStatus = NS_FRAME_COMPLETE; 1.211 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 1.212 + // Call DidReflow() for the child frames we successfully did reflow. 1.213 + DidReflowChildren(mFrames.FirstChild(), childFrame); 1.214 + return rv; 1.215 + } 1.216 + 1.217 + //////////// 1.218 + // Prepare the radical symbol and the overline bar 1.219 + 1.220 + nsRefPtr<nsFontMetrics> fm; 1.221 + nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); 1.222 + renderingContext.SetFont(fm); 1.223 + 1.224 + // For radical glyphs from TeX fonts and some of the radical glyphs from 1.225 + // Mathematica fonts, the thickness of the overline can be obtained from the 1.226 + // ascent of the glyph. Most fonts however have radical glyphs above the 1.227 + // baseline so no assumption can be made about the meaning of the ascent. 1.228 + nscoord ruleThickness, leading, em; 1.229 + GetRuleThickness(renderingContext, fm, ruleThickness); 1.230 + 1.231 + char16_t one = '1'; 1.232 + nsBoundingMetrics bmOne = renderingContext.GetBoundingMetrics(&one, 1); 1.233 + 1.234 + // get the leading to be left at the top of the resulting frame 1.235 + // this seems more reliable than using fm->GetLeading() on suspicious fonts 1.236 + GetEmHeight(fm, em); 1.237 + leading = nscoord(0.2f * em); 1.238 + 1.239 + // Rule 11, App. G, TeXbook 1.240 + // psi = clearance between rule and content 1.241 + nscoord phi = 0, psi = 0; 1.242 + if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) 1.243 + phi = fm->XHeight(); 1.244 + else 1.245 + phi = ruleThickness; 1.246 + psi = ruleThickness + phi/4; 1.247 + 1.248 + // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131) 1.249 + if (bmOne.ascent > bmBase.ascent) 1.250 + psi += bmOne.ascent - bmBase.ascent; 1.251 + 1.252 + // make sure that the rule appears on on screen 1.253 + nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); 1.254 + if (ruleThickness < onePixel) { 1.255 + ruleThickness = onePixel; 1.256 + } 1.257 + 1.258 + // adjust clearance psi to get an exact number of pixels -- this 1.259 + // gives a nicer & uniform look on stacked radicals (bug 130282) 1.260 + nscoord delta = psi % onePixel; 1.261 + if (delta) 1.262 + psi += onePixel - delta; // round up 1.263 + 1.264 + // Stretch the radical symbol to the appropriate height if it is not big enough. 1.265 + nsBoundingMetrics contSize = bmBase; 1.266 + contSize.descent = bmBase.ascent + bmBase.descent + psi; 1.267 + contSize.ascent = ruleThickness; 1.268 + 1.269 + // height(radical) should be >= height(base) + psi + ruleThickness 1.270 + nsBoundingMetrics radicalSize; 1.271 + mSqrChar.Stretch(aPresContext, renderingContext, 1.272 + NS_STRETCH_DIRECTION_VERTICAL, 1.273 + contSize, radicalSize, 1.274 + NS_STRETCH_LARGER, 1.275 + StyleVisibility()->mDirection); 1.276 + // radicalSize have changed at this point, and should match with 1.277 + // the bounding metrics of the char 1.278 + mSqrChar.GetBoundingMetrics(bmSqr); 1.279 + 1.280 + // Update the desired size for the container (like msqrt, index is not yet included) 1.281 + // the baseline will be that of the base. 1.282 + mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness; 1.283 + mBoundingMetrics.descent = 1.284 + std::max(bmBase.descent, 1.285 + (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent)); 1.286 + mBoundingMetrics.width = bmSqr.width + bmBase.width; 1.287 + mBoundingMetrics.leftBearing = bmSqr.leftBearing; 1.288 + mBoundingMetrics.rightBearing = bmSqr.width + 1.289 + std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule 1.290 + 1.291 + aDesiredSize.SetTopAscent(mBoundingMetrics.ascent + leading); 1.292 + aDesiredSize.Height() = aDesiredSize.TopAscent() + 1.293 + std::max(baseSize.Height() - baseSize.TopAscent(), 1.294 + mBoundingMetrics.descent + ruleThickness); 1.295 + aDesiredSize.Width() = mBoundingMetrics.width; 1.296 + 1.297 + ///////////// 1.298 + // Re-adjust the desired size to include the index. 1.299 + 1.300 + // the index is raised by some fraction of the height 1.301 + // of the radical, see \mroot macro in App. B, TexBook 1.302 + nscoord raiseIndexDelta = NSToCoordRound(0.6f * (bmSqr.ascent + bmSqr.descent)); 1.303 + nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical 1.304 + - (bmSqr.ascent + bmSqr.descent) // to bottom of radical 1.305 + + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index 1.306 + 1.307 + nscoord indexClearance = 0; 1.308 + if (mBoundingMetrics.ascent < indexRaisedAscent) { 1.309 + indexClearance = 1.310 + indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index 1.311 + mBoundingMetrics.ascent = indexRaisedAscent; 1.312 + nscoord descent = aDesiredSize.Height() - aDesiredSize.TopAscent(); 1.313 + aDesiredSize.SetTopAscent(mBoundingMetrics.ascent + leading); 1.314 + aDesiredSize.Height() = aDesiredSize.TopAscent() + descent; 1.315 + } 1.316 + 1.317 + nscoord dxIndex, dxSqr; 1.318 + GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr); 1.319 + 1.320 + mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width; 1.321 + mBoundingMetrics.leftBearing = 1.322 + std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing); 1.323 + mBoundingMetrics.rightBearing = dxSqr + bmSqr.width + 1.324 + std::max(bmBase.width, bmBase.rightBearing); 1.325 + 1.326 + aDesiredSize.Width() = mBoundingMetrics.width; 1.327 + aDesiredSize.mBoundingMetrics = mBoundingMetrics; 1.328 + GatherAndStoreOverflow(&aDesiredSize); 1.329 + 1.330 + // place the index 1.331 + nscoord dx = dxIndex; 1.332 + nscoord dy = aDesiredSize.TopAscent() - (indexRaisedAscent + indexSize.TopAscent() - bmIndex.ascent); 1.333 + FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr, 1.334 + MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx), 1.335 + dy, 0); 1.336 + 1.337 + // place the radical symbol and the radical bar 1.338 + dx = dxSqr; 1.339 + dy = indexClearance + leading; // leave a leading at the top 1.340 + mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx), 1.341 + dy, bmSqr.width, bmSqr.ascent + bmSqr.descent)); 1.342 + dx += bmSqr.width; 1.343 + mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx), 1.344 + dy, bmBase.width, ruleThickness); 1.345 + 1.346 + // place the base 1.347 + dy = aDesiredSize.TopAscent() - baseSize.TopAscent(); 1.348 + FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr, 1.349 + MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx), 1.350 + dy, 0); 1.351 + 1.352 + mReference.x = 0; 1.353 + mReference.y = aDesiredSize.TopAscent(); 1.354 + 1.355 + aStatus = NS_FRAME_COMPLETE; 1.356 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 1.357 + return NS_OK; 1.358 +} 1.359 + 1.360 +/* virtual */ void 1.361 +nsMathMLmrootFrame::GetIntrinsicWidthMetrics(nsRenderingContext* aRenderingContext, nsHTMLReflowMetrics& aDesiredSize) 1.362 +{ 1.363 + nsIFrame* baseFrame = mFrames.FirstChild(); 1.364 + nsIFrame* indexFrame = nullptr; 1.365 + if (baseFrame) 1.366 + indexFrame = baseFrame->GetNextSibling(); 1.367 + if (!indexFrame || indexFrame->GetNextSibling()) { 1.368 + ReflowError(*aRenderingContext, aDesiredSize); 1.369 + return; 1.370 + } 1.371 + 1.372 + nscoord baseWidth = 1.373 + nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame, 1.374 + nsLayoutUtils::PREF_WIDTH); 1.375 + nscoord indexWidth = 1.376 + nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame, 1.377 + nsLayoutUtils::PREF_WIDTH); 1.378 + nscoord sqrWidth = mSqrChar.GetMaxWidth(PresContext(), *aRenderingContext); 1.379 + 1.380 + nscoord dxSqr; 1.381 + nsRefPtr<nsFontMetrics> fm; 1.382 + nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); 1.383 + GetRadicalXOffsets(indexWidth, sqrWidth, fm, nullptr, &dxSqr); 1.384 + 1.385 + nscoord width = dxSqr + sqrWidth + baseWidth; 1.386 + 1.387 + aDesiredSize.Width() = width; 1.388 + aDesiredSize.mBoundingMetrics.width = width; 1.389 + aDesiredSize.mBoundingMetrics.leftBearing = 0; 1.390 + aDesiredSize.mBoundingMetrics.rightBearing = width; 1.391 +} 1.392 + 1.393 +// ---------------------- 1.394 +// the Style System will use these to pass the proper style context to our MathMLChar 1.395 +nsStyleContext* 1.396 +nsMathMLmrootFrame::GetAdditionalStyleContext(int32_t aIndex) const 1.397 +{ 1.398 + switch (aIndex) { 1.399 + case NS_SQR_CHAR_STYLE_CONTEXT_INDEX: 1.400 + return mSqrChar.GetStyleContext(); 1.401 + break; 1.402 + default: 1.403 + return nullptr; 1.404 + } 1.405 +} 1.406 + 1.407 +void 1.408 +nsMathMLmrootFrame::SetAdditionalStyleContext(int32_t aIndex, 1.409 + nsStyleContext* aStyleContext) 1.410 +{ 1.411 + switch (aIndex) { 1.412 + case NS_SQR_CHAR_STYLE_CONTEXT_INDEX: 1.413 + mSqrChar.SetStyleContext(aStyleContext); 1.414 + break; 1.415 + } 1.416 +}