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 :
7 : #include "nsMathMLmmultiscriptsFrame.h"
8 : #include "nsPresContext.h"
9 : #include <algorithm>
10 : #include "gfxContext.h"
11 : #include "gfxMathTable.h"
12 :
13 : using mozilla::WritingMode;
14 :
15 : //
16 : // <mmultiscripts> -- attach prescripts and tensor indices to a base - implementation
17 : // <msub> -- attach a subscript to a base - implementation
18 : // <msubsup> -- attach a subscript-superscript pair to a base - implementation
19 : // <msup> -- attach a superscript to a base - implementation
20 : //
21 :
22 : nsIFrame*
23 0 : NS_NewMathMLmmultiscriptsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
24 : {
25 0 : return new (aPresShell) nsMathMLmmultiscriptsFrame(aContext);
26 : }
27 :
28 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmmultiscriptsFrame)
29 :
30 0 : nsMathMLmmultiscriptsFrame::~nsMathMLmmultiscriptsFrame()
31 : {
32 0 : }
33 :
34 : uint8_t
35 0 : nsMathMLmmultiscriptsFrame::ScriptIncrement(nsIFrame* aFrame)
36 : {
37 0 : if (!aFrame)
38 0 : return 0;
39 0 : if (mFrames.ContainsFrame(aFrame)) {
40 0 : if (mFrames.FirstChild() == aFrame ||
41 0 : aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) {
42 0 : return 0; // No script increment for base frames or prescript markers
43 : }
44 0 : return 1;
45 : }
46 0 : return 0; //not a child
47 : }
48 :
49 : NS_IMETHODIMP
50 0 : nsMathMLmmultiscriptsFrame::TransmitAutomaticData()
51 : {
52 : // if our base is an embellished operator, let its state bubble to us
53 0 : mPresentationData.baseFrame = mFrames.FirstChild();
54 0 : GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData);
55 :
56 : // The TeXbook (Ch 17. p.141) says the superscript inherits the compression
57 : // while the subscript is compressed. So here we collect subscripts and set
58 : // the compression flag in them.
59 :
60 0 : int32_t count = 0;
61 0 : bool isSubScript = !mContent->IsMathMLElement(nsGkAtoms::msup_);
62 :
63 0 : AutoTArray<nsIFrame*, 8> subScriptFrames;
64 0 : nsIFrame* childFrame = mFrames.FirstChild();
65 0 : while (childFrame) {
66 0 : if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) {
67 : // mprescripts frame
68 0 : } else if (0 == count) {
69 : // base frame
70 : } else {
71 : // super/subscript block
72 0 : if (isSubScript) {
73 : // subscript
74 0 : subScriptFrames.AppendElement(childFrame);
75 : } else {
76 : // superscript
77 : }
78 0 : PropagateFrameFlagFor(childFrame, NS_FRAME_MATHML_SCRIPT_DESCENDANT);
79 0 : isSubScript = !isSubScript;
80 : }
81 0 : count++;
82 0 : childFrame = childFrame->GetNextSibling();
83 : }
84 0 : for (int32_t i = subScriptFrames.Length() - 1; i >= 0; i--) {
85 0 : childFrame = subScriptFrames[i];
86 : PropagatePresentationDataFor(childFrame,
87 0 : NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
88 : }
89 :
90 0 : return NS_OK;
91 : }
92 :
93 : /* virtual */ nsresult
94 0 : nsMathMLmmultiscriptsFrame::Place(DrawTarget* aDrawTarget,
95 : bool aPlaceOrigin,
96 : ReflowOutput& aDesiredSize)
97 : {
98 0 : nscoord subScriptShift = 0;
99 0 : nscoord supScriptShift = 0;
100 0 : float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
101 :
102 : // subscriptshift
103 : //
104 : // "Specifies the minimum amount to shift the baseline of subscript down; the
105 : // default is for the rendering agent to use its own positioning rules."
106 : //
107 : // values: length
108 : // default: automatic
109 : //
110 : // We use 0 as the default value so unitless values can be ignored.
111 : // As a minimum, negative values can be ignored.
112 : //
113 0 : nsAutoString value;
114 0 : if (!mContent->IsMathMLElement(nsGkAtoms::msup_)) {
115 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::subscriptshift_, value);
116 0 : if (!value.IsEmpty()) {
117 0 : ParseNumericValue(value, &subScriptShift, 0, PresContext(),
118 0 : mStyleContext, fontSizeInflation);
119 : }
120 : }
121 : // superscriptshift
122 : //
123 : // "Specifies the minimum amount to shift the baseline of superscript up; the
124 : // default is for the rendering agent to use its own positioning rules."
125 : //
126 : // values: length
127 : // default: automatic
128 : //
129 : // We use 0 as the default value so unitless values can be ignored.
130 : // As a minimum, negative values can be ignored.
131 : //
132 0 : if (!mContent->IsMathMLElement(nsGkAtoms::msub_)) {
133 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::superscriptshift_, value);
134 0 : if (!value.IsEmpty()) {
135 0 : ParseNumericValue(value, &supScriptShift, 0, PresContext(),
136 0 : mStyleContext, fontSizeInflation);
137 : }
138 : }
139 0 : return PlaceMultiScript(PresContext(), aDrawTarget, aPlaceOrigin,
140 : aDesiredSize, this, subScriptShift, supScriptShift,
141 0 : fontSizeInflation);
142 : }
143 :
144 : // exported routine that both munderover and mmultiscripts share.
145 : // munderover uses this when movablelimits is set.
146 : nsresult
147 0 : nsMathMLmmultiscriptsFrame::PlaceMultiScript(nsPresContext* aPresContext,
148 : DrawTarget* aDrawTarget,
149 : bool aPlaceOrigin,
150 : ReflowOutput& aDesiredSize,
151 : nsMathMLContainerFrame* aFrame,
152 : nscoord aUserSubScriptShift,
153 : nscoord aUserSupScriptShift,
154 : float aFontSizeInflation)
155 : {
156 0 : nsIAtom* tag = aFrame->GetContent()->NodeInfo()->NameAtom();
157 :
158 : // This function deals with both munderover etc. as well as msubsup etc.
159 : // As the former behaves identically to the later, we treat it as such
160 : // to avoid additional checks later.
161 0 : if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mover_))
162 0 : tag = nsGkAtoms::msup_;
163 0 : else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munder_))
164 0 : tag = nsGkAtoms::msub_;
165 0 : else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munderover_))
166 0 : tag = nsGkAtoms::msubsup_;
167 :
168 0 : nsBoundingMetrics bmFrame;
169 :
170 : nscoord minShiftFromXHeight, subDrop, supDrop;
171 :
172 : ////////////////////////////////////////
173 : // Initialize super/sub shifts that
174 : // depend only on the current font
175 : ////////////////////////////////////////
176 :
177 0 : nsIFrame* baseFrame = aFrame->PrincipalChildList().FirstChild();
178 :
179 0 : if (!baseFrame) {
180 0 : if (tag == nsGkAtoms::mmultiscripts_)
181 0 : aFrame->ReportErrorToConsole("NoBase");
182 : else
183 0 : aFrame->ReportChildCountError();
184 0 : return aFrame->ReflowError(aDrawTarget, aDesiredSize);
185 : }
186 :
187 : // get x-height (an ex)
188 0 : const nsStyleFont* font = aFrame->StyleFont();
189 : RefPtr<nsFontMetrics> fm =
190 0 : nsLayoutUtils::GetFontMetricsForFrame(baseFrame, aFontSizeInflation);
191 :
192 0 : nscoord xHeight = fm->XHeight();
193 :
194 0 : nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
195 0 : gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
196 : // scriptspace from TeX for extra spacing after sup/subscript
197 : nscoord scriptSpace;
198 0 : if (mathFont) {
199 0 : scriptSpace = mathFont->MathTable()->
200 0 : Constant(gfxMathTable::SpaceAfterScript, oneDevPixel);
201 : } else {
202 : // (0.5pt in plain TeX)
203 0 : scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f);
204 : }
205 :
206 : // Try and read sub and sup drops from the MATH table.
207 0 : if (mathFont) {
208 0 : subDrop = mathFont->MathTable()->
209 0 : Constant(gfxMathTable::SubscriptBaselineDropMin, oneDevPixel);
210 0 : supDrop = mathFont->MathTable()->
211 0 : Constant(gfxMathTable::SuperscriptBaselineDropMax, oneDevPixel);
212 : }
213 :
214 : // force the scriptSpace to be at least 1 pixel
215 0 : nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
216 0 : scriptSpace = std::max(onePixel, scriptSpace);
217 :
218 : /////////////////////////////////////
219 : // first the shift for the subscript
220 :
221 : nscoord subScriptShift;
222 0 : if (mathFont) {
223 : // Try and get the sub script shift from the MATH table. Note that contrary
224 : // to TeX we only have one parameter.
225 0 : subScriptShift = mathFont->MathTable()->
226 0 : Constant(gfxMathTable::SubscriptShiftDown, oneDevPixel);
227 : } else {
228 : // subScriptShift{1,2}
229 : // = minimum amount to shift the subscript down
230 : // = sub{1,2} in TeXbook
231 : // subScriptShift1 = subscriptshift attribute * x-height
232 : nscoord subScriptShift1, subScriptShift2;
233 : // Get subScriptShift{1,2} default from font
234 0 : GetSubScriptShifts (fm, subScriptShift1, subScriptShift2);
235 0 : if (tag == nsGkAtoms::msub_) {
236 0 : subScriptShift = subScriptShift1;
237 : } else {
238 0 : subScriptShift = std::max(subScriptShift1, subScriptShift2);
239 : }
240 : }
241 :
242 0 : if (0 < aUserSubScriptShift) {
243 : // the user has set the subscriptshift attribute
244 0 : subScriptShift = std::max(subScriptShift, aUserSubScriptShift);
245 : }
246 :
247 : /////////////////////////////////////
248 : // next the shift for the superscript
249 :
250 : nscoord supScriptShift;
251 0 : nsPresentationData presentationData;
252 0 : aFrame->GetPresentationData(presentationData);
253 0 : if (mathFont) {
254 : // Try and get the super script shift from the MATH table. Note that
255 : // contrary to TeX we only have two parameters.
256 0 : supScriptShift = mathFont->
257 0 : MathTable()->Constant(NS_MATHML_IS_COMPRESSED(presentationData.flags) ?
258 : gfxMathTable::SuperscriptShiftUpCramped :
259 : gfxMathTable::SuperscriptShiftUp,
260 : oneDevPixel);
261 : } else {
262 : // supScriptShift{1,2,3}
263 : // = minimum amount to shift the supscript up
264 : // = sup{1,2,3} in TeX
265 : // supScriptShift1 = superscriptshift attribute * x-height
266 : // Note that there are THREE values for supscript shifts depending
267 : // on the current style
268 : nscoord supScriptShift1, supScriptShift2, supScriptShift3;
269 : // Set supScriptShift{1,2,3} default from font
270 0 : GetSupScriptShifts (fm, supScriptShift1, supScriptShift2, supScriptShift3);
271 :
272 : // get sup script shift depending on current script level and display style
273 : // Rule 18c, App. G, TeXbook
274 0 : if (font->mScriptLevel == 0 &&
275 0 : font->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK &&
276 0 : !NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
277 : // Style D in TeXbook
278 0 : supScriptShift = supScriptShift1;
279 0 : } else if (NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
280 : // Style C' in TeXbook = D',T',S',SS'
281 0 : supScriptShift = supScriptShift3;
282 : } else {
283 : // everything else = T,S,SS
284 0 : supScriptShift = supScriptShift2;
285 : }
286 : }
287 :
288 0 : if (0 < aUserSupScriptShift) {
289 : // the user has set the supscriptshift attribute
290 0 : supScriptShift = std::max(supScriptShift, aUserSupScriptShift);
291 : }
292 :
293 : ////////////////////////////////////
294 : // Get the children's sizes
295 : ////////////////////////////////////
296 :
297 0 : const WritingMode wm(aDesiredSize.GetWritingMode());
298 0 : nscoord width = 0, prescriptsWidth = 0, rightBearing = 0;
299 0 : nscoord minSubScriptShift = 0, minSupScriptShift = 0;
300 0 : nscoord trySubScriptShift = subScriptShift;
301 0 : nscoord trySupScriptShift = supScriptShift;
302 0 : nscoord maxSubScriptShift = subScriptShift;
303 0 : nscoord maxSupScriptShift = supScriptShift;
304 0 : ReflowOutput baseSize(wm);
305 0 : ReflowOutput subScriptSize(wm);
306 0 : ReflowOutput supScriptSize(wm);
307 0 : ReflowOutput multiSubSize(wm), multiSupSize(wm);
308 0 : baseFrame = nullptr;
309 0 : nsIFrame* subScriptFrame = nullptr;
310 0 : nsIFrame* supScriptFrame = nullptr;
311 0 : nsIFrame* prescriptsFrame = nullptr; // frame of <mprescripts/>, if there.
312 :
313 0 : bool firstPrescriptsPair = false;
314 0 : nsBoundingMetrics bmBase, bmSubScript, bmSupScript, bmMultiSub, bmMultiSup;
315 0 : multiSubSize.SetBlockStartAscent(-0x7FFFFFFF);
316 0 : multiSupSize.SetBlockStartAscent(-0x7FFFFFFF);
317 0 : bmMultiSub.ascent = bmMultiSup.ascent = -0x7FFFFFFF;
318 0 : bmMultiSub.descent = bmMultiSup.descent = -0x7FFFFFFF;
319 0 : nscoord italicCorrection = 0;
320 :
321 0 : nsBoundingMetrics boundingMetrics;
322 0 : boundingMetrics.width = 0;
323 0 : boundingMetrics.ascent = boundingMetrics.descent = -0x7FFFFFFF;
324 0 : aDesiredSize.Width() = aDesiredSize.Height() = 0;
325 :
326 0 : int32_t count = 0;
327 0 : bool foundNoneTag = false;
328 :
329 : // Boolean to determine whether the current child is a subscript.
330 : // Note that only msup starts with a superscript.
331 0 : bool isSubScript = (tag != nsGkAtoms::msup_);
332 :
333 0 : nsIFrame* childFrame = aFrame->PrincipalChildList().FirstChild();
334 0 : while (childFrame) {
335 0 : if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) {
336 0 : if (tag != nsGkAtoms::mmultiscripts_) {
337 0 : if (aPlaceOrigin) {
338 0 : aFrame->ReportInvalidChildError(nsGkAtoms::mprescripts_);
339 : }
340 0 : return aFrame->ReflowError(aDrawTarget, aDesiredSize);
341 : }
342 0 : if (prescriptsFrame) {
343 : // duplicate <mprescripts/> found
344 : // report an error, encourage people to get their markups in order
345 0 : if (aPlaceOrigin) {
346 0 : aFrame->ReportErrorToConsole("DuplicateMprescripts");
347 : }
348 0 : return aFrame->ReflowError(aDrawTarget, aDesiredSize);
349 : }
350 0 : if (!isSubScript) {
351 0 : if (aPlaceOrigin) {
352 0 : aFrame->ReportErrorToConsole("SubSupMismatch");
353 : }
354 0 : return aFrame->ReflowError(aDrawTarget, aDesiredSize);
355 : }
356 :
357 0 : prescriptsFrame = childFrame;
358 0 : firstPrescriptsPair = true;
359 0 : } else if (0 == count) {
360 : // base
361 :
362 0 : if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::none)) {
363 0 : if (tag == nsGkAtoms::mmultiscripts_) {
364 0 : if (aPlaceOrigin) {
365 0 : aFrame->ReportErrorToConsole("NoBase");
366 : }
367 0 : return aFrame->ReflowError(aDrawTarget, aDesiredSize);
368 : } else {
369 : //A different error message is triggered later for the other tags
370 0 : foundNoneTag = true;
371 : }
372 : }
373 0 : baseFrame = childFrame;
374 0 : GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
375 :
376 0 : if (tag != nsGkAtoms::msub_) {
377 : // Apply italics correction if there is the potential for a
378 : // postsupscript.
379 0 : GetItalicCorrection(bmBase, italicCorrection);
380 : // If italics correction is applied, we always add "a little to spare"
381 : // (see TeXbook Ch.11, p.64), as we estimate the italic creation
382 : // ourselves and it isn't the same as TeX.
383 0 : italicCorrection += onePixel;
384 : }
385 :
386 : // we update boundingMetrics.{ascent,descent} with that
387 : // of the baseFrame only after processing all the sup/sub pairs
388 0 : boundingMetrics.width = bmBase.width;
389 0 : boundingMetrics.rightBearing = bmBase.rightBearing;
390 0 : boundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten
391 : } else {
392 : // super/subscript block
393 0 : if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::none)) {
394 0 : foundNoneTag = true;
395 : }
396 :
397 0 : if (isSubScript) {
398 : // subscript
399 0 : subScriptFrame = childFrame;
400 0 : GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
401 0 : if (!mathFont) {
402 : // get the subdrop from the subscript font
403 0 : GetSubDropFromChild (subScriptFrame, subDrop, aFontSizeInflation);
404 : }
405 :
406 : // parameter v, Rule 18a, App. G, TeXbook
407 0 : minSubScriptShift = bmBase.descent + subDrop;
408 0 : trySubScriptShift = std::max(minSubScriptShift,subScriptShift);
409 0 : multiSubSize.SetBlockStartAscent(
410 0 : std::max(multiSubSize.BlockStartAscent(),
411 0 : subScriptSize.BlockStartAscent()));
412 0 : bmMultiSub.ascent = std::max(bmMultiSub.ascent, bmSubScript.ascent);
413 0 : bmMultiSub.descent = std::max(bmMultiSub.descent, bmSubScript.descent);
414 0 : multiSubSize.Height() =
415 0 : std::max(multiSubSize.Height(),
416 0 : subScriptSize.Height() - subScriptSize.BlockStartAscent());
417 0 : if (bmSubScript.width)
418 0 : width = bmSubScript.width + scriptSpace;
419 0 : rightBearing = bmSubScript.rightBearing;
420 :
421 0 : if (tag == nsGkAtoms::msub_) {
422 0 : boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
423 0 : boundingMetrics.width += width;
424 :
425 : nscoord subscriptTopMax;
426 0 : if (mathFont) {
427 : subscriptTopMax =
428 0 : mathFont->MathTable()->Constant(gfxMathTable::SubscriptTopMax,
429 0 : oneDevPixel);
430 : } else {
431 : // get min subscript shift limit from x-height
432 : // = h(x) - 4/5 * sigma_5, Rule 18b, App. G, TeXbook
433 0 : subscriptTopMax = NSToCoordRound((4.0f/5.0f) * xHeight);
434 : }
435 0 : nscoord minShiftFromXHeight = bmSubScript.ascent - subscriptTopMax;
436 0 : maxSubScriptShift = std::max(trySubScriptShift,minShiftFromXHeight);
437 :
438 0 : maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
439 0 : trySubScriptShift = subScriptShift;
440 : }
441 : } else {
442 : // supscript
443 0 : supScriptFrame = childFrame;
444 0 : GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
445 0 : if (!mathFont) {
446 : // get the supdrop from the supscript font
447 0 : GetSupDropFromChild (supScriptFrame, supDrop, aFontSizeInflation);
448 : }
449 : // parameter u, Rule 18a, App. G, TeXbook
450 0 : minSupScriptShift = bmBase.ascent - supDrop;
451 : nscoord superscriptBottomMin;
452 0 : if (mathFont) {
453 : superscriptBottomMin =
454 0 : mathFont->MathTable()->Constant(gfxMathTable::SuperscriptBottomMin,
455 0 : oneDevPixel);
456 : } else {
457 : // get min supscript shift limit from x-height
458 : // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook
459 0 : superscriptBottomMin = NSToCoordRound((1.0f / 4.0f) * xHeight);
460 : }
461 0 : minShiftFromXHeight = bmSupScript.descent + superscriptBottomMin;
462 0 : trySupScriptShift = std::max(minSupScriptShift,
463 : std::max(minShiftFromXHeight,
464 0 : supScriptShift));
465 0 : multiSupSize.SetBlockStartAscent(
466 0 : std::max(multiSupSize.BlockStartAscent(),
467 0 : supScriptSize.BlockStartAscent()));
468 0 : bmMultiSup.ascent = std::max(bmMultiSup.ascent, bmSupScript.ascent);
469 0 : bmMultiSup.descent = std::max(bmMultiSup.descent, bmSupScript.descent);
470 0 : multiSupSize.Height() =
471 0 : std::max(multiSupSize.Height(),
472 0 : supScriptSize.Height() - supScriptSize.BlockStartAscent());
473 :
474 0 : if (bmSupScript.width)
475 0 : width = std::max(width, bmSupScript.width + scriptSpace);
476 :
477 0 : if (!prescriptsFrame) { // we are still looping over base & postscripts
478 0 : rightBearing = std::max(rightBearing,
479 0 : italicCorrection + bmSupScript.rightBearing);
480 0 : boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
481 0 : boundingMetrics.width += width;
482 : } else {
483 0 : prescriptsWidth += width;
484 0 : if (firstPrescriptsPair) {
485 0 : firstPrescriptsPair = false;
486 0 : boundingMetrics.leftBearing =
487 0 : std::min(bmSubScript.leftBearing, bmSupScript.leftBearing);
488 : }
489 : }
490 0 : width = rightBearing = 0;
491 :
492 : // negotiate between the various shifts so that
493 : // there is enough gap between the sup and subscripts
494 : // Rule 18e, App. G, TeXbook
495 0 : if (tag == nsGkAtoms::mmultiscripts_ ||
496 0 : tag == nsGkAtoms::msubsup_) {
497 : nscoord subSuperscriptGapMin;
498 0 : if (mathFont) {
499 : subSuperscriptGapMin = mathFont->MathTable()->
500 0 : Constant(gfxMathTable::SubSuperscriptGapMin, oneDevPixel);
501 : } else {
502 : nscoord ruleSize;
503 0 : GetRuleThickness(aDrawTarget, fm, ruleSize);
504 0 : subSuperscriptGapMin = 4 * ruleSize;
505 : }
506 : nscoord gap =
507 0 : (trySupScriptShift - bmSupScript.descent) -
508 0 : (bmSubScript.ascent - trySubScriptShift);
509 0 : if (gap < subSuperscriptGapMin) {
510 : // adjust trySubScriptShift to get a gap of subSuperscriptGapMin
511 0 : trySubScriptShift += subSuperscriptGapMin - gap;
512 : }
513 :
514 : // next we want to ensure that the bottom of the superscript
515 : // will be > superscriptBottomMaxWithSubscript
516 : nscoord superscriptBottomMaxWithSubscript;
517 0 : if (mathFont) {
518 : superscriptBottomMaxWithSubscript = mathFont->MathTable()->
519 0 : Constant(gfxMathTable::SuperscriptBottomMaxWithSubscript,
520 0 : oneDevPixel);
521 : } else {
522 : superscriptBottomMaxWithSubscript =
523 0 : NSToCoordRound((4.0f / 5.0f) * xHeight);
524 : }
525 0 : gap = superscriptBottomMaxWithSubscript -
526 0 : (trySupScriptShift - bmSupScript.descent);
527 0 : if (gap > 0) {
528 0 : trySupScriptShift += gap;
529 0 : trySubScriptShift -= gap;
530 : }
531 : }
532 :
533 0 : maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
534 0 : maxSupScriptShift = std::max(maxSupScriptShift, trySupScriptShift);
535 :
536 0 : trySubScriptShift = subScriptShift;
537 0 : trySupScriptShift = supScriptShift;
538 : }
539 :
540 0 : isSubScript = !isSubScript;
541 : }
542 0 : count++;
543 0 : childFrame = childFrame->GetNextSibling();
544 : }
545 :
546 : //NoBase error may also have been reported above
547 0 : if ((count != 2 && (tag == nsGkAtoms::msup_ || tag == nsGkAtoms::msub_)) ||
548 0 : (count != 3 && tag == nsGkAtoms::msubsup_) || !baseFrame ||
549 0 : (foundNoneTag && tag != nsGkAtoms::mmultiscripts_) ||
550 0 : (!isSubScript && tag == nsGkAtoms::mmultiscripts_)) {
551 : // report an error, encourage people to get their markups in order
552 0 : if (aPlaceOrigin) {
553 0 : if ((count != 2 && (tag == nsGkAtoms::msup_ ||
554 0 : tag == nsGkAtoms::msub_)) ||
555 0 : (count != 3 && tag == nsGkAtoms::msubsup_ )) {
556 0 : aFrame->ReportChildCountError();
557 0 : } else if (foundNoneTag && tag != nsGkAtoms::mmultiscripts_) {
558 0 : aFrame->ReportInvalidChildError(nsGkAtoms::none);
559 0 : } else if (!baseFrame) {
560 0 : aFrame->ReportErrorToConsole("NoBase");
561 : } else {
562 0 : aFrame->ReportErrorToConsole("SubSupMismatch");
563 : }
564 : }
565 0 : return aFrame->ReflowError(aDrawTarget, aDesiredSize);
566 : }
567 :
568 : // we left out the width of prescripts, so ...
569 0 : boundingMetrics.rightBearing += prescriptsWidth;
570 0 : boundingMetrics.width += prescriptsWidth;
571 :
572 : // Zero out the shifts in where a frame isn't present to avoid the potential
573 : // for overflow.
574 0 : if (!subScriptFrame)
575 0 : maxSubScriptShift = 0;
576 0 : if (!supScriptFrame)
577 0 : maxSupScriptShift = 0;
578 :
579 : // we left out the base during our bounding box updates, so ...
580 0 : if (tag == nsGkAtoms::msub_) {
581 0 : boundingMetrics.ascent = std::max(bmBase.ascent,
582 0 : bmMultiSub.ascent - maxSubScriptShift);
583 : } else {
584 0 : boundingMetrics.ascent =
585 0 : std::max(bmBase.ascent, (bmMultiSup.ascent + maxSupScriptShift));
586 : }
587 0 : if (tag == nsGkAtoms::msup_) {
588 0 : boundingMetrics.descent = std::max(bmBase.descent,
589 0 : bmMultiSup.descent - maxSupScriptShift);
590 : } else {
591 0 : boundingMetrics.descent =
592 0 : std::max(bmBase.descent, (bmMultiSub.descent + maxSubScriptShift));
593 : }
594 0 : aFrame->SetBoundingMetrics(boundingMetrics);
595 :
596 : // get the reflow metrics ...
597 0 : aDesiredSize.SetBlockStartAscent(
598 0 : std::max(baseSize.BlockStartAscent(),
599 0 : std::max(multiSubSize.BlockStartAscent() - maxSubScriptShift,
600 0 : multiSupSize.BlockStartAscent() + maxSupScriptShift)));
601 0 : aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
602 0 : std::max(baseSize.Height() - baseSize.BlockStartAscent(),
603 0 : std::max(multiSubSize.Height() + maxSubScriptShift,
604 0 : multiSupSize.Height() - maxSupScriptShift));
605 0 : aDesiredSize.Width() = boundingMetrics.width;
606 0 : aDesiredSize.mBoundingMetrics = boundingMetrics;
607 :
608 0 : aFrame->SetReference(nsPoint(0, aDesiredSize.BlockStartAscent()));
609 :
610 : //////////////////
611 : // Place Children
612 :
613 : // Place prescripts, followed by base, and then postscripts.
614 : // The list of frames is in the order: {base} {postscripts} {prescripts}
615 : // We go over the list in a circular manner, starting at <prescripts/>
616 :
617 0 : if (aPlaceOrigin) {
618 0 : nscoord dx = 0, dy = 0;
619 :
620 : // With msub and msup there is only one element and
621 : // subscriptFrame/supScriptFrame have already been set above where
622 : // relevant. In these cases we skip to the reflow part.
623 0 : if (tag == nsGkAtoms::msub_ || tag == nsGkAtoms::msup_)
624 0 : count = 1;
625 : else
626 0 : count = 0;
627 0 : childFrame = prescriptsFrame;
628 0 : bool isPreScript = true;
629 0 : do {
630 0 : if (!childFrame) { // end of prescripts,
631 0 : isPreScript = false;
632 : // place the base ...
633 0 : childFrame = baseFrame;
634 0 : dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
635 0 : FinishReflowChild (baseFrame, aPresContext, baseSize, nullptr,
636 0 : aFrame->MirrorIfRTL(aDesiredSize.Width(),
637 0 : baseSize.Width(),
638 : dx),
639 0 : dy, 0);
640 0 : dx += bmBase.width;
641 0 : } else if (prescriptsFrame == childFrame) {
642 : // Clear reflow flags of prescripts frame.
643 0 : prescriptsFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
644 : } else {
645 : // process each sup/sub pair
646 0 : if (0 == count) {
647 0 : subScriptFrame = childFrame;
648 0 : count = 1;
649 0 : } else if (1 == count) {
650 0 : if (tag != nsGkAtoms::msub_)
651 0 : supScriptFrame = childFrame;
652 0 : count = 0;
653 :
654 : // get the ascent/descent of sup/subscripts stored in their rects
655 : // rect.x = descent, rect.y = ascent
656 0 : if (subScriptFrame)
657 0 : GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
658 0 : if (supScriptFrame)
659 0 : GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
660 :
661 0 : width = std::max(subScriptSize.Width(), supScriptSize.Width());
662 :
663 0 : if (subScriptFrame) {
664 0 : nscoord x = dx;
665 : // prescripts should be right aligned
666 : // https://bugzilla.mozilla.org/show_bug.cgi?id=928675
667 0 : if (isPreScript)
668 0 : x += width - subScriptSize.Width();
669 0 : dy = aDesiredSize.BlockStartAscent() - subScriptSize.BlockStartAscent() +
670 : maxSubScriptShift;
671 0 : FinishReflowChild (subScriptFrame, aPresContext, subScriptSize,
672 : nullptr,
673 0 : aFrame->MirrorIfRTL(aDesiredSize.Width(),
674 0 : subScriptSize.Width(),
675 : x),
676 0 : dy, 0);
677 : }
678 :
679 0 : if (supScriptFrame) {
680 0 : nscoord x = dx;
681 0 : if (isPreScript) {
682 0 : x += width - supScriptSize.Width();
683 : } else {
684 : // post superscripts are shifted by the italic correction value
685 0 : x += italicCorrection;
686 : }
687 0 : dy = aDesiredSize.BlockStartAscent() - supScriptSize.BlockStartAscent() -
688 : maxSupScriptShift;
689 0 : FinishReflowChild (supScriptFrame, aPresContext, supScriptSize,
690 : nullptr,
691 0 : aFrame->MirrorIfRTL(aDesiredSize.Width(),
692 0 : supScriptSize.Width(),
693 : x),
694 0 : dy, 0);
695 : }
696 0 : dx += width + scriptSpace;
697 : }
698 : }
699 0 : childFrame = childFrame->GetNextSibling();
700 0 : } while (prescriptsFrame != childFrame);
701 : }
702 :
703 0 : return NS_OK;
704 : }
|