no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / layout / mathml / nsMathMLmencloseFrame.cpp
blob9f0afc98574b78ae8aa552c6bc0cdd1a211811fc
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsMathMLmencloseFrame.h"
9 #include "gfx2DGlue.h"
10 #include "gfxUtils.h"
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/dom/Element.h"
13 #include "mozilla/PresShell.h"
14 #include "mozilla/StaticPrefs_mathml.h"
15 #include "mozilla/gfx/2D.h"
16 #include "mozilla/gfx/PathHelpers.h"
17 #include "nsLayoutUtils.h"
18 #include "nsPresContext.h"
19 #include "nsWhitespaceTokenizer.h"
21 #include "nsDisplayList.h"
22 #include "gfxContext.h"
23 #include "nsMathMLChar.h"
24 #include <algorithm>
26 using namespace mozilla;
27 using namespace mozilla::gfx;
30 // <menclose> -- enclose content with a stretching symbol such
31 // as a long division sign. - implementation
33 // longdiv:
34 // Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis
35 // renders better with current font support.
36 static const char16_t kLongDivChar = ')';
38 // radical: 'SQUARE ROOT'
39 static const char16_t kRadicalChar = 0x221A;
41 // updiagonalstrike
42 static const uint8_t kArrowHeadSize = 10;
44 // phasorangle
45 static const uint8_t kPhasorangleWidth = 8;
47 nsIFrame* NS_NewMathMLmencloseFrame(PresShell* aPresShell,
48 ComputedStyle* aStyle) {
49 return new (aPresShell)
50 nsMathMLmencloseFrame(aStyle, aPresShell->GetPresContext());
53 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame)
55 nsMathMLmencloseFrame::nsMathMLmencloseFrame(ComputedStyle* aStyle,
56 nsPresContext* aPresContext,
57 ClassID aID)
58 : nsMathMLContainerFrame(aStyle, aPresContext, aID),
59 mRuleThickness(0),
60 mRadicalRuleThickness(0),
61 mLongDivCharIndex(-1),
62 mRadicalCharIndex(-1),
63 mContentWidth(0) {}
65 nsMathMLmencloseFrame::~nsMathMLmencloseFrame() = default;
67 nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask) {
68 // Is the char already allocated?
69 if ((mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) ||
70 (mask == NOTATION_RADICAL && mRadicalCharIndex >= 0))
71 return NS_OK;
73 // No need to track the ComputedStyle given to our MathML chars.
74 // The Style System will use Get/SetAdditionalComputedStyle() to keep it
75 // up-to-date if dynamic changes arise.
76 uint32_t i = mMathMLChar.Length();
77 nsAutoString Char;
79 // XXX(Bug 1631371) Check if this should use a fallible operation as it
80 // pretended earlier, or change the return type to void.
81 mMathMLChar.AppendElement();
83 if (mask == NOTATION_LONGDIV) {
84 Char.Assign(kLongDivChar);
85 mLongDivCharIndex = i;
86 } else if (mask == NOTATION_RADICAL) {
87 Char.Assign(kRadicalChar);
88 mRadicalCharIndex = i;
91 mMathMLChar[i].SetData(Char);
92 mMathMLChar[i].SetComputedStyle(Style());
94 return NS_OK;
98 * Add a notation to draw, if the argument is the name of a known notation.
99 * @param aNotation string name of a notation
101 nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation) {
102 nsresult rv;
104 if (aNotation.EqualsLiteral("longdiv")) {
105 rv = AllocateMathMLChar(NOTATION_LONGDIV);
106 NS_ENSURE_SUCCESS(rv, rv);
107 mNotationsToDraw += NOTATION_LONGDIV;
108 } else if (aNotation.EqualsLiteral("actuarial")) {
109 mNotationsToDraw += NOTATION_RIGHT;
110 mNotationsToDraw += NOTATION_TOP;
111 } else if (aNotation.EqualsLiteral("box")) {
112 mNotationsToDraw += NOTATION_LEFT;
113 mNotationsToDraw += NOTATION_RIGHT;
114 mNotationsToDraw += NOTATION_TOP;
115 mNotationsToDraw += NOTATION_BOTTOM;
116 } else if (aNotation.EqualsLiteral("roundedbox")) {
117 mNotationsToDraw += NOTATION_ROUNDEDBOX;
118 } else if (aNotation.EqualsLiteral("circle")) {
119 mNotationsToDraw += NOTATION_CIRCLE;
120 } else if (aNotation.EqualsLiteral("left")) {
121 mNotationsToDraw += NOTATION_LEFT;
122 } else if (aNotation.EqualsLiteral("right")) {
123 mNotationsToDraw += NOTATION_RIGHT;
124 } else if (aNotation.EqualsLiteral("top")) {
125 mNotationsToDraw += NOTATION_TOP;
126 } else if (aNotation.EqualsLiteral("bottom")) {
127 mNotationsToDraw += NOTATION_BOTTOM;
128 } else if (aNotation.EqualsLiteral("updiagonalstrike")) {
129 mNotationsToDraw += NOTATION_UPDIAGONALSTRIKE;
130 } else if (aNotation.EqualsLiteral("updiagonalarrow")) {
131 mNotationsToDraw += NOTATION_UPDIAGONALARROW;
132 } else if (aNotation.EqualsLiteral("downdiagonalstrike")) {
133 mNotationsToDraw += NOTATION_DOWNDIAGONALSTRIKE;
134 } else if (aNotation.EqualsLiteral("verticalstrike")) {
135 mNotationsToDraw += NOTATION_VERTICALSTRIKE;
136 } else if (aNotation.EqualsLiteral("horizontalstrike")) {
137 mNotationsToDraw += NOTATION_HORIZONTALSTRIKE;
138 } else if (aNotation.EqualsLiteral("madruwb")) {
139 mNotationsToDraw += NOTATION_RIGHT;
140 mNotationsToDraw += NOTATION_BOTTOM;
141 } else if (aNotation.EqualsLiteral("phasorangle")) {
142 mNotationsToDraw += NOTATION_BOTTOM;
143 mNotationsToDraw += NOTATION_PHASORANGLE;
146 return NS_OK;
150 * Initialize the list of notations to draw
152 void nsMathMLmencloseFrame::InitNotations() {
153 MarkNeedsDisplayItemRebuild();
154 mNotationsToDraw.clear();
155 mLongDivCharIndex = mRadicalCharIndex = -1;
156 mMathMLChar.Clear();
158 nsAutoString value;
160 if (mContent->AsElement()->GetAttr(nsGkAtoms::notation_, value)) {
161 // parse the notation attribute
162 nsWhitespaceTokenizer tokenizer(value);
164 while (tokenizer.hasMoreTokens()) AddNotation(tokenizer.nextToken());
166 if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
167 // For <menclose notation="updiagonalstrike updiagonalarrow">, if
168 // the two notations are drawn then the strike line may cause the point of
169 // the arrow to be too wide. Hence we will only draw the updiagonalarrow
170 // and the arrow shaft may be thought to be the updiagonalstrike.
171 mNotationsToDraw -= NOTATION_UPDIAGONALSTRIKE;
173 } else {
174 // default: longdiv
175 if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV))) return;
176 mNotationsToDraw += NOTATION_LONGDIV;
180 NS_IMETHODIMP
181 nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent) {
182 // let the base class get the default from our parent
183 nsMathMLContainerFrame::InheritAutomaticData(aParent);
185 mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
187 InitNotations();
189 return NS_OK;
192 NS_IMETHODIMP
193 nsMathMLmencloseFrame::TransmitAutomaticData() {
194 if (IsToDraw(NOTATION_RADICAL)) {
195 // The TeXBook (Ch 17. p.141) says that \sqrt is cramped
196 UpdatePresentationDataFromChildAt(0, -1, NS_MATHML_COMPRESSED,
197 NS_MATHML_COMPRESSED);
200 return NS_OK;
203 void nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
204 const nsDisplayListSet& aLists) {
205 /////////////
206 // paint the menclosed content
207 nsMathMLContainerFrame::BuildDisplayList(aBuilder, aLists);
209 nsRect mencloseRect = nsIFrame::GetRect();
210 mencloseRect.x = mencloseRect.y = 0;
212 if (IsToDraw(NOTATION_RADICAL)) {
213 mMathMLChar[mRadicalCharIndex].Display(aBuilder, this, aLists, 0);
215 nsRect rect;
216 mMathMLChar[mRadicalCharIndex].GetRect(rect);
217 rect.MoveBy(StyleVisibility()->mDirection == StyleDirection::Rtl
218 ? -mContentWidth
219 : rect.width,
221 rect.SizeTo(mContentWidth, mRadicalRuleThickness);
222 DisplayBar(aBuilder, this, rect, aLists, NOTATION_RADICAL);
225 if (IsToDraw(NOTATION_PHASORANGLE)) {
226 DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness,
227 NOTATION_PHASORANGLE);
230 if (IsToDraw(NOTATION_LONGDIV)) {
231 mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1);
233 nsRect rect;
234 mMathMLChar[mLongDivCharIndex].GetRect(rect);
235 rect.SizeTo(rect.width + mContentWidth, mRuleThickness);
236 DisplayBar(aBuilder, this, rect, aLists, NOTATION_LONGDIV);
239 if (IsToDraw(NOTATION_TOP)) {
240 nsRect rect(0, 0, mencloseRect.width, mRuleThickness);
241 DisplayBar(aBuilder, this, rect, aLists, NOTATION_TOP);
244 if (IsToDraw(NOTATION_BOTTOM)) {
245 nsRect rect(0, mencloseRect.height - mRuleThickness, mencloseRect.width,
246 mRuleThickness);
247 DisplayBar(aBuilder, this, rect, aLists, NOTATION_BOTTOM);
250 if (IsToDraw(NOTATION_LEFT)) {
251 nsRect rect(0, 0, mRuleThickness, mencloseRect.height);
252 DisplayBar(aBuilder, this, rect, aLists, NOTATION_LEFT);
255 if (IsToDraw(NOTATION_RIGHT)) {
256 nsRect rect(mencloseRect.width - mRuleThickness, 0, mRuleThickness,
257 mencloseRect.height);
258 DisplayBar(aBuilder, this, rect, aLists, NOTATION_RIGHT);
261 if (IsToDraw(NOTATION_ROUNDEDBOX)) {
262 DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness,
263 NOTATION_ROUNDEDBOX);
266 if (IsToDraw(NOTATION_CIRCLE)) {
267 DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness,
268 NOTATION_CIRCLE);
271 if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) {
272 DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness,
273 NOTATION_UPDIAGONALSTRIKE);
276 if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
277 DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness,
278 NOTATION_UPDIAGONALARROW);
281 if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) {
282 DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness,
283 NOTATION_DOWNDIAGONALSTRIKE);
286 if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) {
287 nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2,
288 mencloseRect.width, mRuleThickness);
289 DisplayBar(aBuilder, this, rect, aLists, NOTATION_HORIZONTALSTRIKE);
292 if (IsToDraw(NOTATION_VERTICALSTRIKE)) {
293 nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0, mRuleThickness,
294 mencloseRect.height);
295 DisplayBar(aBuilder, this, rect, aLists, NOTATION_VERTICALSTRIKE);
299 /* virtual */
300 nsresult nsMathMLmencloseFrame::MeasureForWidth(DrawTarget* aDrawTarget,
301 ReflowOutput& aDesiredSize) {
302 return PlaceInternal(aDrawTarget, false, aDesiredSize, true);
305 /* virtual */
306 nsresult nsMathMLmencloseFrame::Place(DrawTarget* aDrawTarget,
307 bool aPlaceOrigin,
308 ReflowOutput& aDesiredSize) {
309 return PlaceInternal(aDrawTarget, aPlaceOrigin, aDesiredSize, false);
312 /* virtual */
313 nsresult nsMathMLmencloseFrame::PlaceInternal(DrawTarget* aDrawTarget,
314 bool aPlaceOrigin,
315 ReflowOutput& aDesiredSize,
316 bool aWidthOnly) {
317 ///////////////
318 // Measure the size of our content using the base class to format like an
319 // inferred mrow.
320 ReflowOutput baseSize(aDesiredSize.GetWritingMode());
321 nsresult rv = nsMathMLContainerFrame::Place(aDrawTarget, false, baseSize);
323 if (NS_FAILED(rv)) {
324 DidReflowChildren(PrincipalChildList().FirstChild());
325 return rv;
328 nsBoundingMetrics bmBase = baseSize.mBoundingMetrics;
329 nscoord dx_left = 0, dx_right = 0;
330 nsBoundingMetrics bmLongdivChar, bmRadicalChar;
331 nscoord radicalAscent = 0, radicalDescent = 0;
332 nscoord longdivAscent = 0, longdivDescent = 0;
333 nscoord psi = 0;
334 nscoord leading = 0;
336 ///////////////
337 // Thickness of bars and font metrics
338 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
340 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
341 RefPtr<nsFontMetrics> fm =
342 nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
343 GetRuleThickness(aDrawTarget, fm, mRuleThickness);
344 if (mRuleThickness < onePixel) {
345 mRuleThickness = onePixel;
348 char16_t one = '1';
349 nsBoundingMetrics bmOne =
350 nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, aDrawTarget);
352 ///////////////
353 // General rules: the menclose element takes the size of the enclosed content.
354 // We add a padding when needed.
356 // determine padding & psi
357 nscoord padding = 3 * mRuleThickness;
358 nscoord delta = padding % onePixel;
359 if (delta) padding += onePixel - delta; // round up
361 if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
362 GetRadicalParameters(fm, StyleFont()->mMathStyle == StyleMathStyle::Normal,
363 mRadicalRuleThickness, leading, psi);
365 // make sure that the rule appears on on screen
366 if (mRadicalRuleThickness < onePixel) {
367 mRadicalRuleThickness = onePixel;
370 // adjust clearance psi to get an exact number of pixels -- this
371 // gives a nicer & uniform look on stacked radicals (bug 130282)
372 delta = psi % onePixel;
373 if (delta) {
374 psi += onePixel - delta; // round up
378 // Set horizontal parameters
379 if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) ||
380 IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_BOTTOM) ||
381 IsToDraw(NOTATION_CIRCLE))
382 dx_left = padding;
384 if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) ||
385 IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_BOTTOM) ||
386 IsToDraw(NOTATION_CIRCLE))
387 dx_right = padding;
389 // Set vertical parameters
390 if (IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) ||
391 IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
392 IsToDraw(NOTATION_UPDIAGONALARROW) ||
393 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
394 IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) ||
395 IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_RADICAL) ||
396 IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_PHASORANGLE)) {
397 // set a minimal value for the base height
398 bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent);
399 bmBase.descent = std::max(0, bmBase.descent);
402 mBoundingMetrics.ascent = bmBase.ascent;
403 mBoundingMetrics.descent = bmBase.descent;
405 if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) ||
406 IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_RIGHT) ||
407 IsToDraw(NOTATION_CIRCLE))
408 mBoundingMetrics.ascent += padding;
410 if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_LEFT) ||
411 IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_BOTTOM) ||
412 IsToDraw(NOTATION_CIRCLE))
413 mBoundingMetrics.descent += padding;
415 ///////////////
416 // phasorangle notation
417 if (IsToDraw(NOTATION_PHASORANGLE)) {
418 nscoord phasorangleWidth = kPhasorangleWidth * mRuleThickness;
419 // Update horizontal parameters
420 dx_left = std::max(dx_left, phasorangleWidth);
423 ///////////////
424 // updiagonal arrow notation. We need enough space at the top right corner to
425 // draw the arrow head.
426 if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
427 // This is an estimate, see nsDisplayNotation::Paint for the exact head size
428 nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness;
430 // We want that the arrow shaft strikes the menclose content and that the
431 // arrow head does not overlap with that content. Hence we add some space
432 // on the right. We don't add space on the top but only ensure that the
433 // ascent is large enough.
434 dx_right = std::max(dx_right, arrowHeadSize);
435 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize);
438 ///////////////
439 // circle notation: we don't want the ellipse to overlap the enclosed
440 // content. Hence, we need to increase the size of the bounding box by a
441 // factor of at least sqrt(2).
442 if (IsToDraw(NOTATION_CIRCLE)) {
443 double ratio = (sqrt(2.0) - 1.0) / 2.0;
444 nscoord padding2;
446 // Update horizontal parameters
447 padding2 = ratio * bmBase.width;
449 dx_left = std::max(dx_left, padding2);
450 dx_right = std::max(dx_right, padding2);
452 // Update vertical parameters
453 padding2 = ratio * (bmBase.ascent + bmBase.descent);
455 mBoundingMetrics.ascent =
456 std::max(mBoundingMetrics.ascent, bmBase.ascent + padding2);
457 mBoundingMetrics.descent =
458 std::max(mBoundingMetrics.descent, bmBase.descent + padding2);
461 ///////////////
462 // longdiv notation:
463 if (IsToDraw(NOTATION_LONGDIV)) {
464 if (aWidthOnly) {
465 nscoord longdiv_width = mMathMLChar[mLongDivCharIndex].GetMaxWidth(
466 this, aDrawTarget, fontSizeInflation);
468 // Update horizontal parameters
469 dx_left = std::max(dx_left, longdiv_width);
470 } else {
471 // Stretch the parenthesis to the appropriate height if it is not
472 // big enough.
473 nsBoundingMetrics contSize = bmBase;
474 contSize.ascent = mRuleThickness;
475 contSize.descent = bmBase.ascent + bmBase.descent + psi;
477 // height(longdiv) should be >= height(base) + psi + mRuleThickness
478 mMathMLChar[mLongDivCharIndex].Stretch(
479 this, aDrawTarget, fontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL,
480 contSize, bmLongdivChar, NS_STRETCH_LARGER, false);
481 mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar);
483 // Update horizontal parameters
484 dx_left = std::max(dx_left, bmLongdivChar.width);
486 // Update vertical parameters
487 longdivAscent = bmBase.ascent + psi + mRuleThickness;
488 longdivDescent = std::max(
489 bmBase.descent,
490 (bmLongdivChar.ascent + bmLongdivChar.descent - longdivAscent));
492 mBoundingMetrics.ascent =
493 std::max(mBoundingMetrics.ascent, longdivAscent);
494 mBoundingMetrics.descent =
495 std::max(mBoundingMetrics.descent, longdivDescent);
499 ///////////////
500 // radical notation:
501 if (IsToDraw(NOTATION_RADICAL)) {
502 nscoord* dx_leading = StyleVisibility()->mDirection == StyleDirection::Rtl
503 ? &dx_right
504 : &dx_left;
506 if (aWidthOnly) {
507 nscoord radical_width = mMathMLChar[mRadicalCharIndex].GetMaxWidth(
508 this, aDrawTarget, fontSizeInflation);
510 // Update horizontal parameters
511 *dx_leading = std::max(*dx_leading, radical_width);
512 } else {
513 // Stretch the radical symbol to the appropriate height if it is not
514 // big enough.
515 nsBoundingMetrics contSize = bmBase;
516 contSize.ascent = mRadicalRuleThickness;
517 contSize.descent = bmBase.ascent + bmBase.descent + psi;
519 // height(radical) should be >= height(base) + psi + mRadicalRuleThickness
520 mMathMLChar[mRadicalCharIndex].Stretch(
521 this, aDrawTarget, fontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL,
522 contSize, bmRadicalChar, NS_STRETCH_LARGER,
523 StyleVisibility()->mDirection == StyleDirection::Rtl);
524 mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar);
526 // Update horizontal parameters
527 *dx_leading = std::max(*dx_leading, bmRadicalChar.width);
529 // Update vertical parameters
530 radicalAscent = bmBase.ascent + psi + mRadicalRuleThickness;
531 radicalDescent = std::max(
532 bmBase.descent,
533 (bmRadicalChar.ascent + bmRadicalChar.descent - radicalAscent));
535 mBoundingMetrics.ascent =
536 std::max(mBoundingMetrics.ascent, radicalAscent);
537 mBoundingMetrics.descent =
538 std::max(mBoundingMetrics.descent, radicalDescent);
542 ///////////////
544 if (IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) ||
545 (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) {
546 // center the menclose around the content (horizontally)
547 dx_left = dx_right = std::max(dx_left, dx_right);
550 ///////////////
551 // The maximum size is now computed: set the remaining parameters
552 mBoundingMetrics.width = dx_left + bmBase.width + dx_right;
554 mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing);
555 mBoundingMetrics.rightBearing =
556 std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing);
558 aDesiredSize.Width() = mBoundingMetrics.width;
560 aDesiredSize.SetBlockStartAscent(
561 std::max(mBoundingMetrics.ascent, baseSize.BlockStartAscent()));
562 aDesiredSize.Height() =
563 aDesiredSize.BlockStartAscent() +
564 std::max(mBoundingMetrics.descent,
565 baseSize.Height() - baseSize.BlockStartAscent());
567 if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
568 nscoord desiredSizeAscent = aDesiredSize.BlockStartAscent();
569 nscoord desiredSizeDescent =
570 aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
572 if (IsToDraw(NOTATION_LONGDIV)) {
573 desiredSizeAscent = std::max(desiredSizeAscent, longdivAscent + leading);
574 desiredSizeDescent =
575 std::max(desiredSizeDescent, longdivDescent + mRuleThickness);
578 if (IsToDraw(NOTATION_RADICAL)) {
579 desiredSizeAscent = std::max(desiredSizeAscent, radicalAscent + leading);
580 desiredSizeDescent =
581 std::max(desiredSizeDescent, radicalDescent + mRadicalRuleThickness);
584 aDesiredSize.SetBlockStartAscent(desiredSizeAscent);
585 aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent;
588 if (IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) ||
589 (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) {
590 // center the menclose around the content (vertically)
591 nscoord dy = std::max(aDesiredSize.BlockStartAscent() - bmBase.ascent,
592 aDesiredSize.Height() -
593 aDesiredSize.BlockStartAscent() - bmBase.descent);
595 aDesiredSize.SetBlockStartAscent(bmBase.ascent + dy);
596 aDesiredSize.Height() =
597 aDesiredSize.BlockStartAscent() + bmBase.descent + dy;
600 // Update mBoundingMetrics ascent/descent
601 if (IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_RIGHT) ||
602 IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
603 IsToDraw(NOTATION_UPDIAGONALARROW) ||
604 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
605 IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) ||
606 IsToDraw(NOTATION_ROUNDEDBOX))
607 mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent();
609 if (IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_RIGHT) ||
610 IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
611 IsToDraw(NOTATION_UPDIAGONALARROW) ||
612 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
613 IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) ||
614 IsToDraw(NOTATION_ROUNDEDBOX))
615 mBoundingMetrics.descent =
616 aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
618 // phasorangle notation:
619 // move up from the bottom by the angled line height
620 if (IsToDraw(NOTATION_PHASORANGLE))
621 mBoundingMetrics.ascent = std::max(
622 mBoundingMetrics.ascent,
623 2 * kPhasorangleWidth * mRuleThickness - mBoundingMetrics.descent);
625 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
627 mReference.x = 0;
628 mReference.y = aDesiredSize.BlockStartAscent();
630 if (aPlaceOrigin) {
631 //////////////////
632 // Set position and size of MathMLChars
633 if (IsToDraw(NOTATION_LONGDIV))
634 mMathMLChar[mLongDivCharIndex].SetRect(nsRect(
635 dx_left - bmLongdivChar.width,
636 aDesiredSize.BlockStartAscent() - longdivAscent, bmLongdivChar.width,
637 bmLongdivChar.ascent + bmLongdivChar.descent));
639 if (IsToDraw(NOTATION_RADICAL)) {
640 nscoord dx = (StyleVisibility()->mDirection == StyleDirection::Rtl
641 ? dx_left + bmBase.width
642 : dx_left - bmRadicalChar.width);
644 mMathMLChar[mRadicalCharIndex].SetRect(nsRect(
645 dx, aDesiredSize.BlockStartAscent() - radicalAscent,
646 bmRadicalChar.width, bmRadicalChar.ascent + bmRadicalChar.descent));
649 mContentWidth = bmBase.width;
651 //////////////////
652 // Finish reflowing child frames
653 PositionRowChildFrames(dx_left, aDesiredSize.BlockStartAscent());
656 return NS_OK;
659 nscoord nsMathMLmencloseFrame::FixInterFrameSpacing(
660 ReflowOutput& aDesiredSize) {
661 nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
662 if (!gap) return 0;
664 // Move the MathML characters
665 nsRect rect;
666 for (uint32_t i = 0; i < mMathMLChar.Length(); i++) {
667 mMathMLChar[i].GetRect(rect);
668 rect.MoveBy(gap, 0);
669 mMathMLChar[i].SetRect(rect);
672 return gap;
675 nsresult nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID,
676 nsAtom* aAttribute,
677 int32_t aModType) {
678 if (aAttribute == nsGkAtoms::notation_) {
679 InitNotations();
682 return nsMathMLContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
683 aModType);
686 void nsMathMLmencloseFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
687 nsMathMLContainerFrame::DidSetComputedStyle(aOldStyle);
688 for (auto& ch : mMathMLChar) {
689 ch.SetComputedStyle(Style());
693 //////////////////
695 namespace mozilla {
697 class nsDisplayNotation final : public nsPaintedDisplayItem {
698 public:
699 nsDisplayNotation(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
700 const nsRect& aRect, nscoord aThickness,
701 nsMencloseNotation aType)
702 : nsPaintedDisplayItem(aBuilder, aFrame),
703 mRect(aRect),
704 mThickness(aThickness),
705 mType(aType) {
706 MOZ_COUNT_CTOR(nsDisplayNotation);
708 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayNotation)
710 virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
711 NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION)
713 private:
714 nsRect mRect;
715 nscoord mThickness;
716 nsMencloseNotation mType;
719 void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder,
720 gfxContext* aCtx) {
721 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
722 nsPresContext* presContext = mFrame->PresContext();
724 Float strokeWidth = presContext->AppUnitsToGfxUnits(mThickness);
726 Rect rect = NSRectToRect(mRect + ToReferenceFrame(),
727 presContext->AppUnitsPerDevPixel());
728 rect.Deflate(strokeWidth / 2.f);
730 ColorPattern color(ToDeviceColor(
731 mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
733 StrokeOptions strokeOptions(strokeWidth);
735 switch (mType) {
736 case NOTATION_CIRCLE: {
737 RefPtr<Path> ellipse =
738 MakePathForEllipse(aDrawTarget, rect.Center(), rect.Size());
739 aDrawTarget.Stroke(ellipse, color, strokeOptions);
740 return;
742 case NOTATION_ROUNDEDBOX: {
743 Float radius = 3 * strokeWidth;
744 RectCornerRadii radii(radius, radius);
745 RefPtr<Path> roundedRect =
746 MakePathForRoundedRect(aDrawTarget, rect, radii, true);
747 aDrawTarget.Stroke(roundedRect, color, strokeOptions);
748 return;
750 case NOTATION_UPDIAGONALSTRIKE: {
751 aDrawTarget.StrokeLine(rect.BottomLeft(), rect.TopRight(), color,
752 strokeOptions);
753 return;
755 case NOTATION_DOWNDIAGONALSTRIKE: {
756 aDrawTarget.StrokeLine(rect.TopLeft(), rect.BottomRight(), color,
757 strokeOptions);
758 return;
760 case NOTATION_UPDIAGONALARROW: {
761 // Compute some parameters to draw the updiagonalarrow. The values below
762 // are taken from MathJax's HTML-CSS output.
763 Float W = rect.Width();
764 gfxFloat H = rect.Height();
765 Float l = sqrt(W * W + H * H);
766 Float f = Float(kArrowHeadSize) * strokeWidth / l;
767 Float w = W * f;
768 gfxFloat h = H * f;
770 // Draw the arrow shaft
771 aDrawTarget.StrokeLine(rect.BottomLeft(),
772 rect.TopRight() + Point(-.7 * w, .7 * h), color,
773 strokeOptions);
775 // Draw the arrow head
776 RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
777 builder->MoveTo(rect.TopRight());
778 builder->LineTo(
779 rect.TopRight() +
780 Point(-w - .4 * h, std::max(-strokeWidth / 2.0, h - .4 * w)));
781 builder->LineTo(rect.TopRight() + Point(-.7 * w, .7 * h));
782 builder->LineTo(
783 rect.TopRight() +
784 Point(std::min(strokeWidth / 2.0, -w + .4 * h), h + .4 * w));
785 builder->Close();
786 RefPtr<Path> path = builder->Finish();
787 aDrawTarget.Fill(path, color);
788 return;
790 case NOTATION_PHASORANGLE: {
791 // Compute some parameters to draw the angled line,
792 // that uses a slope of 2 (angle = tan^-1(2)).
793 // H = w * tan(angle) = w * 2
794 Float w = Float(kPhasorangleWidth) * strokeWidth;
795 Float H = 2 * w;
797 // Draw the angled line
798 aDrawTarget.StrokeLine(rect.BottomLeft(),
799 rect.BottomLeft() + Point(w, -H), color,
800 strokeOptions);
801 return;
803 default:
804 MOZ_ASSERT_UNREACHABLE(
805 "This notation can not be drawn using "
806 "nsDisplayNotation");
810 } // namespace mozilla
812 void nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder,
813 nsIFrame* aFrame,
814 const nsRect& aRect,
815 const nsDisplayListSet& aLists,
816 nscoord aThickness,
817 nsMencloseNotation aType) {
818 if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() ||
819 aThickness <= 0)
820 return;
822 const uint16_t index = aType;
823 aLists.Content()->AppendNewToTopWithIndex<nsDisplayNotation>(
824 aBuilder, aFrame, index, aRect, aThickness, aType);