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 "gfxContext.h"
7 : #include "nsMathMLmfencedFrame.h"
8 : #include "nsMathMLChar.h"
9 : #include <algorithm>
10 :
11 : using namespace mozilla;
12 :
13 : //
14 : // <mfenced> -- surround content with a pair of fences
15 : //
16 :
17 : nsIFrame*
18 0 : NS_NewMathMLmfencedFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
19 : {
20 0 : return new (aPresShell) nsMathMLmfencedFrame(aContext);
21 : }
22 :
23 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmfencedFrame)
24 :
25 0 : nsMathMLmfencedFrame::~nsMathMLmfencedFrame()
26 : {
27 0 : RemoveFencesAndSeparators();
28 0 : }
29 :
30 : NS_IMETHODIMP
31 0 : nsMathMLmfencedFrame::InheritAutomaticData(nsIFrame* aParent)
32 : {
33 : // let the base class get the default from our parent
34 0 : nsMathMLContainerFrame::InheritAutomaticData(aParent);
35 :
36 0 : mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
37 :
38 0 : RemoveFencesAndSeparators();
39 0 : CreateFencesAndSeparators(PresContext());
40 :
41 0 : return NS_OK;
42 : }
43 :
44 : void
45 0 : nsMathMLmfencedFrame::SetInitialChildList(ChildListID aListID,
46 : nsFrameList& aChildList)
47 : {
48 : // First, let the base class do its work
49 0 : nsMathMLContainerFrame::SetInitialChildList(aListID, aChildList);
50 :
51 : // InheritAutomaticData will not get called if our parent is not a mathml
52 : // frame, so initialize NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY for
53 : // GetPreferredStretchSize() from Reflow().
54 0 : mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
55 : // No need to track the style contexts given to our MathML chars.
56 : // The Style System will use Get/SetAdditionalStyleContext() to keep them
57 : // up-to-date if dynamic changes arise.
58 0 : CreateFencesAndSeparators(PresContext());
59 0 : }
60 :
61 : nsresult
62 0 : nsMathMLmfencedFrame::AttributeChanged(int32_t aNameSpaceID,
63 : nsIAtom* aAttribute,
64 : int32_t aModType)
65 : {
66 0 : RemoveFencesAndSeparators();
67 0 : CreateFencesAndSeparators(PresContext());
68 :
69 : return nsMathMLContainerFrame::
70 0 : AttributeChanged(aNameSpaceID, aAttribute, aModType);
71 : }
72 :
73 : nsresult
74 0 : nsMathMLmfencedFrame::ChildListChanged(int32_t aModType)
75 : {
76 0 : RemoveFencesAndSeparators();
77 0 : CreateFencesAndSeparators(PresContext());
78 :
79 0 : return nsMathMLContainerFrame::ChildListChanged(aModType);
80 : }
81 :
82 : void
83 0 : nsMathMLmfencedFrame::RemoveFencesAndSeparators()
84 : {
85 0 : delete mOpenChar;
86 0 : delete mCloseChar;
87 0 : if (mSeparatorsChar) delete[] mSeparatorsChar;
88 :
89 0 : mOpenChar = nullptr;
90 0 : mCloseChar = nullptr;
91 0 : mSeparatorsChar = nullptr;
92 0 : mSeparatorsCount = 0;
93 0 : }
94 :
95 : void
96 0 : nsMathMLmfencedFrame::CreateFencesAndSeparators(nsPresContext* aPresContext)
97 : {
98 0 : nsAutoString value;
99 :
100 : //////////////
101 : // see if the opening fence is there ...
102 0 : if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::open, value)) {
103 0 : value = char16_t('('); // default as per the MathML REC
104 : } else {
105 0 : value.CompressWhitespace();
106 : }
107 :
108 0 : if (!value.IsEmpty()) {
109 0 : mOpenChar = new nsMathMLChar;
110 0 : mOpenChar->SetData(value);
111 0 : ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext, mOpenChar);
112 : }
113 :
114 : //////////////
115 : // see if the closing fence is there ...
116 0 : if(!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::close, value)) {
117 0 : value = char16_t(')'); // default as per the MathML REC
118 : } else {
119 0 : value.CompressWhitespace();
120 : }
121 :
122 0 : if (!value.IsEmpty()) {
123 0 : mCloseChar = new nsMathMLChar;
124 0 : mCloseChar->SetData(value);
125 0 : ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext, mCloseChar);
126 : }
127 :
128 : //////////////
129 : // see if separators are there ...
130 0 : if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::separators_, value)) {
131 0 : value = char16_t(','); // default as per the MathML REC
132 : } else {
133 0 : value.StripWhitespace();
134 : }
135 :
136 0 : mSeparatorsCount = value.Length();
137 0 : if (0 < mSeparatorsCount) {
138 0 : int32_t sepCount = mFrames.GetLength() - 1;
139 0 : if (0 < sepCount) {
140 0 : mSeparatorsChar = new nsMathMLChar[sepCount];
141 0 : nsAutoString sepChar;
142 0 : for (int32_t i = 0; i < sepCount; i++) {
143 0 : if (i < mSeparatorsCount) {
144 0 : sepChar = value[i];
145 : }
146 : else {
147 0 : sepChar = value[mSeparatorsCount-1];
148 : }
149 0 : mSeparatorsChar[i].SetData(sepChar);
150 0 : ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext, &mSeparatorsChar[i]);
151 : }
152 0 : mSeparatorsCount = sepCount;
153 : } else {
154 : // No separators. Note that sepCount can be -1 here, so don't
155 : // set mSeparatorsCount to it.
156 0 : mSeparatorsCount = 0;
157 : }
158 : }
159 0 : }
160 :
161 : void
162 0 : nsMathMLmfencedFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
163 : const nsRect& aDirtyRect,
164 : const nsDisplayListSet& aLists)
165 : {
166 : /////////////
167 : // display the content
168 0 : nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
169 :
170 : ////////////
171 : // display fences and separators
172 0 : uint32_t count = 0;
173 0 : if (mOpenChar) {
174 0 : mOpenChar->Display(aBuilder, this, aLists, count++);
175 : }
176 :
177 0 : if (mCloseChar) {
178 0 : mCloseChar->Display(aBuilder, this, aLists, count++);
179 : }
180 :
181 0 : for (int32_t i = 0; i < mSeparatorsCount; i++) {
182 0 : mSeparatorsChar[i].Display(aBuilder, this, aLists, count++);
183 : }
184 0 : }
185 :
186 : /* @param aMetrics is an IN/OUT. Provide the current metrics for the mFenced
187 : frame and it will be enlarged as necessary.
188 : For simplicity the width of the container is always incremented by the width
189 : of the nsMathMLChar. As we only stretch fences and separators in the vertical
190 : direction, this has no impact on overall appearance.
191 : */
192 : static void
193 0 : ApplyUnstretchedMetrics(nsPresContext* aPresContext,
194 : DrawTarget* aDrawTarget,
195 : float aFontSizeInflation,
196 : nsMathMLChar* aMathMLChar,
197 : nsBoundingMetrics& aMetrics,
198 : bool aIsRTL)
199 : {
200 0 : if (aMathMLChar && 0 < aMathMLChar->Length()) {
201 0 : nsBoundingMetrics charSize;
202 0 : aMathMLChar->Stretch(aPresContext, aDrawTarget, aFontSizeInflation,
203 : NS_STRETCH_DIRECTION_DEFAULT,
204 : aMetrics, // size is unimportant as we aren't stretching
205 0 : charSize, NS_STRETCH_NONE, aIsRTL);
206 0 : aMetrics += charSize;
207 : }
208 0 : }
209 :
210 : void
211 0 : nsMathMLmfencedFrame::Reflow(nsPresContext* aPresContext,
212 : ReflowOutput& aDesiredSize,
213 : const ReflowInput& aReflowInput,
214 : nsReflowStatus& aStatus)
215 : {
216 0 : MarkInReflow();
217 0 : mPresentationData.flags &= ~NS_MATHML_ERROR;
218 0 : aDesiredSize.ClearSize();
219 0 : aDesiredSize.SetBlockStartAscent(0);
220 0 : aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
221 :
222 : int32_t i;
223 0 : const nsStyleFont* font = StyleFont();
224 0 : float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
225 : RefPtr<nsFontMetrics> fm =
226 0 : nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
227 : nscoord axisHeight, em;
228 0 : GetAxisHeight(aReflowInput.mRenderingContext->GetDrawTarget(), fm, axisHeight);
229 0 : GetEmHeight(fm, em);
230 : // leading to be left at the top and the bottom of stretched chars
231 0 : nscoord leading = NSToCoordRound(0.2f * em);
232 :
233 : /////////////
234 : // Reflow children
235 : // Asking each child to cache its bounding metrics
236 :
237 : // Note that we don't use the base method nsMathMLContainerFrame::Reflow()
238 : // because we want to stretch our fences, separators and stretchy frames using
239 : // the *same* initial aDesiredSize.mBoundingMetrics. If we were to use the base
240 : // method here, our stretchy frames will be stretched and placed, and we may
241 : // end up stretching our fences/separators with a different aDesiredSize.
242 : // XXX The above decision was revisited in bug 121748 and this code can be
243 : // refactored to use nsMathMLContainerFrame::Reflow() at some stage.
244 :
245 0 : nsReflowStatus childStatus;
246 0 : nsIFrame* firstChild = PrincipalChildList().FirstChild();
247 0 : nsIFrame* childFrame = firstChild;
248 0 : nscoord ascent = 0, descent = 0;
249 0 : if (firstChild || mOpenChar || mCloseChar || mSeparatorsCount > 0) {
250 : // We use the ASCII metrics to get our minimum height. This way,
251 : // if we have borders or a background, they will fit better with
252 : // other elements on the line.
253 0 : ascent = fm->MaxAscent();
254 0 : descent = fm->MaxDescent();
255 : }
256 0 : while (childFrame) {
257 : ReflowOutput childDesiredSize(aReflowInput,
258 0 : aDesiredSize.mFlags
259 0 : | NS_REFLOW_CALC_BOUNDING_METRICS);
260 0 : WritingMode wm = childFrame->GetWritingMode();
261 0 : LogicalSize availSize = aReflowInput.ComputedSize(wm);
262 0 : availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
263 : ReflowInput childReflowInput(aPresContext, aReflowInput,
264 0 : childFrame, availSize);
265 0 : ReflowChild(childFrame, aPresContext, childDesiredSize,
266 0 : childReflowInput, childStatus);
267 : //NS_ASSERTION(childStatus.IsComplete(), "bad status");
268 : SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
269 0 : childDesiredSize.mBoundingMetrics);
270 :
271 0 : mozilla::WritingMode outerWM = aReflowInput.GetWritingMode();
272 0 : nscoord childDescent = childDesiredSize.BSize(outerWM) -
273 0 : childDesiredSize.BlockStartAscent();
274 0 : if (descent < childDescent)
275 0 : descent = childDescent;
276 0 : if (ascent < childDesiredSize.BlockStartAscent())
277 0 : ascent = childDesiredSize.BlockStartAscent();
278 :
279 0 : childFrame = childFrame->GetNextSibling();
280 : }
281 :
282 : /////////////
283 : // Ask stretchy children to stretch themselves
284 :
285 0 : nsBoundingMetrics containerSize;
286 0 : nsStretchDirection stretchDir = NS_STRETCH_DIRECTION_VERTICAL;
287 :
288 0 : DrawTarget* drawTarget = aReflowInput.mRenderingContext->GetDrawTarget();
289 :
290 0 : GetPreferredStretchSize(drawTarget,
291 : 0, /* i.e., without embellishments */
292 0 : stretchDir, containerSize);
293 0 : childFrame = firstChild;
294 0 : while (childFrame) {
295 0 : nsIMathMLFrame* mathmlChild = do_QueryFrame(childFrame);
296 0 : if (mathmlChild) {
297 0 : ReflowOutput childDesiredSize(aReflowInput);
298 : // retrieve the metrics that was stored at the previous pass
299 : GetReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
300 0 : childDesiredSize.mBoundingMetrics);
301 :
302 : mathmlChild->Stretch(drawTarget,
303 0 : stretchDir, containerSize, childDesiredSize);
304 : // store the updated metrics
305 : SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
306 0 : childDesiredSize.mBoundingMetrics);
307 :
308 0 : nscoord childDescent = childDesiredSize.Height() - childDesiredSize.BlockStartAscent();
309 0 : if (descent < childDescent)
310 0 : descent = childDescent;
311 0 : if (ascent < childDesiredSize.BlockStartAscent())
312 0 : ascent = childDesiredSize.BlockStartAscent();
313 : }
314 0 : childFrame = childFrame->GetNextSibling();
315 : }
316 :
317 : // bug 121748: for surrounding fences & separators, use a size that covers everything
318 0 : GetPreferredStretchSize(drawTarget, STRETCH_CONSIDER_EMBELLISHMENTS,
319 0 : stretchDir, containerSize);
320 :
321 0 : bool isRTL = StyleVisibility()->mDirection;
322 :
323 : // To achieve a minimum size of "1", the container should be enlarged by the
324 : // unstretched metrics of the fences and separators.
325 0 : ApplyUnstretchedMetrics(aPresContext, drawTarget,
326 : fontSizeInflation, mOpenChar,
327 0 : containerSize, isRTL);
328 0 : for (i = 0; i < mSeparatorsCount; i++) {
329 0 : ApplyUnstretchedMetrics(aPresContext, drawTarget,
330 0 : fontSizeInflation, &mSeparatorsChar[i],
331 0 : containerSize, isRTL);
332 : }
333 0 : ApplyUnstretchedMetrics(aPresContext, drawTarget,
334 : fontSizeInflation, mCloseChar,
335 0 : containerSize, isRTL);
336 :
337 : //////////////////////////////////////////
338 : // Prepare the opening fence, separators, and closing fence, and
339 : // adjust the origin of children.
340 :
341 : // we need to center around the axis
342 0 : nscoord delta = std::max(containerSize.ascent - axisHeight,
343 0 : containerSize.descent + axisHeight);
344 0 : containerSize.ascent = delta + axisHeight;
345 0 : containerSize.descent = delta - axisHeight;
346 :
347 : /////////////////
348 : // opening fence ...
349 0 : ReflowChar(aPresContext, drawTarget, *fm,
350 : fontSizeInflation, mOpenChar,
351 0 : NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel,
352 0 : axisHeight, leading, em, containerSize, ascent, descent, isRTL);
353 : /////////////////
354 : // separators ...
355 0 : for (i = 0; i < mSeparatorsCount; i++) {
356 0 : ReflowChar(aPresContext, drawTarget, *fm,
357 0 : fontSizeInflation, &mSeparatorsChar[i],
358 0 : NS_MATHML_OPERATOR_FORM_INFIX, font->mScriptLevel,
359 0 : axisHeight, leading, em, containerSize, ascent, descent, isRTL);
360 : }
361 : /////////////////
362 : // closing fence ...
363 0 : ReflowChar(aPresContext, drawTarget, *fm,
364 : fontSizeInflation, mCloseChar,
365 0 : NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel,
366 0 : axisHeight, leading, em, containerSize, ascent, descent, isRTL);
367 :
368 : //////////////////
369 : // Adjust the origins of each child.
370 : // and update our bounding metrics
371 :
372 0 : i = 0;
373 0 : nscoord dx = 0;
374 0 : nsBoundingMetrics bm;
375 0 : bool firstTime = true;
376 : nsMathMLChar *leftChar, *rightChar;
377 0 : if (isRTL) {
378 0 : leftChar = mCloseChar;
379 0 : rightChar = mOpenChar;
380 : } else {
381 0 : leftChar = mOpenChar;
382 0 : rightChar = mCloseChar;
383 : }
384 :
385 0 : if (leftChar) {
386 0 : PlaceChar(leftChar, ascent, bm, dx);
387 0 : aDesiredSize.mBoundingMetrics = bm;
388 0 : firstTime = false;
389 : }
390 :
391 0 : if (isRTL) {
392 0 : childFrame = this->GetChildList(nsIFrame::kPrincipalList).LastChild();
393 : } else {
394 0 : childFrame = firstChild;
395 : }
396 0 : while (childFrame) {
397 0 : ReflowOutput childSize(aReflowInput);
398 0 : GetReflowAndBoundingMetricsFor(childFrame, childSize, bm);
399 0 : if (firstTime) {
400 0 : firstTime = false;
401 0 : aDesiredSize.mBoundingMetrics = bm;
402 : }
403 : else
404 0 : aDesiredSize.mBoundingMetrics += bm;
405 :
406 0 : FinishReflowChild(childFrame, aPresContext, childSize, nullptr,
407 0 : dx, ascent - childSize.BlockStartAscent(), 0);
408 0 : dx += childSize.Width();
409 :
410 0 : if (i < mSeparatorsCount) {
411 0 : PlaceChar(&mSeparatorsChar[isRTL ? mSeparatorsCount - 1 - i : i],
412 0 : ascent, bm, dx);
413 0 : aDesiredSize.mBoundingMetrics += bm;
414 : }
415 0 : i++;
416 :
417 0 : if (isRTL) {
418 0 : childFrame = childFrame->GetPrevSibling();
419 : } else {
420 0 : childFrame = childFrame->GetNextSibling();
421 : }
422 : }
423 :
424 0 : if (rightChar) {
425 0 : PlaceChar(rightChar, ascent, bm, dx);
426 0 : if (firstTime)
427 0 : aDesiredSize.mBoundingMetrics = bm;
428 : else
429 0 : aDesiredSize.mBoundingMetrics += bm;
430 : }
431 :
432 0 : aDesiredSize.Width() = aDesiredSize.mBoundingMetrics.width;
433 0 : aDesiredSize.Height() = ascent + descent;
434 0 : aDesiredSize.SetBlockStartAscent(ascent);
435 :
436 0 : SetBoundingMetrics(aDesiredSize.mBoundingMetrics);
437 0 : SetReference(nsPoint(0, aDesiredSize.BlockStartAscent()));
438 :
439 : // see if we should fix the spacing
440 0 : FixInterFrameSpacing(aDesiredSize);
441 :
442 : // Finished with these:
443 0 : ClearSavedChildMetrics();
444 :
445 : // Set our overflow area
446 0 : GatherAndStoreOverflow(&aDesiredSize);
447 :
448 0 : aStatus.Reset();
449 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
450 0 : }
451 :
452 : static void
453 0 : GetCharSpacing(nsMathMLChar* aMathMLChar,
454 : nsOperatorFlags aForm,
455 : int32_t aScriptLevel,
456 : nscoord em,
457 : nscoord& aLeftSpace,
458 : nscoord& aRightSpace)
459 : {
460 0 : nsAutoString data;
461 0 : aMathMLChar->GetData(data);
462 0 : nsOperatorFlags flags = 0;
463 0 : float lspace = 0.0f;
464 0 : float rspace = 0.0f;
465 : bool found = nsMathMLOperators::LookupOperator(data, aForm,
466 0 : &flags, &lspace, &rspace);
467 :
468 : // We don't want extra space when we are a script
469 0 : if (found && aScriptLevel > 0) {
470 0 : lspace /= 2.0f;
471 0 : rspace /= 2.0f;
472 : }
473 :
474 0 : aLeftSpace = NSToCoordRound(lspace * em);
475 0 : aRightSpace = NSToCoordRound(rspace * em);
476 0 : }
477 :
478 : // helper functions to perform the common task of formatting our chars
479 : /*static*/ nsresult
480 0 : nsMathMLmfencedFrame::ReflowChar(nsPresContext* aPresContext,
481 : DrawTarget* aDrawTarget,
482 : nsFontMetrics& aFontMetrics,
483 : float aFontSizeInflation,
484 : nsMathMLChar* aMathMLChar,
485 : nsOperatorFlags aForm,
486 : int32_t aScriptLevel,
487 : nscoord axisHeight,
488 : nscoord leading,
489 : nscoord em,
490 : nsBoundingMetrics& aContainerSize,
491 : nscoord& aAscent,
492 : nscoord& aDescent,
493 : bool aRTL)
494 : {
495 0 : if (aMathMLChar && 0 < aMathMLChar->Length()) {
496 : nscoord leftSpace;
497 : nscoord rightSpace;
498 0 : GetCharSpacing(aMathMLChar, aForm, aScriptLevel, em, leftSpace, rightSpace);
499 :
500 : // stretch the char to the appropriate height if it is not big enough.
501 0 : nsBoundingMetrics charSize;
502 0 : nsresult res = aMathMLChar->Stretch(aPresContext, aDrawTarget,
503 : aFontSizeInflation,
504 : NS_STRETCH_DIRECTION_VERTICAL,
505 : aContainerSize, charSize,
506 0 : NS_STRETCH_NORMAL, aRTL);
507 :
508 0 : if (NS_STRETCH_DIRECTION_UNSUPPORTED != aMathMLChar->GetStretchDirection()) {
509 : // has changed... so center the char around the axis
510 0 : nscoord height = charSize.ascent + charSize.descent;
511 0 : charSize.ascent = height/2 + axisHeight;
512 0 : charSize.descent = height - charSize.ascent;
513 : }
514 : else {
515 : // either it hasn't changed or stretching the char failed (i.e.,
516 : // nsLayoutUtils::AppUnitBoundsOfString failed)
517 0 : leading = 0;
518 0 : if (NS_FAILED(res)) {
519 0 : nsAutoString data;
520 0 : aMathMLChar->GetData(data);
521 : nsBoundingMetrics metrics =
522 : nsLayoutUtils::AppUnitBoundsOfString(data.get(), data.Length(),
523 0 : aFontMetrics, aDrawTarget);
524 0 : charSize.ascent = metrics.ascent;
525 0 : charSize.descent = metrics.descent;
526 0 : charSize.width = metrics.width;
527 : // Set this as the bounding metrics of the MathMLChar to leave
528 : // the necessary room to paint the char.
529 0 : aMathMLChar->SetBoundingMetrics(charSize);
530 : }
531 : }
532 :
533 0 : if (aAscent < charSize.ascent + leading)
534 0 : aAscent = charSize.ascent + leading;
535 0 : if (aDescent < charSize.descent + leading)
536 0 : aDescent = charSize.descent + leading;
537 :
538 : // account the spacing
539 0 : charSize.width += leftSpace + rightSpace;
540 :
541 : // x-origin is used to store lspace ...
542 : // y-origin is used to stored the ascent ...
543 0 : aMathMLChar->SetRect(nsRect(leftSpace,
544 : charSize.ascent, charSize.width,
545 0 : charSize.ascent + charSize.descent));
546 : }
547 0 : return NS_OK;
548 : }
549 :
550 : /*static*/ void
551 0 : nsMathMLmfencedFrame::PlaceChar(nsMathMLChar* aMathMLChar,
552 : nscoord aDesiredAscent,
553 : nsBoundingMetrics& bm,
554 : nscoord& dx)
555 : {
556 0 : aMathMLChar->GetBoundingMetrics(bm);
557 :
558 : // the char's x-origin was used to store lspace ...
559 : // the char's y-origin was used to store the ascent ...
560 : // the char's width was used to store the advance with (with spacing) ...
561 0 : nsRect rect;
562 0 : aMathMLChar->GetRect(rect);
563 :
564 0 : nscoord dy = aDesiredAscent - rect.y;
565 0 : if (aMathMLChar->GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED) {
566 : // the stretchy char will be centered around the axis
567 : // so we adjust the returned bounding metrics accordingly
568 0 : bm.descent = (bm.ascent + bm.descent) - rect.y;
569 0 : bm.ascent = rect.y;
570 : }
571 :
572 0 : aMathMLChar->SetRect(nsRect(dx + rect.x, dy, bm.width, rect.height));
573 :
574 0 : bm.leftBearing += rect.x;
575 0 : bm.rightBearing += rect.x;
576 :
577 : // return rect.width since it includes lspace and rspace
578 0 : bm.width = rect.width;
579 0 : dx += rect.width;
580 0 : }
581 :
582 : static nscoord
583 0 : GetMaxCharWidth(nsPresContext* aPresContext,
584 : DrawTarget* aDrawTarget,
585 : float aFontSizeInflation,
586 : nsMathMLChar* aMathMLChar,
587 : nsOperatorFlags aForm,
588 : int32_t aScriptLevel,
589 : nscoord em)
590 : {
591 : nscoord width = aMathMLChar->GetMaxWidth(aPresContext, aDrawTarget,
592 0 : aFontSizeInflation);
593 :
594 0 : if (0 < aMathMLChar->Length()) {
595 : nscoord leftSpace;
596 : nscoord rightSpace;
597 0 : GetCharSpacing(aMathMLChar, aForm, aScriptLevel, em, leftSpace, rightSpace);
598 :
599 0 : width += leftSpace + rightSpace;
600 : }
601 :
602 0 : return width;
603 : }
604 :
605 : /* virtual */ void
606 0 : nsMathMLmfencedFrame::GetIntrinsicISizeMetrics(gfxContext* aRenderingContext, ReflowOutput& aDesiredSize)
607 : {
608 0 : nscoord width = 0;
609 :
610 0 : nsPresContext* presContext = PresContext();
611 0 : const nsStyleFont* font = StyleFont();
612 0 : float fontSizeInflation = nsLayoutUtils:: FontSizeInflationFor(this);
613 : RefPtr<nsFontMetrics> fm =
614 0 : nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
615 : nscoord em;
616 0 : GetEmHeight(fm, em);
617 :
618 0 : if (mOpenChar) {
619 0 : width +=
620 0 : GetMaxCharWidth(presContext, aRenderingContext->GetDrawTarget(),
621 : fontSizeInflation, mOpenChar,
622 0 : NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel, em);
623 : }
624 :
625 0 : int32_t i = 0;
626 0 : for (nsIFrame* childFrame : PrincipalChildList()) {
627 : // XXX This includes margin while Reflow currently doesn't consider
628 : // margin, so we may end up with too much space, but, with stretchy
629 : // characters, this is an approximation anyway.
630 0 : width += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
631 : nsLayoutUtils::PREF_ISIZE);
632 :
633 0 : if (i < mSeparatorsCount) {
634 0 : width +=
635 0 : GetMaxCharWidth(presContext, aRenderingContext->GetDrawTarget(),
636 0 : fontSizeInflation, &mSeparatorsChar[i],
637 0 : NS_MATHML_OPERATOR_FORM_INFIX, font->mScriptLevel, em);
638 : }
639 0 : i++;
640 : }
641 :
642 0 : if (mCloseChar) {
643 0 : width +=
644 0 : GetMaxCharWidth(presContext, aRenderingContext->GetDrawTarget(),
645 : fontSizeInflation, mCloseChar,
646 0 : NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel, em);
647 : }
648 :
649 0 : aDesiredSize.Width() = width;
650 0 : aDesiredSize.mBoundingMetrics.width = width;
651 0 : aDesiredSize.mBoundingMetrics.leftBearing = 0;
652 0 : aDesiredSize.mBoundingMetrics.rightBearing = width;
653 0 : }
654 :
655 : nscoord
656 0 : nsMathMLmfencedFrame::FixInterFrameSpacing(ReflowOutput& aDesiredSize)
657 : {
658 0 : nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
659 0 : if (!gap) return 0;
660 :
661 0 : nsRect rect;
662 0 : if (mOpenChar) {
663 0 : mOpenChar->GetRect(rect);
664 0 : rect.MoveBy(gap, 0);
665 0 : mOpenChar->SetRect(rect);
666 : }
667 0 : if (mCloseChar) {
668 0 : mCloseChar->GetRect(rect);
669 0 : rect.MoveBy(gap, 0);
670 0 : mCloseChar->SetRect(rect);
671 : }
672 0 : for (int32_t i = 0; i < mSeparatorsCount; i++) {
673 0 : mSeparatorsChar[i].GetRect(rect);
674 0 : rect.MoveBy(gap, 0);
675 0 : mSeparatorsChar[i].SetRect(rect);
676 : }
677 0 : return gap;
678 : }
679 :
680 : // ----------------------
681 : // the Style System will use these to pass the proper style context to our MathMLChar
682 : nsStyleContext*
683 0 : nsMathMLmfencedFrame::GetAdditionalStyleContext(int32_t aIndex) const
684 : {
685 0 : int32_t openIndex = -1;
686 0 : int32_t closeIndex = -1;
687 0 : int32_t lastIndex = mSeparatorsCount-1;
688 :
689 0 : if (mOpenChar) {
690 0 : lastIndex++;
691 0 : openIndex = lastIndex;
692 : }
693 0 : if (mCloseChar) {
694 0 : lastIndex++;
695 0 : closeIndex = lastIndex;
696 : }
697 0 : if (aIndex < 0 || aIndex > lastIndex) {
698 0 : return nullptr;
699 : }
700 :
701 0 : if (aIndex < mSeparatorsCount) {
702 0 : return mSeparatorsChar[aIndex].GetStyleContext();
703 : }
704 0 : else if (aIndex == openIndex) {
705 0 : return mOpenChar->GetStyleContext();
706 : }
707 0 : else if (aIndex == closeIndex) {
708 0 : return mCloseChar->GetStyleContext();
709 : }
710 0 : return nullptr;
711 : }
712 :
713 : void
714 0 : nsMathMLmfencedFrame::SetAdditionalStyleContext(int32_t aIndex,
715 : nsStyleContext* aStyleContext)
716 : {
717 0 : int32_t openIndex = -1;
718 0 : int32_t closeIndex = -1;
719 0 : int32_t lastIndex = mSeparatorsCount-1;
720 :
721 0 : if (mOpenChar) {
722 0 : lastIndex++;
723 0 : openIndex = lastIndex;
724 : }
725 0 : if (mCloseChar) {
726 0 : lastIndex++;
727 0 : closeIndex = lastIndex;
728 : }
729 0 : if (aIndex < 0 || aIndex > lastIndex) {
730 0 : return;
731 : }
732 :
733 0 : if (aIndex < mSeparatorsCount) {
734 0 : mSeparatorsChar[aIndex].SetStyleContext(aStyleContext);
735 : }
736 0 : else if (aIndex == openIndex) {
737 0 : mOpenChar->SetStyleContext(aStyleContext);
738 : }
739 0 : else if (aIndex == closeIndex) {
740 0 : mCloseChar->SetStyleContext(aStyleContext);
741 : }
742 : }
|