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 :
7 : #include "nsMathMLmfracFrame.h"
8 :
9 : #include "gfxUtils.h"
10 : #include "mozilla/gfx/2D.h"
11 : #include "mozilla/RefPtr.h"
12 : #include "nsLayoutUtils.h"
13 : #include "nsPresContext.h"
14 : #include "nsDisplayList.h"
15 : #include "gfxContext.h"
16 : #include "nsMathMLElement.h"
17 : #include <algorithm>
18 : #include "gfxMathTable.h"
19 :
20 : using namespace mozilla;
21 : using namespace mozilla::gfx;
22 :
23 : //
24 : // <mfrac> -- form a fraction from two subexpressions - implementation
25 : //
26 :
27 : // various fraction line thicknesses (multiplicative values of the default rule thickness)
28 :
29 : #define THIN_FRACTION_LINE 0.5f
30 : #define THIN_FRACTION_LINE_MINIMUM_PIXELS 1 // minimum of 1 pixel
31 :
32 : #define THICK_FRACTION_LINE 2.0f
33 : #define THICK_FRACTION_LINE_MINIMUM_PIXELS 2 // minimum of 2 pixels
34 :
35 : nsIFrame*
36 0 : NS_NewMathMLmfracFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
37 : {
38 0 : return new (aPresShell) nsMathMLmfracFrame(aContext);
39 : }
40 :
41 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmfracFrame)
42 :
43 0 : nsMathMLmfracFrame::~nsMathMLmfracFrame()
44 : {
45 0 : }
46 :
47 : eMathMLFrameType
48 0 : nsMathMLmfracFrame::GetMathMLFrameType()
49 : {
50 : // frac is "inner" in TeXBook, Appendix G, rule 15e. See also page 170.
51 0 : return eMathMLFrameType_Inner;
52 : }
53 :
54 : uint8_t
55 0 : nsMathMLmfracFrame::ScriptIncrement(nsIFrame* aFrame)
56 : {
57 0 : if (!StyleFont()->mMathDisplay &&
58 0 : aFrame && (mFrames.FirstChild() == aFrame ||
59 0 : mFrames.LastChild() == aFrame)) {
60 0 : return 1;
61 : }
62 0 : return 0;
63 : }
64 :
65 : NS_IMETHODIMP
66 0 : nsMathMLmfracFrame::TransmitAutomaticData()
67 : {
68 : // The TeXbook (Ch 17. p.141) says the numerator inherits the compression
69 : // while the denominator is compressed
70 0 : UpdatePresentationDataFromChildAt(1, 1,
71 : NS_MATHML_COMPRESSED,
72 0 : NS_MATHML_COMPRESSED);
73 :
74 : // If displaystyle is false, then scriptlevel is incremented, so notify the
75 : // children of this.
76 0 : if (!StyleFont()->mMathDisplay) {
77 0 : PropagateFrameFlagFor(mFrames.FirstChild(),
78 0 : NS_FRAME_MATHML_SCRIPT_DESCENDANT);
79 0 : PropagateFrameFlagFor(mFrames.LastChild(),
80 0 : NS_FRAME_MATHML_SCRIPT_DESCENDANT);
81 : }
82 :
83 : // if our numerator is an embellished operator, let its state bubble to us
84 0 : GetEmbellishDataFrom(mFrames.FirstChild(), mEmbellishData);
85 0 : if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {
86 : // even when embellished, we need to record that <mfrac> won't fire
87 : // Stretch() on its embellished child
88 0 : mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
89 : }
90 :
91 0 : return NS_OK;
92 : }
93 :
94 : nscoord
95 0 : nsMathMLmfracFrame::CalcLineThickness(nsPresContext* aPresContext,
96 : nsStyleContext* aStyleContext,
97 : nsString& aThicknessAttribute,
98 : nscoord onePixel,
99 : nscoord aDefaultRuleThickness,
100 : float aFontSizeInflation)
101 : {
102 0 : nscoord defaultThickness = aDefaultRuleThickness;
103 0 : nscoord lineThickness = aDefaultRuleThickness;
104 0 : nscoord minimumThickness = onePixel;
105 :
106 : // linethickness
107 : //
108 : // "Specifies the thickness of the horizontal 'fraction bar', or 'rule'. The
109 : // default value is 'medium', 'thin' is thinner, but visible, 'thick' is
110 : // thicker; the exact thickness of these is left up to the rendering agent."
111 : //
112 : // values: length | "thin" | "medium" | "thick"
113 : // default: medium
114 : //
115 0 : if (!aThicknessAttribute.IsEmpty()) {
116 0 : if (aThicknessAttribute.EqualsLiteral("thin")) {
117 0 : lineThickness = NSToCoordFloor(defaultThickness * THIN_FRACTION_LINE);
118 0 : minimumThickness = onePixel * THIN_FRACTION_LINE_MINIMUM_PIXELS;
119 : // should visually decrease by at least one pixel, if default is not a pixel
120 0 : if (defaultThickness > onePixel && lineThickness > defaultThickness - onePixel)
121 0 : lineThickness = defaultThickness - onePixel;
122 : }
123 0 : else if (aThicknessAttribute.EqualsLiteral("medium")) {
124 : // medium is default
125 : }
126 0 : else if (aThicknessAttribute.EqualsLiteral("thick")) {
127 0 : lineThickness = NSToCoordCeil(defaultThickness * THICK_FRACTION_LINE);
128 0 : minimumThickness = onePixel * THICK_FRACTION_LINE_MINIMUM_PIXELS;
129 : // should visually increase by at least one pixel
130 0 : if (lineThickness < defaultThickness + onePixel)
131 0 : lineThickness = defaultThickness + onePixel;
132 : }
133 : else {
134 : // length value
135 0 : lineThickness = defaultThickness;
136 : ParseNumericValue(aThicknessAttribute, &lineThickness,
137 : nsMathMLElement::PARSE_ALLOW_UNITLESS,
138 0 : aPresContext, aStyleContext, aFontSizeInflation);
139 : }
140 : }
141 :
142 : // use minimum if the lineThickness is a non-zero value less than minimun
143 0 : if (lineThickness && lineThickness < minimumThickness)
144 0 : lineThickness = minimumThickness;
145 :
146 0 : return lineThickness;
147 : }
148 :
149 : void
150 0 : nsMathMLmfracFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
151 : const nsRect& aDirtyRect,
152 : const nsDisplayListSet& aLists)
153 : {
154 : /////////////
155 : // paint the numerator and denominator
156 0 : nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
157 :
158 : /////////////
159 : // paint the fraction line
160 0 : if (mIsBevelled) {
161 0 : DisplaySlash(aBuilder, this, mLineRect, mLineThickness, aLists);
162 : } else {
163 0 : DisplayBar(aBuilder, this, mLineRect, aLists);
164 : }
165 0 : }
166 :
167 :
168 : nsresult
169 0 : nsMathMLmfracFrame::AttributeChanged(int32_t aNameSpaceID,
170 : nsIAtom* aAttribute,
171 : int32_t aModType)
172 : {
173 0 : if (nsGkAtoms::linethickness_ == aAttribute) {
174 0 : InvalidateFrame();
175 : }
176 : return
177 0 : nsMathMLContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
178 0 : aModType);
179 : }
180 :
181 : /* virtual */ nsresult
182 0 : nsMathMLmfracFrame::MeasureForWidth(DrawTarget* aDrawTarget,
183 : ReflowOutput& aDesiredSize)
184 : {
185 0 : return PlaceInternal(aDrawTarget, false, aDesiredSize, true);
186 : }
187 :
188 : nscoord
189 0 : nsMathMLmfracFrame::FixInterFrameSpacing(ReflowOutput& aDesiredSize)
190 : {
191 0 : nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
192 0 : if (!gap) return 0;
193 :
194 0 : mLineRect.MoveBy(gap, 0);
195 0 : return gap;
196 : }
197 :
198 : /* virtual */ nsresult
199 0 : nsMathMLmfracFrame::Place(DrawTarget* aDrawTarget,
200 : bool aPlaceOrigin,
201 : ReflowOutput& aDesiredSize)
202 : {
203 0 : return PlaceInternal(aDrawTarget, aPlaceOrigin, aDesiredSize, false);
204 : }
205 :
206 : nsresult
207 0 : nsMathMLmfracFrame::PlaceInternal(DrawTarget* aDrawTarget,
208 : bool aPlaceOrigin,
209 : ReflowOutput& aDesiredSize,
210 : bool aWidthOnly)
211 : {
212 : ////////////////////////////////////
213 : // Get the children's desired sizes
214 0 : nsBoundingMetrics bmNum, bmDen;
215 0 : ReflowOutput sizeNum(aDesiredSize.GetWritingMode());
216 0 : ReflowOutput sizeDen(aDesiredSize.GetWritingMode());
217 0 : nsIFrame* frameDen = nullptr;
218 0 : nsIFrame* frameNum = mFrames.FirstChild();
219 0 : if (frameNum)
220 0 : frameDen = frameNum->GetNextSibling();
221 0 : if (!frameNum || !frameDen || frameDen->GetNextSibling()) {
222 : // report an error, encourage people to get their markups in order
223 0 : if (aPlaceOrigin) {
224 0 : ReportChildCountError();
225 : }
226 0 : return ReflowError(aDrawTarget, aDesiredSize);
227 : }
228 0 : GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum);
229 0 : GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen);
230 :
231 0 : nsPresContext* presContext = PresContext();
232 0 : nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
233 :
234 0 : float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
235 : RefPtr<nsFontMetrics> fm =
236 0 : nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
237 :
238 : nscoord defaultRuleThickness, axisHeight;
239 0 : nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
240 0 : gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
241 0 : if (mathFont) {
242 0 : defaultRuleThickness = mathFont->MathTable()->
243 0 : Constant(gfxMathTable::FractionRuleThickness, oneDevPixel);
244 : } else {
245 0 : GetRuleThickness(aDrawTarget, fm, defaultRuleThickness);
246 : }
247 0 : GetAxisHeight(aDrawTarget, fm, axisHeight);
248 :
249 0 : bool outermostEmbellished = false;
250 0 : if (mEmbellishData.coreFrame) {
251 0 : nsEmbellishData parentData;
252 0 : GetEmbellishDataFrom(GetParent(), parentData);
253 0 : outermostEmbellished = parentData.coreFrame != mEmbellishData.coreFrame;
254 : }
255 :
256 : // see if the linethickness attribute is there
257 0 : nsAutoString value;
258 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::linethickness_, value);
259 0 : mLineThickness = CalcLineThickness(presContext, mStyleContext, value,
260 : onePixel, defaultRuleThickness,
261 : fontSizeInflation);
262 :
263 : // bevelled attribute
264 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::bevelled_, value);
265 0 : mIsBevelled = value.EqualsLiteral("true");
266 :
267 0 : bool displayStyle = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK;
268 :
269 0 : if (!mIsBevelled) {
270 0 : mLineRect.height = mLineThickness;
271 :
272 : // by default, leave at least one-pixel padding at either end, and add
273 : // lspace & rspace that may come from <mo> if we are an outermost
274 : // embellished container (we fetch values from the core since they may use
275 : // units that depend on style data, and style changes could have occurred
276 : // in the core since our last visit there)
277 0 : nscoord leftSpace = onePixel;
278 0 : nscoord rightSpace = onePixel;
279 0 : if (outermostEmbellished) {
280 0 : nsEmbellishData coreData;
281 0 : GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
282 0 : leftSpace += StyleVisibility()->mDirection ?
283 0 : coreData.trailingSpace : coreData.leadingSpace;
284 0 : rightSpace += StyleVisibility()->mDirection ?
285 0 : coreData.leadingSpace : coreData.trailingSpace;
286 : }
287 :
288 0 : nscoord actualRuleThickness = mLineThickness;
289 :
290 : //////////////////
291 : // Get shifts
292 0 : nscoord numShift = 0;
293 0 : nscoord denShift = 0;
294 :
295 : // Rule 15b, App. G, TeXbook
296 : nscoord numShift1, numShift2, numShift3;
297 : nscoord denShift1, denShift2;
298 :
299 0 : GetNumeratorShifts(fm, numShift1, numShift2, numShift3);
300 0 : GetDenominatorShifts(fm, denShift1, denShift2);
301 :
302 0 : if (0 == actualRuleThickness) {
303 0 : numShift = displayStyle ? numShift1 : numShift3;
304 0 : denShift = displayStyle ? denShift1 : denShift2;
305 0 : if (mathFont) {
306 : numShift = mathFont->
307 0 : MathTable()->Constant(displayStyle ?
308 : gfxMathTable::StackTopDisplayStyleShiftUp :
309 : gfxMathTable::StackTopShiftUp,
310 0 : oneDevPixel);
311 : denShift = mathFont->
312 0 : MathTable()->Constant(displayStyle ?
313 : gfxMathTable::StackBottomDisplayStyleShiftDown :
314 : gfxMathTable::StackBottomShiftDown,
315 0 : oneDevPixel);
316 : }
317 : } else {
318 0 : numShift = displayStyle ? numShift1 : numShift2;
319 0 : denShift = displayStyle ? denShift1 : denShift2;
320 0 : if (mathFont) {
321 : numShift = mathFont->MathTable()->
322 0 : Constant(displayStyle ?
323 : gfxMathTable::FractionNumeratorDisplayStyleShiftUp :
324 : gfxMathTable::FractionNumeratorShiftUp,
325 0 : oneDevPixel);
326 : denShift = mathFont->MathTable()->
327 0 : Constant(displayStyle ?
328 : gfxMathTable::FractionDenominatorDisplayStyleShiftDown :
329 : gfxMathTable::FractionDenominatorShiftDown,
330 0 : oneDevPixel);
331 : }
332 : }
333 :
334 0 : if (0 == actualRuleThickness) {
335 : // Rule 15c, App. G, TeXbook
336 :
337 : // min clearance between numerator and denominator
338 0 : nscoord minClearance = displayStyle ?
339 0 : 7 * defaultRuleThickness : 3 * defaultRuleThickness;
340 0 : if (mathFont) {
341 : minClearance = mathFont->MathTable()->
342 0 : Constant(displayStyle ?
343 : gfxMathTable::StackDisplayStyleGapMin :
344 : gfxMathTable::StackGapMin,
345 0 : oneDevPixel);
346 : }
347 : // Factor in axis height
348 : // http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2
349 0 : numShift += axisHeight;
350 0 : denShift += axisHeight;
351 :
352 : nscoord actualClearance =
353 0 : (numShift - bmNum.descent) - (bmDen.ascent - denShift);
354 : // actualClearance should be >= minClearance
355 0 : if (actualClearance < minClearance) {
356 0 : nscoord halfGap = (minClearance - actualClearance)/2;
357 0 : numShift += halfGap;
358 0 : denShift += halfGap;
359 : }
360 : }
361 : else {
362 : // Rule 15d, App. G, TeXbook
363 :
364 : // min clearance between numerator or denominator and middle of bar
365 :
366 : // TeX has a different interpretation of the thickness.
367 : // Try $a \above10pt b$ to see. Here is what TeX does:
368 : // minClearance = displayStyle ?
369 : // 3 * actualRuleThickness : actualRuleThickness;
370 :
371 : // we slightly depart from TeX here. We use the defaultRuleThickness instead
372 : // of the value coming from the linethickness attribute, i.e., we recover what
373 : // TeX does if the user hasn't set linethickness. But when the linethickness
374 : // is set, we avoid the wide gap problem.
375 0 : nscoord minClearanceNum = displayStyle ?
376 0 : 3 * defaultRuleThickness : defaultRuleThickness + onePixel;
377 0 : nscoord minClearanceDen = minClearanceNum;
378 0 : if (mathFont) {
379 : minClearanceNum = mathFont->
380 0 : MathTable()->Constant(displayStyle ?
381 : gfxMathTable::FractionNumDisplayStyleGapMin :
382 : gfxMathTable::FractionNumeratorGapMin,
383 0 : oneDevPixel);
384 : minClearanceDen = mathFont->
385 0 : MathTable()->Constant(displayStyle ?
386 : gfxMathTable::FractionDenomDisplayStyleGapMin :
387 : gfxMathTable::FractionDenominatorGapMin,
388 0 : oneDevPixel);
389 : }
390 :
391 : // adjust numShift to maintain minClearanceNum if needed
392 : nscoord actualClearanceNum =
393 0 : (numShift - bmNum.descent) - (axisHeight + actualRuleThickness/2);
394 0 : if (actualClearanceNum < minClearanceNum) {
395 0 : numShift += (minClearanceNum - actualClearanceNum);
396 : }
397 : // adjust denShift to maintain minClearanceDen if needed
398 : nscoord actualClearanceDen =
399 0 : (axisHeight - actualRuleThickness/2) - (bmDen.ascent - denShift);
400 0 : if (actualClearanceDen < minClearanceDen) {
401 0 : denShift += (minClearanceDen - actualClearanceDen);
402 : }
403 : }
404 :
405 : //////////////////
406 : // Place Children
407 :
408 : // XXX Need revisiting the width. TeX uses the exact width
409 : // e.g. in $$\huge\frac{\displaystyle\int}{i}$$
410 0 : nscoord width = std::max(bmNum.width, bmDen.width);
411 0 : nscoord dxNum = leftSpace + (width - sizeNum.Width())/2;
412 0 : nscoord dxDen = leftSpace + (width - sizeDen.Width())/2;
413 0 : width += leftSpace + rightSpace;
414 :
415 : // see if the numalign attribute is there
416 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::numalign_, value);
417 0 : if (value.EqualsLiteral("left"))
418 0 : dxNum = leftSpace;
419 0 : else if (value.EqualsLiteral("right"))
420 0 : dxNum = width - rightSpace - sizeNum.Width();
421 :
422 : // see if the denomalign attribute is there
423 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::denomalign_, value);
424 0 : if (value.EqualsLiteral("left"))
425 0 : dxDen = leftSpace;
426 0 : else if (value.EqualsLiteral("right"))
427 0 : dxDen = width - rightSpace - sizeDen.Width();
428 :
429 0 : mBoundingMetrics.rightBearing =
430 0 : std::max(dxNum + bmNum.rightBearing, dxDen + bmDen.rightBearing);
431 0 : if (mBoundingMetrics.rightBearing < width - rightSpace)
432 0 : mBoundingMetrics.rightBearing = width - rightSpace;
433 0 : mBoundingMetrics.leftBearing =
434 0 : std::min(dxNum + bmNum.leftBearing, dxDen + bmDen.leftBearing);
435 0 : if (mBoundingMetrics.leftBearing > leftSpace)
436 0 : mBoundingMetrics.leftBearing = leftSpace;
437 0 : mBoundingMetrics.ascent = bmNum.ascent + numShift;
438 0 : mBoundingMetrics.descent = bmDen.descent + denShift;
439 0 : mBoundingMetrics.width = width;
440 :
441 0 : aDesiredSize.SetBlockStartAscent(sizeNum.BlockStartAscent() + numShift);
442 0 : aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
443 0 : sizeDen.Height() - sizeDen.BlockStartAscent() + denShift;
444 0 : aDesiredSize.Width() = mBoundingMetrics.width;
445 0 : aDesiredSize.mBoundingMetrics = mBoundingMetrics;
446 :
447 0 : mReference.x = 0;
448 0 : mReference.y = aDesiredSize.BlockStartAscent();
449 :
450 0 : if (aPlaceOrigin) {
451 : nscoord dy;
452 : // place numerator
453 0 : dy = 0;
454 0 : FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dxNum, dy, 0);
455 : // place denominator
456 0 : dy = aDesiredSize.Height() - sizeDen.Height();
457 0 : FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dxDen, dy, 0);
458 : // place the fraction bar - dy is top of bar
459 0 : dy = aDesiredSize.BlockStartAscent() - (axisHeight + actualRuleThickness/2);
460 0 : mLineRect.SetRect(leftSpace, dy, width - (leftSpace + rightSpace),
461 0 : actualRuleThickness);
462 : }
463 : } else {
464 0 : nscoord numShift = 0.0;
465 0 : nscoord denShift = 0.0;
466 0 : nscoord padding = 3 * defaultRuleThickness;
467 0 : nscoord slashRatio = 3;
468 :
469 : // Define the constant used in the expression of the maximum width
470 0 : nscoord em = fm->EmHeight();
471 0 : nscoord slashMaxWidthConstant = 2 * em;
472 :
473 : // For large line thicknesses the minimum slash height is limited to the
474 : // largest expected height of a fraction
475 : nscoord slashMinHeight = slashRatio *
476 0 : std::min(2 * mLineThickness, slashMaxWidthConstant);
477 :
478 0 : nscoord leadingSpace = padding;
479 0 : nscoord trailingSpace = padding;
480 0 : if (outermostEmbellished) {
481 0 : nsEmbellishData coreData;
482 0 : GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
483 0 : leadingSpace += coreData.leadingSpace;
484 0 : trailingSpace += coreData.trailingSpace;
485 : }
486 : nscoord delta;
487 :
488 : // ___________
489 : // | | /
490 : // {|-NUMERATOR-| /
491 : // {|___________| S
492 : // { L
493 : // numShift{ A
494 : // ------------------------------------------------------- baseline
495 : // S _____________ } denShift
496 : // H | |}
497 : // / |-DENOMINATOR-|}
498 : // / |_____________|
499 : //
500 :
501 : // first, ensure that the top of the numerator is at least as high as the
502 : // top of the denominator (and the reverse for the bottoms)
503 0 : delta = std::max(bmDen.ascent - bmNum.ascent,
504 0 : bmNum.descent - bmDen.descent) / 2;
505 0 : if (delta > 0) {
506 0 : numShift += delta;
507 0 : denShift += delta;
508 : }
509 :
510 0 : if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) {
511 0 : delta = std::min(bmDen.ascent + bmDen.descent,
512 0 : bmNum.ascent + bmNum.descent) / 2;
513 0 : numShift += delta;
514 0 : denShift += delta;
515 : } else {
516 0 : nscoord xHeight = fm->XHeight();
517 0 : numShift += xHeight / 2;
518 0 : denShift += xHeight / 4;
519 : }
520 :
521 : // Set the ascent/descent of our BoundingMetrics.
522 0 : mBoundingMetrics.ascent = bmNum.ascent + numShift;
523 0 : mBoundingMetrics.descent = bmDen.descent + denShift;
524 :
525 : // At this point the height of the slash is
526 : // mBoundingMetrics.ascent + mBoundingMetrics.descent
527 : // Ensure that it is greater than slashMinHeight
528 0 : delta = (slashMinHeight -
529 0 : (mBoundingMetrics.ascent + mBoundingMetrics.descent)) / 2;
530 0 : if (delta > 0) {
531 0 : mBoundingMetrics.ascent += delta;
532 0 : mBoundingMetrics.descent += delta;
533 : }
534 :
535 : // Set the width of the slash
536 0 : if (aWidthOnly) {
537 0 : mLineRect.width = mLineThickness + slashMaxWidthConstant;
538 : } else {
539 0 : mLineRect.width = mLineThickness +
540 0 : std::min(slashMaxWidthConstant,
541 0 : (mBoundingMetrics.ascent + mBoundingMetrics.descent) /
542 0 : slashRatio);
543 : }
544 :
545 : // Set horizontal bounding metrics
546 0 : if (StyleVisibility()->mDirection) {
547 0 : mBoundingMetrics.leftBearing = trailingSpace + bmDen.leftBearing;
548 0 : mBoundingMetrics.rightBearing = trailingSpace + bmDen.width + mLineRect.width + bmNum.rightBearing;
549 : } else {
550 0 : mBoundingMetrics.leftBearing = leadingSpace + bmNum.leftBearing;
551 0 : mBoundingMetrics.rightBearing = leadingSpace + bmNum.width + mLineRect.width + bmDen.rightBearing;
552 : }
553 0 : mBoundingMetrics.width =
554 0 : leadingSpace + bmNum.width + mLineRect.width + bmDen.width +
555 : trailingSpace;
556 :
557 : // Set aDesiredSize
558 0 : aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + padding);
559 0 : aDesiredSize.Height() =
560 0 : mBoundingMetrics.ascent + mBoundingMetrics.descent + 2 * padding;
561 0 : aDesiredSize.Width() = mBoundingMetrics.width;
562 0 : aDesiredSize.mBoundingMetrics = mBoundingMetrics;
563 :
564 0 : mReference.x = 0;
565 0 : mReference.y = aDesiredSize.BlockStartAscent();
566 :
567 0 : if (aPlaceOrigin) {
568 : nscoord dx, dy;
569 :
570 : // place numerator
571 0 : dx = MirrorIfRTL(aDesiredSize.Width(), sizeNum.Width(),
572 0 : leadingSpace);
573 0 : dy = aDesiredSize.BlockStartAscent() - numShift - sizeNum.BlockStartAscent();
574 0 : FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dx, dy, 0);
575 :
576 : // place the fraction bar
577 0 : dx = MirrorIfRTL(aDesiredSize.Width(), mLineRect.width,
578 0 : leadingSpace + bmNum.width);
579 0 : dy = aDesiredSize.BlockStartAscent() - mBoundingMetrics.ascent;
580 0 : mLineRect.SetRect(dx, dy,
581 0 : mLineRect.width, aDesiredSize.Height() - 2 * padding);
582 :
583 : // place denominator
584 0 : dx = MirrorIfRTL(aDesiredSize.Width(), sizeDen.Width(),
585 0 : leadingSpace + bmNum.width + mLineRect.width);
586 0 : dy = aDesiredSize.BlockStartAscent() + denShift - sizeDen.BlockStartAscent();
587 0 : FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dx, dy, 0);
588 : }
589 :
590 : }
591 :
592 0 : return NS_OK;
593 : }
594 :
595 : class nsDisplayMathMLSlash : public nsDisplayItem {
596 : public:
597 0 : nsDisplayMathMLSlash(nsDisplayListBuilder* aBuilder,
598 : nsIFrame* aFrame, const nsRect& aRect,
599 : nscoord aThickness, bool aRTL)
600 0 : : nsDisplayItem(aBuilder, aFrame), mRect(aRect), mThickness(aThickness),
601 0 : mRTL(aRTL) {
602 0 : MOZ_COUNT_CTOR(nsDisplayMathMLSlash);
603 0 : }
604 : #ifdef NS_BUILD_REFCNT_LOGGING
605 0 : virtual ~nsDisplayMathMLSlash() {
606 0 : MOZ_COUNT_DTOR(nsDisplayMathMLSlash);
607 0 : }
608 : #endif
609 :
610 : virtual void Paint(nsDisplayListBuilder* aBuilder,
611 : gfxContext* aCtx) override;
612 0 : NS_DISPLAY_DECL_NAME("MathMLSlash", TYPE_MATHML_SLASH)
613 :
614 : private:
615 : nsRect mRect;
616 : nscoord mThickness;
617 : bool mRTL;
618 : };
619 :
620 0 : void nsDisplayMathMLSlash::Paint(nsDisplayListBuilder* aBuilder,
621 : gfxContext* aCtx)
622 : {
623 0 : DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
624 :
625 : // get the gfxRect
626 0 : nsPresContext* presContext = mFrame->PresContext();
627 0 : Rect rect = NSRectToRect(mRect + ToReferenceFrame(),
628 0 : presContext->AppUnitsPerDevPixel());
629 :
630 0 : ColorPattern color(ToDeviceColor(
631 0 : mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
632 :
633 : // draw the slash as a parallelogram
634 0 : Point delta = Point(presContext->AppUnitsToGfxUnits(mThickness), 0);
635 0 : RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
636 0 : if (mRTL) {
637 0 : builder->MoveTo(rect.TopLeft());
638 0 : builder->LineTo(rect.TopLeft() + delta);
639 0 : builder->LineTo(rect.BottomRight());
640 0 : builder->LineTo(rect.BottomRight() - delta);
641 : } else {
642 0 : builder->MoveTo(rect.BottomLeft());
643 0 : builder->LineTo(rect.BottomLeft() + delta);
644 0 : builder->LineTo(rect.TopRight());
645 0 : builder->LineTo(rect.TopRight() - delta);
646 : }
647 0 : RefPtr<Path> path = builder->Finish();
648 0 : aDrawTarget.Fill(path, color);
649 0 : }
650 :
651 : void
652 0 : nsMathMLmfracFrame::DisplaySlash(nsDisplayListBuilder* aBuilder,
653 : nsIFrame* aFrame, const nsRect& aRect,
654 : nscoord aThickness,
655 : const nsDisplayListSet& aLists) {
656 0 : if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty())
657 0 : return;
658 :
659 0 : aLists.Content()->AppendNewToTop(new (aBuilder)
660 : nsDisplayMathMLSlash(aBuilder, aFrame, aRect, aThickness,
661 0 : StyleVisibility()->mDirection));
662 : }
|