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