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