Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "nsMathMLmrootFrame.h"
7 : #include "nsPresContext.h"
8 : #include <algorithm>
9 : #include "gfxContext.h"
10 : #include "gfxMathTable.h"
11 :
12 : using namespace mozilla;
13 :
14 : //
15 : // <mroot> -- form a radical - implementation
16 : //
17 :
18 : // additional style context to be used by our MathMLChar.
19 : #define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0
20 :
21 : static const char16_t kSqrChar = char16_t(0x221A);
22 :
23 : nsIFrame*
24 0 : NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
25 : {
26 0 : return new (aPresShell) nsMathMLmrootFrame(aContext);
27 : }
28 :
29 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame)
30 :
31 0 : nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext* aContext) :
32 : nsMathMLContainerFrame(aContext, kClassID),
33 : mSqrChar(),
34 0 : mBarRect()
35 : {
36 0 : }
37 :
38 0 : nsMathMLmrootFrame::~nsMathMLmrootFrame()
39 : {
40 0 : }
41 :
42 : void
43 0 : nsMathMLmrootFrame::Init(nsIContent* aContent,
44 : nsContainerFrame* aParent,
45 : nsIFrame* aPrevInFlow)
46 : {
47 0 : nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
48 :
49 0 : nsPresContext *presContext = PresContext();
50 :
51 : // No need to track the style context given to our MathML char.
52 : // The Style System will use Get/SetAdditionalStyleContext() to keep it
53 : // up-to-date if dynamic changes arise.
54 0 : nsAutoString sqrChar; sqrChar.Assign(kSqrChar);
55 0 : mSqrChar.SetData(sqrChar);
56 0 : ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mSqrChar);
57 0 : }
58 :
59 : NS_IMETHODIMP
60 0 : nsMathMLmrootFrame::TransmitAutomaticData()
61 : {
62 : // 1. The REC says:
63 : // The <mroot> element increments scriptlevel by 2, and sets displaystyle to
64 : // "false", within index, but leaves both attributes unchanged within base.
65 : // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
66 0 : UpdatePresentationDataFromChildAt(1, 1,
67 : NS_MATHML_COMPRESSED,
68 0 : NS_MATHML_COMPRESSED);
69 0 : UpdatePresentationDataFromChildAt(0, 0,
70 0 : NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
71 :
72 0 : PropagateFrameFlagFor(mFrames.LastChild(),
73 0 : NS_FRAME_MATHML_SCRIPT_DESCENDANT);
74 :
75 0 : return NS_OK;
76 : }
77 :
78 : void
79 0 : nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
80 : const nsRect& aDirtyRect,
81 : const nsDisplayListSet& aLists)
82 : {
83 : /////////////
84 : // paint the content we are square-rooting
85 0 : nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
86 :
87 : /////////////
88 : // paint the sqrt symbol
89 0 : if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
90 0 : mSqrChar.Display(aBuilder, this, aLists, 0);
91 :
92 0 : DisplayBar(aBuilder, this, mBarRect, aLists);
93 :
94 : #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
95 : // for visual debug
96 : nsRect rect;
97 : mSqrChar.GetRect(rect);
98 : nsBoundingMetrics bm;
99 : mSqrChar.GetBoundingMetrics(bm);
100 : DisplayBoundingMetrics(aBuilder, this, rect.TopLeft(), bm, aLists);
101 : #endif
102 : }
103 0 : }
104 :
105 : void
106 0 : nsMathMLmrootFrame::GetRadicalXOffsets(nscoord aIndexWidth, nscoord aSqrWidth,
107 : nsFontMetrics* aFontMetrics,
108 : nscoord* aIndexOffset,
109 : nscoord* aSqrOffset)
110 : {
111 : // The index is tucked in closer to the radical while making sure
112 : // that the kern does not make the index and radical collide
113 : nscoord dxIndex, dxSqr;
114 0 : nscoord xHeight = aFontMetrics->XHeight();
115 0 : nscoord indexRadicalKern = NSToCoordRound(1.35f * xHeight);
116 0 : nscoord oneDevPixel = aFontMetrics->AppUnitsPerDevPixel();
117 0 : gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
118 0 : if (mathFont) {
119 : indexRadicalKern =
120 0 : mathFont->MathTable()->Constant(gfxMathTable::RadicalKernAfterDegree,
121 0 : oneDevPixel);
122 0 : indexRadicalKern = -indexRadicalKern;
123 : }
124 0 : if (indexRadicalKern > aIndexWidth) {
125 0 : dxIndex = indexRadicalKern - aIndexWidth;
126 0 : dxSqr = 0;
127 : }
128 : else {
129 0 : dxIndex = 0;
130 0 : dxSqr = aIndexWidth - indexRadicalKern;
131 : }
132 :
133 0 : if (mathFont) {
134 : // add some kern before the radical index
135 0 : nscoord indexRadicalKernBefore = 0;
136 : indexRadicalKernBefore =
137 0 : mathFont->MathTable()->Constant(gfxMathTable::RadicalKernBeforeDegree,
138 0 : oneDevPixel);
139 0 : dxIndex += indexRadicalKernBefore;
140 0 : dxSqr += indexRadicalKernBefore;
141 : } else {
142 : // avoid collision by leaving a minimum space between index and radical
143 0 : nscoord minimumClearance = aSqrWidth / 2;
144 0 : if (dxIndex + aIndexWidth + minimumClearance > dxSqr + aSqrWidth) {
145 0 : if (aIndexWidth + minimumClearance < aSqrWidth) {
146 0 : dxIndex = aSqrWidth - (aIndexWidth + minimumClearance);
147 0 : dxSqr = 0;
148 : }
149 : else {
150 0 : dxIndex = 0;
151 0 : dxSqr = (aIndexWidth + minimumClearance) - aSqrWidth;
152 : }
153 : }
154 : }
155 :
156 0 : if (aIndexOffset)
157 0 : *aIndexOffset = dxIndex;
158 0 : if (aSqrOffset)
159 0 : *aSqrOffset = dxSqr;
160 0 : }
161 :
162 : void
163 0 : nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext,
164 : ReflowOutput& aDesiredSize,
165 : const ReflowInput& aReflowInput,
166 : nsReflowStatus& aStatus)
167 : {
168 0 : MarkInReflow();
169 0 : nsReflowStatus childStatus;
170 :
171 0 : mPresentationData.flags &= ~NS_MATHML_ERROR;
172 0 : aDesiredSize.ClearSize();
173 0 : aDesiredSize.SetBlockStartAscent(0);
174 :
175 0 : nsBoundingMetrics bmSqr, bmBase, bmIndex;
176 0 : DrawTarget* drawTarget = aReflowInput.mRenderingContext->GetDrawTarget();
177 :
178 : //////////////////
179 : // Reflow Children
180 :
181 0 : int32_t count = 0;
182 0 : nsIFrame* baseFrame = nullptr;
183 0 : nsIFrame* indexFrame = nullptr;
184 0 : ReflowOutput baseSize(aReflowInput);
185 0 : ReflowOutput indexSize(aReflowInput);
186 0 : nsIFrame* childFrame = mFrames.FirstChild();
187 0 : while (childFrame) {
188 : // ask our children to compute their bounding metrics
189 : ReflowOutput childDesiredSize(aReflowInput,
190 0 : aDesiredSize.mFlags
191 0 : | NS_REFLOW_CALC_BOUNDING_METRICS);
192 0 : WritingMode wm = childFrame->GetWritingMode();
193 0 : LogicalSize availSize = aReflowInput.ComputedSize(wm);
194 0 : availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
195 : ReflowInput childReflowInput(aPresContext, aReflowInput,
196 0 : childFrame, availSize);
197 0 : ReflowChild(childFrame, aPresContext,
198 0 : childDesiredSize, childReflowInput, childStatus);
199 : //NS_ASSERTION(childStatus.IsComplete(), "bad status");
200 0 : if (0 == count) {
201 : // base
202 0 : baseFrame = childFrame;
203 0 : baseSize = childDesiredSize;
204 0 : bmBase = childDesiredSize.mBoundingMetrics;
205 : }
206 0 : else if (1 == count) {
207 : // index
208 0 : indexFrame = childFrame;
209 0 : indexSize = childDesiredSize;
210 0 : bmIndex = childDesiredSize.mBoundingMetrics;
211 : }
212 0 : count++;
213 0 : childFrame = childFrame->GetNextSibling();
214 : }
215 0 : if (2 != count) {
216 : // report an error, encourage people to get their markups in order
217 0 : ReportChildCountError();
218 0 : ReflowError(drawTarget, aDesiredSize);
219 0 : aStatus.Reset();
220 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
221 : // Call DidReflow() for the child frames we successfully did reflow.
222 0 : DidReflowChildren(mFrames.FirstChild(), childFrame);
223 0 : return;
224 : }
225 :
226 : ////////////
227 : // Prepare the radical symbol and the overline bar
228 :
229 0 : float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
230 : RefPtr<nsFontMetrics> fm =
231 0 : nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
232 :
233 : nscoord ruleThickness, leading, psi;
234 0 : GetRadicalParameters(fm, StyleFont()->mMathDisplay ==
235 : NS_MATHML_DISPLAYSTYLE_BLOCK,
236 0 : ruleThickness, leading, psi);
237 :
238 : // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
239 0 : char16_t one = '1';
240 : nsBoundingMetrics bmOne =
241 0 : nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, drawTarget);
242 0 : if (bmOne.ascent > bmBase.ascent)
243 0 : psi += bmOne.ascent - bmBase.ascent;
244 :
245 : // make sure that the rule appears on on screen
246 0 : nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
247 0 : if (ruleThickness < onePixel) {
248 0 : ruleThickness = onePixel;
249 : }
250 :
251 : // adjust clearance psi to get an exact number of pixels -- this
252 : // gives a nicer & uniform look on stacked radicals (bug 130282)
253 0 : nscoord delta = psi % onePixel;
254 0 : if (delta)
255 0 : psi += onePixel - delta; // round up
256 :
257 : // Stretch the radical symbol to the appropriate height if it is not big enough.
258 0 : nsBoundingMetrics contSize = bmBase;
259 0 : contSize.descent = bmBase.ascent + bmBase.descent + psi;
260 0 : contSize.ascent = ruleThickness;
261 :
262 : // height(radical) should be >= height(base) + psi + ruleThickness
263 0 : nsBoundingMetrics radicalSize;
264 0 : mSqrChar.Stretch(aPresContext, drawTarget,
265 : fontSizeInflation,
266 : NS_STRETCH_DIRECTION_VERTICAL,
267 : contSize, radicalSize,
268 : NS_STRETCH_LARGER,
269 0 : StyleVisibility()->mDirection);
270 : // radicalSize have changed at this point, and should match with
271 : // the bounding metrics of the char
272 0 : mSqrChar.GetBoundingMetrics(bmSqr);
273 :
274 : // Update the desired size for the container (like msqrt, index is not yet included)
275 : // the baseline will be that of the base.
276 0 : mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness;
277 0 : mBoundingMetrics.descent =
278 0 : std::max(bmBase.descent,
279 0 : (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
280 0 : mBoundingMetrics.width = bmSqr.width + bmBase.width;
281 0 : mBoundingMetrics.leftBearing = bmSqr.leftBearing;
282 0 : mBoundingMetrics.rightBearing = bmSqr.width +
283 0 : std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule
284 :
285 0 : aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
286 0 : aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
287 0 : std::max(baseSize.Height() - baseSize.BlockStartAscent(),
288 0 : mBoundingMetrics.descent + ruleThickness);
289 0 : aDesiredSize.Width() = mBoundingMetrics.width;
290 :
291 : /////////////
292 : // Re-adjust the desired size to include the index.
293 :
294 : // the index is raised by some fraction of the height
295 : // of the radical, see \mroot macro in App. B, TexBook
296 0 : float raiseIndexPercent = 0.6f;
297 0 : gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
298 0 : if (mathFont) {
299 0 : raiseIndexPercent = mathFont->MathTable()->
300 0 : Constant(gfxMathTable::RadicalDegreeBottomRaisePercent);
301 : }
302 0 : nscoord raiseIndexDelta = NSToCoordRound(raiseIndexPercent *
303 0 : (bmSqr.ascent + bmSqr.descent));
304 0 : nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical
305 0 : - (bmSqr.ascent + bmSqr.descent) // to bottom of radical
306 0 : + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index
307 :
308 0 : nscoord indexClearance = 0;
309 0 : if (mBoundingMetrics.ascent < indexRaisedAscent) {
310 0 : indexClearance =
311 0 : indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index
312 0 : mBoundingMetrics.ascent = indexRaisedAscent;
313 0 : nscoord descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
314 0 : aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
315 0 : aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + descent;
316 : }
317 :
318 : nscoord dxIndex, dxSqr;
319 0 : GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr);
320 :
321 0 : mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width;
322 0 : mBoundingMetrics.leftBearing =
323 0 : std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
324 0 : mBoundingMetrics.rightBearing = dxSqr + bmSqr.width +
325 0 : std::max(bmBase.width, bmBase.rightBearing);
326 :
327 0 : aDesiredSize.Width() = mBoundingMetrics.width;
328 0 : aDesiredSize.mBoundingMetrics = mBoundingMetrics;
329 0 : GatherAndStoreOverflow(&aDesiredSize);
330 :
331 : // place the index
332 0 : nscoord dx = dxIndex;
333 0 : nscoord dy = aDesiredSize.BlockStartAscent() -
334 0 : (indexRaisedAscent + indexSize.BlockStartAscent() - bmIndex.ascent);
335 0 : FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr,
336 0 : MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx),
337 0 : dy, 0);
338 :
339 : // place the radical symbol and the radical bar
340 0 : dx = dxSqr;
341 0 : dy = indexClearance + leading; // leave a leading at the top
342 0 : mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx),
343 0 : dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
344 0 : dx += bmSqr.width;
345 0 : mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx),
346 0 : dy, bmBase.width, ruleThickness);
347 :
348 : // place the base
349 0 : dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
350 0 : FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr,
351 0 : MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx),
352 0 : dy, 0);
353 :
354 0 : mReference.x = 0;
355 0 : mReference.y = aDesiredSize.BlockStartAscent();
356 :
357 0 : aStatus.Reset();
358 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
359 : }
360 :
361 : /* virtual */ void
362 0 : nsMathMLmrootFrame::GetIntrinsicISizeMetrics(gfxContext* aRenderingContext, ReflowOutput& aDesiredSize)
363 : {
364 0 : nsIFrame* baseFrame = mFrames.FirstChild();
365 0 : nsIFrame* indexFrame = nullptr;
366 0 : if (baseFrame)
367 0 : indexFrame = baseFrame->GetNextSibling();
368 0 : if (!indexFrame || indexFrame->GetNextSibling()) {
369 0 : ReflowError(aRenderingContext->GetDrawTarget(), aDesiredSize);
370 0 : return;
371 : }
372 :
373 0 : float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
374 : nscoord baseWidth =
375 : nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame,
376 0 : nsLayoutUtils::PREF_ISIZE);
377 : nscoord indexWidth =
378 : nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame,
379 0 : nsLayoutUtils::PREF_ISIZE);
380 0 : nscoord sqrWidth = mSqrChar.GetMaxWidth(PresContext(),
381 : aRenderingContext->GetDrawTarget(),
382 0 : fontSizeInflation);
383 :
384 : nscoord dxSqr;
385 : RefPtr<nsFontMetrics> fm =
386 0 : nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
387 0 : GetRadicalXOffsets(indexWidth, sqrWidth, fm, nullptr, &dxSqr);
388 :
389 0 : nscoord width = dxSqr + sqrWidth + baseWidth;
390 :
391 0 : aDesiredSize.Width() = width;
392 0 : aDesiredSize.mBoundingMetrics.width = width;
393 0 : aDesiredSize.mBoundingMetrics.leftBearing = 0;
394 0 : aDesiredSize.mBoundingMetrics.rightBearing = width;
395 : }
396 :
397 : // ----------------------
398 : // the Style System will use these to pass the proper style context to our MathMLChar
399 : nsStyleContext*
400 0 : nsMathMLmrootFrame::GetAdditionalStyleContext(int32_t aIndex) const
401 : {
402 0 : switch (aIndex) {
403 : case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
404 0 : return mSqrChar.GetStyleContext();
405 : default:
406 0 : return nullptr;
407 : }
408 : }
409 :
410 : void
411 0 : nsMathMLmrootFrame::SetAdditionalStyleContext(int32_t aIndex,
412 : nsStyleContext* aStyleContext)
413 : {
414 0 : switch (aIndex) {
415 : case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
416 0 : mSqrChar.SetStyleContext(aStyleContext);
417 0 : break;
418 : }
419 0 : }
|