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/. */
9 #include "nsPresContext.h"
10 #include "nsStyleContext.h"
11 #include "nsStyleConsts.h"
12 #include "nsRenderingContext.h"
14 #include "nsMathMLmrootFrame.h"
17 // <msqrt> and <mroot> -- form a radical - implementation
21 // The code assumes that TeX fonts are picked.
22 // There is no fall-back to draw the branches of the sqrt explicitly
23 // in the case where TeX fonts are not there. In general, there are no
24 // fall-back(s) in MathML when some (freely-downloadable) fonts are missing.
25 // Otherwise, this will add much work and unnecessary complexity to the core
26 // MathML engine. Assuming that authors have the free fonts is part of the
27 // deal. We are not responsible for cases of misconfigurations out there.
29 // additional style context to be used by our MathMLChar.
30 #define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0
32 static const PRUnichar kSqrChar
= PRUnichar(0x221A);
35 NS_NewMathMLmrootFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
37 return new (aPresShell
) nsMathMLmrootFrame(aContext
);
40 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame
)
42 nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext
* aContext
) :
43 nsMathMLContainerFrame(aContext
),
49 nsMathMLmrootFrame::~nsMathMLmrootFrame()
54 nsMathMLmrootFrame::Init(nsIContent
* aContent
,
56 nsIFrame
* aPrevInFlow
)
58 nsresult rv
= nsMathMLContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
60 nsPresContext
*presContext
= PresContext();
62 // No need to track the style context given to our MathML char.
63 // The Style System will use Get/SetAdditionalStyleContext() to keep it
64 // up-to-date if dynamic changes arise.
65 nsAutoString sqrChar
; sqrChar
.Assign(kSqrChar
);
66 mSqrChar
.SetData(presContext
, sqrChar
);
67 ResolveMathMLCharStyle(presContext
, mContent
, mStyleContext
, &mSqrChar
, true);
73 nsMathMLmrootFrame::TransmitAutomaticData()
76 // The <mroot> element increments scriptlevel by 2, and sets displaystyle to
77 // "false", within index, but leaves both attributes unchanged within base.
78 // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
79 UpdatePresentationDataFromChildAt(1, 1,
80 ~NS_MATHML_DISPLAYSTYLE
| NS_MATHML_COMPRESSED
,
81 NS_MATHML_DISPLAYSTYLE
| NS_MATHML_COMPRESSED
);
82 UpdatePresentationDataFromChildAt(0, 0,
83 NS_MATHML_COMPRESSED
, NS_MATHML_COMPRESSED
);
89 nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
90 const nsRect
& aDirtyRect
,
91 const nsDisplayListSet
& aLists
)
94 // paint the content we are square-rooting
95 nsresult rv
= nsMathMLContainerFrame::BuildDisplayList(aBuilder
, aDirtyRect
, aLists
);
96 NS_ENSURE_SUCCESS(rv
, rv
);
99 // paint the sqrt symbol
100 if (!NS_MATHML_HAS_ERROR(mPresentationData
.flags
)) {
101 rv
= mSqrChar
.Display(aBuilder
, this, aLists
, 0);
102 NS_ENSURE_SUCCESS(rv
, rv
);
104 rv
= DisplayBar(aBuilder
, this, mBarRect
, aLists
);
105 NS_ENSURE_SUCCESS(rv
, rv
);
107 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
110 mSqrChar
.GetRect(rect
);
111 nsBoundingMetrics bm
;
112 mSqrChar
.GetBoundingMetrics(bm
);
113 rv
= DisplayBoundingMetrics(aBuilder
, this, rect
.TopLeft(), bm
, aLists
);
121 GetRadicalXOffsets(nscoord aIndexWidth
, nscoord aSqrWidth
,
122 nsFontMetrics
* aFontMetrics
,
123 nscoord
* aIndexOffset
, nscoord
* aSqrOffset
)
125 // The index is tucked in closer to the radical while making sure
126 // that the kern does not make the index and radical collide
127 nscoord dxIndex
, dxSqr
;
128 nscoord xHeight
= aFontMetrics
->XHeight();
129 nscoord indexRadicalKern
= NSToCoordRound(1.35f
* xHeight
);
130 if (indexRadicalKern
> aIndexWidth
) {
131 dxIndex
= indexRadicalKern
- aIndexWidth
;
136 dxSqr
= aIndexWidth
- indexRadicalKern
;
138 // avoid collision by leaving a minimum space between index and radical
139 nscoord minimumClearance
= aSqrWidth
/2;
140 if (dxIndex
+ aIndexWidth
+ minimumClearance
> dxSqr
+ aSqrWidth
) {
141 if (aIndexWidth
+ minimumClearance
< aSqrWidth
) {
142 dxIndex
= aSqrWidth
- (aIndexWidth
+ minimumClearance
);
147 dxSqr
= (aIndexWidth
+ minimumClearance
) - aSqrWidth
;
152 *aIndexOffset
= dxIndex
;
158 nsMathMLmrootFrame::Reflow(nsPresContext
* aPresContext
,
159 nsHTMLReflowMetrics
& aDesiredSize
,
160 const nsHTMLReflowState
& aReflowState
,
161 nsReflowStatus
& aStatus
)
164 nsSize
availSize(aReflowState
.ComputedWidth(), NS_UNCONSTRAINEDSIZE
);
165 nsReflowStatus childStatus
;
167 aDesiredSize
.width
= aDesiredSize
.height
= 0;
168 aDesiredSize
.ascent
= 0;
170 nsBoundingMetrics bmSqr
, bmBase
, bmIndex
;
171 nsRenderingContext
& renderingContext
= *aReflowState
.rendContext
;
177 nsIFrame
* baseFrame
= nullptr;
178 nsIFrame
* indexFrame
= nullptr;
179 nsHTMLReflowMetrics baseSize
;
180 nsHTMLReflowMetrics indexSize
;
181 nsIFrame
* childFrame
= mFrames
.FirstChild();
183 // ask our children to compute their bounding metrics
184 nsHTMLReflowMetrics
childDesiredSize(aDesiredSize
.mFlags
185 | NS_REFLOW_CALC_BOUNDING_METRICS
);
186 nsHTMLReflowState
childReflowState(aPresContext
, aReflowState
,
187 childFrame
, availSize
);
188 rv
= ReflowChild(childFrame
, aPresContext
,
189 childDesiredSize
, childReflowState
, childStatus
);
190 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
192 // Call DidReflow() for the child frames we successfully did reflow.
193 DidReflowChildren(mFrames
.FirstChild(), childFrame
);
198 baseFrame
= childFrame
;
199 baseSize
= childDesiredSize
;
200 bmBase
= childDesiredSize
.mBoundingMetrics
;
202 else if (1 == count
) {
204 indexFrame
= childFrame
;
205 indexSize
= childDesiredSize
;
206 bmIndex
= childDesiredSize
.mBoundingMetrics
;
209 childFrame
= childFrame
->GetNextSibling();
212 // report an error, encourage people to get their markups in order
213 rv
= ReflowError(renderingContext
, aDesiredSize
);
214 aStatus
= NS_FRAME_COMPLETE
;
215 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
216 // Call DidReflow() for the child frames we successfully did reflow.
217 DidReflowChildren(mFrames
.FirstChild(), childFrame
);
222 // Prepare the radical symbol and the overline bar
224 nsRefPtr
<nsFontMetrics
> fm
;
225 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
));
226 renderingContext
.SetFont(fm
);
228 // For radical glyphs from TeX fonts and some of the radical glyphs from
229 // Mathematica fonts, the thickness of the overline can be obtained from the
230 // ascent of the glyph. Most fonts however have radical glyphs above the
231 // baseline so no assumption can be made about the meaning of the ascent.
232 nscoord ruleThickness
, leading
, em
;
233 GetRuleThickness(renderingContext
, fm
, ruleThickness
);
236 nsBoundingMetrics bmOne
= renderingContext
.GetBoundingMetrics(&one
, 1);
238 // get the leading to be left at the top of the resulting frame
239 // this seems more reliable than using fm->GetLeading() on suspicious fonts
241 leading
= nscoord(0.2f
* em
);
243 // Rule 11, App. G, TeXbook
244 // psi = clearance between rule and content
245 nscoord phi
= 0, psi
= 0;
246 if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData
.flags
))
250 psi
= ruleThickness
+ phi
/4;
252 // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
253 if (bmOne
.ascent
> bmBase
.ascent
)
254 psi
+= bmOne
.ascent
- bmBase
.ascent
;
256 // make sure that the rule appears on on screen
257 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
258 if (ruleThickness
< onePixel
) {
259 ruleThickness
= onePixel
;
262 // adjust clearance psi to get an exact number of pixels -- this
263 // gives a nicer & uniform look on stacked radicals (bug 130282)
264 nscoord delta
= psi
% onePixel
;
266 psi
+= onePixel
- delta
; // round up
268 // Stretch the radical symbol to the appropriate height if it is not big enough.
269 nsBoundingMetrics contSize
= bmBase
;
270 contSize
.descent
= bmBase
.ascent
+ bmBase
.descent
+ psi
;
271 contSize
.ascent
= ruleThickness
;
273 // height(radical) should be >= height(base) + psi + ruleThickness
274 nsBoundingMetrics radicalSize
;
275 mSqrChar
.Stretch(aPresContext
, renderingContext
,
276 NS_STRETCH_DIRECTION_VERTICAL
,
277 contSize
, radicalSize
,
279 NS_MATHML_IS_RTL(mPresentationData
.flags
));
280 // radicalSize have changed at this point, and should match with
281 // the bounding metrics of the char
282 mSqrChar
.GetBoundingMetrics(bmSqr
);
284 // Update the desired size for the container (like msqrt, index is not yet included)
285 // the baseline will be that of the base.
286 mBoundingMetrics
.ascent
= bmBase
.ascent
+ psi
+ ruleThickness
;
287 mBoundingMetrics
.descent
=
288 NS_MAX(bmBase
.descent
,
289 (bmSqr
.ascent
+ bmSqr
.descent
- mBoundingMetrics
.ascent
));
290 mBoundingMetrics
.width
= bmSqr
.width
+ bmBase
.width
;
291 mBoundingMetrics
.leftBearing
= bmSqr
.leftBearing
;
292 mBoundingMetrics
.rightBearing
= bmSqr
.width
+
293 NS_MAX(bmBase
.width
, bmBase
.rightBearing
); // take also care of the rule
295 aDesiredSize
.ascent
= mBoundingMetrics
.ascent
+ leading
;
296 aDesiredSize
.height
= aDesiredSize
.ascent
+
297 NS_MAX(baseSize
.height
- baseSize
.ascent
,
298 mBoundingMetrics
.descent
+ ruleThickness
);
299 aDesiredSize
.width
= mBoundingMetrics
.width
;
302 // Re-adjust the desired size to include the index.
304 // the index is raised by some fraction of the height
305 // of the radical, see \mroot macro in App. B, TexBook
306 nscoord raiseIndexDelta
= NSToCoordRound(0.6f
* (bmSqr
.ascent
+ bmSqr
.descent
));
307 nscoord indexRaisedAscent
= mBoundingMetrics
.ascent
// top of radical
308 - (bmSqr
.ascent
+ bmSqr
.descent
) // to bottom of radical
309 + raiseIndexDelta
+ bmIndex
.ascent
+ bmIndex
.descent
; // to top of raised index
311 nscoord indexClearance
= 0;
312 if (mBoundingMetrics
.ascent
< indexRaisedAscent
) {
314 indexRaisedAscent
- mBoundingMetrics
.ascent
; // excess gap introduced by a tall index
315 mBoundingMetrics
.ascent
= indexRaisedAscent
;
316 nscoord descent
= aDesiredSize
.height
- aDesiredSize
.ascent
;
317 aDesiredSize
.ascent
= mBoundingMetrics
.ascent
+ leading
;
318 aDesiredSize
.height
= aDesiredSize
.ascent
+ descent
;
321 nscoord dxIndex
, dxSqr
;
322 GetRadicalXOffsets(bmIndex
.width
, bmSqr
.width
, fm
, &dxIndex
, &dxSqr
);
324 mBoundingMetrics
.width
= dxSqr
+ bmSqr
.width
+ bmBase
.width
;
325 mBoundingMetrics
.leftBearing
=
326 NS_MIN(dxIndex
+ bmIndex
.leftBearing
, dxSqr
+ bmSqr
.leftBearing
);
327 mBoundingMetrics
.rightBearing
= dxSqr
+ bmSqr
.width
+
328 NS_MAX(bmBase
.width
, bmBase
.rightBearing
);
330 aDesiredSize
.width
= mBoundingMetrics
.width
;
331 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
332 GatherAndStoreOverflow(&aDesiredSize
);
335 nscoord dx
= dxIndex
;
336 nscoord dy
= aDesiredSize
.ascent
- (indexRaisedAscent
+ indexSize
.ascent
- bmIndex
.ascent
);
337 FinishReflowChild(indexFrame
, aPresContext
, nullptr, indexSize
,
338 MirrorIfRTL(aDesiredSize
.width
, indexSize
.width
, dx
),
341 // place the radical symbol and the radical bar
343 dy
= indexClearance
+ leading
; // leave a leading at the top
344 mSqrChar
.SetRect(nsRect(MirrorIfRTL(aDesiredSize
.width
, bmSqr
.width
, dx
),
345 dy
, bmSqr
.width
, bmSqr
.ascent
+ bmSqr
.descent
));
347 mBarRect
.SetRect(MirrorIfRTL(aDesiredSize
.width
, bmBase
.width
, dx
),
348 dy
, bmBase
.width
, ruleThickness
);
351 dy
= aDesiredSize
.ascent
- baseSize
.ascent
;
352 FinishReflowChild(baseFrame
, aPresContext
, nullptr, baseSize
,
353 MirrorIfRTL(aDesiredSize
.width
, baseSize
.width
, dx
),
357 mReference
.y
= aDesiredSize
.ascent
;
359 aStatus
= NS_FRAME_COMPLETE
;
360 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
364 /* virtual */ nscoord
365 nsMathMLmrootFrame::GetIntrinsicWidth(nsRenderingContext
* aRenderingContext
)
367 nsIFrame
* baseFrame
= mFrames
.FirstChild();
368 nsIFrame
* indexFrame
= nullptr;
370 indexFrame
= baseFrame
->GetNextSibling();
371 if (!indexFrame
|| indexFrame
->GetNextSibling()) {
372 nsHTMLReflowMetrics desiredSize
;
373 ReflowError(*aRenderingContext
, desiredSize
);
374 return desiredSize
.width
;
378 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
, baseFrame
,
379 nsLayoutUtils::PREF_WIDTH
);
381 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
, indexFrame
,
382 nsLayoutUtils::PREF_WIDTH
);
383 nscoord sqrWidth
= mSqrChar
.GetMaxWidth(PresContext(), *aRenderingContext
);
386 GetRadicalXOffsets(indexWidth
, sqrWidth
, aRenderingContext
->FontMetrics(),
389 return dxSqr
+ sqrWidth
+ baseWidth
;
392 // ----------------------
393 // the Style System will use these to pass the proper style context to our MathMLChar
395 nsMathMLmrootFrame::GetAdditionalStyleContext(int32_t aIndex
) const
398 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX
:
399 return mSqrChar
.GetStyleContext();
407 nsMathMLmrootFrame::SetAdditionalStyleContext(int32_t aIndex
,
408 nsStyleContext
* aStyleContext
)
411 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX
:
412 mSqrChar
.SetStyleContext(aStyleContext
);