layout/mathml/nsMathMLmencloseFrame.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 "nsMathMLmencloseFrame.h"
michael@0 7 #include "nsPresContext.h"
michael@0 8 #include "nsRenderingContext.h"
michael@0 9 #include "nsWhitespaceTokenizer.h"
michael@0 10
michael@0 11 #include "nsDisplayList.h"
michael@0 12 #include "gfxContext.h"
michael@0 13 #include "nsMathMLChar.h"
michael@0 14 #include <algorithm>
michael@0 15
michael@0 16 //
michael@0 17 // <menclose> -- enclose content with a stretching symbol such
michael@0 18 // as a long division sign. - implementation
michael@0 19
michael@0 20 // longdiv:
michael@0 21 // Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis
michael@0 22 // renders better with current font support.
michael@0 23 static const char16_t kLongDivChar = ')';
michael@0 24
michael@0 25 // radical: 'SQUARE ROOT'
michael@0 26 static const char16_t kRadicalChar = 0x221A;
michael@0 27
michael@0 28 // updiagonalstrike
michael@0 29 static const uint8_t kArrowHeadSize = 10;
michael@0 30
michael@0 31 nsIFrame*
michael@0 32 NS_NewMathMLmencloseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 33 {
michael@0 34 return new (aPresShell) nsMathMLmencloseFrame(aContext);
michael@0 35 }
michael@0 36
michael@0 37 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame)
michael@0 38
michael@0 39 nsMathMLmencloseFrame::nsMathMLmencloseFrame(nsStyleContext* aContext) :
michael@0 40 nsMathMLContainerFrame(aContext), mNotationsToDraw(0),
michael@0 41 mLongDivCharIndex(-1), mRadicalCharIndex(-1), mContentWidth(0)
michael@0 42 {
michael@0 43 }
michael@0 44
michael@0 45 nsMathMLmencloseFrame::~nsMathMLmencloseFrame()
michael@0 46 {
michael@0 47 }
michael@0 48
michael@0 49 nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask)
michael@0 50 {
michael@0 51 // Is the char already allocated?
michael@0 52 if ((mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) ||
michael@0 53 (mask == NOTATION_RADICAL && mRadicalCharIndex >= 0))
michael@0 54 return NS_OK;
michael@0 55
michael@0 56 // No need to track the style context given to our MathML chars.
michael@0 57 // The Style System will use Get/SetAdditionalStyleContext() to keep it
michael@0 58 // up-to-date if dynamic changes arise.
michael@0 59 uint32_t i = mMathMLChar.Length();
michael@0 60 nsAutoString Char;
michael@0 61
michael@0 62 if (!mMathMLChar.AppendElement())
michael@0 63 return NS_ERROR_OUT_OF_MEMORY;
michael@0 64
michael@0 65 if (mask == NOTATION_LONGDIV) {
michael@0 66 Char.Assign(kLongDivChar);
michael@0 67 mLongDivCharIndex = i;
michael@0 68 } else if (mask == NOTATION_RADICAL) {
michael@0 69 Char.Assign(kRadicalChar);
michael@0 70 mRadicalCharIndex = i;
michael@0 71 }
michael@0 72
michael@0 73 nsPresContext *presContext = PresContext();
michael@0 74 mMathMLChar[i].SetData(presContext, Char);
michael@0 75 ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar[i]);
michael@0 76
michael@0 77 return NS_OK;
michael@0 78 }
michael@0 79
michael@0 80 /*
michael@0 81 * Add a notation to draw, if the argument is the name of a known notation.
michael@0 82 * @param aNotation string name of a notation
michael@0 83 */
michael@0 84 nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation)
michael@0 85 {
michael@0 86 nsresult rv;
michael@0 87
michael@0 88 if (aNotation.EqualsLiteral("longdiv")) {
michael@0 89 rv = AllocateMathMLChar(NOTATION_LONGDIV);
michael@0 90 NS_ENSURE_SUCCESS(rv, rv);
michael@0 91 mNotationsToDraw |= NOTATION_LONGDIV;
michael@0 92 } else if (aNotation.EqualsLiteral("actuarial")) {
michael@0 93 mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_TOP);
michael@0 94 } else if (aNotation.EqualsLiteral("radical")) {
michael@0 95 rv = AllocateMathMLChar(NOTATION_RADICAL);
michael@0 96 NS_ENSURE_SUCCESS(rv, rv);
michael@0 97 mNotationsToDraw |= NOTATION_RADICAL;
michael@0 98 } else if (aNotation.EqualsLiteral("box")) {
michael@0 99 mNotationsToDraw |= (NOTATION_LEFT | NOTATION_RIGHT |
michael@0 100 NOTATION_TOP | NOTATION_BOTTOM);
michael@0 101 } else if (aNotation.EqualsLiteral("roundedbox")) {
michael@0 102 mNotationsToDraw |= NOTATION_ROUNDEDBOX;
michael@0 103 } else if (aNotation.EqualsLiteral("circle")) {
michael@0 104 mNotationsToDraw |= NOTATION_CIRCLE;
michael@0 105 } else if (aNotation.EqualsLiteral("left")) {
michael@0 106 mNotationsToDraw |= NOTATION_LEFT;
michael@0 107 } else if (aNotation.EqualsLiteral("right")) {
michael@0 108 mNotationsToDraw |= NOTATION_RIGHT;
michael@0 109 } else if (aNotation.EqualsLiteral("top")) {
michael@0 110 mNotationsToDraw |= NOTATION_TOP;
michael@0 111 } else if (aNotation.EqualsLiteral("bottom")) {
michael@0 112 mNotationsToDraw |= NOTATION_BOTTOM;
michael@0 113 } else if (aNotation.EqualsLiteral("updiagonalstrike")) {
michael@0 114 mNotationsToDraw |= NOTATION_UPDIAGONALSTRIKE;
michael@0 115 } else if (aNotation.EqualsLiteral("updiagonalarrow")) {
michael@0 116 mNotationsToDraw |= NOTATION_UPDIAGONALARROW;
michael@0 117 } else if (aNotation.EqualsLiteral("downdiagonalstrike")) {
michael@0 118 mNotationsToDraw |= NOTATION_DOWNDIAGONALSTRIKE;
michael@0 119 } else if (aNotation.EqualsLiteral("verticalstrike")) {
michael@0 120 mNotationsToDraw |= NOTATION_VERTICALSTRIKE;
michael@0 121 } else if (aNotation.EqualsLiteral("horizontalstrike")) {
michael@0 122 mNotationsToDraw |= NOTATION_HORIZONTALSTRIKE;
michael@0 123 } else if (aNotation.EqualsLiteral("madruwb")) {
michael@0 124 mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_BOTTOM);
michael@0 125 }
michael@0 126
michael@0 127 return NS_OK;
michael@0 128 }
michael@0 129
michael@0 130 /*
michael@0 131 * Initialize the list of notations to draw
michael@0 132 */
michael@0 133 void nsMathMLmencloseFrame::InitNotations()
michael@0 134 {
michael@0 135 mNotationsToDraw = 0;
michael@0 136 mLongDivCharIndex = mRadicalCharIndex = -1;
michael@0 137 mMathMLChar.Clear();
michael@0 138
michael@0 139 nsAutoString value;
michael@0 140
michael@0 141 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::notation_, value)) {
michael@0 142 // parse the notation attribute
michael@0 143 nsWhitespaceTokenizer tokenizer(value);
michael@0 144
michael@0 145 while (tokenizer.hasMoreTokens())
michael@0 146 AddNotation(tokenizer.nextToken());
michael@0 147
michael@0 148 if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
michael@0 149 // For <menclose notation="updiagonalstrike updiagonalarrow">, if
michael@0 150 // the two notations are drawn then the strike line may cause the point of
michael@0 151 // the arrow to be too wide. Hence we will only draw the updiagonalarrow
michael@0 152 // and the arrow shaft may be thought to be the updiagonalstrike.
michael@0 153 mNotationsToDraw &= ~NOTATION_UPDIAGONALSTRIKE;
michael@0 154 }
michael@0 155 } else {
michael@0 156 // default: longdiv
michael@0 157 if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV)))
michael@0 158 return;
michael@0 159 mNotationsToDraw = NOTATION_LONGDIV;
michael@0 160 }
michael@0 161 }
michael@0 162
michael@0 163 NS_IMETHODIMP
michael@0 164 nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent)
michael@0 165 {
michael@0 166 // let the base class get the default from our parent
michael@0 167 nsMathMLContainerFrame::InheritAutomaticData(aParent);
michael@0 168
michael@0 169 mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
michael@0 170
michael@0 171 InitNotations();
michael@0 172
michael@0 173 return NS_OK;
michael@0 174 }
michael@0 175
michael@0 176 NS_IMETHODIMP
michael@0 177 nsMathMLmencloseFrame::TransmitAutomaticData()
michael@0 178 {
michael@0 179 if (IsToDraw(NOTATION_RADICAL)) {
michael@0 180 // The TeXBook (Ch 17. p.141) says that \sqrt is cramped
michael@0 181 UpdatePresentationDataFromChildAt(0, -1,
michael@0 182 NS_MATHML_COMPRESSED,
michael@0 183 NS_MATHML_COMPRESSED);
michael@0 184 }
michael@0 185
michael@0 186 return NS_OK;
michael@0 187 }
michael@0 188
michael@0 189 void
michael@0 190 nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 191 const nsRect& aDirtyRect,
michael@0 192 const nsDisplayListSet& aLists)
michael@0 193 {
michael@0 194 /////////////
michael@0 195 // paint the menclosed content
michael@0 196 nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
michael@0 197
michael@0 198 if (NS_MATHML_HAS_ERROR(mPresentationData.flags))
michael@0 199 return;
michael@0 200
michael@0 201 nsRect mencloseRect = nsIFrame::GetRect();
michael@0 202 mencloseRect.x = mencloseRect.y = 0;
michael@0 203
michael@0 204 if (IsToDraw(NOTATION_RADICAL)) {
michael@0 205 mMathMLChar[mRadicalCharIndex].Display(aBuilder, this, aLists, 0);
michael@0 206
michael@0 207 nsRect rect;
michael@0 208 mMathMLChar[mRadicalCharIndex].GetRect(rect);
michael@0 209 rect.MoveBy(StyleVisibility()->mDirection ? -mContentWidth : rect.width, 0);
michael@0 210 rect.SizeTo(mContentWidth, mRuleThickness);
michael@0 211 DisplayBar(aBuilder, this, rect, aLists);
michael@0 212 }
michael@0 213
michael@0 214 if (IsToDraw(NOTATION_LONGDIV)) {
michael@0 215 mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1);
michael@0 216
michael@0 217 nsRect rect;
michael@0 218 mMathMLChar[mLongDivCharIndex].GetRect(rect);
michael@0 219 rect.SizeTo(rect.width + mContentWidth, mRuleThickness);
michael@0 220 DisplayBar(aBuilder, this, rect, aLists);
michael@0 221 }
michael@0 222
michael@0 223 if (IsToDraw(NOTATION_TOP)) {
michael@0 224 nsRect rect(0, 0, mencloseRect.width, mRuleThickness);
michael@0 225 DisplayBar(aBuilder, this, rect, aLists);
michael@0 226 }
michael@0 227
michael@0 228 if (IsToDraw(NOTATION_BOTTOM)) {
michael@0 229 nsRect rect(0, mencloseRect.height - mRuleThickness,
michael@0 230 mencloseRect.width, mRuleThickness);
michael@0 231 DisplayBar(aBuilder, this, rect, aLists);
michael@0 232 }
michael@0 233
michael@0 234 if (IsToDraw(NOTATION_LEFT)) {
michael@0 235 nsRect rect(0, 0, mRuleThickness, mencloseRect.height);
michael@0 236 DisplayBar(aBuilder, this, rect, aLists);
michael@0 237 }
michael@0 238
michael@0 239 if (IsToDraw(NOTATION_RIGHT)) {
michael@0 240 nsRect rect(mencloseRect.width - mRuleThickness, 0,
michael@0 241 mRuleThickness, mencloseRect.height);
michael@0 242 DisplayBar(aBuilder, this, rect, aLists);
michael@0 243 }
michael@0 244
michael@0 245 if (IsToDraw(NOTATION_ROUNDEDBOX)) {
michael@0 246 DisplayNotation(aBuilder, this, mencloseRect, aLists,
michael@0 247 mRuleThickness, NOTATION_ROUNDEDBOX);
michael@0 248 }
michael@0 249
michael@0 250 if (IsToDraw(NOTATION_CIRCLE)) {
michael@0 251 DisplayNotation(aBuilder, this, mencloseRect, aLists,
michael@0 252 mRuleThickness, NOTATION_CIRCLE);
michael@0 253 }
michael@0 254
michael@0 255 if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) {
michael@0 256 DisplayNotation(aBuilder, this, mencloseRect, aLists,
michael@0 257 mRuleThickness, NOTATION_UPDIAGONALSTRIKE);
michael@0 258 }
michael@0 259
michael@0 260 if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
michael@0 261 DisplayNotation(aBuilder, this, mencloseRect, aLists,
michael@0 262 mRuleThickness, NOTATION_UPDIAGONALARROW);
michael@0 263 }
michael@0 264
michael@0 265 if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) {
michael@0 266 DisplayNotation(aBuilder, this, mencloseRect, aLists,
michael@0 267 mRuleThickness, NOTATION_DOWNDIAGONALSTRIKE);
michael@0 268 }
michael@0 269
michael@0 270 if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) {
michael@0 271 nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2,
michael@0 272 mencloseRect.width, mRuleThickness);
michael@0 273 DisplayBar(aBuilder, this, rect, aLists);
michael@0 274 }
michael@0 275
michael@0 276 if (IsToDraw(NOTATION_VERTICALSTRIKE)) {
michael@0 277 nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0,
michael@0 278 mRuleThickness, mencloseRect.height);
michael@0 279 DisplayBar(aBuilder, this, rect, aLists);
michael@0 280 }
michael@0 281 }
michael@0 282
michael@0 283 /* virtual */ nsresult
michael@0 284 nsMathMLmencloseFrame::MeasureForWidth(nsRenderingContext& aRenderingContext,
michael@0 285 nsHTMLReflowMetrics& aDesiredSize)
michael@0 286 {
michael@0 287 return PlaceInternal(aRenderingContext, false, aDesiredSize, true);
michael@0 288 }
michael@0 289
michael@0 290 /* virtual */ nsresult
michael@0 291 nsMathMLmencloseFrame::Place(nsRenderingContext& aRenderingContext,
michael@0 292 bool aPlaceOrigin,
michael@0 293 nsHTMLReflowMetrics& aDesiredSize)
michael@0 294 {
michael@0 295 return PlaceInternal(aRenderingContext, aPlaceOrigin, aDesiredSize, false);
michael@0 296 }
michael@0 297
michael@0 298 /* virtual */ nsresult
michael@0 299 nsMathMLmencloseFrame::PlaceInternal(nsRenderingContext& aRenderingContext,
michael@0 300 bool aPlaceOrigin,
michael@0 301 nsHTMLReflowMetrics& aDesiredSize,
michael@0 302 bool aWidthOnly)
michael@0 303 {
michael@0 304 ///////////////
michael@0 305 // Measure the size of our content using the base class to format like an
michael@0 306 // inferred mrow.
michael@0 307 nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode());
michael@0 308 nsresult rv =
michael@0 309 nsMathMLContainerFrame::Place(aRenderingContext, false, baseSize);
michael@0 310
michael@0 311 if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
michael@0 312 DidReflowChildren(GetFirstPrincipalChild());
michael@0 313 return rv;
michael@0 314 }
michael@0 315
michael@0 316 nsBoundingMetrics bmBase = baseSize.mBoundingMetrics;
michael@0 317 nscoord dx_left = 0, dx_right = 0;
michael@0 318 nsBoundingMetrics bmLongdivChar, bmRadicalChar;
michael@0 319 nscoord radicalAscent = 0, radicalDescent = 0;
michael@0 320 nscoord longdivAscent = 0, longdivDescent = 0;
michael@0 321 nscoord psi = 0;
michael@0 322
michael@0 323 ///////////////
michael@0 324 // Thickness of bars and font metrics
michael@0 325 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
michael@0 326
michael@0 327 nscoord mEmHeight;
michael@0 328 nsRefPtr<nsFontMetrics> fm;
michael@0 329 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
michael@0 330 aRenderingContext.SetFont(fm);
michael@0 331 GetRuleThickness(aRenderingContext, fm, mRuleThickness);
michael@0 332 GetEmHeight(fm, mEmHeight);
michael@0 333
michael@0 334 char16_t one = '1';
michael@0 335 nsBoundingMetrics bmOne = aRenderingContext.GetBoundingMetrics(&one, 1);
michael@0 336
michael@0 337 ///////////////
michael@0 338 // General rules: the menclose element takes the size of the enclosed content.
michael@0 339 // We add a padding when needed.
michael@0 340
michael@0 341 // determine padding & psi
michael@0 342 nscoord padding = 3 * mRuleThickness;
michael@0 343 nscoord delta = padding % onePixel;
michael@0 344 if (delta)
michael@0 345 padding += onePixel - delta; // round up
michael@0 346
michael@0 347 if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
michael@0 348 nscoord phi;
michael@0 349 // Rule 11, App. G, TeXbook
michael@0 350 // psi = clearance between rule and content
michael@0 351 if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK)
michael@0 352 phi = fm->XHeight();
michael@0 353 else
michael@0 354 phi = mRuleThickness;
michael@0 355 psi = mRuleThickness + phi / 4;
michael@0 356
michael@0 357 delta = psi % onePixel;
michael@0 358 if (delta)
michael@0 359 psi += onePixel - delta; // round up
michael@0 360 }
michael@0 361
michael@0 362 if (mRuleThickness < onePixel)
michael@0 363 mRuleThickness = onePixel;
michael@0 364
michael@0 365 // Set horizontal parameters
michael@0 366 if (IsToDraw(NOTATION_ROUNDEDBOX) ||
michael@0 367 IsToDraw(NOTATION_TOP) ||
michael@0 368 IsToDraw(NOTATION_LEFT) ||
michael@0 369 IsToDraw(NOTATION_BOTTOM) ||
michael@0 370 IsToDraw(NOTATION_CIRCLE))
michael@0 371 dx_left = padding;
michael@0 372
michael@0 373 if (IsToDraw(NOTATION_ROUNDEDBOX) ||
michael@0 374 IsToDraw(NOTATION_TOP) ||
michael@0 375 IsToDraw(NOTATION_RIGHT) ||
michael@0 376 IsToDraw(NOTATION_BOTTOM) ||
michael@0 377 IsToDraw(NOTATION_CIRCLE))
michael@0 378 dx_right = padding;
michael@0 379
michael@0 380 // Set vertical parameters
michael@0 381 if (IsToDraw(NOTATION_RIGHT) ||
michael@0 382 IsToDraw(NOTATION_LEFT) ||
michael@0 383 IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
michael@0 384 IsToDraw(NOTATION_UPDIAGONALARROW) ||
michael@0 385 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
michael@0 386 IsToDraw(NOTATION_VERTICALSTRIKE) ||
michael@0 387 IsToDraw(NOTATION_CIRCLE) ||
michael@0 388 IsToDraw(NOTATION_ROUNDEDBOX) ||
michael@0 389 IsToDraw(NOTATION_RADICAL) ||
michael@0 390 IsToDraw(NOTATION_LONGDIV)) {
michael@0 391 // set a minimal value for the base height
michael@0 392 bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent);
michael@0 393 bmBase.descent = std::max(0, bmBase.descent);
michael@0 394 }
michael@0 395
michael@0 396 mBoundingMetrics.ascent = bmBase.ascent;
michael@0 397 mBoundingMetrics.descent = bmBase.descent;
michael@0 398
michael@0 399 if (IsToDraw(NOTATION_ROUNDEDBOX) ||
michael@0 400 IsToDraw(NOTATION_TOP) ||
michael@0 401 IsToDraw(NOTATION_LEFT) ||
michael@0 402 IsToDraw(NOTATION_RIGHT) ||
michael@0 403 IsToDraw(NOTATION_CIRCLE))
michael@0 404 mBoundingMetrics.ascent += padding;
michael@0 405
michael@0 406 if (IsToDraw(NOTATION_ROUNDEDBOX) ||
michael@0 407 IsToDraw(NOTATION_LEFT) ||
michael@0 408 IsToDraw(NOTATION_RIGHT) ||
michael@0 409 IsToDraw(NOTATION_BOTTOM) ||
michael@0 410 IsToDraw(NOTATION_CIRCLE))
michael@0 411 mBoundingMetrics.descent += padding;
michael@0 412
michael@0 413 ///////////////
michael@0 414 // updiagonal arrow notation. We need enough space at the top right corner to
michael@0 415 // draw the arrow head.
michael@0 416 if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
michael@0 417 // This is an estimate, see nsDisplayNotation::Paint for the exact head size
michael@0 418 nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness;
michael@0 419
michael@0 420 // We want that the arrow shaft strikes the menclose content and that the
michael@0 421 // arrow head does not overlap with that content. Hence we add some space
michael@0 422 // on the right. We don't add space on the top but only ensure that the
michael@0 423 // ascent is large enough.
michael@0 424 dx_right = std::max(dx_right, arrowHeadSize);
michael@0 425 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize);
michael@0 426 }
michael@0 427
michael@0 428 ///////////////
michael@0 429 // circle notation: we don't want the ellipse to overlap the enclosed
michael@0 430 // content. Hence, we need to increase the size of the bounding box by a
michael@0 431 // factor of at least sqrt(2).
michael@0 432 if (IsToDraw(NOTATION_CIRCLE)) {
michael@0 433 double ratio = (sqrt(2.0) - 1.0) / 2.0;
michael@0 434 nscoord padding2;
michael@0 435
michael@0 436 // Update horizontal parameters
michael@0 437 padding2 = ratio * bmBase.width;
michael@0 438
michael@0 439 dx_left = std::max(dx_left, padding2);
michael@0 440 dx_right = std::max(dx_right, padding2);
michael@0 441
michael@0 442 // Update vertical parameters
michael@0 443 padding2 = ratio * (bmBase.ascent + bmBase.descent);
michael@0 444
michael@0 445 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
michael@0 446 bmBase.ascent + padding2);
michael@0 447 mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
michael@0 448 bmBase.descent + padding2);
michael@0 449 }
michael@0 450
michael@0 451 ///////////////
michael@0 452 // longdiv notation:
michael@0 453 if (IsToDraw(NOTATION_LONGDIV)) {
michael@0 454 if (aWidthOnly) {
michael@0 455 nscoord longdiv_width = mMathMLChar[mLongDivCharIndex].
michael@0 456 GetMaxWidth(PresContext(), aRenderingContext);
michael@0 457
michael@0 458 // Update horizontal parameters
michael@0 459 dx_left = std::max(dx_left, longdiv_width);
michael@0 460 } else {
michael@0 461 // Stretch the parenthesis to the appropriate height if it is not
michael@0 462 // big enough.
michael@0 463 nsBoundingMetrics contSize = bmBase;
michael@0 464 contSize.ascent = mRuleThickness;
michael@0 465 contSize.descent = bmBase.ascent + bmBase.descent + psi;
michael@0 466
michael@0 467 // height(longdiv) should be >= height(base) + psi + mRuleThickness
michael@0 468 mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aRenderingContext,
michael@0 469 NS_STRETCH_DIRECTION_VERTICAL,
michael@0 470 contSize, bmLongdivChar,
michael@0 471 NS_STRETCH_LARGER, false);
michael@0 472 mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar);
michael@0 473
michael@0 474 // Update horizontal parameters
michael@0 475 dx_left = std::max(dx_left, bmLongdivChar.width);
michael@0 476
michael@0 477 // Update vertical parameters
michael@0 478 longdivAscent = bmBase.ascent + psi + mRuleThickness;
michael@0 479 longdivDescent = std::max(bmBase.descent,
michael@0 480 (bmLongdivChar.ascent + bmLongdivChar.descent -
michael@0 481 longdivAscent));
michael@0 482
michael@0 483 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
michael@0 484 longdivAscent);
michael@0 485 mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
michael@0 486 longdivDescent);
michael@0 487 }
michael@0 488 }
michael@0 489
michael@0 490 ///////////////
michael@0 491 // radical notation:
michael@0 492 if (IsToDraw(NOTATION_RADICAL)) {
michael@0 493 nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left;
michael@0 494
michael@0 495 if (aWidthOnly) {
michael@0 496 nscoord radical_width = mMathMLChar[mRadicalCharIndex].
michael@0 497 GetMaxWidth(PresContext(), aRenderingContext);
michael@0 498
michael@0 499 // Update horizontal parameters
michael@0 500 *dx_leading = std::max(*dx_leading, radical_width);
michael@0 501 } else {
michael@0 502 // Stretch the radical symbol to the appropriate height if it is not
michael@0 503 // big enough.
michael@0 504 nsBoundingMetrics contSize = bmBase;
michael@0 505 contSize.ascent = mRuleThickness;
michael@0 506 contSize.descent = bmBase.ascent + bmBase.descent + psi;
michael@0 507
michael@0 508 // height(radical) should be >= height(base) + psi + mRuleThickness
michael@0 509 mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aRenderingContext,
michael@0 510 NS_STRETCH_DIRECTION_VERTICAL,
michael@0 511 contSize, bmRadicalChar,
michael@0 512 NS_STRETCH_LARGER,
michael@0 513 StyleVisibility()->mDirection);
michael@0 514 mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar);
michael@0 515
michael@0 516 // Update horizontal parameters
michael@0 517 *dx_leading = std::max(*dx_leading, bmRadicalChar.width);
michael@0 518
michael@0 519 // Update vertical parameters
michael@0 520 radicalAscent = bmBase.ascent + psi + mRuleThickness;
michael@0 521 radicalDescent = std::max(bmBase.descent,
michael@0 522 (bmRadicalChar.ascent + bmRadicalChar.descent -
michael@0 523 radicalAscent));
michael@0 524
michael@0 525 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
michael@0 526 radicalAscent);
michael@0 527 mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
michael@0 528 radicalDescent);
michael@0 529 }
michael@0 530 }
michael@0 531
michael@0 532 ///////////////
michael@0 533 //
michael@0 534 if (IsToDraw(NOTATION_CIRCLE) ||
michael@0 535 IsToDraw(NOTATION_ROUNDEDBOX) ||
michael@0 536 (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) {
michael@0 537 // center the menclose around the content (horizontally)
michael@0 538 dx_left = dx_right = std::max(dx_left, dx_right);
michael@0 539 }
michael@0 540
michael@0 541 ///////////////
michael@0 542 // The maximum size is now computed: set the remaining parameters
michael@0 543 mBoundingMetrics.width = dx_left + bmBase.width + dx_right;
michael@0 544
michael@0 545 mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing);
michael@0 546 mBoundingMetrics.rightBearing =
michael@0 547 std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing);
michael@0 548
michael@0 549 aDesiredSize.Width() = mBoundingMetrics.width;
michael@0 550
michael@0 551 aDesiredSize.SetTopAscent(std::max(mBoundingMetrics.ascent, baseSize.TopAscent()));
michael@0 552 aDesiredSize.Height() = aDesiredSize.TopAscent() +
michael@0 553 std::max(mBoundingMetrics.descent, baseSize.Height() - baseSize.TopAscent());
michael@0 554
michael@0 555 if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
michael@0 556 // get the leading to be left at the top of the resulting frame
michael@0 557 // this seems more reliable than using fm->GetLeading() on suspicious
michael@0 558 // fonts
michael@0 559 nscoord leading = nscoord(0.2f * mEmHeight);
michael@0 560 nscoord desiredSizeAscent = aDesiredSize.TopAscent();
michael@0 561 nscoord desiredSizeDescent = aDesiredSize.Height() - aDesiredSize.TopAscent();
michael@0 562
michael@0 563 if (IsToDraw(NOTATION_LONGDIV)) {
michael@0 564 desiredSizeAscent = std::max(desiredSizeAscent,
michael@0 565 longdivAscent + leading);
michael@0 566 desiredSizeDescent = std::max(desiredSizeDescent,
michael@0 567 longdivDescent + mRuleThickness);
michael@0 568 }
michael@0 569
michael@0 570 if (IsToDraw(NOTATION_RADICAL)) {
michael@0 571 desiredSizeAscent = std::max(desiredSizeAscent,
michael@0 572 radicalAscent + leading);
michael@0 573 desiredSizeDescent = std::max(desiredSizeDescent,
michael@0 574 radicalDescent + mRuleThickness);
michael@0 575 }
michael@0 576
michael@0 577 aDesiredSize.SetTopAscent(desiredSizeAscent);
michael@0 578 aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent;
michael@0 579 }
michael@0 580
michael@0 581 if (IsToDraw(NOTATION_CIRCLE) ||
michael@0 582 IsToDraw(NOTATION_ROUNDEDBOX) ||
michael@0 583 (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) {
michael@0 584 // center the menclose around the content (vertically)
michael@0 585 nscoord dy = std::max(aDesiredSize.TopAscent() - bmBase.ascent,
michael@0 586 aDesiredSize.Height() - aDesiredSize.TopAscent() -
michael@0 587 bmBase.descent);
michael@0 588
michael@0 589 aDesiredSize.SetTopAscent(bmBase.ascent + dy);
michael@0 590 aDesiredSize.Height() = aDesiredSize.TopAscent() + bmBase.descent + dy;
michael@0 591 }
michael@0 592
michael@0 593 // Update mBoundingMetrics ascent/descent
michael@0 594 if (IsToDraw(NOTATION_TOP) ||
michael@0 595 IsToDraw(NOTATION_RIGHT) ||
michael@0 596 IsToDraw(NOTATION_LEFT) ||
michael@0 597 IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
michael@0 598 IsToDraw(NOTATION_UPDIAGONALARROW) ||
michael@0 599 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
michael@0 600 IsToDraw(NOTATION_VERTICALSTRIKE) ||
michael@0 601 IsToDraw(NOTATION_CIRCLE) ||
michael@0 602 IsToDraw(NOTATION_ROUNDEDBOX))
michael@0 603 mBoundingMetrics.ascent = aDesiredSize.TopAscent();
michael@0 604
michael@0 605 if (IsToDraw(NOTATION_BOTTOM) ||
michael@0 606 IsToDraw(NOTATION_RIGHT) ||
michael@0 607 IsToDraw(NOTATION_LEFT) ||
michael@0 608 IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
michael@0 609 IsToDraw(NOTATION_UPDIAGONALARROW) ||
michael@0 610 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
michael@0 611 IsToDraw(NOTATION_VERTICALSTRIKE) ||
michael@0 612 IsToDraw(NOTATION_CIRCLE) ||
michael@0 613 IsToDraw(NOTATION_ROUNDEDBOX))
michael@0 614 mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.TopAscent();
michael@0 615
michael@0 616 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
michael@0 617
michael@0 618 mReference.x = 0;
michael@0 619 mReference.y = aDesiredSize.TopAscent();
michael@0 620
michael@0 621 if (aPlaceOrigin) {
michael@0 622 //////////////////
michael@0 623 // Set position and size of MathMLChars
michael@0 624 if (IsToDraw(NOTATION_LONGDIV))
michael@0 625 mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left -
michael@0 626 bmLongdivChar.width,
michael@0 627 aDesiredSize.TopAscent() -
michael@0 628 longdivAscent,
michael@0 629 bmLongdivChar.width,
michael@0 630 bmLongdivChar.ascent +
michael@0 631 bmLongdivChar.descent));
michael@0 632
michael@0 633 if (IsToDraw(NOTATION_RADICAL)) {
michael@0 634 nscoord dx = (StyleVisibility()->mDirection ?
michael@0 635 dx_left + bmBase.width : dx_left - bmRadicalChar.width);
michael@0 636
michael@0 637 mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx,
michael@0 638 aDesiredSize.TopAscent() -
michael@0 639 radicalAscent,
michael@0 640 bmRadicalChar.width,
michael@0 641 bmRadicalChar.ascent +
michael@0 642 bmRadicalChar.descent));
michael@0 643 }
michael@0 644
michael@0 645 mContentWidth = bmBase.width;
michael@0 646
michael@0 647 //////////////////
michael@0 648 // Finish reflowing child frames
michael@0 649 PositionRowChildFrames(dx_left, aDesiredSize.TopAscent());
michael@0 650 }
michael@0 651
michael@0 652 return NS_OK;
michael@0 653 }
michael@0 654
michael@0 655 nscoord
michael@0 656 nsMathMLmencloseFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
michael@0 657 {
michael@0 658 nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
michael@0 659 if (!gap)
michael@0 660 return 0;
michael@0 661
michael@0 662 // Move the MathML characters
michael@0 663 nsRect rect;
michael@0 664 for (uint32_t i = 0; i < mMathMLChar.Length(); i++) {
michael@0 665 mMathMLChar[i].GetRect(rect);
michael@0 666 rect.MoveBy(gap, 0);
michael@0 667 mMathMLChar[i].SetRect(rect);
michael@0 668 }
michael@0 669
michael@0 670 return gap;
michael@0 671 }
michael@0 672
michael@0 673 nsresult
michael@0 674 nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 675 nsIAtom* aAttribute,
michael@0 676 int32_t aModType)
michael@0 677 {
michael@0 678 if (aAttribute == nsGkAtoms::notation_) {
michael@0 679 InitNotations();
michael@0 680 }
michael@0 681
michael@0 682 return nsMathMLContainerFrame::
michael@0 683 AttributeChanged(aNameSpaceID, aAttribute, aModType);
michael@0 684 }
michael@0 685
michael@0 686 //////////////////
michael@0 687 // the Style System will use these to pass the proper style context to our
michael@0 688 // MathMLChar
michael@0 689 nsStyleContext*
michael@0 690 nsMathMLmencloseFrame::GetAdditionalStyleContext(int32_t aIndex) const
michael@0 691 {
michael@0 692 int32_t len = mMathMLChar.Length();
michael@0 693 if (aIndex >= 0 && aIndex < len)
michael@0 694 return mMathMLChar[aIndex].GetStyleContext();
michael@0 695 else
michael@0 696 return nullptr;
michael@0 697 }
michael@0 698
michael@0 699 void
michael@0 700 nsMathMLmencloseFrame::SetAdditionalStyleContext(int32_t aIndex,
michael@0 701 nsStyleContext* aStyleContext)
michael@0 702 {
michael@0 703 int32_t len = mMathMLChar.Length();
michael@0 704 if (aIndex >= 0 && aIndex < len)
michael@0 705 mMathMLChar[aIndex].SetStyleContext(aStyleContext);
michael@0 706 }
michael@0 707
michael@0 708 class nsDisplayNotation : public nsDisplayItem
michael@0 709 {
michael@0 710 public:
michael@0 711 nsDisplayNotation(nsDisplayListBuilder* aBuilder,
michael@0 712 nsIFrame* aFrame, const nsRect& aRect,
michael@0 713 nscoord aThickness, nsMencloseNotation aType)
michael@0 714 : nsDisplayItem(aBuilder, aFrame), mRect(aRect),
michael@0 715 mThickness(aThickness), mType(aType) {
michael@0 716 MOZ_COUNT_CTOR(nsDisplayNotation);
michael@0 717 }
michael@0 718 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 719 virtual ~nsDisplayNotation() {
michael@0 720 MOZ_COUNT_DTOR(nsDisplayNotation);
michael@0 721 }
michael@0 722 #endif
michael@0 723
michael@0 724 virtual void Paint(nsDisplayListBuilder* aBuilder,
michael@0 725 nsRenderingContext* aCtx) MOZ_OVERRIDE;
michael@0 726 NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION)
michael@0 727
michael@0 728 private:
michael@0 729 nsRect mRect;
michael@0 730 nscoord mThickness;
michael@0 731 nsMencloseNotation mType;
michael@0 732 };
michael@0 733
michael@0 734 void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder,
michael@0 735 nsRenderingContext* aCtx)
michael@0 736 {
michael@0 737 // get the gfxRect
michael@0 738 nsPresContext* presContext = mFrame->PresContext();
michael@0 739 gfxRect rect = presContext->AppUnitsToGfxUnits(mRect + ToReferenceFrame());
michael@0 740
michael@0 741 // paint the frame with the current text color
michael@0 742 aCtx->SetColor(mFrame->GetVisitedDependentColor(eCSSProperty_color));
michael@0 743
michael@0 744 // change line width to mThickness
michael@0 745 gfxContext *gfxCtx = aCtx->ThebesContext();
michael@0 746 gfxFloat e = presContext->AppUnitsToGfxUnits(mThickness);
michael@0 747 gfxCtx->Save();
michael@0 748 gfxCtx->SetLineWidth(e);
michael@0 749
michael@0 750 rect.Deflate(e / 2.0);
michael@0 751
michael@0 752 switch(mType)
michael@0 753 {
michael@0 754 case NOTATION_CIRCLE:
michael@0 755 gfxCtx->NewPath();
michael@0 756 gfxCtx->Ellipse(rect.Center(), rect.Size());
michael@0 757 gfxCtx->Stroke();
michael@0 758 break;
michael@0 759
michael@0 760 case NOTATION_ROUNDEDBOX:
michael@0 761 gfxCtx->NewPath();
michael@0 762 gfxCtx->RoundedRectangle(rect, gfxCornerSizes(3 * e), true);
michael@0 763 gfxCtx->Stroke();
michael@0 764 break;
michael@0 765
michael@0 766 case NOTATION_UPDIAGONALSTRIKE:
michael@0 767 gfxCtx->NewPath();
michael@0 768 gfxCtx->Line(rect.BottomLeft(), rect.TopRight());
michael@0 769 gfxCtx->Stroke();
michael@0 770 break;
michael@0 771
michael@0 772 case NOTATION_DOWNDIAGONALSTRIKE:
michael@0 773 gfxCtx->NewPath();
michael@0 774 gfxCtx->Line(rect.TopLeft(), rect.BottomRight());
michael@0 775 gfxCtx->Stroke();
michael@0 776 break;
michael@0 777
michael@0 778 case NOTATION_UPDIAGONALARROW: {
michael@0 779 // Compute some parameters to draw the updiagonalarrow. The values below
michael@0 780 // are taken from MathJax's HTML-CSS output.
michael@0 781 gfxFloat W = rect.Width(); gfxFloat H = rect.Height();
michael@0 782 gfxFloat l = sqrt(W*W + H*H);
michael@0 783 gfxFloat f = gfxFloat(kArrowHeadSize) * e / l;
michael@0 784 gfxFloat w = W * f; gfxFloat h = H * f;
michael@0 785
michael@0 786 // Draw the arrow shaft
michael@0 787 gfxCtx->NewPath();
michael@0 788 gfxCtx->Line(rect.BottomLeft(), rect.TopRight() + gfxPoint(-.7*w, .7*h));
michael@0 789 gfxCtx->Stroke();
michael@0 790
michael@0 791 // Draw the arrow head
michael@0 792 gfxCtx->NewPath();
michael@0 793 gfxPoint p[] = {
michael@0 794 rect.TopRight(),
michael@0 795 rect.TopRight() + gfxPoint(-w -.4*h, std::max(-e / 2.0, h - .4*w)),
michael@0 796 rect.TopRight() + gfxPoint(-.7*w, .7*h),
michael@0 797 rect.TopRight() + gfxPoint(std::min(e / 2.0, -w + .4*h), h + .4*w),
michael@0 798 rect.TopRight()
michael@0 799 };
michael@0 800 gfxCtx->Polygon(p, MOZ_ARRAY_LENGTH(p));
michael@0 801 gfxCtx->Fill();
michael@0 802 }
michael@0 803 break;
michael@0 804
michael@0 805 default:
michael@0 806 NS_NOTREACHED("This notation can not be drawn using nsDisplayNotation");
michael@0 807 break;
michael@0 808 }
michael@0 809
michael@0 810 gfxCtx->Restore();
michael@0 811 }
michael@0 812
michael@0 813 void
michael@0 814 nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder,
michael@0 815 nsIFrame* aFrame, const nsRect& aRect,
michael@0 816 const nsDisplayListSet& aLists,
michael@0 817 nscoord aThickness,
michael@0 818 nsMencloseNotation aType)
michael@0 819 {
michael@0 820 if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() ||
michael@0 821 aThickness <= 0)
michael@0 822 return;
michael@0 823
michael@0 824 aLists.Content()->AppendNewToTop(new (aBuilder)
michael@0 825 nsDisplayNotation(aBuilder, aFrame, aRect, aThickness, aType));
michael@0 826 }

mercurial