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"
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"
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
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;
42 static const uint8_t kArrowHeadSize
= 10;
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
,
58 : nsMathMLContainerFrame(aStyle
, aPresContext
, aID
),
60 mRadicalRuleThickness(0),
61 mLongDivCharIndex(-1),
62 mRadicalCharIndex(-1),
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))
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();
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());
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
) {
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
;
150 * Initialize the list of notations to draw
152 void nsMathMLmencloseFrame::InitNotations() {
153 MarkNeedsDisplayItemRebuild();
154 mNotationsToDraw
.clear();
155 mLongDivCharIndex
= mRadicalCharIndex
= -1;
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
;
175 if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV
))) return;
176 mNotationsToDraw
+= NOTATION_LONGDIV
;
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
;
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
);
203 void nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
204 const nsDisplayListSet
& aLists
) {
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);
216 mMathMLChar
[mRadicalCharIndex
].GetRect(rect
);
217 rect
.MoveBy(StyleVisibility()->mDirection
== StyleDirection::Rtl
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);
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
,
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
,
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
);
300 nsresult
nsMathMLmencloseFrame::MeasureForWidth(DrawTarget
* aDrawTarget
,
301 ReflowOutput
& aDesiredSize
) {
302 return PlaceInternal(aDrawTarget
, false, aDesiredSize
, true);
306 nsresult
nsMathMLmencloseFrame::Place(DrawTarget
* aDrawTarget
,
308 ReflowOutput
& aDesiredSize
) {
309 return PlaceInternal(aDrawTarget
, aPlaceOrigin
, aDesiredSize
, false);
313 nsresult
nsMathMLmencloseFrame::PlaceInternal(DrawTarget
* aDrawTarget
,
315 ReflowOutput
& aDesiredSize
,
318 // Measure the size of our content using the base class to format like an
320 ReflowOutput
baseSize(aDesiredSize
.GetWritingMode());
321 nsresult rv
= nsMathMLContainerFrame::Place(aDrawTarget
, false, baseSize
);
324 DidReflowChildren(PrincipalChildList().FirstChild());
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;
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
;
349 nsBoundingMetrics bmOne
=
350 nsLayoutUtils::AppUnitBoundsOfString(&one
, 1, *fm
, aDrawTarget
);
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
;
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
))
384 if (IsToDraw(NOTATION_ROUNDEDBOX
) || IsToDraw(NOTATION_TOP
) ||
385 IsToDraw(NOTATION_RIGHT
) || IsToDraw(NOTATION_BOTTOM
) ||
386 IsToDraw(NOTATION_CIRCLE
))
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
;
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
);
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
);
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;
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
);
463 if (IsToDraw(NOTATION_LONGDIV
)) {
465 nscoord longdiv_width
= mMathMLChar
[mLongDivCharIndex
].GetMaxWidth(
466 this, aDrawTarget
, fontSizeInflation
);
468 // Update horizontal parameters
469 dx_left
= std::max(dx_left
, longdiv_width
);
471 // Stretch the parenthesis to the appropriate height if it is not
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(
490 (bmLongdivChar
.ascent
+ bmLongdivChar
.descent
- longdivAscent
));
492 mBoundingMetrics
.ascent
=
493 std::max(mBoundingMetrics
.ascent
, longdivAscent
);
494 mBoundingMetrics
.descent
=
495 std::max(mBoundingMetrics
.descent
, longdivDescent
);
501 if (IsToDraw(NOTATION_RADICAL
)) {
502 nscoord
* dx_leading
= StyleVisibility()->mDirection
== StyleDirection::Rtl
507 nscoord radical_width
= mMathMLChar
[mRadicalCharIndex
].GetMaxWidth(
508 this, aDrawTarget
, fontSizeInflation
);
510 // Update horizontal parameters
511 *dx_leading
= std::max(*dx_leading
, radical_width
);
513 // Stretch the radical symbol to the appropriate height if it is not
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(
533 (bmRadicalChar
.ascent
+ bmRadicalChar
.descent
- radicalAscent
));
535 mBoundingMetrics
.ascent
=
536 std::max(mBoundingMetrics
.ascent
, radicalAscent
);
537 mBoundingMetrics
.descent
=
538 std::max(mBoundingMetrics
.descent
, radicalDescent
);
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
);
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
);
575 std::max(desiredSizeDescent
, longdivDescent
+ mRuleThickness
);
578 if (IsToDraw(NOTATION_RADICAL
)) {
579 desiredSizeAscent
= std::max(desiredSizeAscent
, radicalAscent
+ leading
);
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
;
628 mReference
.y
= aDesiredSize
.BlockStartAscent();
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
;
652 // Finish reflowing child frames
653 PositionRowChildFrames(dx_left
, aDesiredSize
.BlockStartAscent());
659 nscoord
nsMathMLmencloseFrame::FixInterFrameSpacing(
660 ReflowOutput
& aDesiredSize
) {
661 nscoord gap
= nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize
);
664 // Move the MathML characters
666 for (uint32_t i
= 0; i
< mMathMLChar
.Length(); i
++) {
667 mMathMLChar
[i
].GetRect(rect
);
669 mMathMLChar
[i
].SetRect(rect
);
675 nsresult
nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID
,
678 if (aAttribute
== nsGkAtoms::notation_
) {
682 return nsMathMLContainerFrame::AttributeChanged(aNameSpaceID
, aAttribute
,
686 void nsMathMLmencloseFrame::DidSetComputedStyle(ComputedStyle
* aOldStyle
) {
687 nsMathMLContainerFrame::DidSetComputedStyle(aOldStyle
);
688 for (auto& ch
: mMathMLChar
) {
689 ch
.SetComputedStyle(Style());
697 class nsDisplayNotation final
: public nsPaintedDisplayItem
{
699 nsDisplayNotation(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
700 const nsRect
& aRect
, nscoord aThickness
,
701 nsMencloseNotation aType
)
702 : nsPaintedDisplayItem(aBuilder
, aFrame
),
704 mThickness(aThickness
),
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
)
716 nsMencloseNotation mType
;
719 void nsDisplayNotation::Paint(nsDisplayListBuilder
* aBuilder
,
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
);
736 case NOTATION_CIRCLE
: {
737 RefPtr
<Path
> ellipse
=
738 MakePathForEllipse(aDrawTarget
, rect
.Center(), rect
.Size());
739 aDrawTarget
.Stroke(ellipse
, color
, strokeOptions
);
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
);
750 case NOTATION_UPDIAGONALSTRIKE
: {
751 aDrawTarget
.StrokeLine(rect
.BottomLeft(), rect
.TopRight(), color
,
755 case NOTATION_DOWNDIAGONALSTRIKE
: {
756 aDrawTarget
.StrokeLine(rect
.TopLeft(), rect
.BottomRight(), color
,
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
;
770 // Draw the arrow shaft
771 aDrawTarget
.StrokeLine(rect
.BottomLeft(),
772 rect
.TopRight() + Point(-.7 * w
, .7 * h
), color
,
775 // Draw the arrow head
776 RefPtr
<PathBuilder
> builder
= aDrawTarget
.CreatePathBuilder();
777 builder
->MoveTo(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
));
784 Point(std::min(strokeWidth
/ 2.0, -w
+ .4 * h
), h
+ .4 * w
));
786 RefPtr
<Path
> path
= builder
->Finish();
787 aDrawTarget
.Fill(path
, color
);
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
;
797 // Draw the angled line
798 aDrawTarget
.StrokeLine(rect
.BottomLeft(),
799 rect
.BottomLeft() + Point(w
, -H
), color
,
804 MOZ_ASSERT_UNREACHABLE(
805 "This notation can not be drawn using "
806 "nsDisplayNotation");
810 } // namespace mozilla
812 void nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder
* aBuilder
,
815 const nsDisplayListSet
& aLists
,
817 nsMencloseNotation aType
) {
818 if (!aFrame
->StyleVisibility()->IsVisible() || aRect
.IsEmpty() ||
822 const uint16_t index
= aType
;
823 aLists
.Content()->AppendNewToTopWithIndex
<nsDisplayNotation
>(
824 aBuilder
, aFrame
, index
, aRect
, aThickness
, aType
);