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
4 : *
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/
9 : *
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
13 : * License.
14 : *
15 : * The Original Code is Mozilla MathML Project.
16 : *
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.
21 : *
22 : * Contributor(s):
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>
27 : *
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.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 :
43 : #include "nsCOMPtr.h"
44 : #include "nsFrame.h"
45 : #include "nsPresContext.h"
46 : #include "nsStyleContext.h"
47 : #include "nsStyleConsts.h"
48 : #include "nsRenderingContext.h"
49 :
50 : #include "nsMathMLmrootFrame.h"
51 :
52 : //
53 : // <msqrt> and <mroot> -- form a radical - implementation
54 : //
55 :
56 : //NOTE:
57 : // The code assumes that TeX fonts are picked.
58 : // There is no fall-back to draw the branches of the sqrt explicitly
59 : // in the case where TeX fonts are not there. In general, there are no
60 : // fall-back(s) in MathML when some (freely-downloadable) fonts are missing.
61 : // Otherwise, this will add much work and unnecessary complexity to the core
62 : // MathML engine. Assuming that authors have the free fonts is part of the
63 : // deal. We are not responsible for cases of misconfigurations out there.
64 :
65 : // additional style context to be used by our MathMLChar.
66 : #define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0
67 :
68 : static const PRUnichar kSqrChar = PRUnichar(0x221A);
69 :
70 : nsIFrame*
71 0 : NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
72 : {
73 0 : return new (aPresShell) nsMathMLmrootFrame(aContext);
74 : }
75 :
76 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame)
77 :
78 0 : nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext* aContext) :
79 : nsMathMLContainerFrame(aContext),
80 : mSqrChar(),
81 0 : mBarRect()
82 : {
83 0 : }
84 :
85 0 : nsMathMLmrootFrame::~nsMathMLmrootFrame()
86 : {
87 0 : }
88 :
89 : NS_IMETHODIMP
90 0 : nsMathMLmrootFrame::Init(nsIContent* aContent,
91 : nsIFrame* aParent,
92 : nsIFrame* aPrevInFlow)
93 : {
94 0 : nsresult rv = nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
95 :
96 0 : nsPresContext *presContext = PresContext();
97 :
98 : // No need to track the style context given to our MathML char.
99 : // The Style System will use Get/SetAdditionalStyleContext() to keep it
100 : // up-to-date if dynamic changes arise.
101 0 : nsAutoString sqrChar; sqrChar.Assign(kSqrChar);
102 0 : mSqrChar.SetData(presContext, sqrChar);
103 0 : ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mSqrChar, true);
104 :
105 0 : return rv;
106 : }
107 :
108 : NS_IMETHODIMP
109 0 : nsMathMLmrootFrame::TransmitAutomaticData()
110 : {
111 : // 1. The REC says:
112 : // The <mroot> element increments scriptlevel by 2, and sets displaystyle to
113 : // "false", within index, but leaves both attributes unchanged within base.
114 : // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
115 : UpdatePresentationDataFromChildAt(1, 1,
116 : ~NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED,
117 0 : NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED);
118 : UpdatePresentationDataFromChildAt(0, 0,
119 0 : NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
120 :
121 0 : return NS_OK;
122 : }
123 :
124 : NS_IMETHODIMP
125 0 : nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
126 : const nsRect& aDirtyRect,
127 : const nsDisplayListSet& aLists)
128 : {
129 : /////////////
130 : // paint the content we are square-rooting
131 0 : nsresult rv = nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
132 0 : NS_ENSURE_SUCCESS(rv, rv);
133 :
134 : /////////////
135 : // paint the sqrt symbol
136 0 : if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
137 0 : rv = mSqrChar.Display(aBuilder, this, aLists);
138 0 : NS_ENSURE_SUCCESS(rv, rv);
139 :
140 0 : rv = DisplayBar(aBuilder, this, mBarRect, aLists);
141 0 : NS_ENSURE_SUCCESS(rv, rv);
142 :
143 : #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
144 : // for visual debug
145 : nsRect rect;
146 : mSqrChar.GetRect(rect);
147 : nsBoundingMetrics bm;
148 : mSqrChar.GetBoundingMetrics(bm);
149 : rv = DisplayBoundingMetrics(aBuilder, this, rect.TopLeft(), bm, aLists);
150 : #endif
151 : }
152 :
153 0 : return rv;
154 : }
155 :
156 : static void
157 0 : GetRadicalXOffsets(nscoord aIndexWidth, nscoord aSqrWidth,
158 : nsFontMetrics* aFontMetrics,
159 : nscoord* aIndexOffset, nscoord* aSqrOffset)
160 : {
161 : // The index is tucked in closer to the radical while making sure
162 : // that the kern does not make the index and radical collide
163 : nscoord dxIndex, dxSqr;
164 0 : nscoord xHeight = aFontMetrics->XHeight();
165 0 : nscoord indexRadicalKern = NSToCoordRound(1.35f * xHeight);
166 0 : if (indexRadicalKern > aIndexWidth) {
167 0 : dxIndex = indexRadicalKern - aIndexWidth;
168 0 : dxSqr = 0;
169 : }
170 : else {
171 0 : dxIndex = 0;
172 0 : dxSqr = aIndexWidth - indexRadicalKern;
173 : }
174 : // avoid collision by leaving a minimum space between index and radical
175 0 : nscoord minimumClearance = aSqrWidth/2;
176 0 : if (dxIndex + aIndexWidth + minimumClearance > dxSqr + aSqrWidth) {
177 0 : if (aIndexWidth + minimumClearance < aSqrWidth) {
178 0 : dxIndex = aSqrWidth - (aIndexWidth + minimumClearance);
179 0 : dxSqr = 0;
180 : }
181 : else {
182 0 : dxIndex = 0;
183 0 : dxSqr = (aIndexWidth + minimumClearance) - aSqrWidth;
184 : }
185 : }
186 :
187 0 : if (aIndexOffset)
188 0 : *aIndexOffset = dxIndex;
189 0 : if (aSqrOffset)
190 0 : *aSqrOffset = dxSqr;
191 0 : }
192 :
193 : NS_IMETHODIMP
194 0 : nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext,
195 : nsHTMLReflowMetrics& aDesiredSize,
196 : const nsHTMLReflowState& aReflowState,
197 : nsReflowStatus& aStatus)
198 : {
199 0 : nsresult rv = NS_OK;
200 0 : nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
201 : nsReflowStatus childStatus;
202 :
203 0 : aDesiredSize.width = aDesiredSize.height = 0;
204 0 : aDesiredSize.ascent = 0;
205 :
206 0 : nsBoundingMetrics bmSqr, bmBase, bmIndex;
207 0 : nsRenderingContext& renderingContext = *aReflowState.rendContext;
208 :
209 : //////////////////
210 : // Reflow Children
211 :
212 0 : PRInt32 count = 0;
213 0 : nsIFrame* baseFrame = nsnull;
214 0 : nsIFrame* indexFrame = nsnull;
215 0 : nsHTMLReflowMetrics baseSize;
216 0 : nsHTMLReflowMetrics indexSize;
217 0 : nsIFrame* childFrame = mFrames.FirstChild();
218 0 : while (childFrame) {
219 : // ask our children to compute their bounding metrics
220 : nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mFlags
221 0 : | NS_REFLOW_CALC_BOUNDING_METRICS);
222 : nsHTMLReflowState childReflowState(aPresContext, aReflowState,
223 0 : childFrame, availSize);
224 : rv = ReflowChild(childFrame, aPresContext,
225 0 : childDesiredSize, childReflowState, childStatus);
226 : //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
227 0 : if (NS_FAILED(rv)) {
228 : // Call DidReflow() for the child frames we successfully did reflow.
229 0 : DidReflowChildren(mFrames.FirstChild(), childFrame);
230 0 : return rv;
231 : }
232 0 : if (0 == count) {
233 : // base
234 0 : baseFrame = childFrame;
235 0 : baseSize = childDesiredSize;
236 0 : bmBase = childDesiredSize.mBoundingMetrics;
237 : }
238 0 : else if (1 == count) {
239 : // index
240 0 : indexFrame = childFrame;
241 0 : indexSize = childDesiredSize;
242 0 : bmIndex = childDesiredSize.mBoundingMetrics;
243 : }
244 0 : count++;
245 0 : childFrame = childFrame->GetNextSibling();
246 : }
247 0 : if (2 != count) {
248 : // report an error, encourage people to get their markups in order
249 0 : rv = ReflowError(renderingContext, aDesiredSize);
250 0 : aStatus = NS_FRAME_COMPLETE;
251 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
252 : // Call DidReflow() for the child frames we successfully did reflow.
253 0 : DidReflowChildren(mFrames.FirstChild(), childFrame);
254 0 : return rv;
255 : }
256 :
257 : ////////////
258 : // Prepare the radical symbol and the overline bar
259 :
260 0 : nsRefPtr<nsFontMetrics> fm;
261 0 : nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
262 0 : renderingContext.SetFont(fm);
263 :
264 : // For radical glyphs from TeX fonts and some of the radical glyphs from
265 : // Mathematica fonts, the thickness of the overline can be obtained from the
266 : // ascent of the glyph. Most fonts however have radical glyphs above the
267 : // baseline so no assumption can be made about the meaning of the ascent.
268 : nscoord ruleThickness, leading, em;
269 0 : GetRuleThickness(renderingContext, fm, ruleThickness);
270 :
271 0 : PRUnichar one = '1';
272 0 : nsBoundingMetrics bmOne = renderingContext.GetBoundingMetrics(&one, 1);
273 :
274 : // get the leading to be left at the top of the resulting frame
275 : // this seems more reliable than using fm->GetLeading() on suspicious fonts
276 0 : GetEmHeight(fm, em);
277 0 : leading = nscoord(0.2f * em);
278 :
279 : // Rule 11, App. G, TeXbook
280 : // psi = clearance between rule and content
281 0 : nscoord phi = 0, psi = 0;
282 0 : if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags))
283 0 : phi = fm->XHeight();
284 : else
285 0 : phi = ruleThickness;
286 0 : psi = ruleThickness + phi/4;
287 :
288 : // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
289 0 : if (bmOne.ascent > bmBase.ascent)
290 0 : psi += bmOne.ascent - bmBase.ascent;
291 :
292 : // make sure that the rule appears on on screen
293 0 : nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
294 0 : if (ruleThickness < onePixel) {
295 0 : ruleThickness = onePixel;
296 : }
297 :
298 : // adjust clearance psi to get an exact number of pixels -- this
299 : // gives a nicer & uniform look on stacked radicals (bug 130282)
300 0 : nscoord delta = psi % onePixel;
301 0 : if (delta)
302 0 : psi += onePixel - delta; // round up
303 :
304 : // Stretch the radical symbol to the appropriate height if it is not big enough.
305 0 : nsBoundingMetrics contSize = bmBase;
306 0 : contSize.descent = bmBase.ascent + bmBase.descent + psi;
307 0 : contSize.ascent = ruleThickness;
308 :
309 : // height(radical) should be >= height(base) + psi + ruleThickness
310 0 : nsBoundingMetrics radicalSize;
311 : mSqrChar.Stretch(aPresContext, renderingContext,
312 : NS_STRETCH_DIRECTION_VERTICAL,
313 : contSize, radicalSize,
314 : NS_STRETCH_LARGER,
315 0 : NS_MATHML_IS_RTL(mPresentationData.flags));
316 : // radicalSize have changed at this point, and should match with
317 : // the bounding metrics of the char
318 0 : mSqrChar.GetBoundingMetrics(bmSqr);
319 :
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 0 : mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness;
323 : mBoundingMetrics.descent =
324 : NS_MAX(bmBase.descent,
325 0 : (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
326 0 : mBoundingMetrics.width = bmSqr.width + bmBase.width;
327 0 : mBoundingMetrics.leftBearing = bmSqr.leftBearing;
328 : mBoundingMetrics.rightBearing = bmSqr.width +
329 0 : NS_MAX(bmBase.width, bmBase.rightBearing); // take also care of the rule
330 :
331 0 : aDesiredSize.ascent = mBoundingMetrics.ascent + leading;
332 : aDesiredSize.height = aDesiredSize.ascent +
333 : NS_MAX(baseSize.height - baseSize.ascent,
334 0 : mBoundingMetrics.descent + ruleThickness);
335 0 : aDesiredSize.width = mBoundingMetrics.width;
336 :
337 : /////////////
338 : // Re-adjust the desired size to include the index.
339 :
340 : // the index is raised by some fraction of the height
341 : // of the radical, see \mroot macro in App. B, TexBook
342 0 : 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 0 : + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index
346 :
347 0 : nscoord indexClearance = 0;
348 0 : if (mBoundingMetrics.ascent < indexRaisedAscent) {
349 : indexClearance =
350 0 : indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index
351 0 : mBoundingMetrics.ascent = indexRaisedAscent;
352 0 : nscoord descent = aDesiredSize.height - aDesiredSize.ascent;
353 0 : aDesiredSize.ascent = mBoundingMetrics.ascent + leading;
354 0 : aDesiredSize.height = aDesiredSize.ascent + descent;
355 : }
356 :
357 : nscoord dxIndex, dxSqr;
358 0 : GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr);
359 :
360 0 : mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width;
361 : mBoundingMetrics.leftBearing =
362 0 : NS_MIN(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
363 : mBoundingMetrics.rightBearing = dxSqr + bmSqr.width +
364 0 : NS_MAX(bmBase.width, bmBase.rightBearing);
365 :
366 0 : aDesiredSize.width = mBoundingMetrics.width;
367 0 : aDesiredSize.mBoundingMetrics = mBoundingMetrics;
368 0 : GatherAndStoreOverflow(&aDesiredSize);
369 :
370 : // place the index
371 0 : nscoord dx = dxIndex;
372 0 : nscoord dy = aDesiredSize.ascent - (indexRaisedAscent + indexSize.ascent - bmIndex.ascent);
373 : FinishReflowChild(indexFrame, aPresContext, nsnull, indexSize,
374 : MirrorIfRTL(aDesiredSize.width, indexSize.width, dx),
375 0 : dy, 0);
376 :
377 : // place the radical symbol and the radical bar
378 0 : dx = dxSqr;
379 0 : dy = indexClearance + leading; // leave a leading at the top
380 : mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.width, bmSqr.width, dx),
381 0 : dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
382 0 : dx += bmSqr.width;
383 : mBarRect.SetRect(MirrorIfRTL(aDesiredSize.width, bmBase.width, dx),
384 0 : dy, bmBase.width, ruleThickness);
385 :
386 : // place the base
387 0 : dy = aDesiredSize.ascent - baseSize.ascent;
388 : FinishReflowChild(baseFrame, aPresContext, nsnull, baseSize,
389 : MirrorIfRTL(aDesiredSize.width, baseSize.width, dx),
390 0 : dy, 0);
391 :
392 0 : mReference.x = 0;
393 0 : mReference.y = aDesiredSize.ascent;
394 :
395 0 : aStatus = NS_FRAME_COMPLETE;
396 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
397 0 : return NS_OK;
398 : }
399 :
400 : /* virtual */ nscoord
401 0 : nsMathMLmrootFrame::GetIntrinsicWidth(nsRenderingContext* aRenderingContext)
402 : {
403 0 : nsIFrame* baseFrame = mFrames.FirstChild();
404 0 : nsIFrame* indexFrame = nsnull;
405 0 : if (baseFrame)
406 0 : indexFrame = baseFrame->GetNextSibling();
407 0 : if (!indexFrame || indexFrame->GetNextSibling()) {
408 0 : nsHTMLReflowMetrics desiredSize;
409 0 : ReflowError(*aRenderingContext, desiredSize);
410 0 : return desiredSize.width;
411 : }
412 :
413 : nscoord baseWidth =
414 : nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame,
415 0 : nsLayoutUtils::PREF_WIDTH);
416 : nscoord indexWidth =
417 : nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame,
418 0 : nsLayoutUtils::PREF_WIDTH);
419 0 : nscoord sqrWidth = mSqrChar.GetMaxWidth(PresContext(), *aRenderingContext);
420 :
421 : nscoord dxSqr;
422 : GetRadicalXOffsets(indexWidth, sqrWidth, aRenderingContext->FontMetrics(),
423 0 : nsnull, &dxSqr);
424 :
425 0 : return dxSqr + sqrWidth + baseWidth;
426 : }
427 :
428 : // ----------------------
429 : // the Style System will use these to pass the proper style context to our MathMLChar
430 : nsStyleContext*
431 0 : nsMathMLmrootFrame::GetAdditionalStyleContext(PRInt32 aIndex) const
432 : {
433 0 : switch (aIndex) {
434 : case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
435 0 : return mSqrChar.GetStyleContext();
436 : break;
437 : default:
438 0 : return nsnull;
439 : }
440 : }
441 :
442 : void
443 0 : nsMathMLmrootFrame::SetAdditionalStyleContext(PRInt32 aIndex,
444 : nsStyleContext* aStyleContext)
445 : {
446 0 : switch (aIndex) {
447 : case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
448 0 : mSqrChar.SetStyleContext(aStyleContext);
449 0 : break;
450 : }
451 0 : }
|