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