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