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 nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext
* aContext
) :
78 nsMathMLContainerFrame(aContext
),
84 nsMathMLmrootFrame::~nsMathMLmrootFrame()
89 nsMathMLmrootFrame::Init(nsIContent
* aContent
,
91 nsIFrame
* aPrevInFlow
)
93 nsresult rv
= nsMathMLContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
95 nsPresContext
*presContext
= PresContext();
97 // No need to tract the style context given to our MathML char.
98 // The Style System will use Get/SetAdditionalStyleContext() to keep it
99 // up-to-date if dynamic changes arise.
100 nsAutoString sqrChar
; sqrChar
.Assign(kSqrChar
);
101 mSqrChar
.SetData(presContext
, sqrChar
);
102 ResolveMathMLCharStyle(presContext
, mContent
, mStyleContext
, &mSqrChar
, PR_TRUE
);
108 nsMathMLmrootFrame::TransmitAutomaticData()
111 // The <mroot> element increments scriptlevel by 2, and sets displaystyle to
112 // "false", within index, but leaves both attributes unchanged within base.
113 // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
114 UpdatePresentationDataFromChildAt(1, 1,
115 ~NS_MATHML_DISPLAYSTYLE
| NS_MATHML_COMPRESSED
,
116 NS_MATHML_DISPLAYSTYLE
| NS_MATHML_COMPRESSED
);
117 UpdatePresentationDataFromChildAt(0, 0,
118 NS_MATHML_COMPRESSED
, NS_MATHML_COMPRESSED
);
124 nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
125 const nsRect
& aDirtyRect
,
126 const nsDisplayListSet
& aLists
)
129 // paint the content we are square-rooting
130 nsresult rv
= nsMathMLContainerFrame::BuildDisplayList(aBuilder
, aDirtyRect
, aLists
);
131 NS_ENSURE_SUCCESS(rv
, rv
);
134 // paint the sqrt symbol
135 if (!NS_MATHML_HAS_ERROR(mPresentationData
.flags
)) {
136 rv
= mSqrChar
.Display(aBuilder
, this, aLists
);
137 NS_ENSURE_SUCCESS(rv
, rv
);
139 rv
= DisplayBar(aBuilder
, this, mBarRect
, aLists
);
140 NS_ENSURE_SUCCESS(rv
, rv
);
142 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
145 mSqrChar
.GetRect(rect
);
146 nsBoundingMetrics bm
;
147 mSqrChar
.GetBoundingMetrics(bm
);
148 rv
= DisplayBoundingMetrics(aBuilder
, this, rect
.TopLeft(), bm
, aLists
);
156 GetRadicalXOffsets(nscoord aIndexWidth
, nscoord aSqrWidth
,
157 nsIFontMetrics
* aFontMetrics
,
158 nscoord
* aIndexOffset
, nscoord
* aSqrOffset
)
160 // The index is tucked in closer to the radical while making sure
161 // that the kern does not make the index and radical collide
162 nscoord dxIndex
, dxSqr
;
164 aFontMetrics
->GetXHeight(xHeight
);
165 nscoord indexRadicalKern
= NSToCoordRound(1.35f
* xHeight
);
166 if (indexRadicalKern
> aIndexWidth
) {
167 dxIndex
= indexRadicalKern
- aIndexWidth
;
172 dxSqr
= aIndexWidth
- indexRadicalKern
;
174 // avoid collision by leaving a minimum space between index and radical
175 nscoord minimumClearance
= aSqrWidth
/2;
176 if (dxIndex
+ aIndexWidth
+ minimumClearance
> dxSqr
+ aSqrWidth
) {
177 if (aIndexWidth
+ minimumClearance
< aSqrWidth
) {
178 dxIndex
= aSqrWidth
- (aIndexWidth
+ minimumClearance
);
183 dxSqr
= (aIndexWidth
+ minimumClearance
) - aSqrWidth
;
188 *aIndexOffset
= dxIndex
;
194 nsMathMLmrootFrame::Reflow(nsPresContext
* aPresContext
,
195 nsHTMLReflowMetrics
& aDesiredSize
,
196 const nsHTMLReflowState
& aReflowState
,
197 nsReflowStatus
& aStatus
)
200 nsSize
availSize(aReflowState
.ComputedWidth(), NS_UNCONSTRAINEDSIZE
);
201 nsReflowStatus childStatus
;
203 aDesiredSize
.width
= aDesiredSize
.height
= 0;
204 aDesiredSize
.ascent
= 0;
206 nsBoundingMetrics bmSqr
, bmBase
, bmIndex
;
207 nsIRenderingContext
& renderingContext
= *aReflowState
.rendContext
;
213 nsIFrame
* baseFrame
= nsnull
;
214 nsIFrame
* indexFrame
= nsnull
;
215 nsHTMLReflowMetrics baseSize
;
216 nsHTMLReflowMetrics indexSize
;
217 nsIFrame
* childFrame
= mFrames
.FirstChild();
219 // ask our children to compute their bounding metrics
220 nsHTMLReflowMetrics
childDesiredSize(aDesiredSize
.mFlags
221 | NS_REFLOW_CALC_BOUNDING_METRICS
);
222 nsHTMLReflowState
childReflowState(aPresContext
, aReflowState
,
223 childFrame
, availSize
);
224 rv
= ReflowChild(childFrame
, aPresContext
,
225 childDesiredSize
, childReflowState
, childStatus
);
226 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
228 // Call DidReflow() for the child frames we successfully did reflow.
229 DidReflowChildren(mFrames
.FirstChild(), childFrame
);
234 baseFrame
= childFrame
;
235 baseSize
= childDesiredSize
;
236 bmBase
= childDesiredSize
.mBoundingMetrics
;
238 else if (1 == count
) {
240 indexFrame
= childFrame
;
241 indexSize
= childDesiredSize
;
242 bmIndex
= childDesiredSize
.mBoundingMetrics
;
245 childFrame
= childFrame
->GetNextSibling();
248 // report an error, encourage people to get their markups in order
249 rv
= ReflowError(renderingContext
, aDesiredSize
);
250 aStatus
= NS_FRAME_COMPLETE
;
251 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
252 // Call DidReflow() for the child frames we successfully did reflow.
253 DidReflowChildren(mFrames
.FirstChild(), childFrame
);
258 // Prepare the radical symbol and the overline bar
260 renderingContext
.SetFont(GetStyleFont()->mFont
, nsnull
,
261 aPresContext
->GetUserFontSet());
262 nsCOMPtr
<nsIFontMetrics
> fm
;
263 renderingContext
.GetFontMetrics(*getter_AddRefs(fm
));
265 // For radical glyphs from TeX fonts and some of the radical glyphs from
266 // Mathematica fonts, the thickness of the overline can be obtained from the
267 // ascent of the glyph. Most fonts however have radical glyphs above the
268 // baseline so no assumption can be made about the meaning of the ascent.
269 nscoord ruleThickness
, leading
, em
;
270 GetRuleThickness(renderingContext
, fm
, ruleThickness
);
272 nsBoundingMetrics bmOne
;
273 renderingContext
.GetBoundingMetrics(NS_LITERAL_STRING("1").get(), 1, bmOne
);
275 // get the leading to be left at the top of the resulting frame
276 // this seems more reliable than using fm->GetLeading() on suspicious fonts
278 leading
= nscoord(0.2f
* em
);
280 // Rule 11, App. G, TeXbook
281 // psi = clearance between rule and content
282 nscoord phi
= 0, psi
= 0;
283 if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData
.flags
))
287 psi
= ruleThickness
+ phi
/4;
289 // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
290 if (bmOne
.ascent
> bmBase
.ascent
)
291 psi
+= bmOne
.ascent
- bmBase
.ascent
;
293 // make sure that the rule appears on on screen
294 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
295 if (ruleThickness
< onePixel
) {
296 ruleThickness
= onePixel
;
299 // adjust clearance psi to get an exact number of pixels -- this
300 // gives a nicer & uniform look on stacked radicals (bug 130282)
301 nscoord delta
= psi
% onePixel
;
303 psi
+= onePixel
- delta
; // round up
305 // Stretch the radical symbol to the appropriate height if it is not big enough.
306 nsBoundingMetrics contSize
= bmBase
;
307 contSize
.descent
= bmBase
.ascent
+ bmBase
.descent
+ psi
;
308 contSize
.ascent
= ruleThickness
;
310 // height(radical) should be >= height(base) + psi + ruleThickness
311 nsBoundingMetrics radicalSize
;
312 mSqrChar
.Stretch(aPresContext
, renderingContext
,
313 NS_STRETCH_DIRECTION_VERTICAL
,
314 contSize
, radicalSize
,
316 // radicalSize have changed at this point, and should match with
317 // the bounding metrics of the char
318 mSqrChar
.GetBoundingMetrics(bmSqr
);
320 // Update the desired size for the container (like msqrt, index is not yet included)
321 // the baseline will be that of the base.
322 mBoundingMetrics
.ascent
= bmBase
.ascent
+ psi
+ ruleThickness
;
323 mBoundingMetrics
.descent
=
324 PR_MAX(bmBase
.descent
,
325 (bmSqr
.ascent
+ bmSqr
.descent
- mBoundingMetrics
.ascent
));
326 mBoundingMetrics
.width
= bmSqr
.width
+ bmBase
.width
;
327 mBoundingMetrics
.leftBearing
= bmSqr
.leftBearing
;
328 mBoundingMetrics
.rightBearing
= bmSqr
.width
+
329 PR_MAX(bmBase
.width
, bmBase
.rightBearing
); // take also care of the rule
331 aDesiredSize
.ascent
= mBoundingMetrics
.ascent
+ leading
;
332 aDesiredSize
.height
= aDesiredSize
.ascent
+
333 PR_MAX(baseSize
.height
- baseSize
.ascent
,
334 mBoundingMetrics
.descent
+ ruleThickness
);
335 aDesiredSize
.width
= mBoundingMetrics
.width
;
338 // Re-adjust the desired size to include the index.
340 // the index is raised by some fraction of the height
341 // of the radical, see \mroot macro in App. B, TexBook
342 nscoord raiseIndexDelta
= NSToCoordRound(0.6f
* (bmSqr
.ascent
+ bmSqr
.descent
));
343 nscoord indexRaisedAscent
= mBoundingMetrics
.ascent
// top of radical
344 - (bmSqr
.ascent
+ bmSqr
.descent
) // to bottom of radical
345 + raiseIndexDelta
+ bmIndex
.ascent
+ bmIndex
.descent
; // to top of raised index
347 nscoord indexClearance
= 0;
348 if (mBoundingMetrics
.ascent
< indexRaisedAscent
) {
350 indexRaisedAscent
- mBoundingMetrics
.ascent
; // excess gap introduced by a tall index
351 mBoundingMetrics
.ascent
= indexRaisedAscent
;
352 nscoord descent
= aDesiredSize
.height
- aDesiredSize
.ascent
;
353 aDesiredSize
.ascent
= mBoundingMetrics
.ascent
+ leading
;
354 aDesiredSize
.height
= aDesiredSize
.ascent
+ descent
;
357 nscoord dxIndex
, dxSqr
;
358 GetRadicalXOffsets(bmIndex
.width
, bmSqr
.width
, fm
, &dxIndex
, &dxSqr
);
361 nscoord dx
= dxIndex
;
362 nscoord dy
= aDesiredSize
.ascent
- (indexRaisedAscent
+ indexSize
.ascent
- bmIndex
.ascent
);
363 FinishReflowChild(indexFrame
, aPresContext
, nsnull
, indexSize
, dx
, dy
, 0);
365 // place the radical symbol and the radical bar
367 dy
= indexClearance
+ leading
; // leave a leading at the top
368 mSqrChar
.SetRect(nsRect(dx
, dy
, bmSqr
.width
, bmSqr
.ascent
+ bmSqr
.descent
));
370 mBarRect
.SetRect(dx
, dy
, bmBase
.width
, ruleThickness
);
373 dy
= aDesiredSize
.ascent
- baseSize
.ascent
;
374 FinishReflowChild(baseFrame
, aPresContext
, nsnull
, baseSize
, dx
, dy
, 0);
377 mReference
.y
= aDesiredSize
.ascent
;
379 mBoundingMetrics
.width
= dx
+ bmBase
.width
;
380 mBoundingMetrics
.leftBearing
=
381 PR_MIN(dxIndex
+ bmIndex
.leftBearing
, dxSqr
+ bmSqr
.leftBearing
);
382 mBoundingMetrics
.rightBearing
= dx
+
383 PR_MAX(bmBase
.width
, bmBase
.rightBearing
);
385 aDesiredSize
.width
= mBoundingMetrics
.width
;
386 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
387 GatherAndStoreOverflow(&aDesiredSize
);
389 aStatus
= NS_FRAME_COMPLETE
;
390 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
394 /* virtual */ nscoord
395 nsMathMLmrootFrame::GetIntrinsicWidth(nsIRenderingContext
* aRenderingContext
)
397 nsIFrame
* baseFrame
= mFrames
.FirstChild();
398 nsIFrame
* indexFrame
= nsnull
;
400 indexFrame
= baseFrame
->GetNextSibling();
401 if (!indexFrame
|| indexFrame
->GetNextSibling()) {
402 nsHTMLReflowMetrics desiredSize
;
403 ReflowError(*aRenderingContext
, desiredSize
);
404 return desiredSize
.width
;
408 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
, baseFrame
,
409 nsLayoutUtils::PREF_WIDTH
);
411 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
, indexFrame
,
412 nsLayoutUtils::PREF_WIDTH
);
413 nscoord sqrWidth
= mSqrChar
.GetMaxWidth(PresContext(), *aRenderingContext
);
415 nsCOMPtr
<nsIFontMetrics
> fm
;
416 aRenderingContext
->GetFontMetrics(*getter_AddRefs(fm
));
418 GetRadicalXOffsets(indexWidth
, sqrWidth
, fm
, nsnull
, &dxSqr
);
420 return dxSqr
+ sqrWidth
+ baseWidth
;
423 // ----------------------
424 // the Style System will use these to pass the proper style context to our MathMLChar
426 nsMathMLmrootFrame::GetAdditionalStyleContext(PRInt32 aIndex
) const
429 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX
:
430 return mSqrChar
.GetStyleContext();
438 nsMathMLmrootFrame::SetAdditionalStyleContext(PRInt32 aIndex
,
439 nsStyleContext
* aStyleContext
)
442 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX
:
443 mSqrChar
.SetStyleContext(aStyleContext
);