1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla MathML Project.
17 * The Initial Developer of the Original Code is
18 * The University Of Queensland.
19 * Portions created by the Initial Developer are Copyright (C) 1999
20 * the Initial Developer. All Rights Reserved.
23 * Roger B. Sidje <rbs@maths.uq.edu.au>
24 * David J. Fiddes <D.J.Fiddes@hw.ac.uk>
25 * Vilya Harvey <vilya@nag.co.uk>
26 * Shyjan Mahamud <mahamud@cs.cmu.edu>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
45 #include "nsPresContext.h"
46 #include "nsStyleContext.h"
47 #include "nsStyleConsts.h"
48 #include "nsIRenderingContext.h"
49 #include "nsIFontMetrics.h"
51 #include "nsMathMLmrootFrame.h"
54 // <msqrt> and <mroot> -- form a radical - implementation
58 // The code assumes that TeX fonts are picked.
59 // There is no fall-back to draw the branches of the sqrt explicitly
60 // in the case where TeX fonts are not there. In general, there are no
61 // fall-back(s) in MathML when some (freely-downloadable) fonts are missing.
62 // Otherwise, this will add much work and unnecessary complexity to the core
63 // MathML engine. Assuming that authors have the free fonts is part of the
64 // deal. We are not responsible for cases of misconfigurations out there.
66 // additional style context to be used by our MathMLChar.
67 #define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0
69 static const PRUnichar kSqrChar
= PRUnichar(0x221A);
72 NS_NewMathMLmrootFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
74 return new (aPresShell
) nsMathMLmrootFrame(aContext
);
77 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame
)
79 nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext
* aContext
) :
80 nsMathMLContainerFrame(aContext
),
86 nsMathMLmrootFrame::~nsMathMLmrootFrame()
91 nsMathMLmrootFrame::Init(nsIContent
* aContent
,
93 nsIFrame
* aPrevInFlow
)
95 nsresult rv
= nsMathMLContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
97 nsPresContext
*presContext
= PresContext();
99 // No need to track the style context given to our MathML char.
100 // The Style System will use Get/SetAdditionalStyleContext() to keep it
101 // up-to-date if dynamic changes arise.
102 nsAutoString sqrChar
; sqrChar
.Assign(kSqrChar
);
103 mSqrChar
.SetData(presContext
, sqrChar
);
104 ResolveMathMLCharStyle(presContext
, mContent
, mStyleContext
, &mSqrChar
, PR_TRUE
);
110 nsMathMLmrootFrame::TransmitAutomaticData()
113 // The <mroot> element increments scriptlevel by 2, and sets displaystyle to
114 // "false", within index, but leaves both attributes unchanged within base.
115 // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
116 UpdatePresentationDataFromChildAt(1, 1,
117 ~NS_MATHML_DISPLAYSTYLE
| NS_MATHML_COMPRESSED
,
118 NS_MATHML_DISPLAYSTYLE
| NS_MATHML_COMPRESSED
);
119 UpdatePresentationDataFromChildAt(0, 0,
120 NS_MATHML_COMPRESSED
, NS_MATHML_COMPRESSED
);
126 nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
127 const nsRect
& aDirtyRect
,
128 const nsDisplayListSet
& aLists
)
131 // paint the content we are square-rooting
132 nsresult rv
= nsMathMLContainerFrame::BuildDisplayList(aBuilder
, aDirtyRect
, aLists
);
133 NS_ENSURE_SUCCESS(rv
, rv
);
136 // paint the sqrt symbol
137 if (!NS_MATHML_HAS_ERROR(mPresentationData
.flags
)) {
138 rv
= mSqrChar
.Display(aBuilder
, this, aLists
);
139 NS_ENSURE_SUCCESS(rv
, rv
);
141 rv
= DisplayBar(aBuilder
, this, mBarRect
, aLists
);
142 NS_ENSURE_SUCCESS(rv
, rv
);
144 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
147 mSqrChar
.GetRect(rect
);
148 nsBoundingMetrics bm
;
149 mSqrChar
.GetBoundingMetrics(bm
);
150 rv
= DisplayBoundingMetrics(aBuilder
, this, rect
.TopLeft(), bm
, aLists
);
158 GetRadicalXOffsets(nscoord aIndexWidth
, nscoord aSqrWidth
,
159 nsIFontMetrics
* aFontMetrics
,
160 nscoord
* aIndexOffset
, nscoord
* aSqrOffset
)
162 // The index is tucked in closer to the radical while making sure
163 // that the kern does not make the index and radical collide
164 nscoord dxIndex
, dxSqr
;
166 aFontMetrics
->GetXHeight(xHeight
);
167 nscoord indexRadicalKern
= NSToCoordRound(1.35f
* xHeight
);
168 if (indexRadicalKern
> aIndexWidth
) {
169 dxIndex
= indexRadicalKern
- aIndexWidth
;
174 dxSqr
= aIndexWidth
- indexRadicalKern
;
176 // avoid collision by leaving a minimum space between index and radical
177 nscoord minimumClearance
= aSqrWidth
/2;
178 if (dxIndex
+ aIndexWidth
+ minimumClearance
> dxSqr
+ aSqrWidth
) {
179 if (aIndexWidth
+ minimumClearance
< aSqrWidth
) {
180 dxIndex
= aSqrWidth
- (aIndexWidth
+ minimumClearance
);
185 dxSqr
= (aIndexWidth
+ minimumClearance
) - aSqrWidth
;
190 *aIndexOffset
= dxIndex
;
196 nsMathMLmrootFrame::Reflow(nsPresContext
* aPresContext
,
197 nsHTMLReflowMetrics
& aDesiredSize
,
198 const nsHTMLReflowState
& aReflowState
,
199 nsReflowStatus
& aStatus
)
202 nsSize
availSize(aReflowState
.ComputedWidth(), NS_UNCONSTRAINEDSIZE
);
203 nsReflowStatus childStatus
;
205 aDesiredSize
.width
= aDesiredSize
.height
= 0;
206 aDesiredSize
.ascent
= 0;
208 nsBoundingMetrics bmSqr
, bmBase
, bmIndex
;
209 nsIRenderingContext
& renderingContext
= *aReflowState
.rendContext
;
215 nsIFrame
* baseFrame
= nsnull
;
216 nsIFrame
* indexFrame
= nsnull
;
217 nsHTMLReflowMetrics baseSize
;
218 nsHTMLReflowMetrics indexSize
;
219 nsIFrame
* childFrame
= mFrames
.FirstChild();
221 // ask our children to compute their bounding metrics
222 nsHTMLReflowMetrics
childDesiredSize(aDesiredSize
.mFlags
223 | NS_REFLOW_CALC_BOUNDING_METRICS
);
224 nsHTMLReflowState
childReflowState(aPresContext
, aReflowState
,
225 childFrame
, availSize
);
226 rv
= ReflowChild(childFrame
, aPresContext
,
227 childDesiredSize
, childReflowState
, childStatus
);
228 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
230 // Call DidReflow() for the child frames we successfully did reflow.
231 DidReflowChildren(mFrames
.FirstChild(), childFrame
);
236 baseFrame
= childFrame
;
237 baseSize
= childDesiredSize
;
238 bmBase
= childDesiredSize
.mBoundingMetrics
;
240 else if (1 == count
) {
242 indexFrame
= childFrame
;
243 indexSize
= childDesiredSize
;
244 bmIndex
= childDesiredSize
.mBoundingMetrics
;
247 childFrame
= childFrame
->GetNextSibling();
250 // report an error, encourage people to get their markups in order
251 rv
= ReflowError(renderingContext
, aDesiredSize
);
252 aStatus
= NS_FRAME_COMPLETE
;
253 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
254 // Call DidReflow() for the child frames we successfully did reflow.
255 DidReflowChildren(mFrames
.FirstChild(), childFrame
);
260 // Prepare the radical symbol and the overline bar
262 renderingContext
.SetFont(GetStyleFont()->mFont
, nsnull
,
263 aPresContext
->GetUserFontSet());
264 nsCOMPtr
<nsIFontMetrics
> fm
;
265 renderingContext
.GetFontMetrics(*getter_AddRefs(fm
));
267 // For radical glyphs from TeX fonts and some of the radical glyphs from
268 // Mathematica fonts, the thickness of the overline can be obtained from the
269 // ascent of the glyph. Most fonts however have radical glyphs above the
270 // baseline so no assumption can be made about the meaning of the ascent.
271 nscoord ruleThickness
, leading
, em
;
272 GetRuleThickness(renderingContext
, fm
, ruleThickness
);
274 nsBoundingMetrics bmOne
;
275 renderingContext
.GetBoundingMetrics(NS_LITERAL_STRING("1").get(), 1, bmOne
);
277 // get the leading to be left at the top of the resulting frame
278 // this seems more reliable than using fm->GetLeading() on suspicious fonts
280 leading
= nscoord(0.2f
* em
);
282 // Rule 11, App. G, TeXbook
283 // psi = clearance between rule and content
284 nscoord phi
= 0, psi
= 0;
285 if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData
.flags
))
289 psi
= ruleThickness
+ phi
/4;
291 // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
292 if (bmOne
.ascent
> bmBase
.ascent
)
293 psi
+= bmOne
.ascent
- bmBase
.ascent
;
295 // make sure that the rule appears on on screen
296 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
297 if (ruleThickness
< onePixel
) {
298 ruleThickness
= onePixel
;
301 // adjust clearance psi to get an exact number of pixels -- this
302 // gives a nicer & uniform look on stacked radicals (bug 130282)
303 nscoord delta
= psi
% onePixel
;
305 psi
+= onePixel
- delta
; // round up
307 // Stretch the radical symbol to the appropriate height if it is not big enough.
308 nsBoundingMetrics contSize
= bmBase
;
309 contSize
.descent
= bmBase
.ascent
+ bmBase
.descent
+ psi
;
310 contSize
.ascent
= ruleThickness
;
312 // height(radical) should be >= height(base) + psi + ruleThickness
313 nsBoundingMetrics radicalSize
;
314 mSqrChar
.Stretch(aPresContext
, renderingContext
,
315 NS_STRETCH_DIRECTION_VERTICAL
,
316 contSize
, radicalSize
,
318 // radicalSize have changed at this point, and should match with
319 // the bounding metrics of the char
320 mSqrChar
.GetBoundingMetrics(bmSqr
);
322 // Update the desired size for the container (like msqrt, index is not yet included)
323 // the baseline will be that of the base.
324 mBoundingMetrics
.ascent
= bmBase
.ascent
+ psi
+ ruleThickness
;
325 mBoundingMetrics
.descent
=
326 NS_MAX(bmBase
.descent
,
327 (bmSqr
.ascent
+ bmSqr
.descent
- mBoundingMetrics
.ascent
));
328 mBoundingMetrics
.width
= bmSqr
.width
+ bmBase
.width
;
329 mBoundingMetrics
.leftBearing
= bmSqr
.leftBearing
;
330 mBoundingMetrics
.rightBearing
= bmSqr
.width
+
331 NS_MAX(bmBase
.width
, bmBase
.rightBearing
); // take also care of the rule
333 aDesiredSize
.ascent
= mBoundingMetrics
.ascent
+ leading
;
334 aDesiredSize
.height
= aDesiredSize
.ascent
+
335 NS_MAX(baseSize
.height
- baseSize
.ascent
,
336 mBoundingMetrics
.descent
+ ruleThickness
);
337 aDesiredSize
.width
= mBoundingMetrics
.width
;
340 // Re-adjust the desired size to include the index.
342 // the index is raised by some fraction of the height
343 // of the radical, see \mroot macro in App. B, TexBook
344 nscoord raiseIndexDelta
= NSToCoordRound(0.6f
* (bmSqr
.ascent
+ bmSqr
.descent
));
345 nscoord indexRaisedAscent
= mBoundingMetrics
.ascent
// top of radical
346 - (bmSqr
.ascent
+ bmSqr
.descent
) // to bottom of radical
347 + raiseIndexDelta
+ bmIndex
.ascent
+ bmIndex
.descent
; // to top of raised index
349 nscoord indexClearance
= 0;
350 if (mBoundingMetrics
.ascent
< indexRaisedAscent
) {
352 indexRaisedAscent
- mBoundingMetrics
.ascent
; // excess gap introduced by a tall index
353 mBoundingMetrics
.ascent
= indexRaisedAscent
;
354 nscoord descent
= aDesiredSize
.height
- aDesiredSize
.ascent
;
355 aDesiredSize
.ascent
= mBoundingMetrics
.ascent
+ leading
;
356 aDesiredSize
.height
= aDesiredSize
.ascent
+ descent
;
359 nscoord dxIndex
, dxSqr
;
360 GetRadicalXOffsets(bmIndex
.width
, bmSqr
.width
, fm
, &dxIndex
, &dxSqr
);
363 nscoord dx
= dxIndex
;
364 nscoord dy
= aDesiredSize
.ascent
- (indexRaisedAscent
+ indexSize
.ascent
- bmIndex
.ascent
);
365 FinishReflowChild(indexFrame
, aPresContext
, nsnull
, indexSize
, dx
, dy
, 0);
367 // place the radical symbol and the radical bar
369 dy
= indexClearance
+ leading
; // leave a leading at the top
370 mSqrChar
.SetRect(nsRect(dx
, dy
, bmSqr
.width
, bmSqr
.ascent
+ bmSqr
.descent
));
372 mBarRect
.SetRect(dx
, dy
, bmBase
.width
, ruleThickness
);
375 dy
= aDesiredSize
.ascent
- baseSize
.ascent
;
376 FinishReflowChild(baseFrame
, aPresContext
, nsnull
, baseSize
, dx
, dy
, 0);
379 mReference
.y
= aDesiredSize
.ascent
;
381 mBoundingMetrics
.width
= dx
+ bmBase
.width
;
382 mBoundingMetrics
.leftBearing
=
383 NS_MIN(dxIndex
+ bmIndex
.leftBearing
, dxSqr
+ bmSqr
.leftBearing
);
384 mBoundingMetrics
.rightBearing
= dx
+
385 NS_MAX(bmBase
.width
, bmBase
.rightBearing
);
387 aDesiredSize
.width
= mBoundingMetrics
.width
;
388 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
389 GatherAndStoreOverflow(&aDesiredSize
);
391 aStatus
= NS_FRAME_COMPLETE
;
392 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
396 /* virtual */ nscoord
397 nsMathMLmrootFrame::GetIntrinsicWidth(nsIRenderingContext
* aRenderingContext
)
399 nsIFrame
* baseFrame
= mFrames
.FirstChild();
400 nsIFrame
* indexFrame
= nsnull
;
402 indexFrame
= baseFrame
->GetNextSibling();
403 if (!indexFrame
|| indexFrame
->GetNextSibling()) {
404 nsHTMLReflowMetrics desiredSize
;
405 ReflowError(*aRenderingContext
, desiredSize
);
406 return desiredSize
.width
;
410 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
, baseFrame
,
411 nsLayoutUtils::PREF_WIDTH
);
413 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
, indexFrame
,
414 nsLayoutUtils::PREF_WIDTH
);
415 nscoord sqrWidth
= mSqrChar
.GetMaxWidth(PresContext(), *aRenderingContext
);
417 nsCOMPtr
<nsIFontMetrics
> fm
;
418 aRenderingContext
->GetFontMetrics(*getter_AddRefs(fm
));
420 GetRadicalXOffsets(indexWidth
, sqrWidth
, fm
, nsnull
, &dxSqr
);
422 return dxSqr
+ sqrWidth
+ baseWidth
;
425 // ----------------------
426 // the Style System will use these to pass the proper style context to our MathMLChar
428 nsMathMLmrootFrame::GetAdditionalStyleContext(PRInt32 aIndex
) const
431 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX
:
432 return mSqrChar
.GetStyleContext();
440 nsMathMLmrootFrame::SetAdditionalStyleContext(PRInt32 aIndex
,
441 nsStyleContext
* aStyleContext
)
444 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX
:
445 mSqrChar
.SetStyleContext(aStyleContext
);