1 | /* |
2 | * Copyright (C) 2010 Alex Milowski (alex@milowski.com). All rights reserved. |
3 | * Copyright (C) 2013 The MathJax Consortium. |
4 | * Copyright (C) 2016 Igalia S.L. |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * |
15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
18 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
19 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
21 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | |
28 | #include "config.h" |
29 | #include "RenderMathMLScripts.h" |
30 | |
31 | #if ENABLE(MATHML) |
32 | |
33 | #include "MathMLElement.h" |
34 | #include "MathMLScriptsElement.h" |
35 | #include "RenderMathMLOperator.h" |
36 | #include <wtf/IsoMallocInlines.h> |
37 | |
38 | namespace WebCore { |
39 | |
40 | WTF_MAKE_ISO_ALLOCATED_IMPL(RenderMathMLScripts); |
41 | |
42 | static bool isPrescriptDelimiter(const RenderObject& renderObject) |
43 | { |
44 | return renderObject.node() && renderObject.node()->hasTagName(MathMLNames::mprescriptsTag); |
45 | } |
46 | |
47 | RenderMathMLScripts::RenderMathMLScripts(MathMLScriptsElement& element, RenderStyle&& style) |
48 | : RenderMathMLBlock(element, WTFMove(style)) |
49 | { |
50 | } |
51 | |
52 | MathMLScriptsElement& RenderMathMLScripts::element() const |
53 | { |
54 | return static_cast<MathMLScriptsElement&>(nodeForNonAnonymous()); |
55 | } |
56 | |
57 | MathMLScriptsElement::ScriptType RenderMathMLScripts::scriptType() const |
58 | { |
59 | return element().scriptType(); |
60 | } |
61 | |
62 | RenderMathMLOperator* RenderMathMLScripts::unembellishedOperator() const |
63 | { |
64 | auto base = firstChildBox(); |
65 | if (!is<RenderMathMLBlock>(base)) |
66 | return nullptr; |
67 | return downcast<RenderMathMLBlock>(base)->unembellishedOperator(); |
68 | } |
69 | |
70 | Optional<RenderMathMLScripts::ReferenceChildren> RenderMathMLScripts::validateAndGetReferenceChildren() |
71 | { |
72 | // All scripted elements must have at least one child. |
73 | // The first child is the base. |
74 | auto base = firstChildBox(); |
75 | if (!base) |
76 | return WTF::nullopt; |
77 | |
78 | ReferenceChildren reference; |
79 | reference.base = base; |
80 | reference.firstPostScript = nullptr; |
81 | reference.firstPreScript = nullptr; |
82 | reference.prescriptDelimiter = nullptr; |
83 | |
84 | switch (scriptType()) { |
85 | case MathMLScriptsElement::ScriptType::Sub: |
86 | case MathMLScriptsElement::ScriptType::Super: |
87 | case MathMLScriptsElement::ScriptType::Under: |
88 | case MathMLScriptsElement::ScriptType::Over: { |
89 | // These elements must have exactly two children. |
90 | // The second child is a postscript and there are no prescripts. |
91 | // <msub> base subscript </msub> |
92 | // <msup> base superscript </msup> |
93 | // <munder> base underscript </munder> |
94 | // <mover> base overscript </mover> |
95 | auto script = base->nextSiblingBox(); |
96 | if (!script || isPrescriptDelimiter(*script) || script->nextSiblingBox()) |
97 | return WTF::nullopt; |
98 | reference.firstPostScript = script; |
99 | return reference; |
100 | } |
101 | case MathMLScriptsElement::ScriptType::SubSup: |
102 | case MathMLScriptsElement::ScriptType::UnderOver: { |
103 | // These elements must have exactly three children. |
104 | // The second and third children are postscripts and there are no prescripts. |
105 | // <msubsup> base subscript superscript </msubsup> |
106 | // <munderover> base subscript superscript </munderover> |
107 | auto subScript = base->nextSiblingBox(); |
108 | if (!subScript || isPrescriptDelimiter(*subScript)) |
109 | return WTF::nullopt; |
110 | auto superScript = subScript->nextSiblingBox(); |
111 | if (!superScript || isPrescriptDelimiter(*superScript) || superScript->nextSiblingBox()) |
112 | return WTF::nullopt; |
113 | reference.firstPostScript = subScript; |
114 | return reference; |
115 | } |
116 | case MathMLScriptsElement::ScriptType::Multiscripts: { |
117 | // This element accepts the following syntax: |
118 | // |
119 | // <mmultiscripts> |
120 | // base |
121 | // (subscript superscript)* |
122 | // [ <mprescripts/> (presubscript presuperscript)* ] |
123 | // </mmultiscripts> |
124 | // |
125 | // https://www.w3.org/TR/MathML3/chapter3.html#presm.mmultiscripts |
126 | // |
127 | // We set the first postscript, unless (subscript superscript)* is empty. |
128 | if (base->nextSiblingBox() && !isPrescriptDelimiter(*base->nextSiblingBox())) |
129 | reference.firstPostScript = base->nextSiblingBox(); |
130 | |
131 | // We browse the children in order to |
132 | // 1) Set the first prescript, unless (presubscript presuperscript)* is empty. |
133 | // 2) Ensure the validity of the element i.e. |
134 | // a) That the list of postscripts can be grouped into pairs of subscript/superscript. |
135 | // b) That the list of prescripts can be grouped into pairs of subscript/superscript. |
136 | // c) That there is at most one <mprescripts/>. |
137 | bool numberOfScriptIsEven = true; |
138 | for (auto script = base->nextSiblingBox(); script; script = script->nextSiblingBox()) { |
139 | if (isPrescriptDelimiter(*script)) { |
140 | // This is a <mprescripts/>. Let's check 2a) and 2c). |
141 | if (!numberOfScriptIsEven || reference.firstPreScript) |
142 | return WTF::nullopt; |
143 | reference.firstPreScript = script->nextSiblingBox(); // We do 1). |
144 | reference.prescriptDelimiter = script; |
145 | continue; |
146 | } |
147 | numberOfScriptIsEven = !numberOfScriptIsEven; |
148 | } |
149 | return numberOfScriptIsEven ? Optional<ReferenceChildren>(reference) : WTF::nullopt; // We verify 2b). |
150 | } |
151 | } |
152 | |
153 | ASSERT_NOT_REACHED(); |
154 | return WTF::nullopt; |
155 | } |
156 | |
157 | LayoutUnit RenderMathMLScripts::spaceAfterScript() |
158 | { |
159 | const auto& primaryFont = style().fontCascade().primaryFont(); |
160 | if (auto* mathData = primaryFont.mathData()) |
161 | return mathData->getMathConstant(primaryFont, OpenTypeMathData::SpaceAfterScript); |
162 | return style().fontCascade().size() / 5; |
163 | } |
164 | |
165 | LayoutUnit RenderMathMLScripts::italicCorrection(const ReferenceChildren& reference) |
166 | { |
167 | if (is<RenderMathMLBlock>(*reference.base)) { |
168 | if (auto* renderOperator = downcast<RenderMathMLBlock>(*reference.base).unembellishedOperator()) |
169 | return renderOperator->italicCorrection(); |
170 | } |
171 | return 0; |
172 | } |
173 | |
174 | void RenderMathMLScripts::computePreferredLogicalWidths() |
175 | { |
176 | ASSERT(preferredLogicalWidthsDirty()); |
177 | |
178 | m_minPreferredLogicalWidth = 0; |
179 | m_maxPreferredLogicalWidth = 0; |
180 | |
181 | auto possibleReference = validateAndGetReferenceChildren(); |
182 | if (!possibleReference) { |
183 | setPreferredLogicalWidthsDirty(false); |
184 | return; |
185 | } |
186 | auto& reference = possibleReference.value(); |
187 | |
188 | LayoutUnit baseItalicCorrection = std::min(reference.base->maxPreferredLogicalWidth(), italicCorrection(reference)); |
189 | LayoutUnit space = spaceAfterScript(); |
190 | |
191 | switch (scriptType()) { |
192 | case MathMLScriptsElement::ScriptType::Sub: |
193 | case MathMLScriptsElement::ScriptType::Under: |
194 | m_maxPreferredLogicalWidth += reference.base->maxPreferredLogicalWidth(); |
195 | m_maxPreferredLogicalWidth += std::max(0_lu, reference.firstPostScript->maxPreferredLogicalWidth() - baseItalicCorrection + space); |
196 | break; |
197 | case MathMLScriptsElement::ScriptType::Super: |
198 | case MathMLScriptsElement::ScriptType::Over: |
199 | m_maxPreferredLogicalWidth += reference.base->maxPreferredLogicalWidth(); |
200 | m_maxPreferredLogicalWidth += std::max(0_lu, reference.firstPostScript->maxPreferredLogicalWidth() + space); |
201 | break; |
202 | case MathMLScriptsElement::ScriptType::SubSup: |
203 | case MathMLScriptsElement::ScriptType::UnderOver: |
204 | case MathMLScriptsElement::ScriptType::Multiscripts: { |
205 | auto subScript = reference.firstPreScript; |
206 | while (subScript) { |
207 | auto supScript = subScript->nextSiblingBox(); |
208 | ASSERT(supScript); |
209 | LayoutUnit subSupPairWidth = std::max(subScript->maxPreferredLogicalWidth(), supScript->maxPreferredLogicalWidth()); |
210 | m_maxPreferredLogicalWidth += subSupPairWidth + space; |
211 | subScript = supScript->nextSiblingBox(); |
212 | } |
213 | m_maxPreferredLogicalWidth += reference.base->maxPreferredLogicalWidth(); |
214 | subScript = reference.firstPostScript; |
215 | while (subScript && subScript != reference.prescriptDelimiter) { |
216 | auto supScript = subScript->nextSiblingBox(); |
217 | ASSERT(supScript); |
218 | LayoutUnit subSupPairWidth = std::max(std::max(0_lu, subScript->maxPreferredLogicalWidth() - baseItalicCorrection), supScript->maxPreferredLogicalWidth()); |
219 | m_maxPreferredLogicalWidth += subSupPairWidth + space; |
220 | subScript = supScript->nextSiblingBox(); |
221 | } |
222 | } |
223 | } |
224 | |
225 | m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; |
226 | |
227 | setPreferredLogicalWidthsDirty(false); |
228 | } |
229 | |
230 | auto RenderMathMLScripts::verticalParameters() const -> VerticalParameters |
231 | { |
232 | VerticalParameters parameters; |
233 | const auto& primaryFont = style().fontCascade().primaryFont(); |
234 | if (auto* mathData = primaryFont.mathData()) { |
235 | parameters.subscriptShiftDown = mathData->getMathConstant(primaryFont, OpenTypeMathData::SubscriptShiftDown); |
236 | parameters.superscriptShiftUp = mathData->getMathConstant(primaryFont, OpenTypeMathData::SuperscriptShiftUp); |
237 | parameters.subscriptBaselineDropMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::SubscriptBaselineDropMin); |
238 | parameters.superScriptBaselineDropMax = mathData->getMathConstant(primaryFont, OpenTypeMathData::SuperscriptBaselineDropMax); |
239 | parameters.subSuperscriptGapMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::SubSuperscriptGapMin); |
240 | parameters.superscriptBottomMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::SuperscriptBottomMin); |
241 | parameters.subscriptTopMax = mathData->getMathConstant(primaryFont, OpenTypeMathData::SubscriptTopMax); |
242 | parameters.superscriptBottomMaxWithSubscript = mathData->getMathConstant(primaryFont, OpenTypeMathData::SuperscriptBottomMaxWithSubscript); |
243 | } else { |
244 | // Default heuristic values when you do not have a font. |
245 | parameters.subscriptShiftDown = style().fontMetrics().xHeight() / 3; |
246 | parameters.superscriptShiftUp = style().fontMetrics().xHeight(); |
247 | parameters.subscriptBaselineDropMin = style().fontMetrics().xHeight() / 2; |
248 | parameters.superScriptBaselineDropMax = style().fontMetrics().xHeight() / 2; |
249 | parameters.subSuperscriptGapMin = style().fontCascade().size() / 5; |
250 | parameters.superscriptBottomMin = style().fontMetrics().xHeight() / 4; |
251 | parameters.subscriptTopMax = 4 * style().fontMetrics().xHeight() / 5; |
252 | parameters.superscriptBottomMaxWithSubscript = 4 * style().fontMetrics().xHeight() / 5; |
253 | } |
254 | return parameters; |
255 | } |
256 | |
257 | RenderMathMLScripts::VerticalMetrics RenderMathMLScripts::verticalMetrics(const ReferenceChildren& reference) |
258 | { |
259 | VerticalParameters parameters = verticalParameters(); |
260 | VerticalMetrics metrics = { 0, 0, 0, 0 }; |
261 | |
262 | LayoutUnit baseAscent = ascentForChild(*reference.base); |
263 | LayoutUnit baseDescent = reference.base->logicalHeight() - baseAscent; |
264 | if (scriptType() == MathMLScriptsElement::ScriptType::Sub || scriptType() == MathMLScriptsElement::ScriptType::SubSup || scriptType() == MathMLScriptsElement::ScriptType::Multiscripts || scriptType() == MathMLScriptsElement::ScriptType::Under || scriptType() == MathMLScriptsElement::ScriptType::UnderOver) { |
265 | metrics.subShift = std::max(parameters.subscriptShiftDown, baseDescent + parameters.subscriptBaselineDropMin); |
266 | if (!isRenderMathMLUnderOver()) { |
267 | // It is not clear how to interpret the default shift and it is not available yet anyway. |
268 | // Hence we just pass 0 as the default value used by toUserUnits. |
269 | LayoutUnit specifiedMinSubShift = toUserUnits(element().subscriptShift(), style(), 0); |
270 | metrics.subShift = std::max(metrics.subShift, specifiedMinSubShift); |
271 | } |
272 | } |
273 | if (scriptType() == MathMLScriptsElement::ScriptType::Super || scriptType() == MathMLScriptsElement::ScriptType::SubSup || scriptType() == MathMLScriptsElement::ScriptType::Multiscripts || scriptType() == MathMLScriptsElement::ScriptType::Over || scriptType() == MathMLScriptsElement::ScriptType::UnderOver) { |
274 | metrics.supShift = std::max(parameters.superscriptShiftUp, baseAscent - parameters.superScriptBaselineDropMax); |
275 | if (!isRenderMathMLUnderOver()) { |
276 | // It is not clear how to interpret the default shift and it is not available yet anyway. |
277 | // Hence we just pass 0 as the default value used by toUserUnits. |
278 | LayoutUnit specifiedMinSupShift = toUserUnits(element().superscriptShift(), style(), 0); |
279 | metrics.supShift = std::max(metrics.supShift, specifiedMinSupShift); |
280 | } |
281 | } |
282 | |
283 | switch (scriptType()) { |
284 | case MathMLScriptsElement::ScriptType::Sub: |
285 | case MathMLScriptsElement::ScriptType::Under: { |
286 | LayoutUnit subAscent = ascentForChild(*reference.firstPostScript); |
287 | LayoutUnit subDescent = reference.firstPostScript->logicalHeight() - subAscent; |
288 | metrics.descent = subDescent; |
289 | metrics.subShift = std::max(metrics.subShift, subAscent - parameters.subscriptTopMax); |
290 | } |
291 | break; |
292 | case MathMLScriptsElement::ScriptType::Super: |
293 | case MathMLScriptsElement::ScriptType::Over: { |
294 | LayoutUnit supAscent = ascentForChild(*reference.firstPostScript); |
295 | LayoutUnit supDescent = reference.firstPostScript->logicalHeight() - supAscent; |
296 | metrics.ascent = supAscent; |
297 | metrics.supShift = std::max(metrics.supShift, parameters.superscriptBottomMin + supDescent); |
298 | } |
299 | break; |
300 | case MathMLScriptsElement::ScriptType::SubSup: |
301 | case MathMLScriptsElement::ScriptType::UnderOver: |
302 | case MathMLScriptsElement::ScriptType::Multiscripts: { |
303 | // FIXME: We should move the code updating VerticalMetrics for each sub/sup pair in a helper |
304 | // function. That way, SubSup/UnderOver can just make one call and the loop for Multiscripts |
305 | // can be rewritten in a more readable. |
306 | auto subScript = reference.firstPostScript ? reference.firstPostScript : reference.firstPreScript; |
307 | while (subScript) { |
308 | auto supScript = subScript->nextSiblingBox(); |
309 | ASSERT(supScript); |
310 | LayoutUnit subAscent = ascentForChild(*subScript); |
311 | LayoutUnit subDescent = subScript->logicalHeight() - subAscent; |
312 | LayoutUnit supAscent = ascentForChild(*supScript); |
313 | LayoutUnit supDescent = supScript->logicalHeight() - supAscent; |
314 | metrics.ascent = std::max(metrics.ascent, supAscent); |
315 | metrics.descent = std::max(metrics.descent, subDescent); |
316 | LayoutUnit subScriptShift = std::max(parameters.subscriptShiftDown, baseDescent + parameters.subscriptBaselineDropMin); |
317 | subScriptShift = std::max(subScriptShift, subAscent - parameters.subscriptTopMax); |
318 | LayoutUnit supScriptShift = std::max(parameters.superscriptShiftUp, baseAscent - parameters.superScriptBaselineDropMax); |
319 | supScriptShift = std::max(supScriptShift, parameters.superscriptBottomMin + supDescent); |
320 | |
321 | LayoutUnit subSuperscriptGap = (subScriptShift - subAscent) + (supScriptShift - supDescent); |
322 | if (subSuperscriptGap < parameters.subSuperscriptGapMin) { |
323 | // First, we try and push the superscript up. |
324 | LayoutUnit delta = parameters.superscriptBottomMaxWithSubscript - (supScriptShift - supDescent); |
325 | if (delta > 0) { |
326 | delta = std::min(delta, parameters.subSuperscriptGapMin - subSuperscriptGap); |
327 | supScriptShift += delta; |
328 | subSuperscriptGap += delta; |
329 | } |
330 | // If that is not enough, we push the subscript down. |
331 | if (subSuperscriptGap < parameters.subSuperscriptGapMin) |
332 | subScriptShift += parameters.subSuperscriptGapMin - subSuperscriptGap; |
333 | } |
334 | |
335 | metrics.subShift = std::max(metrics.subShift, subScriptShift); |
336 | metrics.supShift = std::max(metrics.supShift, supScriptShift); |
337 | |
338 | subScript = supScript->nextSiblingBox(); |
339 | if (subScript == reference.prescriptDelimiter) |
340 | subScript = reference.firstPreScript; |
341 | } |
342 | } |
343 | } |
344 | |
345 | return metrics; |
346 | } |
347 | |
348 | void RenderMathMLScripts::layoutBlock(bool relayoutChildren, LayoutUnit) |
349 | { |
350 | ASSERT(needsLayout()); |
351 | |
352 | if (!relayoutChildren && simplifiedLayout()) |
353 | return; |
354 | |
355 | auto possibleReference = validateAndGetReferenceChildren(); |
356 | if (!possibleReference) { |
357 | layoutInvalidMarkup(relayoutChildren); |
358 | return; |
359 | } |
360 | auto& reference = possibleReference.value(); |
361 | |
362 | recomputeLogicalWidth(); |
363 | for (auto child = firstChildBox(); child; child = child->nextSiblingBox()) |
364 | child->layoutIfNeeded(); |
365 | |
366 | LayoutUnit space = spaceAfterScript(); |
367 | |
368 | // We determine the minimal shift/size of each script and take the maximum of the values. |
369 | VerticalMetrics metrics = verticalMetrics(reference); |
370 | |
371 | LayoutUnit baseAscent = ascentForChild(*reference.base); |
372 | LayoutUnit baseDescent = reference.base->logicalHeight() - baseAscent; |
373 | LayoutUnit baseItalicCorrection = std::min(reference.base->logicalWidth(), italicCorrection(reference)); |
374 | LayoutUnit horizontalOffset; |
375 | |
376 | LayoutUnit ascent = std::max(baseAscent, metrics.ascent + metrics.supShift); |
377 | LayoutUnit descent = std::max(baseDescent, metrics.descent + metrics.subShift); |
378 | setLogicalHeight(ascent + descent); |
379 | |
380 | switch (scriptType()) { |
381 | case MathMLScriptsElement::ScriptType::Sub: |
382 | case MathMLScriptsElement::ScriptType::Under: { |
383 | setLogicalWidth(reference.base->logicalWidth() + std::max(0_lu, reference.firstPostScript->logicalWidth() - baseItalicCorrection + space)); |
384 | LayoutPoint baseLocation(mirrorIfNeeded(horizontalOffset, *reference.base), ascent - baseAscent); |
385 | reference.base->setLocation(baseLocation); |
386 | horizontalOffset += reference.base->logicalWidth(); |
387 | LayoutUnit scriptAscent = ascentForChild(*reference.firstPostScript); |
388 | LayoutPoint scriptLocation(mirrorIfNeeded(horizontalOffset - baseItalicCorrection, *reference.firstPostScript), ascent + metrics.subShift - scriptAscent); |
389 | reference.firstPostScript->setLocation(scriptLocation); |
390 | } |
391 | break; |
392 | case MathMLScriptsElement::ScriptType::Super: |
393 | case MathMLScriptsElement::ScriptType::Over: { |
394 | setLogicalWidth(reference.base->logicalWidth() + std::max(0_lu, reference.firstPostScript->logicalWidth() + space)); |
395 | LayoutPoint baseLocation(mirrorIfNeeded(horizontalOffset, *reference.base), ascent - baseAscent); |
396 | reference.base->setLocation(baseLocation); |
397 | horizontalOffset += reference.base->logicalWidth(); |
398 | LayoutUnit scriptAscent = ascentForChild(*reference.firstPostScript); |
399 | LayoutPoint scriptLocation(mirrorIfNeeded(horizontalOffset, *reference.firstPostScript), ascent - metrics.supShift - scriptAscent); |
400 | reference.firstPostScript->setLocation(scriptLocation); |
401 | } |
402 | break; |
403 | case MathMLScriptsElement::ScriptType::SubSup: |
404 | case MathMLScriptsElement::ScriptType::UnderOver: |
405 | case MathMLScriptsElement::ScriptType::Multiscripts: { |
406 | // Calculate the logical width. |
407 | LayoutUnit logicalWidth; |
408 | auto subScript = reference.firstPreScript; |
409 | while (subScript) { |
410 | auto supScript = subScript->nextSiblingBox(); |
411 | ASSERT(supScript); |
412 | LayoutUnit subSupPairWidth = std::max(subScript->logicalWidth(), supScript->logicalWidth()); |
413 | logicalWidth += subSupPairWidth + space; |
414 | subScript = supScript->nextSiblingBox(); |
415 | } |
416 | logicalWidth += reference.base->logicalWidth(); |
417 | subScript = reference.firstPostScript; |
418 | while (subScript && subScript != reference.prescriptDelimiter) { |
419 | auto supScript = subScript->nextSiblingBox(); |
420 | ASSERT(supScript); |
421 | LayoutUnit subSupPairWidth = std::max(std::max(0_lu, subScript->logicalWidth() - baseItalicCorrection), supScript->logicalWidth()); |
422 | logicalWidth += subSupPairWidth + space; |
423 | subScript = supScript->nextSiblingBox(); |
424 | } |
425 | setLogicalWidth(logicalWidth); |
426 | |
427 | subScript = reference.firstPreScript; |
428 | while (subScript) { |
429 | auto supScript = subScript->nextSiblingBox(); |
430 | ASSERT(supScript); |
431 | LayoutUnit subSupPairWidth = std::max(subScript->logicalWidth(), supScript->logicalWidth()); |
432 | horizontalOffset += space + subSupPairWidth; |
433 | LayoutUnit subAscent = ascentForChild(*subScript); |
434 | LayoutPoint subScriptLocation(mirrorIfNeeded(horizontalOffset - subScript->logicalWidth(), *subScript), ascent + metrics.subShift - subAscent); |
435 | subScript->setLocation(subScriptLocation); |
436 | LayoutUnit supAscent = ascentForChild(*supScript); |
437 | LayoutPoint supScriptLocation(mirrorIfNeeded(horizontalOffset - supScript->logicalWidth(), *supScript), ascent - metrics.supShift - supAscent); |
438 | supScript->setLocation(supScriptLocation); |
439 | subScript = supScript->nextSiblingBox(); |
440 | } |
441 | LayoutPoint baseLocation(mirrorIfNeeded(horizontalOffset, *reference.base), ascent - baseAscent); |
442 | reference.base->setLocation(baseLocation); |
443 | horizontalOffset += reference.base->logicalWidth(); |
444 | subScript = reference.firstPostScript; |
445 | while (subScript && subScript != reference.prescriptDelimiter) { |
446 | auto supScript = subScript->nextSiblingBox(); |
447 | ASSERT(supScript); |
448 | LayoutUnit subAscent = ascentForChild(*subScript); |
449 | LayoutPoint subScriptLocation(mirrorIfNeeded(horizontalOffset - baseItalicCorrection, *subScript), ascent + metrics.subShift - subAscent); |
450 | subScript->setLocation(subScriptLocation); |
451 | LayoutUnit supAscent = ascentForChild(*supScript); |
452 | LayoutPoint supScriptLocation(mirrorIfNeeded(horizontalOffset, *supScript), ascent - metrics.supShift - supAscent); |
453 | supScript->setLocation(supScriptLocation); |
454 | |
455 | LayoutUnit subSupPairWidth = std::max(subScript->logicalWidth(), supScript->logicalWidth()); |
456 | horizontalOffset += subSupPairWidth + space; |
457 | subScript = supScript->nextSiblingBox(); |
458 | } |
459 | } |
460 | } |
461 | |
462 | layoutPositionedObjects(relayoutChildren); |
463 | |
464 | updateScrollInfoAfterLayout(); |
465 | |
466 | clearNeedsLayout(); |
467 | } |
468 | |
469 | Optional<int> RenderMathMLScripts::firstLineBaseline() const |
470 | { |
471 | auto* base = firstChildBox(); |
472 | if (!base) |
473 | return Optional<int>(); |
474 | return Optional<int>(static_cast<int>(lroundf(ascentForChild(*base) + base->logicalTop()))); |
475 | } |
476 | |
477 | } |
478 | |
479 | #endif // ENABLE(MATHML) |
480 | |