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 * Shyjan Mahamud <mahamud@cs.cmu.edu>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
44 #include "nsPresContext.h"
45 #include "nsStyleContext.h"
46 #include "nsStyleConsts.h"
47 #include "nsIRenderingContext.h"
48 #include "nsIFontMetrics.h"
50 #include "nsMathMLmfencedFrame.h"
51 #include "nsMathMLmfracFrame.h"
54 // <mfrac> -- form a fraction from two subexpressions - implementation
57 // various fraction line thicknesses (multiplicative values of the default rule thickness)
59 #define THIN_FRACTION_LINE 0.5f
60 #define THIN_FRACTION_LINE_MINIMUM_PIXELS 1 // minimum of 1 pixel
62 #define MEDIUM_FRACTION_LINE 1.5f
63 #define MEDIUM_FRACTION_LINE_MINIMUM_PIXELS 2 // minimum of 2 pixels
65 #define THICK_FRACTION_LINE 2.0f
66 #define THICK_FRACTION_LINE_MINIMUM_PIXELS 4 // minimum of 4 pixels
68 // additional style context to be used by our MathMLChar.
69 #define NS_SLASH_CHAR_STYLE_CONTEXT_INDEX 0
71 static const PRUnichar kSlashChar
= PRUnichar('/');
74 NS_NewMathMLmfracFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
76 return new (aPresShell
) nsMathMLmfracFrame(aContext
);
79 nsMathMLmfracFrame::~nsMathMLmfracFrame()
88 nsMathMLmfracFrame::IsBevelled()
91 GetAttribute(mContent
, mPresentationData
.mstyle
, nsGkAtoms::bevelled_
,
93 return value
.EqualsLiteral("true");
97 nsMathMLmfracFrame::Init(nsIContent
* aContent
,
99 nsIFrame
* aPrevInFlow
)
101 nsresult rv
= nsMathMLContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
104 // enable the bevelled rendering
105 mSlashChar
= new nsMathMLChar();
107 nsPresContext
* presContext
= PresContext();
109 nsAutoString slashChar
; slashChar
.Assign(kSlashChar
);
110 mSlashChar
->SetData(presContext
, slashChar
);
111 ResolveMathMLCharStyle(presContext
, mContent
, mStyleContext
, mSlashChar
, PR_TRUE
);
119 nsMathMLmfracFrame::GetMathMLFrameType()
121 // frac is "inner" in TeXBook, Appendix G, rule 15e. See also page 170.
122 return eMathMLFrameType_Inner
;
126 nsMathMLmfracFrame::TransmitAutomaticData()
129 // The <mfrac> element sets displaystyle to "false", or if it was already
130 // false increments scriptlevel by 1, within numerator and denominator.
131 // 2. The TeXbook (Ch 17. p.141) says the numerator inherits the compression
132 // while the denominator is compressed
133 PRBool increment
= !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData
.flags
);
134 SetIncrementScriptLevel(0, increment
);
135 SetIncrementScriptLevel(1, increment
);
137 UpdatePresentationDataFromChildAt(0, -1,
138 ~NS_MATHML_DISPLAYSTYLE
,
139 NS_MATHML_DISPLAYSTYLE
);
140 UpdatePresentationDataFromChildAt(1, 1,
141 NS_MATHML_COMPRESSED
,
142 NS_MATHML_COMPRESSED
);
144 // if our numerator is an embellished operator, let its state bubble to us
145 GetEmbellishDataFrom(mFrames
.FirstChild(), mEmbellishData
);
146 if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData
.flags
)) {
147 // even when embellished, we need to record that <mfrac> won't fire
148 // Stretch() on its embellished child
149 mEmbellishData
.direction
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
156 nsMathMLmfracFrame::CalcLineThickness(nsPresContext
* aPresContext
,
157 nsStyleContext
* aStyleContext
,
158 nsString
& aThicknessAttribute
,
160 nscoord aDefaultRuleThickness
)
162 nscoord defaultThickness
= aDefaultRuleThickness
;
163 nscoord lineThickness
= aDefaultRuleThickness
;
164 nscoord minimumThickness
= onePixel
;
166 if (!aThicknessAttribute
.IsEmpty()) {
167 if (aThicknessAttribute
.EqualsLiteral("thin")) {
168 lineThickness
= NSToCoordFloor(defaultThickness
* THIN_FRACTION_LINE
);
169 minimumThickness
= onePixel
* THIN_FRACTION_LINE_MINIMUM_PIXELS
;
170 // should visually decrease by at least one pixel, if default is not a pixel
171 if (defaultThickness
> onePixel
&& lineThickness
> defaultThickness
- onePixel
)
172 lineThickness
= defaultThickness
- onePixel
;
174 else if (aThicknessAttribute
.EqualsLiteral("medium")) {
175 lineThickness
= NSToCoordRound(defaultThickness
* MEDIUM_FRACTION_LINE
);
176 minimumThickness
= onePixel
* MEDIUM_FRACTION_LINE_MINIMUM_PIXELS
;
177 // should visually increase by at least one pixel
178 if (lineThickness
< defaultThickness
+ onePixel
)
179 lineThickness
= defaultThickness
+ onePixel
;
181 else if (aThicknessAttribute
.EqualsLiteral("thick")) {
182 lineThickness
= NSToCoordCeil(defaultThickness
* THICK_FRACTION_LINE
);
183 minimumThickness
= onePixel
* THICK_FRACTION_LINE_MINIMUM_PIXELS
;
184 // should visually increase by at least two pixels
185 if (lineThickness
< defaultThickness
+ 2*onePixel
)
186 lineThickness
= defaultThickness
+ 2*onePixel
;
188 else { // see if it is a plain number, or a percentage, or a h/v-unit like 1ex, 2px, 1em
190 if (ParseNumericValue(aThicknessAttribute
, cssValue
)) {
191 nsCSSUnit unit
= cssValue
.GetUnit();
192 if (eCSSUnit_Number
== unit
)
193 lineThickness
= nscoord(float(defaultThickness
) * cssValue
.GetFloatValue());
194 else if (eCSSUnit_Percent
== unit
)
195 lineThickness
= nscoord(float(defaultThickness
) * cssValue
.GetPercentValue());
196 else if (eCSSUnit_Null
!= unit
)
197 lineThickness
= CalcLength(aPresContext
, aStyleContext
, cssValue
);
202 // use minimum if the lineThickness is a non-zero value less than minimun
203 if (lineThickness
&& lineThickness
< minimumThickness
)
204 lineThickness
= minimumThickness
;
206 return lineThickness
;
210 nsMathMLmfracFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
211 const nsRect
& aDirtyRect
,
212 const nsDisplayListSet
& aLists
)
215 // paint the numerator and denominator
216 nsresult rv
= nsMathMLContainerFrame::BuildDisplayList(aBuilder
, aDirtyRect
, aLists
);
217 NS_ENSURE_SUCCESS(rv
, rv
);
220 // paint the fraction line
222 // bevelled rendering
223 rv
= mSlashChar
->Display(aBuilder
, this, aLists
);
225 rv
= DisplayBar(aBuilder
, this, mLineRect
, aLists
);
232 nsMathMLmfracFrame::Reflow(nsPresContext
* aPresContext
,
233 nsHTMLReflowMetrics
& aDesiredSize
,
234 const nsHTMLReflowState
& aReflowState
,
235 nsReflowStatus
& aStatus
)
238 // bevelled rendering
239 return nsMathMLmfencedFrame::doReflow(aPresContext
, aReflowState
,
240 aDesiredSize
, aStatus
, this,
241 nsnull
, nsnull
, mSlashChar
, 1);
245 return nsMathMLContainerFrame::Reflow(aPresContext
, aDesiredSize
,
246 aReflowState
, aStatus
);
249 /* virtual */ nscoord
250 nsMathMLmfracFrame::GetIntrinsicWidth(nsIRenderingContext
* aRenderingContext
)
253 // bevelled rendering
254 return nsMathMLmfencedFrame::doGetIntrinsicWidth(aRenderingContext
, this,
260 return nsMathMLContainerFrame::GetIntrinsicWidth(aRenderingContext
);
264 nsMathMLmfracFrame::FixInterFrameSpacing(nsHTMLReflowMetrics
& aDesiredSize
)
266 nscoord gap
= nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize
);
271 mSlashChar
->GetRect(rect
);
273 mSlashChar
->SetRect(rect
);
276 mLineRect
.MoveBy(gap
, 0);
281 /* virtual */ nsresult
282 nsMathMLmfracFrame::Place(nsIRenderingContext
& aRenderingContext
,
284 nsHTMLReflowMetrics
& aDesiredSize
)
286 ////////////////////////////////////
287 // Get the children's desired sizes
288 nsBoundingMetrics bmNum
, bmDen
;
289 nsHTMLReflowMetrics sizeNum
;
290 nsHTMLReflowMetrics sizeDen
;
291 nsIFrame
* frameDen
= nsnull
;
292 nsIFrame
* frameNum
= mFrames
.FirstChild();
294 frameDen
= frameNum
->GetNextSibling();
295 if (!frameNum
|| !frameDen
|| frameDen
->GetNextSibling()) {
296 // report an error, encourage people to get their markups in order
297 return ReflowError(aRenderingContext
, aDesiredSize
);
299 GetReflowAndBoundingMetricsFor(frameNum
, sizeNum
, bmNum
);
300 GetReflowAndBoundingMetricsFor(frameDen
, sizeDen
, bmDen
);
305 nsPresContext
* presContext
= PresContext();
306 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
308 aRenderingContext
.SetFont(GetStyleFont()->mFont
, nsnull
,
309 presContext
->GetUserFontSet());
310 nsCOMPtr
<nsIFontMetrics
> fm
;
311 aRenderingContext
.GetFontMetrics(*getter_AddRefs(fm
));
313 nscoord defaultRuleThickness
, axisHeight
;
314 GetRuleThickness(aRenderingContext
, fm
, defaultRuleThickness
);
315 GetAxisHeight(aRenderingContext
, fm
, axisHeight
);
317 // by default, leave at least one-pixel padding at either end, or use
318 // lspace & rspace that may come from <mo> if we are an embellished container
319 // (we fetch values from the core since they may use units that depend
320 // on style data, and style changes could have occurred in the core since
321 // our last visit there)
322 nsEmbellishData coreData
;
323 GetEmbellishDataFrom(mEmbellishData
.coreFrame
, coreData
);
324 nscoord leftSpace
= PR_MAX(onePixel
, coreData
.leftSpace
);
325 nscoord rightSpace
= PR_MAX(onePixel
, coreData
.rightSpace
);
327 // see if the linethickness attribute is there
329 GetAttribute(mContent
, mPresentationData
.mstyle
, nsGkAtoms::linethickness_
, value
);
330 mLineRect
.height
= CalcLineThickness(presContext
, mStyleContext
, value
,
331 onePixel
, defaultRuleThickness
);
332 nscoord numShift
= 0;
333 nscoord denShift
= 0;
335 // Rule 15b, App. G, TeXbook
336 nscoord numShift1
, numShift2
, numShift3
;
337 nscoord denShift1
, denShift2
;
339 GetNumeratorShifts(fm
, numShift1
, numShift2
, numShift3
);
340 GetDenominatorShifts(fm
, denShift1
, denShift2
);
341 if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData
.flags
)) {
343 numShift
= numShift1
;
344 denShift
= denShift1
;
347 numShift
= (0 < mLineRect
.height
) ? numShift2
: numShift3
;
348 denShift
= denShift2
;
351 nscoord minClearance
= 0;
352 nscoord actualClearance
= 0;
354 nscoord actualRuleThickness
= mLineRect
.height
;
356 if (0 == actualRuleThickness
) {
357 // Rule 15c, App. G, TeXbook
359 // min clearance between numerator and denominator
360 minClearance
= (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData
.flags
)) ?
361 7 * defaultRuleThickness
: 3 * defaultRuleThickness
;
363 (numShift
- bmNum
.descent
) - (bmDen
.ascent
- denShift
);
364 // actualClearance should be >= minClearance
365 if (actualClearance
< minClearance
) {
366 nscoord halfGap
= (minClearance
- actualClearance
)/2;
372 // Rule 15d, App. G, TeXbook
374 // min clearance between numerator or denominator and middle of bar
376 // TeX has a different interpretation of the thickness.
377 // Try $a \above10pt b$ to see. Here is what TeX does:
378 // minClearance = (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) ?
379 // 3 * actualRuleThickness : actualRuleThickness;
381 // we slightly depart from TeX here. We use the defaultRuleThickness instead
382 // of the value coming from the linethickness attribute, i.e., we recover what
383 // TeX does if the user hasn't set linethickness. But when the linethickness
384 // is set, we avoid the wide gap problem.
385 minClearance
= (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData
.flags
)) ?
386 3 * defaultRuleThickness
: defaultRuleThickness
+ onePixel
;
388 // adjust numShift to maintain minClearance if needed
390 (numShift
- bmNum
.descent
) - (axisHeight
+ actualRuleThickness
/2);
391 if (actualClearance
< minClearance
) {
392 numShift
+= (minClearance
- actualClearance
);
394 // adjust denShift to maintain minClearance if needed
396 (axisHeight
- actualRuleThickness
/2) - (bmDen
.ascent
- denShift
);
397 if (actualClearance
< minClearance
) {
398 denShift
+= (minClearance
- actualClearance
);
405 // XXX Need revisiting the width. TeX uses the exact width
406 // e.g. in $$\huge\frac{\displaystyle\int}{i}$$
407 nscoord width
= PR_MAX(bmNum
.width
, bmDen
.width
);
408 nscoord dxNum
= leftSpace
+ (width
- sizeNum
.width
)/2;
409 nscoord dxDen
= leftSpace
+ (width
- sizeDen
.width
)/2;
410 width
+= leftSpace
+ rightSpace
;
412 // see if the numalign attribute is there
413 GetAttribute(mContent
, mPresentationData
.mstyle
, nsGkAtoms::numalign_
,
415 if (value
.EqualsLiteral("left"))
417 else if (value
.EqualsLiteral("right"))
418 dxNum
= width
- rightSpace
- sizeNum
.width
;
420 // see if the denomalign attribute is there
421 GetAttribute(mContent
, mPresentationData
.mstyle
, nsGkAtoms::denomalign_
,
423 if (value
.EqualsLiteral("left"))
425 else if (value
.EqualsLiteral("right"))
426 dxDen
= width
- rightSpace
- sizeDen
.width
;
428 mBoundingMetrics
.rightBearing
=
429 PR_MAX(dxNum
+ bmNum
.rightBearing
, dxDen
+ bmDen
.rightBearing
);
430 if (mBoundingMetrics
.rightBearing
< width
- rightSpace
)
431 mBoundingMetrics
.rightBearing
= width
- rightSpace
;
432 mBoundingMetrics
.leftBearing
=
433 PR_MIN(dxNum
+ bmNum
.leftBearing
, dxDen
+ bmDen
.leftBearing
);
434 if (mBoundingMetrics
.leftBearing
> leftSpace
)
435 mBoundingMetrics
.leftBearing
= leftSpace
;
436 mBoundingMetrics
.ascent
= bmNum
.ascent
+ numShift
;
437 mBoundingMetrics
.descent
= bmDen
.descent
+ denShift
;
438 mBoundingMetrics
.width
= width
;
440 aDesiredSize
.ascent
= sizeNum
.ascent
+ numShift
;
441 aDesiredSize
.height
= aDesiredSize
.ascent
+
442 sizeDen
.height
- sizeDen
.ascent
+ denShift
;
443 aDesiredSize
.width
= mBoundingMetrics
.width
;
444 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
447 mReference
.y
= aDesiredSize
.ascent
;
453 FinishReflowChild(frameNum
, presContext
, nsnull
, sizeNum
, dxNum
, dy
, 0);
455 dy
= aDesiredSize
.height
- sizeDen
.height
;
456 FinishReflowChild(frameDen
, presContext
, nsnull
, sizeDen
, dxDen
, dy
, 0);
457 // place the fraction bar - dy is top of bar
458 dy
= aDesiredSize
.ascent
- (axisHeight
+ actualRuleThickness
/2);
459 mLineRect
.SetRect(leftSpace
, dy
, width
- (leftSpace
+ rightSpace
), actualRuleThickness
);
466 nsMathMLmfracFrame::AttributeChanged(PRInt32 aNameSpaceID
,
470 if (nsGkAtoms::bevelled_
== aAttribute
) {
472 // disable the bevelled rendering
479 // enable the bevelled rendering
481 mSlashChar
= new nsMathMLChar();
483 nsPresContext
* presContext
= PresContext();
484 nsAutoString slashChar
; slashChar
.Assign(kSlashChar
);
485 mSlashChar
->SetData(presContext
, slashChar
);
486 ResolveMathMLCharStyle(presContext
, mContent
, mStyleContext
, mSlashChar
, PR_TRUE
);
491 return nsMathMLContainerFrame::
492 AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
496 nsMathMLmfracFrame::UpdatePresentationDataFromChildAt(PRInt32 aFirstIndex
,
498 PRUint32 aFlagsValues
,
499 PRUint32 aFlagsToUpdate
)
501 // The REC says "The <mfrac> element sets displaystyle to "false" within
502 // numerator and denominator"
504 // At one point I thought that it meant that the displaystyle state of
505 // the numerator and denominator cannot be modified by an ancestor, i.e.,
506 // to change the displaystlye, one has to use displaystyle="true" with mstyle:
507 // <mfrac> <mstyle>numerator</mstyle> <mstyle>denominator</mstyle> </mfrac>
509 // Commenting out for now until it is clear what the intention really is.
510 // See also the variants for <mover>, <munder>, <munderover>
512 aFlagsToUpdate
&= ~NS_MATHML_DISPLAYSTYLE
;
513 aFlagsValues
&= ~NS_MATHML_DISPLAYSTYLE
;
515 return nsMathMLContainerFrame::
516 UpdatePresentationDataFromChildAt(aFirstIndex
, aLastIndex
,
517 aFlagsValues
, aFlagsToUpdate
);
520 // ----------------------
521 // the Style System will use these to pass the proper style context to our MathMLChar
523 nsMathMLmfracFrame::GetAdditionalStyleContext(PRInt32 aIndex
) const
529 case NS_SLASH_CHAR_STYLE_CONTEXT_INDEX
:
530 return mSlashChar
->GetStyleContext();
538 nsMathMLmfracFrame::SetAdditionalStyleContext(PRInt32 aIndex
,
539 nsStyleContext
* aStyleContext
)
545 case NS_SLASH_CHAR_STYLE_CONTEXT_INDEX
:
546 mSlashChar
->SetStyleContext(aStyleContext
);