layout/mathml/nsMathMLmrootFrame.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsMathMLmrootFrame.h"
michael@0 7 #include "nsPresContext.h"
michael@0 8 #include "nsRenderingContext.h"
michael@0 9 #include <algorithm>
michael@0 10
michael@0 11 //
michael@0 12 // <msqrt> and <mroot> -- form a radical - implementation
michael@0 13 //
michael@0 14
michael@0 15 //NOTE:
michael@0 16 // The code assumes that TeX fonts are picked.
michael@0 17 // There is no fall-back to draw the branches of the sqrt explicitly
michael@0 18 // in the case where TeX fonts are not there. In general, there are no
michael@0 19 // fall-back(s) in MathML when some (freely-downloadable) fonts are missing.
michael@0 20 // Otherwise, this will add much work and unnecessary complexity to the core
michael@0 21 // MathML engine. Assuming that authors have the free fonts is part of the
michael@0 22 // deal. We are not responsible for cases of misconfigurations out there.
michael@0 23
michael@0 24 // additional style context to be used by our MathMLChar.
michael@0 25 #define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0
michael@0 26
michael@0 27 static const char16_t kSqrChar = char16_t(0x221A);
michael@0 28
michael@0 29 nsIFrame*
michael@0 30 NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 31 {
michael@0 32 return new (aPresShell) nsMathMLmrootFrame(aContext);
michael@0 33 }
michael@0 34
michael@0 35 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame)
michael@0 36
michael@0 37 nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext* aContext) :
michael@0 38 nsMathMLContainerFrame(aContext),
michael@0 39 mSqrChar(),
michael@0 40 mBarRect()
michael@0 41 {
michael@0 42 }
michael@0 43
michael@0 44 nsMathMLmrootFrame::~nsMathMLmrootFrame()
michael@0 45 {
michael@0 46 }
michael@0 47
michael@0 48 void
michael@0 49 nsMathMLmrootFrame::Init(nsIContent* aContent,
michael@0 50 nsIFrame* aParent,
michael@0 51 nsIFrame* aPrevInFlow)
michael@0 52 {
michael@0 53 nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
michael@0 54
michael@0 55 nsPresContext *presContext = PresContext();
michael@0 56
michael@0 57 // No need to track the style context given to our MathML char.
michael@0 58 // The Style System will use Get/SetAdditionalStyleContext() to keep it
michael@0 59 // up-to-date if dynamic changes arise.
michael@0 60 nsAutoString sqrChar; sqrChar.Assign(kSqrChar);
michael@0 61 mSqrChar.SetData(presContext, sqrChar);
michael@0 62 ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mSqrChar);
michael@0 63 }
michael@0 64
michael@0 65 NS_IMETHODIMP
michael@0 66 nsMathMLmrootFrame::TransmitAutomaticData()
michael@0 67 {
michael@0 68 // 1. The REC says:
michael@0 69 // The <mroot> element increments scriptlevel by 2, and sets displaystyle to
michael@0 70 // "false", within index, but leaves both attributes unchanged within base.
michael@0 71 // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
michael@0 72 UpdatePresentationDataFromChildAt(1, 1,
michael@0 73 NS_MATHML_COMPRESSED,
michael@0 74 NS_MATHML_COMPRESSED);
michael@0 75 UpdatePresentationDataFromChildAt(0, 0,
michael@0 76 NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
michael@0 77
michael@0 78 PropagateFrameFlagFor(mFrames.LastChild(),
michael@0 79 NS_FRAME_MATHML_SCRIPT_DESCENDANT);
michael@0 80
michael@0 81 return NS_OK;
michael@0 82 }
michael@0 83
michael@0 84 void
michael@0 85 nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 86 const nsRect& aDirtyRect,
michael@0 87 const nsDisplayListSet& aLists)
michael@0 88 {
michael@0 89 /////////////
michael@0 90 // paint the content we are square-rooting
michael@0 91 nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
michael@0 92
michael@0 93 /////////////
michael@0 94 // paint the sqrt symbol
michael@0 95 if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
michael@0 96 mSqrChar.Display(aBuilder, this, aLists, 0);
michael@0 97
michael@0 98 DisplayBar(aBuilder, this, mBarRect, aLists);
michael@0 99
michael@0 100 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
michael@0 101 // for visual debug
michael@0 102 nsRect rect;
michael@0 103 mSqrChar.GetRect(rect);
michael@0 104 nsBoundingMetrics bm;
michael@0 105 mSqrChar.GetBoundingMetrics(bm);
michael@0 106 DisplayBoundingMetrics(aBuilder, this, rect.TopLeft(), bm, aLists);
michael@0 107 #endif
michael@0 108 }
michael@0 109 }
michael@0 110
michael@0 111 static void
michael@0 112 GetRadicalXOffsets(nscoord aIndexWidth, nscoord aSqrWidth,
michael@0 113 nsFontMetrics* aFontMetrics,
michael@0 114 nscoord* aIndexOffset, nscoord* aSqrOffset)
michael@0 115 {
michael@0 116 // The index is tucked in closer to the radical while making sure
michael@0 117 // that the kern does not make the index and radical collide
michael@0 118 nscoord dxIndex, dxSqr;
michael@0 119 nscoord xHeight = aFontMetrics->XHeight();
michael@0 120 nscoord indexRadicalKern = NSToCoordRound(1.35f * xHeight);
michael@0 121 if (indexRadicalKern > aIndexWidth) {
michael@0 122 dxIndex = indexRadicalKern - aIndexWidth;
michael@0 123 dxSqr = 0;
michael@0 124 }
michael@0 125 else {
michael@0 126 dxIndex = 0;
michael@0 127 dxSqr = aIndexWidth - indexRadicalKern;
michael@0 128 }
michael@0 129 // avoid collision by leaving a minimum space between index and radical
michael@0 130 nscoord minimumClearance = aSqrWidth/2;
michael@0 131 if (dxIndex + aIndexWidth + minimumClearance > dxSqr + aSqrWidth) {
michael@0 132 if (aIndexWidth + minimumClearance < aSqrWidth) {
michael@0 133 dxIndex = aSqrWidth - (aIndexWidth + minimumClearance);
michael@0 134 dxSqr = 0;
michael@0 135 }
michael@0 136 else {
michael@0 137 dxIndex = 0;
michael@0 138 dxSqr = (aIndexWidth + minimumClearance) - aSqrWidth;
michael@0 139 }
michael@0 140 }
michael@0 141
michael@0 142 if (aIndexOffset)
michael@0 143 *aIndexOffset = dxIndex;
michael@0 144 if (aSqrOffset)
michael@0 145 *aSqrOffset = dxSqr;
michael@0 146 }
michael@0 147
michael@0 148 nsresult
michael@0 149 nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext,
michael@0 150 nsHTMLReflowMetrics& aDesiredSize,
michael@0 151 const nsHTMLReflowState& aReflowState,
michael@0 152 nsReflowStatus& aStatus)
michael@0 153 {
michael@0 154 nsresult rv = NS_OK;
michael@0 155 nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
michael@0 156 nsReflowStatus childStatus;
michael@0 157
michael@0 158 aDesiredSize.Width() = aDesiredSize.Height() = 0;
michael@0 159 aDesiredSize.SetTopAscent(0);
michael@0 160
michael@0 161 nsBoundingMetrics bmSqr, bmBase, bmIndex;
michael@0 162 nsRenderingContext& renderingContext = *aReflowState.rendContext;
michael@0 163
michael@0 164 //////////////////
michael@0 165 // Reflow Children
michael@0 166
michael@0 167 int32_t count = 0;
michael@0 168 nsIFrame* baseFrame = nullptr;
michael@0 169 nsIFrame* indexFrame = nullptr;
michael@0 170 nsHTMLReflowMetrics baseSize(aReflowState);
michael@0 171 nsHTMLReflowMetrics indexSize(aReflowState);
michael@0 172 nsIFrame* childFrame = mFrames.FirstChild();
michael@0 173 while (childFrame) {
michael@0 174 // ask our children to compute their bounding metrics
michael@0 175 nsHTMLReflowMetrics childDesiredSize(aReflowState,
michael@0 176 aDesiredSize.mFlags
michael@0 177 | NS_REFLOW_CALC_BOUNDING_METRICS);
michael@0 178 nsHTMLReflowState childReflowState(aPresContext, aReflowState,
michael@0 179 childFrame, availSize);
michael@0 180 rv = ReflowChild(childFrame, aPresContext,
michael@0 181 childDesiredSize, childReflowState, childStatus);
michael@0 182 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
michael@0 183 if (NS_FAILED(rv)) {
michael@0 184 // Call DidReflow() for the child frames we successfully did reflow.
michael@0 185 DidReflowChildren(mFrames.FirstChild(), childFrame);
michael@0 186 return rv;
michael@0 187 }
michael@0 188 if (0 == count) {
michael@0 189 // base
michael@0 190 baseFrame = childFrame;
michael@0 191 baseSize = childDesiredSize;
michael@0 192 bmBase = childDesiredSize.mBoundingMetrics;
michael@0 193 }
michael@0 194 else if (1 == count) {
michael@0 195 // index
michael@0 196 indexFrame = childFrame;
michael@0 197 indexSize = childDesiredSize;
michael@0 198 bmIndex = childDesiredSize.mBoundingMetrics;
michael@0 199 }
michael@0 200 count++;
michael@0 201 childFrame = childFrame->GetNextSibling();
michael@0 202 }
michael@0 203 if (2 != count) {
michael@0 204 // report an error, encourage people to get their markups in order
michael@0 205 ReportChildCountError();
michael@0 206 rv = ReflowError(renderingContext, aDesiredSize);
michael@0 207 aStatus = NS_FRAME_COMPLETE;
michael@0 208 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
michael@0 209 // Call DidReflow() for the child frames we successfully did reflow.
michael@0 210 DidReflowChildren(mFrames.FirstChild(), childFrame);
michael@0 211 return rv;
michael@0 212 }
michael@0 213
michael@0 214 ////////////
michael@0 215 // Prepare the radical symbol and the overline bar
michael@0 216
michael@0 217 nsRefPtr<nsFontMetrics> fm;
michael@0 218 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
michael@0 219 renderingContext.SetFont(fm);
michael@0 220
michael@0 221 // For radical glyphs from TeX fonts and some of the radical glyphs from
michael@0 222 // Mathematica fonts, the thickness of the overline can be obtained from the
michael@0 223 // ascent of the glyph. Most fonts however have radical glyphs above the
michael@0 224 // baseline so no assumption can be made about the meaning of the ascent.
michael@0 225 nscoord ruleThickness, leading, em;
michael@0 226 GetRuleThickness(renderingContext, fm, ruleThickness);
michael@0 227
michael@0 228 char16_t one = '1';
michael@0 229 nsBoundingMetrics bmOne = renderingContext.GetBoundingMetrics(&one, 1);
michael@0 230
michael@0 231 // get the leading to be left at the top of the resulting frame
michael@0 232 // this seems more reliable than using fm->GetLeading() on suspicious fonts
michael@0 233 GetEmHeight(fm, em);
michael@0 234 leading = nscoord(0.2f * em);
michael@0 235
michael@0 236 // Rule 11, App. G, TeXbook
michael@0 237 // psi = clearance between rule and content
michael@0 238 nscoord phi = 0, psi = 0;
michael@0 239 if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK)
michael@0 240 phi = fm->XHeight();
michael@0 241 else
michael@0 242 phi = ruleThickness;
michael@0 243 psi = ruleThickness + phi/4;
michael@0 244
michael@0 245 // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
michael@0 246 if (bmOne.ascent > bmBase.ascent)
michael@0 247 psi += bmOne.ascent - bmBase.ascent;
michael@0 248
michael@0 249 // make sure that the rule appears on on screen
michael@0 250 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
michael@0 251 if (ruleThickness < onePixel) {
michael@0 252 ruleThickness = onePixel;
michael@0 253 }
michael@0 254
michael@0 255 // adjust clearance psi to get an exact number of pixels -- this
michael@0 256 // gives a nicer & uniform look on stacked radicals (bug 130282)
michael@0 257 nscoord delta = psi % onePixel;
michael@0 258 if (delta)
michael@0 259 psi += onePixel - delta; // round up
michael@0 260
michael@0 261 // Stretch the radical symbol to the appropriate height if it is not big enough.
michael@0 262 nsBoundingMetrics contSize = bmBase;
michael@0 263 contSize.descent = bmBase.ascent + bmBase.descent + psi;
michael@0 264 contSize.ascent = ruleThickness;
michael@0 265
michael@0 266 // height(radical) should be >= height(base) + psi + ruleThickness
michael@0 267 nsBoundingMetrics radicalSize;
michael@0 268 mSqrChar.Stretch(aPresContext, renderingContext,
michael@0 269 NS_STRETCH_DIRECTION_VERTICAL,
michael@0 270 contSize, radicalSize,
michael@0 271 NS_STRETCH_LARGER,
michael@0 272 StyleVisibility()->mDirection);
michael@0 273 // radicalSize have changed at this point, and should match with
michael@0 274 // the bounding metrics of the char
michael@0 275 mSqrChar.GetBoundingMetrics(bmSqr);
michael@0 276
michael@0 277 // Update the desired size for the container (like msqrt, index is not yet included)
michael@0 278 // the baseline will be that of the base.
michael@0 279 mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness;
michael@0 280 mBoundingMetrics.descent =
michael@0 281 std::max(bmBase.descent,
michael@0 282 (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
michael@0 283 mBoundingMetrics.width = bmSqr.width + bmBase.width;
michael@0 284 mBoundingMetrics.leftBearing = bmSqr.leftBearing;
michael@0 285 mBoundingMetrics.rightBearing = bmSqr.width +
michael@0 286 std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule
michael@0 287
michael@0 288 aDesiredSize.SetTopAscent(mBoundingMetrics.ascent + leading);
michael@0 289 aDesiredSize.Height() = aDesiredSize.TopAscent() +
michael@0 290 std::max(baseSize.Height() - baseSize.TopAscent(),
michael@0 291 mBoundingMetrics.descent + ruleThickness);
michael@0 292 aDesiredSize.Width() = mBoundingMetrics.width;
michael@0 293
michael@0 294 /////////////
michael@0 295 // Re-adjust the desired size to include the index.
michael@0 296
michael@0 297 // the index is raised by some fraction of the height
michael@0 298 // of the radical, see \mroot macro in App. B, TexBook
michael@0 299 nscoord raiseIndexDelta = NSToCoordRound(0.6f * (bmSqr.ascent + bmSqr.descent));
michael@0 300 nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical
michael@0 301 - (bmSqr.ascent + bmSqr.descent) // to bottom of radical
michael@0 302 + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index
michael@0 303
michael@0 304 nscoord indexClearance = 0;
michael@0 305 if (mBoundingMetrics.ascent < indexRaisedAscent) {
michael@0 306 indexClearance =
michael@0 307 indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index
michael@0 308 mBoundingMetrics.ascent = indexRaisedAscent;
michael@0 309 nscoord descent = aDesiredSize.Height() - aDesiredSize.TopAscent();
michael@0 310 aDesiredSize.SetTopAscent(mBoundingMetrics.ascent + leading);
michael@0 311 aDesiredSize.Height() = aDesiredSize.TopAscent() + descent;
michael@0 312 }
michael@0 313
michael@0 314 nscoord dxIndex, dxSqr;
michael@0 315 GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr);
michael@0 316
michael@0 317 mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width;
michael@0 318 mBoundingMetrics.leftBearing =
michael@0 319 std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
michael@0 320 mBoundingMetrics.rightBearing = dxSqr + bmSqr.width +
michael@0 321 std::max(bmBase.width, bmBase.rightBearing);
michael@0 322
michael@0 323 aDesiredSize.Width() = mBoundingMetrics.width;
michael@0 324 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
michael@0 325 GatherAndStoreOverflow(&aDesiredSize);
michael@0 326
michael@0 327 // place the index
michael@0 328 nscoord dx = dxIndex;
michael@0 329 nscoord dy = aDesiredSize.TopAscent() - (indexRaisedAscent + indexSize.TopAscent() - bmIndex.ascent);
michael@0 330 FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr,
michael@0 331 MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx),
michael@0 332 dy, 0);
michael@0 333
michael@0 334 // place the radical symbol and the radical bar
michael@0 335 dx = dxSqr;
michael@0 336 dy = indexClearance + leading; // leave a leading at the top
michael@0 337 mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx),
michael@0 338 dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
michael@0 339 dx += bmSqr.width;
michael@0 340 mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx),
michael@0 341 dy, bmBase.width, ruleThickness);
michael@0 342
michael@0 343 // place the base
michael@0 344 dy = aDesiredSize.TopAscent() - baseSize.TopAscent();
michael@0 345 FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr,
michael@0 346 MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx),
michael@0 347 dy, 0);
michael@0 348
michael@0 349 mReference.x = 0;
michael@0 350 mReference.y = aDesiredSize.TopAscent();
michael@0 351
michael@0 352 aStatus = NS_FRAME_COMPLETE;
michael@0 353 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
michael@0 354 return NS_OK;
michael@0 355 }
michael@0 356
michael@0 357 /* virtual */ void
michael@0 358 nsMathMLmrootFrame::GetIntrinsicWidthMetrics(nsRenderingContext* aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
michael@0 359 {
michael@0 360 nsIFrame* baseFrame = mFrames.FirstChild();
michael@0 361 nsIFrame* indexFrame = nullptr;
michael@0 362 if (baseFrame)
michael@0 363 indexFrame = baseFrame->GetNextSibling();
michael@0 364 if (!indexFrame || indexFrame->GetNextSibling()) {
michael@0 365 ReflowError(*aRenderingContext, aDesiredSize);
michael@0 366 return;
michael@0 367 }
michael@0 368
michael@0 369 nscoord baseWidth =
michael@0 370 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame,
michael@0 371 nsLayoutUtils::PREF_WIDTH);
michael@0 372 nscoord indexWidth =
michael@0 373 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame,
michael@0 374 nsLayoutUtils::PREF_WIDTH);
michael@0 375 nscoord sqrWidth = mSqrChar.GetMaxWidth(PresContext(), *aRenderingContext);
michael@0 376
michael@0 377 nscoord dxSqr;
michael@0 378 nsRefPtr<nsFontMetrics> fm;
michael@0 379 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
michael@0 380 GetRadicalXOffsets(indexWidth, sqrWidth, fm, nullptr, &dxSqr);
michael@0 381
michael@0 382 nscoord width = dxSqr + sqrWidth + baseWidth;
michael@0 383
michael@0 384 aDesiredSize.Width() = width;
michael@0 385 aDesiredSize.mBoundingMetrics.width = width;
michael@0 386 aDesiredSize.mBoundingMetrics.leftBearing = 0;
michael@0 387 aDesiredSize.mBoundingMetrics.rightBearing = width;
michael@0 388 }
michael@0 389
michael@0 390 // ----------------------
michael@0 391 // the Style System will use these to pass the proper style context to our MathMLChar
michael@0 392 nsStyleContext*
michael@0 393 nsMathMLmrootFrame::GetAdditionalStyleContext(int32_t aIndex) const
michael@0 394 {
michael@0 395 switch (aIndex) {
michael@0 396 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
michael@0 397 return mSqrChar.GetStyleContext();
michael@0 398 break;
michael@0 399 default:
michael@0 400 return nullptr;
michael@0 401 }
michael@0 402 }
michael@0 403
michael@0 404 void
michael@0 405 nsMathMLmrootFrame::SetAdditionalStyleContext(int32_t aIndex,
michael@0 406 nsStyleContext* aStyleContext)
michael@0 407 {
michael@0 408 switch (aIndex) {
michael@0 409 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
michael@0 410 mSqrChar.SetStyleContext(aStyleContext);
michael@0 411 break;
michael@0 412 }
michael@0 413 }

mercurial