1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsMathMLmencloseFrame.h"
10 #include "mozilla/gfx/2D.h"
11 #include "mozilla/gfx/PathHelpers.h"
12 #include "nsPresContext.h"
13 #include "nsRenderingContext.h"
14 #include "nsWhitespaceTokenizer.h"
16 #include "nsDisplayList.h"
17 #include "gfxContext.h"
18 #include "nsMathMLChar.h"
21 using namespace mozilla
;
22 using namespace mozilla::gfx
;
25 // <menclose> -- enclose content with a stretching symbol such
26 // as a long division sign. - implementation
29 // Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis
30 // renders better with current font support.
31 static const char16_t kLongDivChar
= ')';
33 // radical: 'SQUARE ROOT'
34 static const char16_t kRadicalChar
= 0x221A;
37 static const uint8_t kArrowHeadSize
= 10;
40 static const uint8_t kPhasorangleWidth
= 8;
43 NS_NewMathMLmencloseFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
45 return new (aPresShell
) nsMathMLmencloseFrame(aContext
);
48 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame
)
50 nsMathMLmencloseFrame::nsMathMLmencloseFrame(nsStyleContext
* aContext
) :
51 nsMathMLContainerFrame(aContext
), mNotationsToDraw(0),
52 mLongDivCharIndex(-1), mRadicalCharIndex(-1), mContentWidth(0)
56 nsMathMLmencloseFrame::~nsMathMLmencloseFrame()
60 nsresult
nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask
)
62 // Is the char already allocated?
63 if ((mask
== NOTATION_LONGDIV
&& mLongDivCharIndex
>= 0) ||
64 (mask
== NOTATION_RADICAL
&& mRadicalCharIndex
>= 0))
67 // No need to track the style context given to our MathML chars.
68 // The Style System will use Get/SetAdditionalStyleContext() to keep it
69 // up-to-date if dynamic changes arise.
70 uint32_t i
= mMathMLChar
.Length();
73 if (!mMathMLChar
.AppendElement())
74 return NS_ERROR_OUT_OF_MEMORY
;
76 if (mask
== NOTATION_LONGDIV
) {
77 Char
.Assign(kLongDivChar
);
78 mLongDivCharIndex
= i
;
79 } else if (mask
== NOTATION_RADICAL
) {
80 Char
.Assign(kRadicalChar
);
81 mRadicalCharIndex
= i
;
84 nsPresContext
*presContext
= PresContext();
85 mMathMLChar
[i
].SetData(presContext
, Char
);
86 ResolveMathMLCharStyle(presContext
, mContent
, mStyleContext
, &mMathMLChar
[i
]);
92 * Add a notation to draw, if the argument is the name of a known notation.
93 * @param aNotation string name of a notation
95 nsresult
nsMathMLmencloseFrame::AddNotation(const nsAString
& aNotation
)
99 if (aNotation
.EqualsLiteral("longdiv")) {
100 rv
= AllocateMathMLChar(NOTATION_LONGDIV
);
101 NS_ENSURE_SUCCESS(rv
, rv
);
102 mNotationsToDraw
|= NOTATION_LONGDIV
;
103 } else if (aNotation
.EqualsLiteral("actuarial")) {
104 mNotationsToDraw
|= (NOTATION_RIGHT
| NOTATION_TOP
);
105 } else if (aNotation
.EqualsLiteral("radical")) {
106 rv
= AllocateMathMLChar(NOTATION_RADICAL
);
107 NS_ENSURE_SUCCESS(rv
, rv
);
108 mNotationsToDraw
|= NOTATION_RADICAL
;
109 } else if (aNotation
.EqualsLiteral("box")) {
110 mNotationsToDraw
|= (NOTATION_LEFT
| NOTATION_RIGHT
|
111 NOTATION_TOP
| NOTATION_BOTTOM
);
112 } else if (aNotation
.EqualsLiteral("roundedbox")) {
113 mNotationsToDraw
|= NOTATION_ROUNDEDBOX
;
114 } else if (aNotation
.EqualsLiteral("circle")) {
115 mNotationsToDraw
|= NOTATION_CIRCLE
;
116 } else if (aNotation
.EqualsLiteral("left")) {
117 mNotationsToDraw
|= NOTATION_LEFT
;
118 } else if (aNotation
.EqualsLiteral("right")) {
119 mNotationsToDraw
|= NOTATION_RIGHT
;
120 } else if (aNotation
.EqualsLiteral("top")) {
121 mNotationsToDraw
|= NOTATION_TOP
;
122 } else if (aNotation
.EqualsLiteral("bottom")) {
123 mNotationsToDraw
|= NOTATION_BOTTOM
;
124 } else if (aNotation
.EqualsLiteral("updiagonalstrike")) {
125 mNotationsToDraw
|= NOTATION_UPDIAGONALSTRIKE
;
126 } else if (aNotation
.EqualsLiteral("updiagonalarrow")) {
127 mNotationsToDraw
|= NOTATION_UPDIAGONALARROW
;
128 } else if (aNotation
.EqualsLiteral("downdiagonalstrike")) {
129 mNotationsToDraw
|= NOTATION_DOWNDIAGONALSTRIKE
;
130 } else if (aNotation
.EqualsLiteral("verticalstrike")) {
131 mNotationsToDraw
|= NOTATION_VERTICALSTRIKE
;
132 } else if (aNotation
.EqualsLiteral("horizontalstrike")) {
133 mNotationsToDraw
|= NOTATION_HORIZONTALSTRIKE
;
134 } else if (aNotation
.EqualsLiteral("madruwb")) {
135 mNotationsToDraw
|= (NOTATION_RIGHT
| NOTATION_BOTTOM
);
136 } else if (aNotation
.EqualsLiteral("phasorangle")) {
137 mNotationsToDraw
|= (NOTATION_BOTTOM
| NOTATION_PHASORANGLE
);
144 * Initialize the list of notations to draw
146 void nsMathMLmencloseFrame::InitNotations()
148 mNotationsToDraw
= 0;
149 mLongDivCharIndex
= mRadicalCharIndex
= -1;
154 if (mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::notation_
, value
)) {
155 // parse the notation attribute
156 nsWhitespaceTokenizer
tokenizer(value
);
158 while (tokenizer
.hasMoreTokens())
159 AddNotation(tokenizer
.nextToken());
161 if (IsToDraw(NOTATION_UPDIAGONALARROW
)) {
162 // For <menclose notation="updiagonalstrike updiagonalarrow">, if
163 // the two notations are drawn then the strike line may cause the point of
164 // the arrow to be too wide. Hence we will only draw the updiagonalarrow
165 // and the arrow shaft may be thought to be the updiagonalstrike.
166 mNotationsToDraw
&= ~NOTATION_UPDIAGONALSTRIKE
;
170 if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV
)))
172 mNotationsToDraw
= NOTATION_LONGDIV
;
177 nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame
* aParent
)
179 // let the base class get the default from our parent
180 nsMathMLContainerFrame::InheritAutomaticData(aParent
);
182 mPresentationData
.flags
|= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY
;
190 nsMathMLmencloseFrame::TransmitAutomaticData()
192 if (IsToDraw(NOTATION_RADICAL
)) {
193 // The TeXBook (Ch 17. p.141) says that \sqrt is cramped
194 UpdatePresentationDataFromChildAt(0, -1,
195 NS_MATHML_COMPRESSED
,
196 NS_MATHML_COMPRESSED
);
203 nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
204 const nsRect
& aDirtyRect
,
205 const nsDisplayListSet
& aLists
)
208 // paint the menclosed content
209 nsMathMLContainerFrame::BuildDisplayList(aBuilder
, aDirtyRect
, aLists
);
211 if (NS_MATHML_HAS_ERROR(mPresentationData
.flags
))
214 nsRect mencloseRect
= nsIFrame::GetRect();
215 mencloseRect
.x
= mencloseRect
.y
= 0;
217 if (IsToDraw(NOTATION_RADICAL
)) {
218 mMathMLChar
[mRadicalCharIndex
].Display(aBuilder
, this, aLists
, 0);
221 mMathMLChar
[mRadicalCharIndex
].GetRect(rect
);
222 rect
.MoveBy(StyleVisibility()->mDirection
? -mContentWidth
: rect
.width
, 0);
223 rect
.SizeTo(mContentWidth
, mRadicalRuleThickness
);
224 DisplayBar(aBuilder
, this, rect
, aLists
);
227 if (IsToDraw(NOTATION_PHASORANGLE
)) {
228 DisplayNotation(aBuilder
, this, mencloseRect
, aLists
,
229 mRuleThickness
, NOTATION_PHASORANGLE
);
232 if (IsToDraw(NOTATION_LONGDIV
)) {
233 mMathMLChar
[mLongDivCharIndex
].Display(aBuilder
, this, aLists
, 1);
236 mMathMLChar
[mLongDivCharIndex
].GetRect(rect
);
237 rect
.SizeTo(rect
.width
+ mContentWidth
, mRuleThickness
);
238 DisplayBar(aBuilder
, this, rect
, aLists
);
241 if (IsToDraw(NOTATION_TOP
)) {
242 nsRect
rect(0, 0, mencloseRect
.width
, mRuleThickness
);
243 DisplayBar(aBuilder
, this, rect
, aLists
);
246 if (IsToDraw(NOTATION_BOTTOM
)) {
247 nsRect
rect(0, mencloseRect
.height
- mRuleThickness
,
248 mencloseRect
.width
, mRuleThickness
);
249 DisplayBar(aBuilder
, this, rect
, aLists
);
252 if (IsToDraw(NOTATION_LEFT
)) {
253 nsRect
rect(0, 0, mRuleThickness
, mencloseRect
.height
);
254 DisplayBar(aBuilder
, this, rect
, aLists
);
257 if (IsToDraw(NOTATION_RIGHT
)) {
258 nsRect
rect(mencloseRect
.width
- mRuleThickness
, 0,
259 mRuleThickness
, mencloseRect
.height
);
260 DisplayBar(aBuilder
, this, rect
, aLists
);
263 if (IsToDraw(NOTATION_ROUNDEDBOX
)) {
264 DisplayNotation(aBuilder
, this, mencloseRect
, aLists
,
265 mRuleThickness
, NOTATION_ROUNDEDBOX
);
268 if (IsToDraw(NOTATION_CIRCLE
)) {
269 DisplayNotation(aBuilder
, this, mencloseRect
, aLists
,
270 mRuleThickness
, NOTATION_CIRCLE
);
273 if (IsToDraw(NOTATION_UPDIAGONALSTRIKE
)) {
274 DisplayNotation(aBuilder
, this, mencloseRect
, aLists
,
275 mRuleThickness
, NOTATION_UPDIAGONALSTRIKE
);
278 if (IsToDraw(NOTATION_UPDIAGONALARROW
)) {
279 DisplayNotation(aBuilder
, this, mencloseRect
, aLists
,
280 mRuleThickness
, NOTATION_UPDIAGONALARROW
);
283 if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE
)) {
284 DisplayNotation(aBuilder
, this, mencloseRect
, aLists
,
285 mRuleThickness
, NOTATION_DOWNDIAGONALSTRIKE
);
288 if (IsToDraw(NOTATION_HORIZONTALSTRIKE
)) {
289 nsRect
rect(0, mencloseRect
.height
/ 2 - mRuleThickness
/ 2,
290 mencloseRect
.width
, mRuleThickness
);
291 DisplayBar(aBuilder
, this, rect
, aLists
);
294 if (IsToDraw(NOTATION_VERTICALSTRIKE
)) {
295 nsRect
rect(mencloseRect
.width
/ 2 - mRuleThickness
/ 2, 0,
296 mRuleThickness
, mencloseRect
.height
);
297 DisplayBar(aBuilder
, this, rect
, aLists
);
301 /* virtual */ nsresult
302 nsMathMLmencloseFrame::MeasureForWidth(nsRenderingContext
& aRenderingContext
,
303 nsHTMLReflowMetrics
& aDesiredSize
)
305 return PlaceInternal(aRenderingContext
, false, aDesiredSize
, true);
308 /* virtual */ nsresult
309 nsMathMLmencloseFrame::Place(nsRenderingContext
& aRenderingContext
,
311 nsHTMLReflowMetrics
& aDesiredSize
)
313 return PlaceInternal(aRenderingContext
, aPlaceOrigin
, aDesiredSize
, false);
316 /* virtual */ nsresult
317 nsMathMLmencloseFrame::PlaceInternal(nsRenderingContext
& aRenderingContext
,
319 nsHTMLReflowMetrics
& aDesiredSize
,
323 // Measure the size of our content using the base class to format like an
325 nsHTMLReflowMetrics
baseSize(aDesiredSize
.GetWritingMode());
327 nsMathMLContainerFrame::Place(aRenderingContext
, false, baseSize
);
329 if (NS_MATHML_HAS_ERROR(mPresentationData
.flags
) || NS_FAILED(rv
)) {
330 DidReflowChildren(GetFirstPrincipalChild());
334 nsBoundingMetrics bmBase
= baseSize
.mBoundingMetrics
;
335 nscoord dx_left
= 0, dx_right
= 0;
336 nsBoundingMetrics bmLongdivChar
, bmRadicalChar
;
337 nscoord radicalAscent
= 0, radicalDescent
= 0;
338 nscoord longdivAscent
= 0, longdivDescent
= 0;
343 // Thickness of bars and font metrics
344 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
346 float fontSizeInflation
= nsLayoutUtils::FontSizeInflationFor(this);
347 nsRefPtr
<nsFontMetrics
> fm
;
348 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
),
350 GetRuleThickness(aRenderingContext
, fm
, mRuleThickness
);
351 if (mRuleThickness
< onePixel
) {
352 mRuleThickness
= onePixel
;
356 nsBoundingMetrics bmOne
=
357 nsLayoutUtils::AppUnitBoundsOfString(&one
, 1, *fm
, aRenderingContext
);
360 // General rules: the menclose element takes the size of the enclosed content.
361 // We add a padding when needed.
363 // determine padding & psi
364 nscoord padding
= 3 * mRuleThickness
;
365 nscoord delta
= padding
% onePixel
;
367 padding
+= onePixel
- delta
; // round up
369 if (IsToDraw(NOTATION_LONGDIV
) || IsToDraw(NOTATION_RADICAL
)) {
370 GetRadicalParameters(fm
, StyleFont()->mMathDisplay
==
371 NS_MATHML_DISPLAYSTYLE_BLOCK
,
372 mRadicalRuleThickness
, leading
, psi
);
374 // make sure that the rule appears on on screen
375 if (mRadicalRuleThickness
< onePixel
) {
376 mRadicalRuleThickness
= onePixel
;
379 // adjust clearance psi to get an exact number of pixels -- this
380 // gives a nicer & uniform look on stacked radicals (bug 130282)
381 delta
= psi
% onePixel
;
383 psi
+= onePixel
- delta
; // round up
387 // Set horizontal parameters
388 if (IsToDraw(NOTATION_ROUNDEDBOX
) ||
389 IsToDraw(NOTATION_TOP
) ||
390 IsToDraw(NOTATION_LEFT
) ||
391 IsToDraw(NOTATION_BOTTOM
) ||
392 IsToDraw(NOTATION_CIRCLE
))
395 if (IsToDraw(NOTATION_ROUNDEDBOX
) ||
396 IsToDraw(NOTATION_TOP
) ||
397 IsToDraw(NOTATION_RIGHT
) ||
398 IsToDraw(NOTATION_BOTTOM
) ||
399 IsToDraw(NOTATION_CIRCLE
))
402 // Set vertical parameters
403 if (IsToDraw(NOTATION_RIGHT
) ||
404 IsToDraw(NOTATION_LEFT
) ||
405 IsToDraw(NOTATION_UPDIAGONALSTRIKE
) ||
406 IsToDraw(NOTATION_UPDIAGONALARROW
) ||
407 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE
) ||
408 IsToDraw(NOTATION_VERTICALSTRIKE
) ||
409 IsToDraw(NOTATION_CIRCLE
) ||
410 IsToDraw(NOTATION_ROUNDEDBOX
) ||
411 IsToDraw(NOTATION_RADICAL
) ||
412 IsToDraw(NOTATION_LONGDIV
) ||
413 IsToDraw(NOTATION_PHASORANGLE
)) {
414 // set a minimal value for the base height
415 bmBase
.ascent
= std::max(bmOne
.ascent
, bmBase
.ascent
);
416 bmBase
.descent
= std::max(0, bmBase
.descent
);
419 mBoundingMetrics
.ascent
= bmBase
.ascent
;
420 mBoundingMetrics
.descent
= bmBase
.descent
;
422 if (IsToDraw(NOTATION_ROUNDEDBOX
) ||
423 IsToDraw(NOTATION_TOP
) ||
424 IsToDraw(NOTATION_LEFT
) ||
425 IsToDraw(NOTATION_RIGHT
) ||
426 IsToDraw(NOTATION_CIRCLE
))
427 mBoundingMetrics
.ascent
+= padding
;
429 if (IsToDraw(NOTATION_ROUNDEDBOX
) ||
430 IsToDraw(NOTATION_LEFT
) ||
431 IsToDraw(NOTATION_RIGHT
) ||
432 IsToDraw(NOTATION_BOTTOM
) ||
433 IsToDraw(NOTATION_CIRCLE
))
434 mBoundingMetrics
.descent
+= padding
;
437 // phasorangle notation
438 if (IsToDraw(NOTATION_PHASORANGLE
)) {
439 nscoord phasorangleWidth
= kPhasorangleWidth
* mRuleThickness
;
440 // Update horizontal parameters
441 dx_left
= std::max(dx_left
, phasorangleWidth
);
445 // updiagonal arrow notation. We need enough space at the top right corner to
446 // draw the arrow head.
447 if (IsToDraw(NOTATION_UPDIAGONALARROW
)) {
448 // This is an estimate, see nsDisplayNotation::Paint for the exact head size
449 nscoord arrowHeadSize
= kArrowHeadSize
* mRuleThickness
;
451 // We want that the arrow shaft strikes the menclose content and that the
452 // arrow head does not overlap with that content. Hence we add some space
453 // on the right. We don't add space on the top but only ensure that the
454 // ascent is large enough.
455 dx_right
= std::max(dx_right
, arrowHeadSize
);
456 mBoundingMetrics
.ascent
= std::max(mBoundingMetrics
.ascent
, arrowHeadSize
);
460 // circle notation: we don't want the ellipse to overlap the enclosed
461 // content. Hence, we need to increase the size of the bounding box by a
462 // factor of at least sqrt(2).
463 if (IsToDraw(NOTATION_CIRCLE
)) {
464 double ratio
= (sqrt(2.0) - 1.0) / 2.0;
467 // Update horizontal parameters
468 padding2
= ratio
* bmBase
.width
;
470 dx_left
= std::max(dx_left
, padding2
);
471 dx_right
= std::max(dx_right
, padding2
);
473 // Update vertical parameters
474 padding2
= ratio
* (bmBase
.ascent
+ bmBase
.descent
);
476 mBoundingMetrics
.ascent
= std::max(mBoundingMetrics
.ascent
,
477 bmBase
.ascent
+ padding2
);
478 mBoundingMetrics
.descent
= std::max(mBoundingMetrics
.descent
,
479 bmBase
.descent
+ padding2
);
484 if (IsToDraw(NOTATION_LONGDIV
)) {
486 nscoord longdiv_width
= mMathMLChar
[mLongDivCharIndex
].
487 GetMaxWidth(PresContext(), aRenderingContext
, fontSizeInflation
);
489 // Update horizontal parameters
490 dx_left
= std::max(dx_left
, longdiv_width
);
492 // Stretch the parenthesis to the appropriate height if it is not
494 nsBoundingMetrics contSize
= bmBase
;
495 contSize
.ascent
= mRuleThickness
;
496 contSize
.descent
= bmBase
.ascent
+ bmBase
.descent
+ psi
;
498 // height(longdiv) should be >= height(base) + psi + mRuleThickness
499 mMathMLChar
[mLongDivCharIndex
].Stretch(PresContext(), aRenderingContext
,
501 NS_STRETCH_DIRECTION_VERTICAL
,
502 contSize
, bmLongdivChar
,
503 NS_STRETCH_LARGER
, false);
504 mMathMLChar
[mLongDivCharIndex
].GetBoundingMetrics(bmLongdivChar
);
506 // Update horizontal parameters
507 dx_left
= std::max(dx_left
, bmLongdivChar
.width
);
509 // Update vertical parameters
510 longdivAscent
= bmBase
.ascent
+ psi
+ mRuleThickness
;
511 longdivDescent
= std::max(bmBase
.descent
,
512 (bmLongdivChar
.ascent
+ bmLongdivChar
.descent
-
515 mBoundingMetrics
.ascent
= std::max(mBoundingMetrics
.ascent
,
517 mBoundingMetrics
.descent
= std::max(mBoundingMetrics
.descent
,
524 if (IsToDraw(NOTATION_RADICAL
)) {
525 nscoord
*dx_leading
= StyleVisibility()->mDirection
? &dx_right
: &dx_left
;
528 nscoord radical_width
= mMathMLChar
[mRadicalCharIndex
].
529 GetMaxWidth(PresContext(), aRenderingContext
, fontSizeInflation
);
531 // Update horizontal parameters
532 *dx_leading
= std::max(*dx_leading
, radical_width
);
534 // Stretch the radical symbol to the appropriate height if it is not
536 nsBoundingMetrics contSize
= bmBase
;
537 contSize
.ascent
= mRadicalRuleThickness
;
538 contSize
.descent
= bmBase
.ascent
+ bmBase
.descent
+ psi
;
540 // height(radical) should be >= height(base) + psi + mRadicalRuleThickness
541 mMathMLChar
[mRadicalCharIndex
].Stretch(PresContext(), aRenderingContext
,
543 NS_STRETCH_DIRECTION_VERTICAL
,
544 contSize
, bmRadicalChar
,
546 StyleVisibility()->mDirection
);
547 mMathMLChar
[mRadicalCharIndex
].GetBoundingMetrics(bmRadicalChar
);
549 // Update horizontal parameters
550 *dx_leading
= std::max(*dx_leading
, bmRadicalChar
.width
);
552 // Update vertical parameters
553 radicalAscent
= bmBase
.ascent
+ psi
+ mRadicalRuleThickness
;
554 radicalDescent
= std::max(bmBase
.descent
,
555 (bmRadicalChar
.ascent
+ bmRadicalChar
.descent
-
558 mBoundingMetrics
.ascent
= std::max(mBoundingMetrics
.ascent
,
560 mBoundingMetrics
.descent
= std::max(mBoundingMetrics
.descent
,
567 if (IsToDraw(NOTATION_CIRCLE
) ||
568 IsToDraw(NOTATION_ROUNDEDBOX
) ||
569 (IsToDraw(NOTATION_LEFT
) && IsToDraw(NOTATION_RIGHT
))) {
570 // center the menclose around the content (horizontally)
571 dx_left
= dx_right
= std::max(dx_left
, dx_right
);
575 // The maximum size is now computed: set the remaining parameters
576 mBoundingMetrics
.width
= dx_left
+ bmBase
.width
+ dx_right
;
578 mBoundingMetrics
.leftBearing
= std::min(0, dx_left
+ bmBase
.leftBearing
);
579 mBoundingMetrics
.rightBearing
=
580 std::max(mBoundingMetrics
.width
, dx_left
+ bmBase
.rightBearing
);
582 aDesiredSize
.Width() = mBoundingMetrics
.width
;
584 aDesiredSize
.SetBlockStartAscent(std::max(mBoundingMetrics
.ascent
,
585 baseSize
.BlockStartAscent()));
586 aDesiredSize
.Height() = aDesiredSize
.BlockStartAscent() +
587 std::max(mBoundingMetrics
.descent
,
588 baseSize
.Height() - baseSize
.BlockStartAscent());
590 if (IsToDraw(NOTATION_LONGDIV
) || IsToDraw(NOTATION_RADICAL
)) {
591 nscoord desiredSizeAscent
= aDesiredSize
.BlockStartAscent();
592 nscoord desiredSizeDescent
= aDesiredSize
.Height() -
593 aDesiredSize
.BlockStartAscent();
595 if (IsToDraw(NOTATION_LONGDIV
)) {
596 desiredSizeAscent
= std::max(desiredSizeAscent
,
597 longdivAscent
+ leading
);
598 desiredSizeDescent
= std::max(desiredSizeDescent
,
599 longdivDescent
+ mRuleThickness
);
602 if (IsToDraw(NOTATION_RADICAL
)) {
603 desiredSizeAscent
= std::max(desiredSizeAscent
,
604 radicalAscent
+ leading
);
605 desiredSizeDescent
= std::max(desiredSizeDescent
,
606 radicalDescent
+ mRadicalRuleThickness
);
609 aDesiredSize
.SetBlockStartAscent(desiredSizeAscent
);
610 aDesiredSize
.Height() = desiredSizeAscent
+ desiredSizeDescent
;
613 if (IsToDraw(NOTATION_CIRCLE
) ||
614 IsToDraw(NOTATION_ROUNDEDBOX
) ||
615 (IsToDraw(NOTATION_TOP
) && IsToDraw(NOTATION_BOTTOM
))) {
616 // center the menclose around the content (vertically)
617 nscoord dy
= std::max(aDesiredSize
.BlockStartAscent() - bmBase
.ascent
,
618 aDesiredSize
.Height() -
619 aDesiredSize
.BlockStartAscent() - bmBase
.descent
);
621 aDesiredSize
.SetBlockStartAscent(bmBase
.ascent
+ dy
);
622 aDesiredSize
.Height() = aDesiredSize
.BlockStartAscent() + bmBase
.descent
+ dy
;
625 // Update mBoundingMetrics ascent/descent
626 if (IsToDraw(NOTATION_TOP
) ||
627 IsToDraw(NOTATION_RIGHT
) ||
628 IsToDraw(NOTATION_LEFT
) ||
629 IsToDraw(NOTATION_UPDIAGONALSTRIKE
) ||
630 IsToDraw(NOTATION_UPDIAGONALARROW
) ||
631 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE
) ||
632 IsToDraw(NOTATION_VERTICALSTRIKE
) ||
633 IsToDraw(NOTATION_CIRCLE
) ||
634 IsToDraw(NOTATION_ROUNDEDBOX
))
635 mBoundingMetrics
.ascent
= aDesiredSize
.BlockStartAscent();
637 if (IsToDraw(NOTATION_BOTTOM
) ||
638 IsToDraw(NOTATION_RIGHT
) ||
639 IsToDraw(NOTATION_LEFT
) ||
640 IsToDraw(NOTATION_UPDIAGONALSTRIKE
) ||
641 IsToDraw(NOTATION_UPDIAGONALARROW
) ||
642 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE
) ||
643 IsToDraw(NOTATION_VERTICALSTRIKE
) ||
644 IsToDraw(NOTATION_CIRCLE
) ||
645 IsToDraw(NOTATION_ROUNDEDBOX
))
646 mBoundingMetrics
.descent
= aDesiredSize
.Height() - aDesiredSize
.BlockStartAscent();
648 // phasorangle notation:
649 // move up from the bottom by the angled line height
650 if (IsToDraw(NOTATION_PHASORANGLE
))
651 mBoundingMetrics
.ascent
= std::max(mBoundingMetrics
.ascent
, 2 * kPhasorangleWidth
* mRuleThickness
- mBoundingMetrics
.descent
);
653 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
656 mReference
.y
= aDesiredSize
.BlockStartAscent();
660 // Set position and size of MathMLChars
661 if (IsToDraw(NOTATION_LONGDIV
))
662 mMathMLChar
[mLongDivCharIndex
].SetRect(nsRect(dx_left
-
664 aDesiredSize
.BlockStartAscent() -
667 bmLongdivChar
.ascent
+
668 bmLongdivChar
.descent
));
670 if (IsToDraw(NOTATION_RADICAL
)) {
671 nscoord dx
= (StyleVisibility()->mDirection
?
672 dx_left
+ bmBase
.width
: dx_left
- bmRadicalChar
.width
);
674 mMathMLChar
[mRadicalCharIndex
].SetRect(nsRect(dx
,
675 aDesiredSize
.BlockStartAscent() -
678 bmRadicalChar
.ascent
+
679 bmRadicalChar
.descent
));
682 mContentWidth
= bmBase
.width
;
685 // Finish reflowing child frames
686 PositionRowChildFrames(dx_left
, aDesiredSize
.BlockStartAscent());
693 nsMathMLmencloseFrame::FixInterFrameSpacing(nsHTMLReflowMetrics
& aDesiredSize
)
695 nscoord gap
= nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize
);
699 // Move the MathML characters
701 for (uint32_t i
= 0; i
< mMathMLChar
.Length(); i
++) {
702 mMathMLChar
[i
].GetRect(rect
);
704 mMathMLChar
[i
].SetRect(rect
);
711 nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID
,
715 if (aAttribute
== nsGkAtoms::notation_
) {
719 return nsMathMLContainerFrame::
720 AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
724 // the Style System will use these to pass the proper style context to our
727 nsMathMLmencloseFrame::GetAdditionalStyleContext(int32_t aIndex
) const
729 int32_t len
= mMathMLChar
.Length();
730 if (aIndex
>= 0 && aIndex
< len
)
731 return mMathMLChar
[aIndex
].GetStyleContext();
737 nsMathMLmencloseFrame::SetAdditionalStyleContext(int32_t aIndex
,
738 nsStyleContext
* aStyleContext
)
740 int32_t len
= mMathMLChar
.Length();
741 if (aIndex
>= 0 && aIndex
< len
)
742 mMathMLChar
[aIndex
].SetStyleContext(aStyleContext
);
745 class nsDisplayNotation
: public nsDisplayItem
748 nsDisplayNotation(nsDisplayListBuilder
* aBuilder
,
749 nsIFrame
* aFrame
, const nsRect
& aRect
,
750 nscoord aThickness
, nsMencloseNotation aType
)
751 : nsDisplayItem(aBuilder
, aFrame
), mRect(aRect
),
752 mThickness(aThickness
), mType(aType
) {
753 MOZ_COUNT_CTOR(nsDisplayNotation
);
755 #ifdef NS_BUILD_REFCNT_LOGGING
756 virtual ~nsDisplayNotation() {
757 MOZ_COUNT_DTOR(nsDisplayNotation
);
761 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
762 nsRenderingContext
* aCtx
) MOZ_OVERRIDE
;
763 NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION
)
768 nsMencloseNotation mType
;
771 void nsDisplayNotation::Paint(nsDisplayListBuilder
* aBuilder
,
772 nsRenderingContext
* aCtx
)
774 DrawTarget
& aDrawTarget
= *aCtx
->GetDrawTarget();
775 nsPresContext
* presContext
= mFrame
->PresContext();
777 Float strokeWidth
= presContext
->AppUnitsToGfxUnits(mThickness
);
779 Rect rect
= NSRectToRect(mRect
+ ToReferenceFrame(),
780 presContext
->AppUnitsPerDevPixel());
781 rect
.Deflate(strokeWidth
/ 2.f
);
783 ColorPattern
color(ToDeviceColor(
784 mFrame
->GetVisitedDependentColor(eCSSProperty_color
)));
786 StrokeOptions
strokeOptions(strokeWidth
);
790 case NOTATION_CIRCLE
: {
791 RefPtr
<Path
> ellipse
=
792 MakePathForEllipse(aDrawTarget
, rect
.Center(), rect
.Size());
793 aDrawTarget
.Stroke(ellipse
, color
, strokeOptions
);
796 case NOTATION_ROUNDEDBOX
: {
797 Float radius
= 3 * strokeWidth
;
798 RectCornerRadii
radii(radius
, radius
);
799 RefPtr
<Path
> roundedRect
=
800 MakePathForRoundedRect(aDrawTarget
, rect
, radii
, true);
801 aDrawTarget
.Stroke(roundedRect
, color
, strokeOptions
);
804 case NOTATION_UPDIAGONALSTRIKE
: {
805 aDrawTarget
.StrokeLine(rect
.BottomLeft(), rect
.TopRight(),
806 color
, strokeOptions
);
809 case NOTATION_DOWNDIAGONALSTRIKE
: {
810 aDrawTarget
.StrokeLine(rect
.TopLeft(), rect
.BottomRight(),
811 color
, strokeOptions
);
814 case NOTATION_UPDIAGONALARROW
: {
815 // Compute some parameters to draw the updiagonalarrow. The values below
816 // are taken from MathJax's HTML-CSS output.
817 Float W
= rect
.Width(); gfxFloat H
= rect
.Height();
818 Float l
= sqrt(W
*W
+ H
*H
);
819 Float f
= Float(kArrowHeadSize
) * strokeWidth
/ l
;
820 Float w
= W
* f
; gfxFloat h
= H
* f
;
822 // Draw the arrow shaft
823 aDrawTarget
.StrokeLine(rect
.BottomLeft(),
824 rect
.TopRight() + Point(-.7*w
, .7*h
),
825 color
, strokeOptions
);
827 // Draw the arrow head
828 RefPtr
<PathBuilder
> builder
= aDrawTarget
.CreatePathBuilder();
829 builder
->MoveTo(rect
.TopRight());
830 builder
->LineTo(rect
.TopRight() + Point(-w
-.4*h
, std::max(-strokeWidth
/ 2.0, h
- .4*w
)));
831 builder
->LineTo(rect
.TopRight() + Point(-.7*w
, .7*h
));
832 builder
->LineTo(rect
.TopRight() + Point(std::min(strokeWidth
/ 2.0, -w
+ .4*h
), h
+ .4*w
));
834 RefPtr
<Path
> path
= builder
->Finish();
835 aDrawTarget
.Fill(path
, color
);
838 case NOTATION_PHASORANGLE
: {
839 // Compute some parameters to draw the angled line,
840 // that uses a slope of 2 (angle = tan^-1(2)).
841 // H = w * tan(angle) = w * 2
842 Float w
= Float(kPhasorangleWidth
) * strokeWidth
;
845 // Draw the angled line
846 aDrawTarget
.StrokeLine(rect
.BottomLeft(),
847 rect
.BottomLeft() + Point(w
, -H
),
848 color
, strokeOptions
);
852 NS_NOTREACHED("This notation can not be drawn using nsDisplayNotation");
857 nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder
* aBuilder
,
858 nsIFrame
* aFrame
, const nsRect
& aRect
,
859 const nsDisplayListSet
& aLists
,
861 nsMencloseNotation aType
)
863 if (!aFrame
->StyleVisibility()->IsVisible() || aRect
.IsEmpty() ||
867 aLists
.Content()->AppendNewToTop(new (aBuilder
)
868 nsDisplayNotation(aBuilder
, aFrame
, aRect
, aThickness
, aType
));