2
* The contents of this file are subject to the Mozilla Public
3
* License Version 1.1 (the "License"); you may not use this file
4
* except in compliance with the License. You may obtain a copy of
5
* the License at http://www.mozilla.org/MPL/
7
* Software distributed under the License is distributed on an "AS
8
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9
* implied. See the License for the specific language governing
10
* rights and limitations under the License.
12
* The Original Code is Mozilla MathML Project.
14
* The Initial Developer of the Original Code is The University Of
15
* Queensland. Portions created by The University Of Queensland are
16
* Copyright (C) 1999 The University Of Queensland. All Rights Reserved.
19
* Roger B. Sidje <rbs@maths.uq.edu.au>
20
* David J. Fiddes <D.J.Fiddes@hw.ac.uk>
21
* Vilya Harvey <vilya@nag.co.uk>
22
* Shyjan Mahamud <mahamud@cs.cmu.edu>
28
#include "nsIPresContext.h"
29
#include "nsUnitConversion.h"
30
#include "nsStyleContext.h"
31
#include "nsStyleConsts.h"
32
#include "nsIRenderingContext.h"
33
#include "nsIFontMetrics.h"
35
#include "nsMathMLmrootFrame.h"
38
// <msqrt> and <mroot> -- form a radical - implementation
42
// The code assumes that TeX fonts are picked.
43
// There is no fall-back to draw the branches of the sqrt explicitly
44
// in the case where TeX fonts are not there. In general, there are no
45
// fall-back(s) in MathML when some (freely-downloadable) fonts are missing.
46
// Otherwise, this will add much work and unnecessary complexity to the core
47
// MathML engine. Assuming that authors have the free fonts is part of the
48
// deal. We are not responsible for cases of misconfigurations out there.
50
// additional style context to be used by our MathMLChar.
51
#define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0
53
static const PRUnichar kSqrChar = PRUnichar(0x221A);
56
NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
58
NS_PRECONDITION(aNewFrame, "null OUT ptr");
59
if (nsnull == aNewFrame) {
60
return NS_ERROR_NULL_POINTER;
62
nsMathMLmrootFrame* it = new (aPresShell) nsMathMLmrootFrame;
64
return NS_ERROR_OUT_OF_MEMORY;
70
nsMathMLmrootFrame::nsMathMLmrootFrame() :
76
nsMathMLmrootFrame::~nsMathMLmrootFrame()
81
nsMathMLmrootFrame::Init(nsIPresContext* aPresContext,
84
nsStyleContext* aContext,
85
nsIFrame* aPrevInFlow)
87
nsresult rv = nsMathMLContainerFrame::Init(aPresContext, aContent, aParent,
88
aContext, aPrevInFlow);
90
// No need to tract the style context given to our MathML char.
91
// The Style System will use Get/SetAdditionalStyleContext() to keep it
92
// up-to-date if dynamic changes arise.
93
nsAutoString sqrChar; sqrChar.Assign(kSqrChar);
94
mSqrChar.SetData(aPresContext, sqrChar);
95
ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext, &mSqrChar, PR_TRUE);
101
nsMathMLmrootFrame::TransmitAutomaticData(nsIPresContext* aPresContext)
104
// The <mroot> element increments scriptlevel by 2, and sets displaystyle to
105
// "false", within index, but leaves both attributes unchanged within base.
106
// 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
107
UpdatePresentationDataFromChildAt(aPresContext, 1, 1, 2,
108
~NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED,
109
NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED);
110
UpdatePresentationDataFromChildAt(aPresContext, 0, 0, 0,
111
NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
117
nsMathMLmrootFrame::Paint(nsIPresContext* aPresContext,
118
nsIRenderingContext& aRenderingContext,
119
const nsRect& aDirtyRect,
120
nsFramePaintLayer aWhichLayer,
124
// paint the content we are square-rooting
125
nsresult rv = nsMathMLContainerFrame::Paint(aPresContext, aRenderingContext,
126
aDirtyRect, aWhichLayer);
128
// paint the sqrt symbol
129
if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
130
mSqrChar.Paint(aPresContext, aRenderingContext,
131
aDirtyRect, aWhichLayer, this);
133
if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer && !mBarRect.IsEmpty()) {
134
// paint the overline bar
135
const nsStyleColor* color = GetStyleColor();
136
aRenderingContext.SetColor(color->mColor);
137
aRenderingContext.FillRect(mBarRect);
140
#if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
142
if (NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData.flags)) {
144
mSqrChar.GetRect(rect);
146
nsBoundingMetrics bm;
147
mSqrChar.GetBoundingMetrics(bm);
149
aRenderingContext.SetColor(NS_RGB(255,0,0));
150
nscoord x = rect.x + bm.leftBearing;
152
nscoord w = bm.rightBearing - bm.leftBearing;
153
nscoord h = bm.ascent + bm.descent;
154
aRenderingContext.DrawRect(x,y,w,h);
163
nsMathMLmrootFrame::Reflow(nsIPresContext* aPresContext,
164
nsHTMLReflowMetrics& aDesiredSize,
165
const nsHTMLReflowState& aReflowState,
166
nsReflowStatus& aStatus)
169
// ask our children to compute their bounding metrics
170
nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mComputeMEW,
171
aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS);
172
nsSize availSize(aReflowState.mComputedWidth, aReflowState.mComputedHeight);
173
nsReflowStatus childStatus;
175
aDesiredSize.width = aDesiredSize.height = 0;
176
aDesiredSize.ascent = aDesiredSize.descent = 0;
178
nsBoundingMetrics bmSqr, bmBase, bmIndex;
179
nsIRenderingContext& renderingContext = *aReflowState.rendContext;
185
nsIFrame* baseFrame = nsnull;
186
nsIFrame* indexFrame = nsnull;
187
nsHTMLReflowMetrics baseSize(nsnull);
188
nsHTMLReflowMetrics indexSize(nsnull);
189
nsIFrame* childFrame = mFrames.FirstChild();
191
nsHTMLReflowState childReflowState(aPresContext, aReflowState,
192
childFrame, availSize);
193
rv = ReflowChild(childFrame, aPresContext,
194
childDesiredSize, childReflowState, childStatus);
195
//NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
196
if (NS_FAILED(rv)) return rv;
199
baseFrame = childFrame;
200
baseSize = childDesiredSize;
201
bmBase = childDesiredSize.mBoundingMetrics;
203
else if (1 == count) {
205
indexFrame = childFrame;
206
indexSize = childDesiredSize;
207
bmIndex = childDesiredSize.mBoundingMetrics;
210
childFrame = childFrame->GetNextSibling();
212
if (aDesiredSize.mComputeMEW) {
213
aDesiredSize.mMaxElementWidth = childDesiredSize.mMaxElementWidth;
216
// report an error, encourage people to get their markups in order
217
NS_WARNING("invalid markup");
218
return ReflowError(aPresContext, renderingContext, aDesiredSize);
222
// Prepare the radical symbol and the overline bar
224
renderingContext.SetFont(GetStyleFont()->mFont, nsnull);
225
nsCOMPtr<nsIFontMetrics> fm;
226
renderingContext.GetFontMetrics(*getter_AddRefs(fm));
228
nscoord ruleThickness, leading, em;
229
GetRuleThickness(renderingContext, fm, ruleThickness);
231
nsBoundingMetrics bmOne;
232
renderingContext.GetBoundingMetrics(NS_LITERAL_STRING("1").get(), 1, bmOne);
234
// get the leading to be left at the top of the resulting frame
235
// this seems more reliable than using fm->GetLeading() on suspicious fonts
237
leading = nscoord(0.2f * em);
239
// Rule 11, App. G, TeXbook
240
// psi = clearance between rule and content
241
nscoord phi = 0, psi = 0;
242
if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags))
246
psi = ruleThickness + phi/4;
248
// built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
249
if (bmOne.ascent > bmBase.ascent)
250
psi += bmOne.ascent - bmBase.ascent;
252
// Stretch the radical symbol to the appropriate height if it is not big enough.
253
nsBoundingMetrics contSize = bmBase;
254
contSize.descent = bmBase.ascent + bmBase.descent + psi;
255
contSize.ascent = ruleThickness;
257
// height(radical) should be >= height(base) + psi + ruleThickness
258
nsBoundingMetrics radicalSize;
259
mSqrChar.Stretch(aPresContext, renderingContext,
260
NS_STRETCH_DIRECTION_VERTICAL,
261
contSize, radicalSize,
263
// radicalSize have changed at this point, and should match with
264
// the bounding metrics of the char
265
mSqrChar.GetBoundingMetrics(bmSqr);
267
// According to TeX, the ascent of the returned radical should be
268
// the thickness of the overline
269
ruleThickness = bmSqr.ascent;
270
// make sure that the rule appears on on screen
272
aPresContext->GetScaledPixelsToTwips(&p2t);
273
nscoord onePixel = NSIntPixelsToTwips(1, p2t);
274
if (ruleThickness < onePixel) {
275
ruleThickness = onePixel;
278
// adjust clearance psi to get an exact number of pixels -- this
279
// gives a nicer & uniform look on stacked radicals (bug 130282)
280
nscoord delta = psi % onePixel;
282
psi += onePixel - delta; // round up
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
PR_MAX(bmBase.descent, (bmSqr.descent - (bmBase.ascent + psi)));
289
mBoundingMetrics.width = bmSqr.width + bmBase.width;
290
mBoundingMetrics.leftBearing = bmSqr.leftBearing;
291
mBoundingMetrics.rightBearing = bmSqr.width +
292
PR_MAX(bmBase.width, bmBase.rightBearing); // take also care of the rule
294
aDesiredSize.ascent = mBoundingMetrics.ascent + leading;
295
aDesiredSize.descent =
296
PR_MAX(baseSize.descent, (mBoundingMetrics.descent + ruleThickness));
297
aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
298
aDesiredSize.width = mBoundingMetrics.width;
301
// Re-adjust the desired size to include the index.
303
// the index is raised by some fraction of the height
304
// of the radical, see \mroot macro in App. B, TexBook
305
nscoord raiseIndexDelta = NSToCoordRound(0.6f * (bmSqr.ascent + bmSqr.descent));
306
nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical
307
- (bmSqr.ascent + bmSqr.descent) // to bottom of radical
308
+ raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index
310
nscoord indexClearance = 0;
311
if (mBoundingMetrics.ascent < indexRaisedAscent) {
313
indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index
314
mBoundingMetrics.ascent = indexRaisedAscent;
315
aDesiredSize.ascent = mBoundingMetrics.ascent + leading;
316
aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
319
// the index is tucked in closer to the radical while making sure
320
// that the kern does not make the index and radical collide
321
nscoord dxIndex, dxSqr, dx, dy;
323
fm->GetXHeight(xHeight);
324
nscoord indexRadicalKern = NSToCoordRound(1.35f * xHeight);
325
if (indexRadicalKern > bmIndex.width) {
326
dxIndex = indexRadicalKern - bmIndex.width;
331
dxSqr = bmIndex.width - indexRadicalKern;
333
// avoid collision by leaving a minimun space between index and radical
334
nscoord minimumClearance = bmSqr.width/2;
335
if (dxIndex + bmIndex.width + minimumClearance > dxSqr + bmSqr.width) {
336
if (bmIndex.width + minimumClearance < bmSqr.width) {
337
dxIndex = bmSqr.width - (bmIndex.width + minimumClearance);
342
dxSqr = (bmIndex.width + minimumClearance) - bmSqr.width;
348
dy = aDesiredSize.ascent - (indexRaisedAscent + indexSize.ascent - bmIndex.ascent);
349
FinishReflowChild(indexFrame, aPresContext, nsnull, indexSize, dx, dy, 0);
351
// place the radical symbol and the radical bar
353
dy = indexClearance + leading; // leave a leading at the top
354
mSqrChar.SetRect(nsRect(dx, dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
356
mBarRect.SetRect(dx, dy, bmBase.width, ruleThickness);
359
dy = aDesiredSize.ascent - baseSize.ascent;
360
FinishReflowChild(baseFrame, aPresContext, nsnull, baseSize, dx, dy, 0);
363
mReference.y = aDesiredSize.ascent;
365
mBoundingMetrics.width = dx + bmBase.width;
366
mBoundingMetrics.leftBearing =
367
PR_MIN(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
368
mBoundingMetrics.rightBearing = dx +
369
PR_MAX(bmBase.width, bmBase.rightBearing);
371
aDesiredSize.width = mBoundingMetrics.width;
372
aDesiredSize.mBoundingMetrics = mBoundingMetrics;
374
if (aDesiredSize.mComputeMEW) {
375
aDesiredSize.mMaxElementWidth = aDesiredSize.width;
377
aStatus = NS_FRAME_COMPLETE;
378
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
383
// ----------------------
384
// the Style System will use these to pass the proper style context to our MathMLChar
386
nsMathMLmrootFrame::GetAdditionalStyleContext(PRInt32 aIndex) const
389
case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
390
return mSqrChar.GetStyleContext();
398
nsMathMLmrootFrame::SetAdditionalStyleContext(PRInt32 aIndex,
399
nsStyleContext* aStyleContext)
402
case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
403
mSqrChar.SetStyleContext(aStyleContext);