Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "nsMathMLmunderoverFrame.h"
7 : #include "nsPresContext.h"
8 : #include "nsMathMLmmultiscriptsFrame.h"
9 : #include "nsMathMLElement.h"
10 : #include <algorithm>
11 : #include "gfxContext.h"
12 : #include "gfxMathTable.h"
13 :
14 : //
15 : // <munderover> -- attach an underscript-overscript pair to a base - implementation
16 : // <mover> -- attach an overscript to a base - implementation
17 : // <munder> -- attach an underscript to a base - implementation
18 : //
19 :
20 : nsIFrame*
21 0 : NS_NewMathMLmunderoverFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
22 : {
23 0 : return new (aPresShell) nsMathMLmunderoverFrame(aContext);
24 : }
25 :
26 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmunderoverFrame)
27 :
28 0 : nsMathMLmunderoverFrame::~nsMathMLmunderoverFrame()
29 : {
30 0 : }
31 :
32 : nsresult
33 0 : nsMathMLmunderoverFrame::AttributeChanged(int32_t aNameSpaceID,
34 : nsIAtom* aAttribute,
35 : int32_t aModType)
36 : {
37 0 : if (nsGkAtoms::accent_ == aAttribute ||
38 0 : nsGkAtoms::accentunder_ == aAttribute) {
39 : // When we have automatic data to update within ourselves, we ask our
40 : // parent to re-layout its children
41 0 : return ReLayoutChildren(GetParent());
42 : }
43 :
44 : return nsMathMLContainerFrame::
45 0 : AttributeChanged(aNameSpaceID, aAttribute, aModType);
46 : }
47 :
48 : NS_IMETHODIMP
49 0 : nsMathMLmunderoverFrame::UpdatePresentationData(uint32_t aFlagsValues,
50 : uint32_t aFlagsToUpdate)
51 : {
52 0 : nsMathMLContainerFrame::UpdatePresentationData(aFlagsValues, aFlagsToUpdate);
53 : // disable the stretch-all flag if we are going to act like a subscript-superscript pair
54 0 : if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
55 0 : StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) {
56 0 : mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
57 : }
58 : else {
59 0 : mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
60 : }
61 0 : return NS_OK;
62 : }
63 :
64 : NS_IMETHODIMP
65 0 : nsMathMLmunderoverFrame::InheritAutomaticData(nsIFrame* aParent)
66 : {
67 : // let the base class get the default from our parent
68 0 : nsMathMLContainerFrame::InheritAutomaticData(aParent);
69 :
70 0 : mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
71 :
72 0 : return NS_OK;
73 : }
74 :
75 : void
76 0 : nsMathMLmunderoverFrame::DestroyFrom(nsIFrame* aDestroyRoot)
77 : {
78 0 : if (!mPostReflowIncrementScriptLevelCommands.IsEmpty()) {
79 0 : PresContext()->PresShell()->CancelReflowCallback(this);
80 : }
81 0 : nsMathMLContainerFrame::DestroyFrom(aDestroyRoot);
82 0 : }
83 :
84 : uint8_t
85 0 : nsMathMLmunderoverFrame::ScriptIncrement(nsIFrame* aFrame)
86 : {
87 0 : nsIFrame* child = mFrames.FirstChild();
88 0 : if (!aFrame || aFrame == child) {
89 0 : return 0;
90 : }
91 0 : child = child->GetNextSibling();
92 0 : if (aFrame == child) {
93 0 : if (mContent->IsMathMLElement(nsGkAtoms::mover_)) {
94 0 : return mIncrementOver ? 1 : 0;
95 : }
96 0 : return mIncrementUnder ? 1 : 0;
97 : }
98 0 : if (child && aFrame == child->GetNextSibling()) {
99 : // must be a over frame of munderover
100 0 : return mIncrementOver ? 1 : 0;
101 : }
102 0 : return 0; // frame not found
103 : }
104 :
105 : void
106 0 : nsMathMLmunderoverFrame::SetIncrementScriptLevel(uint32_t aChildIndex,
107 : bool aIncrement)
108 : {
109 0 : nsIFrame* child = PrincipalChildList().FrameAt(aChildIndex);
110 0 : if (!child || !child->GetContent()->IsMathMLElement() ||
111 0 : child->GetContent()->GetPrimaryFrame() != child) {
112 0 : return;
113 : }
114 :
115 0 : auto element = static_cast<nsMathMLElement*>(child->GetContent());
116 0 : if (element->GetIncrementScriptLevel() == aIncrement) {
117 0 : return;
118 : }
119 :
120 0 : if (mPostReflowIncrementScriptLevelCommands.IsEmpty()) {
121 0 : PresContext()->PresShell()->PostReflowCallback(this);
122 : }
123 :
124 0 : mPostReflowIncrementScriptLevelCommands.AppendElement(
125 0 : SetIncrementScriptLevelCommand { aChildIndex, aIncrement });
126 : }
127 :
128 : bool
129 0 : nsMathMLmunderoverFrame::ReflowFinished()
130 : {
131 0 : SetPendingPostReflowIncrementScriptLevel();
132 0 : return true;
133 : }
134 :
135 : void
136 0 : nsMathMLmunderoverFrame::ReflowCallbackCanceled()
137 : {
138 : // Do nothing, at this point our work will just be useless.
139 0 : mPostReflowIncrementScriptLevelCommands.Clear();
140 0 : }
141 :
142 : void
143 0 : nsMathMLmunderoverFrame::SetPendingPostReflowIncrementScriptLevel()
144 : {
145 0 : MOZ_ASSERT(!mPostReflowIncrementScriptLevelCommands.IsEmpty());
146 :
147 0 : nsTArray<SetIncrementScriptLevelCommand> commands;
148 0 : commands.SwapElements(mPostReflowIncrementScriptLevelCommands);
149 :
150 0 : for (const auto& command : commands) {
151 0 : nsIFrame* child = PrincipalChildList().FrameAt(command.mChildIndex);
152 0 : if (!child || !child->GetContent()->IsMathMLElement()) {
153 0 : continue;
154 : }
155 :
156 0 : auto element = static_cast<nsMathMLElement*>(child->GetContent());
157 0 : element->SetIncrementScriptLevel(command.mDoIncrement, true);
158 : }
159 0 : }
160 :
161 : NS_IMETHODIMP
162 0 : nsMathMLmunderoverFrame::TransmitAutomaticData()
163 : {
164 : // At this stage, all our children are in sync and we can fully
165 : // resolve our own mEmbellishData struct
166 : //---------------------------------------------------------------------
167 :
168 : /*
169 : The REC says:
170 :
171 : As regards munder (respectively mover) :
172 : The default value of accentunder is false, unless underscript
173 : is an <mo> element or an embellished operator. If underscript is
174 : an <mo> element, the value of its accent attribute is used as the
175 : default value of accentunder. If underscript is an embellished
176 : operator, the accent attribute of the <mo> element at its
177 : core is used as the default value. As with all attributes, an
178 : explicitly given value overrides the default.
179 :
180 : XXX The winner is the outermost setting in conflicting settings like these:
181 : <munder accentunder='true'>
182 : <mi>...</mi>
183 : <mo accentunder='false'> ... </mo>
184 : </munder>
185 :
186 : As regards munderover:
187 : The accent and accentunder attributes have the same effect as
188 : the attributes with the same names on <mover> and <munder>,
189 : respectively. Their default values are also computed in the
190 : same manner as described for those elements, with the default
191 : value of accent depending on overscript and the default value
192 : of accentunder depending on underscript.
193 : */
194 :
195 0 : nsIFrame* overscriptFrame = nullptr;
196 0 : nsIFrame* underscriptFrame = nullptr;
197 0 : nsIFrame* baseFrame = mFrames.FirstChild();
198 :
199 0 : if (baseFrame) {
200 0 : if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_,
201 : nsGkAtoms::munderover_)) {
202 0 : underscriptFrame = baseFrame->GetNextSibling();
203 : } else {
204 0 : NS_ASSERTION(mContent->IsMathMLElement(nsGkAtoms::mover_),
205 : "mContent->NodeInfo()->NameAtom() not recognized");
206 0 : overscriptFrame = baseFrame->GetNextSibling();
207 : }
208 : }
209 0 : if (underscriptFrame &&
210 0 : mContent->IsMathMLElement(nsGkAtoms::munderover_)) {
211 0 : overscriptFrame = underscriptFrame->GetNextSibling();
212 :
213 : }
214 :
215 : // if our base is an embellished operator, let its state bubble to us (in particular,
216 : // this is where we get the flag for NS_MATHML_EMBELLISH_MOVABLELIMITS). Our flags
217 : // are reset to the default values of false if the base frame isn't embellished.
218 0 : mPresentationData.baseFrame = baseFrame;
219 0 : GetEmbellishDataFrom(baseFrame, mEmbellishData);
220 :
221 : // The default value of accentunder is false, unless the underscript is embellished
222 : // and its core <mo> is an accent
223 0 : nsEmbellishData embellishData;
224 0 : nsAutoString value;
225 0 : if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_,
226 : nsGkAtoms::munderover_)) {
227 0 : GetEmbellishDataFrom(underscriptFrame, embellishData);
228 0 : if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) {
229 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER;
230 : } else {
231 0 : mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER;
232 : }
233 :
234 : // if we have an accentunder attribute, it overrides what the underscript said
235 0 : if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accentunder_, value)) {
236 0 : if (value.EqualsLiteral("true")) {
237 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER;
238 0 : } else if (value.EqualsLiteral("false")) {
239 0 : mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER;
240 : }
241 : }
242 : }
243 :
244 : // The default value of accent is false, unless the overscript is embellished
245 : // and its core <mo> is an accent
246 0 : if (mContent->IsAnyOfMathMLElements(nsGkAtoms::mover_,
247 : nsGkAtoms::munderover_)) {
248 0 : GetEmbellishDataFrom(overscriptFrame, embellishData);
249 0 : if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) {
250 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER;
251 : } else {
252 0 : mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER;
253 : }
254 :
255 : // if we have an accent attribute, it overrides what the overscript said
256 0 : if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accent_, value)) {
257 0 : if (value.EqualsLiteral("true")) {
258 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER;
259 0 : } else if (value.EqualsLiteral("false")) {
260 0 : mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER;
261 : }
262 : }
263 : }
264 :
265 : bool subsupDisplay =
266 0 : NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
267 0 : StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE;
268 :
269 : // disable the stretch-all flag if we are going to act like a superscript
270 0 : if (subsupDisplay) {
271 0 : mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
272 : }
273 :
274 : // Now transmit any change that we want to our children so that they
275 : // can update their mPresentationData structs
276 : //---------------------------------------------------------------------
277 :
278 : /* The REC says:
279 : Within underscript, <munderover> always sets displaystyle to "false",
280 : but increments scriptlevel by 1 only when accentunder is "false".
281 :
282 : Within overscript, <munderover> always sets displaystyle to "false",
283 : but increments scriptlevel by 1 only when accent is "false".
284 :
285 : Within subscript and superscript it increments scriptlevel by 1, and
286 : sets displaystyle to "false", but leaves both attributes unchanged within
287 : base.
288 :
289 : The TeXBook treats 'over' like a superscript, so p.141 or Rule 13a
290 : say it shouldn't be compressed. However, The TeXBook says
291 : that math accents and \overline change uncramped styles to their
292 : cramped counterparts.
293 : */
294 0 : if (mContent->IsAnyOfMathMLElements(nsGkAtoms::mover_,
295 : nsGkAtoms::munderover_)) {
296 0 : uint32_t compress = NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)
297 0 : ? NS_MATHML_COMPRESSED : 0;
298 0 : mIncrementOver =
299 0 : !NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) ||
300 : subsupDisplay;
301 0 : SetIncrementScriptLevel(
302 0 : mContent->IsMathMLElement(nsGkAtoms::mover_) ? 1 : 2, mIncrementOver);
303 0 : if (mIncrementOver) {
304 : PropagateFrameFlagFor(overscriptFrame,
305 0 : NS_FRAME_MATHML_SCRIPT_DESCENDANT);
306 : }
307 0 : PropagatePresentationDataFor(overscriptFrame, compress, compress);
308 : }
309 : /*
310 : The TeXBook treats 'under' like a subscript, so p.141 or Rule 13a
311 : say it should be compressed
312 : */
313 0 : if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_,
314 : nsGkAtoms::munderover_)) {
315 0 : mIncrementUnder =
316 0 : !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags) ||
317 : subsupDisplay;
318 0 : SetIncrementScriptLevel(1, mIncrementUnder);
319 0 : if (mIncrementUnder) {
320 : PropagateFrameFlagFor(underscriptFrame,
321 0 : NS_FRAME_MATHML_SCRIPT_DESCENDANT);
322 : }
323 : PropagatePresentationDataFor(underscriptFrame,
324 : NS_MATHML_COMPRESSED,
325 0 : NS_MATHML_COMPRESSED);
326 : }
327 :
328 : /* Set flags for dtls font feature settings.
329 :
330 : dtls
331 : Dotless Forms
332 : This feature provides dotless forms for Math Alphanumeric
333 : characters, such as U+1D422 MATHEMATICAL BOLD SMALL I,
334 : U+1D423 MATHEMATICAL BOLD SMALL J, U+1D456
335 : U+MATHEMATICAL ITALIC SMALL I, U+1D457 MATHEMATICAL ITALIC
336 : SMALL J, and so on.
337 : The dotless forms are to be used as base forms for placing
338 : mathematical accents over them.
339 :
340 : To opt out of this change, add the following to the stylesheet:
341 : "font-feature-settings: 'dtls' 0"
342 : */
343 0 : if (overscriptFrame &&
344 0 : NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) &&
345 0 : !NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags)) {
346 0 : PropagatePresentationDataFor(baseFrame, NS_MATHML_DTLS, NS_MATHML_DTLS);
347 : }
348 :
349 0 : return NS_OK;
350 : }
351 :
352 : /*
353 : The REC says:
354 : * If the base is an operator with movablelimits="true" (or an embellished
355 : operator whose <mo> element core has movablelimits="true"), and
356 : displaystyle="false", then underscript and overscript are drawn in
357 : a subscript and superscript position, respectively. In this case,
358 : the accent and accentunder attributes are ignored. This is often
359 : used for limits on symbols such as ∑.
360 :
361 : i.e.,:
362 : if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishDataflags) &&
363 : StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) {
364 : // place like subscript-superscript pair
365 : }
366 : else {
367 : // place like underscript-overscript pair
368 : }
369 : */
370 :
371 : /* virtual */ nsresult
372 0 : nsMathMLmunderoverFrame::Place(DrawTarget* aDrawTarget,
373 : bool aPlaceOrigin,
374 : ReflowOutput& aDesiredSize)
375 : {
376 0 : float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
377 0 : if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
378 0 : StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) {
379 : //place like sub sup or subsup
380 0 : if (mContent->IsMathMLElement(nsGkAtoms::munderover_)) {
381 0 : return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(),
382 : aDrawTarget,
383 : aPlaceOrigin,
384 : aDesiredSize,
385 : this, 0, 0,
386 0 : fontSizeInflation);
387 0 : } else if (mContent->IsMathMLElement( nsGkAtoms::munder_)) {
388 0 : return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(),
389 : aDrawTarget,
390 : aPlaceOrigin,
391 : aDesiredSize,
392 : this, 0, 0,
393 0 : fontSizeInflation);
394 : } else {
395 0 : NS_ASSERTION(mContent->IsMathMLElement(nsGkAtoms::mover_),
396 : "mContent->NodeInfo()->NameAtom() not recognized");
397 0 : return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(),
398 : aDrawTarget,
399 : aPlaceOrigin,
400 : aDesiredSize,
401 : this, 0, 0,
402 0 : fontSizeInflation);
403 : }
404 :
405 : }
406 :
407 : ////////////////////////////////////
408 : // Get the children's desired sizes
409 :
410 0 : nsBoundingMetrics bmBase, bmUnder, bmOver;
411 0 : ReflowOutput baseSize(aDesiredSize.GetWritingMode());
412 0 : ReflowOutput underSize(aDesiredSize.GetWritingMode());
413 0 : ReflowOutput overSize(aDesiredSize.GetWritingMode());
414 0 : nsIFrame* overFrame = nullptr;
415 0 : nsIFrame* underFrame = nullptr;
416 0 : nsIFrame* baseFrame = mFrames.FirstChild();
417 0 : underSize.SetBlockStartAscent(0);
418 0 : overSize.SetBlockStartAscent(0);
419 0 : bool haveError = false;
420 0 : if (baseFrame) {
421 0 : if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_,
422 : nsGkAtoms::munderover_)) {
423 0 : underFrame = baseFrame->GetNextSibling();
424 0 : } else if (mContent->IsMathMLElement(nsGkAtoms::mover_)) {
425 0 : overFrame = baseFrame->GetNextSibling();
426 : }
427 : }
428 0 : if (underFrame && mContent->IsMathMLElement(nsGkAtoms::munderover_)) {
429 0 : overFrame = underFrame->GetNextSibling();
430 : }
431 :
432 0 : if (mContent->IsMathMLElement(nsGkAtoms::munder_)) {
433 0 : if (!baseFrame || !underFrame || underFrame->GetNextSibling()) {
434 : // report an error, encourage people to get their markups in order
435 0 : haveError = true;
436 : }
437 : }
438 0 : if (mContent->IsMathMLElement(nsGkAtoms::mover_)) {
439 0 : if (!baseFrame || !overFrame || overFrame->GetNextSibling()) {
440 : // report an error, encourage people to get their markups in order
441 0 : haveError = true;
442 : }
443 : }
444 0 : if (mContent->IsMathMLElement(nsGkAtoms::munderover_)) {
445 0 : if (!baseFrame || !underFrame || !overFrame || overFrame->GetNextSibling()) {
446 : // report an error, encourage people to get their markups in order
447 0 : haveError = true;
448 : }
449 : }
450 0 : if (haveError) {
451 0 : if (aPlaceOrigin) {
452 0 : ReportChildCountError();
453 : }
454 0 : return ReflowError(aDrawTarget, aDesiredSize);
455 : }
456 0 : GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
457 0 : if (underFrame) {
458 0 : GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder);
459 : }
460 0 : if (overFrame) {
461 0 : GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver);
462 : }
463 :
464 0 : nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
465 :
466 : ////////////////////
467 : // Place Children
468 :
469 : RefPtr<nsFontMetrics> fm =
470 0 : nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
471 :
472 0 : nscoord xHeight = fm->XHeight();
473 0 : nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
474 0 : gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
475 :
476 : nscoord ruleThickness;
477 0 : GetRuleThickness (aDrawTarget, fm, ruleThickness);
478 :
479 0 : nscoord correction = 0;
480 0 : GetItalicCorrection (bmBase, correction);
481 :
482 : // there are 2 different types of placement depending on
483 : // whether we want an accented under or not
484 :
485 0 : nscoord underDelta1 = 0; // gap between base and underscript
486 0 : nscoord underDelta2 = 0; // extra space beneath underscript
487 :
488 0 : if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {
489 : // Rule 13a, App. G, TeXbook
490 : nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy;
491 0 : GetBigOpSpacings (fm,
492 : dummy, bigOpSpacing2,
493 : dummy, bigOpSpacing4,
494 0 : bigOpSpacing5);
495 0 : if (mathFont) {
496 : // XXXfredw The Open Type MATH table has some StretchStack* parameters
497 : // that we may use when the base is a stretchy horizontal operator. See
498 : // bug 963131.
499 0 : bigOpSpacing2 =
500 0 : mathFont->MathTable()->Constant(gfxMathTable::LowerLimitGapMin,
501 : oneDevPixel);
502 0 : bigOpSpacing4 =
503 0 : mathFont->MathTable()->Constant(gfxMathTable::LowerLimitBaselineDropMin,
504 : oneDevPixel);
505 0 : bigOpSpacing5 = 0;
506 : }
507 0 : underDelta1 = std::max(bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent));
508 0 : underDelta2 = bigOpSpacing5;
509 : }
510 : else {
511 : // No corresponding rule in TeXbook - we are on our own here
512 : // XXX tune the gap delta between base and underscript
513 : // XXX Should we use Rule 10 like \underline does?
514 : // XXXfredw Perhaps use the Underbar* parameters of the MATH table. See
515 : // bug 963125.
516 0 : underDelta1 = ruleThickness + onePixel/2;
517 0 : underDelta2 = ruleThickness;
518 : }
519 : // empty under?
520 0 : if (!(bmUnder.ascent + bmUnder.descent)) {
521 0 : underDelta1 = 0;
522 0 : underDelta2 = 0;
523 : }
524 :
525 0 : nscoord overDelta1 = 0; // gap between base and overscript
526 0 : nscoord overDelta2 = 0; // extra space above overscript
527 :
528 0 : if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {
529 : // Rule 13a, App. G, TeXbook
530 : // XXXfredw The Open Type MATH table has some StretchStack* parameters
531 : // that we may use when the base is a stretchy horizontal operator. See
532 : // bug 963131.
533 : nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy;
534 0 : GetBigOpSpacings (fm,
535 : bigOpSpacing1, dummy,
536 : bigOpSpacing3, dummy,
537 0 : bigOpSpacing5);
538 0 : if (mathFont) {
539 : // XXXfredw The Open Type MATH table has some StretchStack* parameters
540 : // that we may use when the base is a stretchy horizontal operator. See
541 : // bug 963131.
542 0 : bigOpSpacing1 =
543 0 : mathFont->MathTable()->Constant(gfxMathTable::UpperLimitGapMin,
544 : oneDevPixel);
545 0 : bigOpSpacing3 =
546 0 : mathFont->MathTable()->Constant(gfxMathTable::UpperLimitBaselineRiseMin,
547 : oneDevPixel);
548 0 : bigOpSpacing5 = 0;
549 : }
550 0 : overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - bmOver.descent));
551 0 : overDelta2 = bigOpSpacing5;
552 :
553 : // XXX This is not a TeX rule...
554 : // delta1 (as computed abvove) can become really big when bmOver.descent is
555 : // negative, e.g., if the content is &OverBar. In such case, we use the height
556 0 : if (bmOver.descent < 0)
557 0 : overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - (bmOver.ascent + bmOver.descent)));
558 : }
559 : else {
560 : // Rule 12, App. G, TeXbook
561 : // We are going to modify this rule to make it more general.
562 : // The idea behind Rule 12 in the TeXBook is to keep the accent
563 : // as close to the base as possible, while ensuring that the
564 : // distance between the *baseline* of the accent char and
565 : // the *baseline* of the base is atleast x-height.
566 : // The idea is that for normal use, we would like all the accents
567 : // on a line to line up atleast x-height above the baseline
568 : // if possible.
569 : // When the ascent of the base is >= x-height,
570 : // the baseline of the accent char is placed just above the base
571 : // (specifically, the baseline of the accent char is placed
572 : // above the baseline of the base by the ascent of the base).
573 : // For ease of implementation,
574 : // this assumes that the font-designer designs accents
575 : // in such a way that the bottom of the accent is atleast x-height
576 : // above its baseline, otherwise there will be collisions
577 : // with the base. Also there should be proper padding between
578 : // the bottom of the accent char and its baseline.
579 : // The above rule may not be obvious from a first
580 : // reading of rule 12 in the TeXBook !!!
581 : // The mathml <mover> tag can use accent chars that
582 : // do not follow this convention. So we modify TeX's rule
583 : // so that TeX's rule gets subsumed for accents that follow
584 : // TeX's convention,
585 : // while also allowing accents that do not follow the convention :
586 : // we try to keep the *bottom* of the accent char atleast x-height
587 : // from the baseline of the base char. we also slap on an extra
588 : // padding between the accent and base chars.
589 0 : overDelta1 = ruleThickness + onePixel/2;
590 0 : nscoord accentBaseHeight = xHeight;
591 0 : if (mathFont) {
592 : accentBaseHeight =
593 0 : mathFont->MathTable()->Constant(gfxMathTable::AccentBaseHeight,
594 0 : oneDevPixel);
595 : }
596 0 : if (bmBase.ascent < accentBaseHeight) {
597 : // also ensure at least accentBaseHeight above the baseline of the base
598 0 : overDelta1 += accentBaseHeight - bmBase.ascent;
599 : }
600 0 : overDelta2 = ruleThickness;
601 : }
602 : // empty over?
603 0 : if (!(bmOver.ascent + bmOver.descent)) {
604 0 : overDelta1 = 0;
605 0 : overDelta2 = 0;
606 : }
607 :
608 0 : nscoord dxBase = 0, dxOver = 0, dxUnder = 0;
609 0 : nsAutoString valueAlign;
610 : enum {
611 : center,
612 : left,
613 : right
614 0 : } alignPosition = center;
615 :
616 0 : if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, valueAlign)) {
617 0 : if (valueAlign.EqualsLiteral("left")) {
618 0 : alignPosition = left;
619 0 : } else if (valueAlign.EqualsLiteral("right")) {
620 0 : alignPosition = right;
621 : }
622 : }
623 :
624 : //////////
625 : // pass 1, do what <mover> does: attach the overscript on the base
626 :
627 : // Ad-hoc - This is to override fonts which have ready-made _accent_
628 : // glyphs with negative lbearing and rbearing. We want to position
629 : // the overscript ourselves
630 0 : nscoord overWidth = bmOver.width;
631 0 : if (!overWidth && (bmOver.rightBearing - bmOver.leftBearing > 0)) {
632 0 : overWidth = bmOver.rightBearing - bmOver.leftBearing;
633 0 : dxOver = -bmOver.leftBearing;
634 : }
635 :
636 0 : if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {
637 0 : mBoundingMetrics.width = bmBase.width;
638 0 : if (alignPosition == center) {
639 0 : dxOver += correction;
640 : }
641 : }
642 : else {
643 0 : mBoundingMetrics.width = std::max(bmBase.width, overWidth);
644 0 : if (alignPosition == center) {
645 0 : dxOver += correction/2;
646 : }
647 : }
648 :
649 0 : if (alignPosition == center) {
650 0 : dxOver += (mBoundingMetrics.width - overWidth)/2;
651 0 : dxBase = (mBoundingMetrics.width - bmBase.width)/2;
652 0 : } else if (alignPosition == right) {
653 0 : dxOver += mBoundingMetrics.width - overWidth;
654 0 : dxBase = mBoundingMetrics.width - bmBase.width;
655 : }
656 :
657 0 : mBoundingMetrics.ascent =
658 0 : bmBase.ascent + overDelta1 + bmOver.ascent + bmOver.descent;
659 0 : mBoundingMetrics.descent = bmBase.descent;
660 0 : mBoundingMetrics.leftBearing =
661 0 : std::min(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing);
662 0 : mBoundingMetrics.rightBearing =
663 0 : std::max(dxBase + bmBase.rightBearing, dxOver + bmOver.rightBearing);
664 :
665 : //////////
666 : // pass 2, do what <munder> does: attach the underscript on the previous
667 : // result. We conceptually view the previous result as an "anynomous base"
668 : // from where to attach the underscript. Hence if the underscript is empty,
669 : // we should end up like <mover>. If the overscript is empty, we should
670 : // end up like <munder>.
671 :
672 0 : nsBoundingMetrics bmAnonymousBase = mBoundingMetrics;
673 : nscoord ascentAnonymousBase =
674 0 : std::max(mBoundingMetrics.ascent + overDelta2,
675 0 : overSize.BlockStartAscent() + bmOver.descent +
676 0 : overDelta1 + bmBase.ascent);
677 0 : ascentAnonymousBase = std::max(ascentAnonymousBase,
678 0 : baseSize.BlockStartAscent());
679 :
680 : // Width of non-spacing marks is zero so use left and right bearing.
681 0 : nscoord underWidth = bmUnder.width;
682 0 : if (!underWidth) {
683 0 : underWidth = bmUnder.rightBearing - bmUnder.leftBearing;
684 0 : dxUnder = -bmUnder.leftBearing;
685 : }
686 :
687 0 : nscoord maxWidth = std::max(bmAnonymousBase.width, underWidth);
688 0 : if (alignPosition == center &&
689 0 : !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {
690 0 : GetItalicCorrection(bmAnonymousBase, correction);
691 0 : dxUnder += -correction/2;
692 : }
693 0 : nscoord dxAnonymousBase = 0;
694 0 : if (alignPosition == center) {
695 0 : dxUnder += (maxWidth - underWidth)/2;
696 0 : dxAnonymousBase = (maxWidth - bmAnonymousBase.width)/2;
697 0 : } else if (alignPosition == right) {
698 0 : dxUnder += maxWidth - underWidth;
699 0 : dxAnonymousBase = maxWidth - bmAnonymousBase.width;
700 : }
701 :
702 : // adjust the offsets of the real base and overscript since their
703 : // final offsets should be relative to us...
704 0 : dxOver += dxAnonymousBase;
705 0 : dxBase += dxAnonymousBase;
706 :
707 0 : mBoundingMetrics.width =
708 0 : std::max(dxAnonymousBase + bmAnonymousBase.width, dxUnder + bmUnder.width);
709 : // At this point, mBoundingMetrics.ascent = bmAnonymousBase.ascent
710 0 : mBoundingMetrics.descent =
711 0 : bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + bmUnder.descent;
712 0 : mBoundingMetrics.leftBearing =
713 0 : std::min(dxAnonymousBase + bmAnonymousBase.leftBearing, dxUnder + bmUnder.leftBearing);
714 0 : mBoundingMetrics.rightBearing =
715 0 : std::max(dxAnonymousBase + bmAnonymousBase.rightBearing, dxUnder + bmUnder.rightBearing);
716 :
717 0 : aDesiredSize.SetBlockStartAscent(ascentAnonymousBase);
718 0 : aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
719 0 : std::max(mBoundingMetrics.descent + underDelta2,
720 0 : bmAnonymousBase.descent + underDelta1 + bmUnder.ascent +
721 0 : underSize.Height() - underSize.BlockStartAscent());
722 0 : aDesiredSize.Height() = std::max(aDesiredSize.Height(),
723 0 : aDesiredSize.BlockStartAscent() +
724 0 : baseSize.Height() - baseSize.BlockStartAscent());
725 0 : aDesiredSize.Width() = mBoundingMetrics.width;
726 0 : aDesiredSize.mBoundingMetrics = mBoundingMetrics;
727 :
728 0 : mReference.x = 0;
729 0 : mReference.y = aDesiredSize.BlockStartAscent();
730 :
731 0 : if (aPlaceOrigin) {
732 : nscoord dy;
733 : // place overscript
734 0 : if (overFrame) {
735 0 : dy = aDesiredSize.BlockStartAscent() -
736 0 : mBoundingMetrics.ascent + bmOver.ascent -
737 0 : overSize.BlockStartAscent();
738 0 : FinishReflowChild (overFrame, PresContext(), overSize, nullptr, dxOver, dy, 0);
739 : }
740 : // place base
741 0 : dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
742 0 : FinishReflowChild (baseFrame, PresContext(), baseSize, nullptr, dxBase, dy, 0);
743 : // place underscript
744 0 : if (underFrame) {
745 0 : dy = aDesiredSize.BlockStartAscent() +
746 0 : mBoundingMetrics.descent - bmUnder.descent -
747 0 : underSize.BlockStartAscent();
748 0 : FinishReflowChild (underFrame, PresContext(), underSize, nullptr,
749 0 : dxUnder, dy, 0);
750 : }
751 : }
752 0 : return NS_OK;
753 : }
|