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