layout/mathml/nsMathMLmfracFrame.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
michael@0 7 #include "nsMathMLmfracFrame.h"
michael@0 8 #include "nsPresContext.h"
michael@0 9 #include "nsRenderingContext.h"
michael@0 10 #include "nsDisplayList.h"
michael@0 11 #include "gfxContext.h"
michael@0 12 #include "nsMathMLElement.h"
michael@0 13 #include <algorithm>
michael@0 14
michael@0 15 //
michael@0 16 // <mfrac> -- form a fraction from two subexpressions - implementation
michael@0 17 //
michael@0 18
michael@0 19 // various fraction line thicknesses (multiplicative values of the default rule thickness)
michael@0 20
michael@0 21 #define THIN_FRACTION_LINE 0.5f
michael@0 22 #define THIN_FRACTION_LINE_MINIMUM_PIXELS 1 // minimum of 1 pixel
michael@0 23
michael@0 24 #define THICK_FRACTION_LINE 2.0f
michael@0 25 #define THICK_FRACTION_LINE_MINIMUM_PIXELS 2 // minimum of 2 pixels
michael@0 26
michael@0 27 nsIFrame*
michael@0 28 NS_NewMathMLmfracFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 29 {
michael@0 30 return new (aPresShell) nsMathMLmfracFrame(aContext);
michael@0 31 }
michael@0 32
michael@0 33 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmfracFrame)
michael@0 34
michael@0 35 nsMathMLmfracFrame::~nsMathMLmfracFrame()
michael@0 36 {
michael@0 37 }
michael@0 38
michael@0 39 eMathMLFrameType
michael@0 40 nsMathMLmfracFrame::GetMathMLFrameType()
michael@0 41 {
michael@0 42 // frac is "inner" in TeXBook, Appendix G, rule 15e. See also page 170.
michael@0 43 return eMathMLFrameType_Inner;
michael@0 44 }
michael@0 45
michael@0 46 uint8_t
michael@0 47 nsMathMLmfracFrame::ScriptIncrement(nsIFrame* aFrame)
michael@0 48 {
michael@0 49 if (!StyleFont()->mMathDisplay &&
michael@0 50 aFrame && (mFrames.FirstChild() == aFrame ||
michael@0 51 mFrames.LastChild() == aFrame)) {
michael@0 52 return 1;
michael@0 53 }
michael@0 54 return 0;
michael@0 55 }
michael@0 56
michael@0 57 NS_IMETHODIMP
michael@0 58 nsMathMLmfracFrame::TransmitAutomaticData()
michael@0 59 {
michael@0 60 // The TeXbook (Ch 17. p.141) says the numerator inherits the compression
michael@0 61 // while the denominator is compressed
michael@0 62 UpdatePresentationDataFromChildAt(1, 1,
michael@0 63 NS_MATHML_COMPRESSED,
michael@0 64 NS_MATHML_COMPRESSED);
michael@0 65
michael@0 66 // If displaystyle is false, then scriptlevel is incremented, so notify the
michael@0 67 // children of this.
michael@0 68 if (!StyleFont()->mMathDisplay) {
michael@0 69 PropagateFrameFlagFor(mFrames.FirstChild(),
michael@0 70 NS_FRAME_MATHML_SCRIPT_DESCENDANT);
michael@0 71 PropagateFrameFlagFor(mFrames.LastChild(),
michael@0 72 NS_FRAME_MATHML_SCRIPT_DESCENDANT);
michael@0 73 }
michael@0 74
michael@0 75 // if our numerator is an embellished operator, let its state bubble to us
michael@0 76 GetEmbellishDataFrom(mFrames.FirstChild(), mEmbellishData);
michael@0 77 if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {
michael@0 78 // even when embellished, we need to record that <mfrac> won't fire
michael@0 79 // Stretch() on its embellished child
michael@0 80 mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
michael@0 81 }
michael@0 82
michael@0 83 return NS_OK;
michael@0 84 }
michael@0 85
michael@0 86 nscoord
michael@0 87 nsMathMLmfracFrame::CalcLineThickness(nsPresContext* aPresContext,
michael@0 88 nsStyleContext* aStyleContext,
michael@0 89 nsString& aThicknessAttribute,
michael@0 90 nscoord onePixel,
michael@0 91 nscoord aDefaultRuleThickness)
michael@0 92 {
michael@0 93 nscoord defaultThickness = aDefaultRuleThickness;
michael@0 94 nscoord lineThickness = aDefaultRuleThickness;
michael@0 95 nscoord minimumThickness = onePixel;
michael@0 96
michael@0 97 // linethickness
michael@0 98 //
michael@0 99 // "Specifies the thickness of the horizontal 'fraction bar', or 'rule'. The
michael@0 100 // default value is 'medium', 'thin' is thinner, but visible, 'thick' is
michael@0 101 // thicker; the exact thickness of these is left up to the rendering agent."
michael@0 102 //
michael@0 103 // values: length | "thin" | "medium" | "thick"
michael@0 104 // default: medium
michael@0 105 //
michael@0 106 if (!aThicknessAttribute.IsEmpty()) {
michael@0 107 if (aThicknessAttribute.EqualsLiteral("thin")) {
michael@0 108 lineThickness = NSToCoordFloor(defaultThickness * THIN_FRACTION_LINE);
michael@0 109 minimumThickness = onePixel * THIN_FRACTION_LINE_MINIMUM_PIXELS;
michael@0 110 // should visually decrease by at least one pixel, if default is not a pixel
michael@0 111 if (defaultThickness > onePixel && lineThickness > defaultThickness - onePixel)
michael@0 112 lineThickness = defaultThickness - onePixel;
michael@0 113 }
michael@0 114 else if (aThicknessAttribute.EqualsLiteral("medium")) {
michael@0 115 // medium is default
michael@0 116 }
michael@0 117 else if (aThicknessAttribute.EqualsLiteral("thick")) {
michael@0 118 lineThickness = NSToCoordCeil(defaultThickness * THICK_FRACTION_LINE);
michael@0 119 minimumThickness = onePixel * THICK_FRACTION_LINE_MINIMUM_PIXELS;
michael@0 120 // should visually increase by at least one pixel
michael@0 121 if (lineThickness < defaultThickness + onePixel)
michael@0 122 lineThickness = defaultThickness + onePixel;
michael@0 123 }
michael@0 124 else {
michael@0 125 // length value
michael@0 126 lineThickness = defaultThickness;
michael@0 127 ParseNumericValue(aThicknessAttribute, &lineThickness,
michael@0 128 nsMathMLElement::PARSE_ALLOW_UNITLESS,
michael@0 129 aPresContext, aStyleContext);
michael@0 130 }
michael@0 131 }
michael@0 132
michael@0 133 // use minimum if the lineThickness is a non-zero value less than minimun
michael@0 134 if (lineThickness && lineThickness < minimumThickness)
michael@0 135 lineThickness = minimumThickness;
michael@0 136
michael@0 137 return lineThickness;
michael@0 138 }
michael@0 139
michael@0 140 void
michael@0 141 nsMathMLmfracFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 142 const nsRect& aDirtyRect,
michael@0 143 const nsDisplayListSet& aLists)
michael@0 144 {
michael@0 145 /////////////
michael@0 146 // paint the numerator and denominator
michael@0 147 nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
michael@0 148
michael@0 149 /////////////
michael@0 150 // paint the fraction line
michael@0 151 if (mIsBevelled) {
michael@0 152 DisplaySlash(aBuilder, this, mLineRect, mLineThickness, aLists);
michael@0 153 } else {
michael@0 154 DisplayBar(aBuilder, this, mLineRect, aLists);
michael@0 155 }
michael@0 156 }
michael@0 157
michael@0 158 /* virtual */ nsresult
michael@0 159 nsMathMLmfracFrame::MeasureForWidth(nsRenderingContext& aRenderingContext,
michael@0 160 nsHTMLReflowMetrics& aDesiredSize)
michael@0 161 {
michael@0 162 return PlaceInternal(aRenderingContext,
michael@0 163 false,
michael@0 164 aDesiredSize,
michael@0 165 true);
michael@0 166 }
michael@0 167
michael@0 168 nscoord
michael@0 169 nsMathMLmfracFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
michael@0 170 {
michael@0 171 nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
michael@0 172 if (!gap) return 0;
michael@0 173
michael@0 174 mLineRect.MoveBy(gap, 0);
michael@0 175 return gap;
michael@0 176 }
michael@0 177
michael@0 178 /* virtual */ nsresult
michael@0 179 nsMathMLmfracFrame::Place(nsRenderingContext& aRenderingContext,
michael@0 180 bool aPlaceOrigin,
michael@0 181 nsHTMLReflowMetrics& aDesiredSize)
michael@0 182 {
michael@0 183 return PlaceInternal(aRenderingContext,
michael@0 184 aPlaceOrigin,
michael@0 185 aDesiredSize,
michael@0 186 false);
michael@0 187 }
michael@0 188
michael@0 189 nsresult
michael@0 190 nsMathMLmfracFrame::PlaceInternal(nsRenderingContext& aRenderingContext,
michael@0 191 bool aPlaceOrigin,
michael@0 192 nsHTMLReflowMetrics& aDesiredSize,
michael@0 193 bool aWidthOnly)
michael@0 194 {
michael@0 195 ////////////////////////////////////
michael@0 196 // Get the children's desired sizes
michael@0 197 nsBoundingMetrics bmNum, bmDen;
michael@0 198 nsHTMLReflowMetrics sizeNum(aDesiredSize.GetWritingMode());
michael@0 199 nsHTMLReflowMetrics sizeDen(aDesiredSize.GetWritingMode());
michael@0 200 nsIFrame* frameDen = nullptr;
michael@0 201 nsIFrame* frameNum = mFrames.FirstChild();
michael@0 202 if (frameNum)
michael@0 203 frameDen = frameNum->GetNextSibling();
michael@0 204 if (!frameNum || !frameDen || frameDen->GetNextSibling()) {
michael@0 205 // report an error, encourage people to get their markups in order
michael@0 206 if (aPlaceOrigin) {
michael@0 207 ReportChildCountError();
michael@0 208 }
michael@0 209 return ReflowError(aRenderingContext, aDesiredSize);
michael@0 210 }
michael@0 211 GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum);
michael@0 212 GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen);
michael@0 213
michael@0 214 nsPresContext* presContext = PresContext();
michael@0 215 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
michael@0 216
michael@0 217 nsRefPtr<nsFontMetrics> fm;
michael@0 218 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
michael@0 219 aRenderingContext.SetFont(fm);
michael@0 220
michael@0 221 nscoord defaultRuleThickness, axisHeight;
michael@0 222 GetRuleThickness(aRenderingContext, fm, defaultRuleThickness);
michael@0 223 GetAxisHeight(aRenderingContext, fm, axisHeight);
michael@0 224
michael@0 225 bool outermostEmbellished = false;
michael@0 226 if (mEmbellishData.coreFrame) {
michael@0 227 nsEmbellishData parentData;
michael@0 228 GetEmbellishDataFrom(mParent, parentData);
michael@0 229 outermostEmbellished = parentData.coreFrame != mEmbellishData.coreFrame;
michael@0 230 }
michael@0 231
michael@0 232 // see if the linethickness attribute is there
michael@0 233 nsAutoString value;
michael@0 234 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::linethickness_, value);
michael@0 235 mLineThickness = CalcLineThickness(presContext, mStyleContext, value,
michael@0 236 onePixel, defaultRuleThickness);
michael@0 237
michael@0 238 // bevelled attribute
michael@0 239 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::bevelled_, value);
michael@0 240 mIsBevelled = value.EqualsLiteral("true");
michael@0 241
michael@0 242 if (!mIsBevelled) {
michael@0 243 mLineRect.height = mLineThickness;
michael@0 244
michael@0 245 // by default, leave at least one-pixel padding at either end, and add
michael@0 246 // lspace & rspace that may come from <mo> if we are an outermost
michael@0 247 // embellished container (we fetch values from the core since they may use
michael@0 248 // units that depend on style data, and style changes could have occurred
michael@0 249 // in the core since our last visit there)
michael@0 250 nscoord leftSpace = onePixel;
michael@0 251 nscoord rightSpace = onePixel;
michael@0 252 if (outermostEmbellished) {
michael@0 253 nsEmbellishData coreData;
michael@0 254 GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
michael@0 255 leftSpace += StyleVisibility()->mDirection ?
michael@0 256 coreData.trailingSpace : coreData.leadingSpace;
michael@0 257 rightSpace += StyleVisibility()->mDirection ?
michael@0 258 coreData.leadingSpace : coreData.trailingSpace;
michael@0 259 }
michael@0 260
michael@0 261 //////////////////
michael@0 262 // Get shifts
michael@0 263 nscoord numShift = 0;
michael@0 264 nscoord denShift = 0;
michael@0 265
michael@0 266 // Rule 15b, App. G, TeXbook
michael@0 267 nscoord numShift1, numShift2, numShift3;
michael@0 268 nscoord denShift1, denShift2;
michael@0 269
michael@0 270 GetNumeratorShifts(fm, numShift1, numShift2, numShift3);
michael@0 271 GetDenominatorShifts(fm, denShift1, denShift2);
michael@0 272 if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) {
michael@0 273 // C > T
michael@0 274 numShift = numShift1;
michael@0 275 denShift = denShift1;
michael@0 276 }
michael@0 277 else {
michael@0 278 numShift = (0 < mLineRect.height) ? numShift2 : numShift3;
michael@0 279 denShift = denShift2;
michael@0 280 }
michael@0 281
michael@0 282 nscoord minClearance = 0;
michael@0 283 nscoord actualClearance = 0;
michael@0 284
michael@0 285 nscoord actualRuleThickness = mLineThickness;
michael@0 286
michael@0 287 if (0 == actualRuleThickness) {
michael@0 288 // Rule 15c, App. G, TeXbook
michael@0 289
michael@0 290 // min clearance between numerator and denominator
michael@0 291 minClearance = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK ?
michael@0 292 7 * defaultRuleThickness : 3 * defaultRuleThickness;
michael@0 293 actualClearance =
michael@0 294 (numShift - bmNum.descent) - (bmDen.ascent - denShift);
michael@0 295 // actualClearance should be >= minClearance
michael@0 296 if (actualClearance < minClearance) {
michael@0 297 nscoord halfGap = (minClearance - actualClearance)/2;
michael@0 298 numShift += halfGap;
michael@0 299 denShift += halfGap;
michael@0 300 }
michael@0 301 }
michael@0 302 else {
michael@0 303 // Rule 15d, App. G, TeXbook
michael@0 304
michael@0 305 // min clearance between numerator or denominator and middle of bar
michael@0 306
michael@0 307 // TeX has a different interpretation of the thickness.
michael@0 308 // Try $a \above10pt b$ to see. Here is what TeX does:
michael@0 309 // minClearance = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK
michael@0 310 // ? 3 * actualRuleThickness : actualRuleThickness;
michael@0 311
michael@0 312 // we slightly depart from TeX here. We use the defaultRuleThickness instead
michael@0 313 // of the value coming from the linethickness attribute, i.e., we recover what
michael@0 314 // TeX does if the user hasn't set linethickness. But when the linethickness
michael@0 315 // is set, we avoid the wide gap problem.
michael@0 316 minClearance = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK ?
michael@0 317 3 * defaultRuleThickness : defaultRuleThickness + onePixel;
michael@0 318
michael@0 319 // adjust numShift to maintain minClearance if needed
michael@0 320 actualClearance =
michael@0 321 (numShift - bmNum.descent) - (axisHeight + actualRuleThickness/2);
michael@0 322 if (actualClearance < minClearance) {
michael@0 323 numShift += (minClearance - actualClearance);
michael@0 324 }
michael@0 325 // adjust denShift to maintain minClearance if needed
michael@0 326 actualClearance =
michael@0 327 (axisHeight - actualRuleThickness/2) - (bmDen.ascent - denShift);
michael@0 328 if (actualClearance < minClearance) {
michael@0 329 denShift += (minClearance - actualClearance);
michael@0 330 }
michael@0 331 }
michael@0 332
michael@0 333 //////////////////
michael@0 334 // Place Children
michael@0 335
michael@0 336 // XXX Need revisiting the width. TeX uses the exact width
michael@0 337 // e.g. in $$\huge\frac{\displaystyle\int}{i}$$
michael@0 338 nscoord width = std::max(bmNum.width, bmDen.width);
michael@0 339 nscoord dxNum = leftSpace + (width - sizeNum.Width())/2;
michael@0 340 nscoord dxDen = leftSpace + (width - sizeDen.Width())/2;
michael@0 341 width += leftSpace + rightSpace;
michael@0 342
michael@0 343 // see if the numalign attribute is there
michael@0 344 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::numalign_, value);
michael@0 345 if (value.EqualsLiteral("left"))
michael@0 346 dxNum = leftSpace;
michael@0 347 else if (value.EqualsLiteral("right"))
michael@0 348 dxNum = width - rightSpace - sizeNum.Width();
michael@0 349
michael@0 350 // see if the denomalign attribute is there
michael@0 351 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::denomalign_, value);
michael@0 352 if (value.EqualsLiteral("left"))
michael@0 353 dxDen = leftSpace;
michael@0 354 else if (value.EqualsLiteral("right"))
michael@0 355 dxDen = width - rightSpace - sizeDen.Width();
michael@0 356
michael@0 357 mBoundingMetrics.rightBearing =
michael@0 358 std::max(dxNum + bmNum.rightBearing, dxDen + bmDen.rightBearing);
michael@0 359 if (mBoundingMetrics.rightBearing < width - rightSpace)
michael@0 360 mBoundingMetrics.rightBearing = width - rightSpace;
michael@0 361 mBoundingMetrics.leftBearing =
michael@0 362 std::min(dxNum + bmNum.leftBearing, dxDen + bmDen.leftBearing);
michael@0 363 if (mBoundingMetrics.leftBearing > leftSpace)
michael@0 364 mBoundingMetrics.leftBearing = leftSpace;
michael@0 365 mBoundingMetrics.ascent = bmNum.ascent + numShift;
michael@0 366 mBoundingMetrics.descent = bmDen.descent + denShift;
michael@0 367 mBoundingMetrics.width = width;
michael@0 368
michael@0 369 aDesiredSize.SetTopAscent(sizeNum.TopAscent() + numShift);
michael@0 370 aDesiredSize.Height() = aDesiredSize.TopAscent() +
michael@0 371 sizeDen.Height() - sizeDen.TopAscent() + denShift;
michael@0 372 aDesiredSize.Width() = mBoundingMetrics.width;
michael@0 373 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
michael@0 374
michael@0 375 mReference.x = 0;
michael@0 376 mReference.y = aDesiredSize.TopAscent();
michael@0 377
michael@0 378 if (aPlaceOrigin) {
michael@0 379 nscoord dy;
michael@0 380 // place numerator
michael@0 381 dy = 0;
michael@0 382 FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dxNum, dy, 0);
michael@0 383 // place denominator
michael@0 384 dy = aDesiredSize.Height() - sizeDen.Height();
michael@0 385 FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dxDen, dy, 0);
michael@0 386 // place the fraction bar - dy is top of bar
michael@0 387 dy = aDesiredSize.TopAscent() - (axisHeight + actualRuleThickness/2);
michael@0 388 mLineRect.SetRect(leftSpace, dy, width - (leftSpace + rightSpace),
michael@0 389 actualRuleThickness);
michael@0 390 }
michael@0 391 } else {
michael@0 392 nscoord numShift = 0.0;
michael@0 393 nscoord denShift = 0.0;
michael@0 394 nscoord padding = 3 * defaultRuleThickness;
michael@0 395 nscoord slashRatio = 3;
michael@0 396
michael@0 397 // Define the constant used in the expression of the maximum width
michael@0 398 nscoord em = fm->EmHeight();
michael@0 399 nscoord slashMaxWidthConstant = 2 * em;
michael@0 400
michael@0 401 // For large line thicknesses the minimum slash height is limited to the
michael@0 402 // largest expected height of a fraction
michael@0 403 nscoord slashMinHeight = slashRatio *
michael@0 404 std::min(2 * mLineThickness, slashMaxWidthConstant);
michael@0 405
michael@0 406 nscoord leadingSpace = padding;
michael@0 407 nscoord trailingSpace = padding;
michael@0 408 if (outermostEmbellished) {
michael@0 409 nsEmbellishData coreData;
michael@0 410 GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
michael@0 411 leadingSpace += coreData.leadingSpace;
michael@0 412 trailingSpace += coreData.trailingSpace;
michael@0 413 }
michael@0 414 nscoord delta;
michael@0 415
michael@0 416 // ___________
michael@0 417 // | | /
michael@0 418 // {|-NUMERATOR-| /
michael@0 419 // {|___________| S
michael@0 420 // { L
michael@0 421 // numShift{ A
michael@0 422 // ------------------------------------------------------- baseline
michael@0 423 // S _____________ } denShift
michael@0 424 // H | |}
michael@0 425 // / |-DENOMINATOR-|}
michael@0 426 // / |_____________|
michael@0 427 //
michael@0 428
michael@0 429 // first, ensure that the top of the numerator is at least as high as the
michael@0 430 // top of the denominator (and the reverse for the bottoms)
michael@0 431 delta = std::max(bmDen.ascent - bmNum.ascent,
michael@0 432 bmNum.descent - bmDen.descent) / 2;
michael@0 433 if (delta > 0) {
michael@0 434 numShift += delta;
michael@0 435 denShift += delta;
michael@0 436 }
michael@0 437
michael@0 438 if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) {
michael@0 439 delta = std::min(bmDen.ascent + bmDen.descent,
michael@0 440 bmNum.ascent + bmNum.descent) / 2;
michael@0 441 numShift += delta;
michael@0 442 denShift += delta;
michael@0 443 } else {
michael@0 444 nscoord xHeight = fm->XHeight();
michael@0 445 numShift += xHeight / 2;
michael@0 446 denShift += xHeight / 4;
michael@0 447 }
michael@0 448
michael@0 449 // Set the ascent/descent of our BoundingMetrics.
michael@0 450 mBoundingMetrics.ascent = bmNum.ascent + numShift;
michael@0 451 mBoundingMetrics.descent = bmDen.descent + denShift;
michael@0 452
michael@0 453 // At this point the height of the slash is
michael@0 454 // mBoundingMetrics.ascent + mBoundingMetrics.descent
michael@0 455 // Ensure that it is greater than slashMinHeight
michael@0 456 delta = (slashMinHeight -
michael@0 457 (mBoundingMetrics.ascent + mBoundingMetrics.descent)) / 2;
michael@0 458 if (delta > 0) {
michael@0 459 mBoundingMetrics.ascent += delta;
michael@0 460 mBoundingMetrics.descent += delta;
michael@0 461 }
michael@0 462
michael@0 463 // Set the width of the slash
michael@0 464 if (aWidthOnly) {
michael@0 465 mLineRect.width = mLineThickness + slashMaxWidthConstant;
michael@0 466 } else {
michael@0 467 mLineRect.width = mLineThickness +
michael@0 468 std::min(slashMaxWidthConstant,
michael@0 469 (mBoundingMetrics.ascent + mBoundingMetrics.descent) /
michael@0 470 slashRatio);
michael@0 471 }
michael@0 472
michael@0 473 // Set horizontal bounding metrics
michael@0 474 if (StyleVisibility()->mDirection) {
michael@0 475 mBoundingMetrics.leftBearing = trailingSpace + bmDen.leftBearing;
michael@0 476 mBoundingMetrics.rightBearing = trailingSpace + bmDen.width + mLineRect.width + bmNum.rightBearing;
michael@0 477 } else {
michael@0 478 mBoundingMetrics.leftBearing = leadingSpace + bmNum.leftBearing;
michael@0 479 mBoundingMetrics.rightBearing = leadingSpace + bmNum.width + mLineRect.width + bmDen.rightBearing;
michael@0 480 }
michael@0 481 mBoundingMetrics.width =
michael@0 482 leadingSpace + bmNum.width + mLineRect.width + bmDen.width +
michael@0 483 trailingSpace;
michael@0 484
michael@0 485 // Set aDesiredSize
michael@0 486 aDesiredSize.SetTopAscent(mBoundingMetrics.ascent + padding);
michael@0 487 aDesiredSize.Height() =
michael@0 488 mBoundingMetrics.ascent + mBoundingMetrics.descent + 2 * padding;
michael@0 489 aDesiredSize.Width() = mBoundingMetrics.width;
michael@0 490 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
michael@0 491
michael@0 492 mReference.x = 0;
michael@0 493 mReference.y = aDesiredSize.TopAscent();
michael@0 494
michael@0 495 if (aPlaceOrigin) {
michael@0 496 nscoord dx, dy;
michael@0 497
michael@0 498 // place numerator
michael@0 499 dx = MirrorIfRTL(aDesiredSize.Width(), sizeNum.Width(),
michael@0 500 leadingSpace);
michael@0 501 dy = aDesiredSize.TopAscent() - numShift - sizeNum.TopAscent();
michael@0 502 FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dx, dy, 0);
michael@0 503
michael@0 504 // place the fraction bar
michael@0 505 dx = MirrorIfRTL(aDesiredSize.Width(), mLineRect.width,
michael@0 506 leadingSpace + bmNum.width);
michael@0 507 dy = aDesiredSize.TopAscent() - mBoundingMetrics.ascent;
michael@0 508 mLineRect.SetRect(dx, dy,
michael@0 509 mLineRect.width, aDesiredSize.Height() - 2 * padding);
michael@0 510
michael@0 511 // place denominator
michael@0 512 dx = MirrorIfRTL(aDesiredSize.Width(), sizeDen.Width(),
michael@0 513 leadingSpace + bmNum.width + mLineRect.width);
michael@0 514 dy = aDesiredSize.TopAscent() + denShift - sizeDen.TopAscent();
michael@0 515 FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dx, dy, 0);
michael@0 516 }
michael@0 517
michael@0 518 }
michael@0 519
michael@0 520 return NS_OK;
michael@0 521 }
michael@0 522
michael@0 523 class nsDisplayMathMLSlash : public nsDisplayItem {
michael@0 524 public:
michael@0 525 nsDisplayMathMLSlash(nsDisplayListBuilder* aBuilder,
michael@0 526 nsIFrame* aFrame, const nsRect& aRect,
michael@0 527 nscoord aThickness, bool aRTL)
michael@0 528 : nsDisplayItem(aBuilder, aFrame), mRect(aRect), mThickness(aThickness),
michael@0 529 mRTL(aRTL) {
michael@0 530 MOZ_COUNT_CTOR(nsDisplayMathMLSlash);
michael@0 531 }
michael@0 532 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 533 virtual ~nsDisplayMathMLSlash() {
michael@0 534 MOZ_COUNT_DTOR(nsDisplayMathMLSlash);
michael@0 535 }
michael@0 536 #endif
michael@0 537
michael@0 538 virtual void Paint(nsDisplayListBuilder* aBuilder,
michael@0 539 nsRenderingContext* aCtx) MOZ_OVERRIDE;
michael@0 540 NS_DISPLAY_DECL_NAME("MathMLSlash", TYPE_MATHML_SLASH)
michael@0 541
michael@0 542 private:
michael@0 543 nsRect mRect;
michael@0 544 nscoord mThickness;
michael@0 545 bool mRTL;
michael@0 546 };
michael@0 547
michael@0 548 void nsDisplayMathMLSlash::Paint(nsDisplayListBuilder* aBuilder,
michael@0 549 nsRenderingContext* aCtx)
michael@0 550 {
michael@0 551 // get the gfxRect
michael@0 552 nsPresContext* presContext = mFrame->PresContext();
michael@0 553 gfxRect rect = presContext->AppUnitsToGfxUnits(mRect + ToReferenceFrame());
michael@0 554
michael@0 555 // paint with the current text color
michael@0 556 aCtx->SetColor(mFrame->GetVisitedDependentColor(eCSSProperty_color));
michael@0 557
michael@0 558 // draw the slash as a parallelogram
michael@0 559 gfxContext *gfxCtx = aCtx->ThebesContext();
michael@0 560 gfxPoint delta = gfxPoint(presContext->AppUnitsToGfxUnits(mThickness), 0);
michael@0 561 gfxCtx->NewPath();
michael@0 562
michael@0 563 if (mRTL) {
michael@0 564 gfxCtx->MoveTo(rect.TopLeft());
michael@0 565 gfxCtx->LineTo(rect.TopLeft() + delta);
michael@0 566 gfxCtx->LineTo(rect.BottomRight());
michael@0 567 gfxCtx->LineTo(rect.BottomRight() - delta);
michael@0 568 } else {
michael@0 569 gfxCtx->MoveTo(rect.BottomLeft());
michael@0 570 gfxCtx->LineTo(rect.BottomLeft() + delta);
michael@0 571 gfxCtx->LineTo(rect.TopRight());
michael@0 572 gfxCtx->LineTo(rect.TopRight() - delta);
michael@0 573 }
michael@0 574
michael@0 575 gfxCtx->ClosePath();
michael@0 576 gfxCtx->Fill();
michael@0 577 }
michael@0 578
michael@0 579 void
michael@0 580 nsMathMLmfracFrame::DisplaySlash(nsDisplayListBuilder* aBuilder,
michael@0 581 nsIFrame* aFrame, const nsRect& aRect,
michael@0 582 nscoord aThickness,
michael@0 583 const nsDisplayListSet& aLists) {
michael@0 584 if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty())
michael@0 585 return;
michael@0 586
michael@0 587 aLists.Content()->AppendNewToTop(new (aBuilder)
michael@0 588 nsDisplayMathMLSlash(aBuilder, aFrame, aRect, aThickness,
michael@0 589 StyleVisibility()->mDirection));
michael@0 590 }

mercurial