1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla MathML Project.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * The University Of Queensland.
19 : * Portions created by the Initial Developer are Copyright (C) 1999
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Roger B. Sidje <rbs@maths.uq.edu.au>
24 : * David J. Fiddes <D.J.Fiddes@hw.ac.uk>
25 : * Shyjan Mahamud <mahamud@cs.cmu.edu>
26 : * Pierre Phaneuf <pp@ludusdesign.com>
27 : * Jonathan Hage <hage.jonathan@gmail.com>
28 : * Frederic Wang <fred.wang@free.fr>
29 : *
30 : * Alternatively, the contents of this file may be used under the terms of
31 : * either of the GNU General Public License Version 2 or later (the "GPL"),
32 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 : * in which case the provisions of the GPL or the LGPL are applicable instead
34 : * of those above. If you wish to allow use of your version of this file only
35 : * under the terms of either the GPL or the LGPL, and not to allow others to
36 : * use your version of this file under the terms of the MPL, indicate your
37 : * decision by deleting the provisions above and replace them with the notice
38 : * and other provisions required by the GPL or the LGPL. If you do not delete
39 : * the provisions above, a recipient may use your version of this file under
40 : * the terms of any one of the MPL, the GPL or the LGPL.
41 : *
42 : * ***** END LICENSE BLOCK ***** */
43 :
44 :
45 : #include "nsCOMPtr.h"
46 : #include "nsFrame.h"
47 : #include "nsPresContext.h"
48 : #include "nsStyleContext.h"
49 : #include "nsStyleConsts.h"
50 : #include "nsINameSpaceManager.h"
51 : #include "nsRenderingContext.h"
52 :
53 : #include "nsMathMLmunderoverFrame.h"
54 : #include "nsMathMLmsubsupFrame.h"
55 : #include "nsMathMLmsupFrame.h"
56 : #include "nsMathMLmsubFrame.h"
57 :
58 : //
59 : // <munderover> -- attach an underscript-overscript pair to a base - implementation
60 : // <mover> -- attach an overscript to a base - implementation
61 : // <munder> -- attach an underscript to a base - implementation
62 : //
63 :
64 : nsIFrame*
65 0 : NS_NewMathMLmunderoverFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
66 : {
67 0 : return new (aPresShell) nsMathMLmunderoverFrame(aContext);
68 : }
69 :
70 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmunderoverFrame)
71 :
72 0 : nsMathMLmunderoverFrame::~nsMathMLmunderoverFrame()
73 : {
74 0 : }
75 :
76 : NS_IMETHODIMP
77 0 : nsMathMLmunderoverFrame::AttributeChanged(PRInt32 aNameSpaceID,
78 : nsIAtom* aAttribute,
79 : PRInt32 aModType)
80 : {
81 0 : if (nsGkAtoms::accent_ == aAttribute ||
82 : nsGkAtoms::accentunder_ == aAttribute) {
83 : // When we have automatic data to update within ourselves, we ask our
84 : // parent to re-layout its children
85 0 : return ReLayoutChildren(mParent);
86 : }
87 :
88 : return nsMathMLContainerFrame::
89 0 : AttributeChanged(aNameSpaceID, aAttribute, aModType);
90 : }
91 :
92 : NS_IMETHODIMP
93 0 : nsMathMLmunderoverFrame::UpdatePresentationData(PRUint32 aFlagsValues,
94 : PRUint32 aFlagsToUpdate)
95 : {
96 0 : nsMathMLContainerFrame::UpdatePresentationData(aFlagsValues, aFlagsToUpdate);
97 : // disable the stretch-all flag if we are going to act like a subscript-superscript pair
98 0 : if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
99 0 : !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) {
100 0 : mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
101 : }
102 : else {
103 0 : mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
104 : }
105 0 : return NS_OK;
106 : }
107 :
108 : NS_IMETHODIMP
109 0 : nsMathMLmunderoverFrame::UpdatePresentationDataFromChildAt(PRInt32 aFirstIndex,
110 : PRInt32 aLastIndex,
111 : PRUint32 aFlagsValues,
112 : PRUint32 aFlagsToUpdate)
113 : {
114 : // munderover is special... The REC says:
115 : // Within underscript, <munder> always sets displaystyle to "false",
116 : // but increments scriptlevel by 1 only when accentunder is "false".
117 : // Within underscript, <munderover> always sets displaystyle to "false",
118 : // but increments scriptlevel by 1 only when accentunder is "false".
119 : // This means that
120 : // 1. don't allow displaystyle to change in the underscript & overscript
121 : // 2a if the value of the accent is changed, we need to recompute the
122 : // scriptlevel of the underscript. The problem is that the accent
123 : // can change in the <mo> deep down the embellished hierarchy
124 : // 2b if the value of the accent is changed, we need to recompute the
125 : // scriptlevel of the overscript. The problem is that the accent
126 : // can change in the <mo> deep down the embellished hierarchy
127 :
128 : // Do #1 here, prevent displaystyle to be changed in the underscript & overscript
129 0 : PRInt32 index = 0;
130 0 : nsIFrame* childFrame = mFrames.FirstChild();
131 0 : while (childFrame) {
132 0 : if ((index >= aFirstIndex) &&
133 : ((aLastIndex <= 0) || ((aLastIndex > 0) && (index <= aLastIndex)))) {
134 0 : if (index > 0) {
135 : // disable the flag
136 0 : aFlagsToUpdate &= ~NS_MATHML_DISPLAYSTYLE;
137 0 : aFlagsValues &= ~NS_MATHML_DISPLAYSTYLE;
138 : }
139 0 : PropagatePresentationDataFor(childFrame, aFlagsValues, aFlagsToUpdate);
140 : }
141 0 : index++;
142 0 : childFrame = childFrame->GetNextSibling();
143 : }
144 0 : return NS_OK;
145 :
146 : // For #2, changing the accent attribute will trigger a re-build of
147 : // all automatic data in the embellished hierarchy
148 : }
149 :
150 : NS_IMETHODIMP
151 0 : nsMathMLmunderoverFrame::InheritAutomaticData(nsIFrame* aParent)
152 : {
153 : // let the base class get the default from our parent
154 0 : nsMathMLContainerFrame::InheritAutomaticData(aParent);
155 :
156 0 : mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
157 :
158 0 : return NS_OK;
159 : }
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 = nsnull;
196 0 : nsIFrame* underscriptFrame = nsnull;
197 0 : nsIFrame* baseFrame = mFrames.FirstChild();
198 0 : nsIAtom* tag = mContent->Tag();
199 :
200 0 : if (baseFrame) {
201 0 : if (tag == nsGkAtoms::munder_ ||
202 : tag == nsGkAtoms::munderover_) {
203 0 : underscriptFrame = baseFrame->GetNextSibling();
204 : } else {
205 0 : NS_ASSERTION(tag == nsGkAtoms::mover_, "mContent->Tag() not recognized");
206 0 : overscriptFrame = baseFrame->GetNextSibling();
207 : }
208 : }
209 0 : if (underscriptFrame &&
210 : tag == 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 (tag == nsGkAtoms::munder_ ||
226 : tag == 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 (GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::accentunder_,
236 0 : value)) {
237 0 : if (value.EqualsLiteral("true")) {
238 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER;
239 0 : } else if (value.EqualsLiteral("false")) {
240 0 : mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER;
241 : }
242 : }
243 : }
244 :
245 : // The default value of accent is false, unless the overscript is embellished
246 : // and its core <mo> is an accent
247 0 : if (tag == nsGkAtoms::mover_ ||
248 : tag == nsGkAtoms::munderover_) {
249 0 : GetEmbellishDataFrom(overscriptFrame, embellishData);
250 0 : if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) {
251 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER;
252 : } else {
253 0 : mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER;
254 : }
255 :
256 : // if we have an accent attribute, it overrides what the overscript said
257 0 : if (GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::accent_,
258 0 : value)) {
259 0 : if (value.EqualsLiteral("true")) {
260 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER;
261 0 : } else if (value.EqualsLiteral("false")) {
262 0 : mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER;
263 : }
264 : }
265 : }
266 :
267 : bool subsupDisplay =
268 : NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
269 0 : !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags);
270 :
271 : // disable the stretch-all flag if we are going to act like a superscript
272 0 : if (subsupDisplay) {
273 0 : mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
274 : }
275 :
276 : // Now transmit any change that we want to our children so that they
277 : // can update their mPresentationData structs
278 : //---------------------------------------------------------------------
279 :
280 : /* The REC says:
281 : Within underscript, <munderover> always sets displaystyle to "false",
282 : but increments scriptlevel by 1 only when accentunder is "false".
283 :
284 : Within overscript, <munderover> always sets displaystyle to "false",
285 : but increments scriptlevel by 1 only when accent is "false".
286 :
287 : Within subscript and superscript it increments scriptlevel by 1, and
288 : sets displaystyle to "false", but leaves both attributes unchanged within
289 : base.
290 :
291 : The TeXBook treats 'over' like a superscript, so p.141 or Rule 13a
292 : say it shouldn't be compressed. However, The TeXBook says
293 : that math accents and \overline change uncramped styles to their
294 : cramped counterparts.
295 : */
296 0 : if (tag == nsGkAtoms::mover_ ||
297 : tag == nsGkAtoms::munderover_) {
298 : PRUint32 compress = NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)
299 0 : ? NS_MATHML_COMPRESSED : 0;
300 : SetIncrementScriptLevel(tag == nsGkAtoms::mover_ ? 1 : 2,
301 0 : !NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) || subsupDisplay);
302 : PropagatePresentationDataFor(overscriptFrame,
303 : ~NS_MATHML_DISPLAYSTYLE | compress,
304 0 : NS_MATHML_DISPLAYSTYLE | compress);
305 : }
306 : /*
307 : The TeXBook treats 'under' like a subscript, so p.141 or Rule 13a
308 : say it should be compressed
309 : */
310 0 : if (tag == nsGkAtoms::munder_ ||
311 : tag == nsGkAtoms::munderover_) {
312 0 : SetIncrementScriptLevel(1, !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags) || subsupDisplay);
313 : PropagatePresentationDataFor(underscriptFrame,
314 : ~NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED,
315 0 : NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED);
316 : }
317 0 : return NS_OK;
318 : }
319 :
320 : /*
321 : The REC says:
322 : * If the base is an operator with movablelimits="true" (or an embellished
323 : operator whose <mo> element core has movablelimits="true"), and
324 : displaystyle="false", then underscript and overscript are drawn in
325 : a subscript and superscript position, respectively. In this case,
326 : the accent and accentunder attributes are ignored. This is often
327 : used for limits on symbols such as ∑.
328 :
329 : i.e.,:
330 : if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishDataflags) &&
331 : !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) {
332 : // place like subscript-superscript pair
333 : }
334 : else {
335 : // place like underscript-overscript pair
336 : }
337 : */
338 :
339 : /* virtual */ nsresult
340 0 : nsMathMLmunderoverFrame::Place(nsRenderingContext& aRenderingContext,
341 : bool aPlaceOrigin,
342 : nsHTMLReflowMetrics& aDesiredSize)
343 : {
344 0 : nsIAtom* tag = mContent->Tag();
345 0 : if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
346 0 : !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) {
347 : //place like sub sup or subsup
348 0 : nscoord scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f);
349 0 : if (tag == nsGkAtoms::munderover_) {
350 : return nsMathMLmsubsupFrame::PlaceSubSupScript(PresContext(),
351 : aRenderingContext,
352 : aPlaceOrigin,
353 : aDesiredSize,
354 : this, 0, 0,
355 0 : scriptSpace);
356 0 : } else if (tag == nsGkAtoms::munder_) {
357 : return nsMathMLmsubFrame::PlaceSubScript(PresContext(),
358 : aRenderingContext,
359 : aPlaceOrigin,
360 : aDesiredSize,
361 : this, 0,
362 0 : scriptSpace);
363 : } else {
364 0 : NS_ASSERTION(tag == nsGkAtoms::mover_, "mContent->Tag() not recognized");
365 : return nsMathMLmsupFrame::PlaceSuperScript(PresContext(),
366 : aRenderingContext,
367 : aPlaceOrigin,
368 : aDesiredSize,
369 : this, 0,
370 0 : scriptSpace);
371 : }
372 :
373 : }
374 :
375 : ////////////////////////////////////
376 : // Get the children's desired sizes
377 :
378 0 : nsBoundingMetrics bmBase, bmUnder, bmOver;
379 0 : nsHTMLReflowMetrics baseSize;
380 0 : nsHTMLReflowMetrics underSize;
381 0 : nsHTMLReflowMetrics overSize;
382 0 : nsIFrame* overFrame = nsnull;
383 0 : nsIFrame* underFrame = nsnull;
384 0 : nsIFrame* baseFrame = mFrames.FirstChild();
385 0 : underSize.ascent = 0;
386 0 : overSize.ascent = 0;
387 0 : if (baseFrame) {
388 0 : if (tag == nsGkAtoms::munder_ ||
389 : tag == nsGkAtoms::munderover_) {
390 0 : underFrame = baseFrame->GetNextSibling();
391 0 : } else if (tag == nsGkAtoms::mover_) {
392 0 : overFrame = baseFrame->GetNextSibling();
393 : }
394 : }
395 0 : if (underFrame && tag == nsGkAtoms::munderover_) {
396 0 : overFrame = underFrame->GetNextSibling();
397 : }
398 :
399 0 : if (tag == nsGkAtoms::munder_) {
400 0 : if (!baseFrame || !underFrame || underFrame->GetNextSibling()) {
401 : // report an error, encourage people to get their markups in order
402 0 : return ReflowError(aRenderingContext, aDesiredSize);
403 : }
404 : }
405 0 : if (tag == nsGkAtoms::mover_) {
406 0 : if (!baseFrame || !overFrame || overFrame->GetNextSibling()) {
407 : // report an error, encourage people to get their markups in order
408 0 : return ReflowError(aRenderingContext, aDesiredSize);
409 : }
410 : }
411 0 : if (tag == nsGkAtoms::munderover_) {
412 0 : if (!baseFrame || !underFrame || !overFrame || overFrame->GetNextSibling()) {
413 : // report an error, encourage people to get their markups in order
414 0 : return ReflowError(aRenderingContext, aDesiredSize);
415 : }
416 : }
417 0 : GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
418 0 : if (underFrame) {
419 0 : GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder);
420 : }
421 0 : if (overFrame) {
422 0 : GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver);
423 : }
424 :
425 0 : nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
426 :
427 : ////////////////////
428 : // Place Children
429 :
430 0 : nsRefPtr<nsFontMetrics> fm;
431 0 : nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
432 0 : aRenderingContext.SetFont(fm);
433 :
434 0 : nscoord xHeight = fm->XHeight();
435 :
436 : nscoord ruleThickness;
437 0 : GetRuleThickness (aRenderingContext, fm, ruleThickness);
438 :
439 0 : nscoord correction = 0;
440 0 : GetItalicCorrection (bmBase, correction);
441 :
442 : // there are 2 different types of placement depending on
443 : // whether we want an accented under or not
444 :
445 0 : nscoord underDelta1 = 0; // gap between base and underscript
446 0 : nscoord underDelta2 = 0; // extra space beneath underscript
447 :
448 0 : if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {
449 : // Rule 13a, App. G, TeXbook
450 : nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy;
451 : GetBigOpSpacings (fm,
452 : dummy, bigOpSpacing2,
453 : dummy, bigOpSpacing4,
454 0 : bigOpSpacing5);
455 0 : underDelta1 = NS_MAX(bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent));
456 0 : underDelta2 = bigOpSpacing5;
457 : }
458 : else {
459 : // No corresponding rule in TeXbook - we are on our own here
460 : // XXX tune the gap delta between base and underscript
461 :
462 : // Should we use Rule 10 like \underline does?
463 0 : underDelta1 = ruleThickness + onePixel/2;
464 0 : underDelta2 = ruleThickness;
465 : }
466 : // empty under?
467 0 : if (!(bmUnder.ascent + bmUnder.descent)) {
468 0 : underDelta1 = 0;
469 0 : underDelta2 = 0;
470 : }
471 :
472 0 : nscoord overDelta1 = 0; // gap between base and overscript
473 0 : nscoord overDelta2 = 0; // extra space above overscript
474 :
475 0 : if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {
476 : // Rule 13a, App. G, TeXbook
477 : nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy;
478 : GetBigOpSpacings (fm,
479 : bigOpSpacing1, dummy,
480 : bigOpSpacing3, dummy,
481 0 : bigOpSpacing5);
482 0 : overDelta1 = NS_MAX(bigOpSpacing1, (bigOpSpacing3 - bmOver.descent));
483 0 : overDelta2 = bigOpSpacing5;
484 :
485 : // XXX This is not a TeX rule...
486 : // delta1 (as computed abvove) can become really big when bmOver.descent is
487 : // negative, e.g., if the content is &OverBar. In such case, we use the height
488 0 : if (bmOver.descent < 0)
489 0 : overDelta1 = NS_MAX(bigOpSpacing1, (bigOpSpacing3 - (bmOver.ascent + bmOver.descent)));
490 : }
491 : else {
492 : // Rule 12, App. G, TeXbook
493 : // We are going to modify this rule to make it more general.
494 : // The idea behind Rule 12 in the TeXBook is to keep the accent
495 : // as close to the base as possible, while ensuring that the
496 : // distance between the *baseline* of the accent char and
497 : // the *baseline* of the base is atleast x-height.
498 : // The idea is that for normal use, we would like all the accents
499 : // on a line to line up atleast x-height above the baseline
500 : // if possible.
501 : // When the ascent of the base is >= x-height,
502 : // the baseline of the accent char is placed just above the base
503 : // (specifically, the baseline of the accent char is placed
504 : // above the baseline of the base by the ascent of the base).
505 : // For ease of implementation,
506 : // this assumes that the font-designer designs accents
507 : // in such a way that the bottom of the accent is atleast x-height
508 : // above its baseline, otherwise there will be collisions
509 : // with the base. Also there should be proper padding between
510 : // the bottom of the accent char and its baseline.
511 : // The above rule may not be obvious from a first
512 : // reading of rule 12 in the TeXBook !!!
513 : // The mathml <mover> tag can use accent chars that
514 : // do not follow this convention. So we modify TeX's rule
515 : // so that TeX's rule gets subsumed for accents that follow
516 : // TeX's convention,
517 : // while also allowing accents that do not follow the convention :
518 : // we try to keep the *bottom* of the accent char atleast x-height
519 : // from the baseline of the base char. we also slap on an extra
520 : // padding between the accent and base chars.
521 0 : overDelta1 = ruleThickness + onePixel/2;
522 0 : if (bmBase.ascent < xHeight) {
523 : // also ensure at least x-height above the baseline of the base
524 0 : overDelta1 += xHeight - bmBase.ascent;
525 : }
526 0 : overDelta2 = ruleThickness;
527 : }
528 : // empty over?
529 0 : if (!(bmOver.ascent + bmOver.descent)) {
530 0 : overDelta1 = 0;
531 0 : overDelta2 = 0;
532 : }
533 :
534 0 : nscoord dxBase = 0, dxOver = 0, dxUnder = 0;
535 0 : nsAutoString valueAlign;
536 : enum {
537 : center,
538 : left,
539 : right
540 0 : } alignPosition = center;
541 :
542 0 : if (GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::align,
543 0 : valueAlign)) {
544 0 : if (valueAlign.EqualsLiteral("left")) {
545 0 : alignPosition = left;
546 0 : } else if (valueAlign.EqualsLiteral("right")) {
547 0 : alignPosition = right;
548 : }
549 : }
550 :
551 : //////////
552 : // pass 1, do what <mover> does: attach the overscript on the base
553 :
554 : // Ad-hoc - This is to override fonts which have ready-made _accent_
555 : // glyphs with negative lbearing and rbearing. We want to position
556 : // the overscript ourselves
557 0 : nscoord overWidth = bmOver.width;
558 0 : if (!overWidth && (bmOver.rightBearing - bmOver.leftBearing > 0)) {
559 0 : overWidth = bmOver.rightBearing - bmOver.leftBearing;
560 0 : dxOver = -bmOver.leftBearing;
561 : }
562 :
563 0 : if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {
564 0 : mBoundingMetrics.width = bmBase.width;
565 0 : if (alignPosition == center) {
566 0 : dxOver += correction;
567 : }
568 : }
569 : else {
570 0 : mBoundingMetrics.width = NS_MAX(bmBase.width, overWidth);
571 0 : if (alignPosition == center) {
572 0 : dxOver += correction/2;
573 : }
574 : }
575 :
576 0 : if (alignPosition == center) {
577 0 : dxOver += (mBoundingMetrics.width - overWidth)/2;
578 0 : dxBase = (mBoundingMetrics.width - bmBase.width)/2;
579 0 : } else if (alignPosition == right) {
580 0 : dxOver += mBoundingMetrics.width - overWidth;
581 0 : dxBase = mBoundingMetrics.width - bmBase.width;
582 : }
583 :
584 : mBoundingMetrics.ascent =
585 0 : bmBase.ascent + overDelta1 + bmOver.ascent + bmOver.descent;
586 0 : mBoundingMetrics.descent = bmBase.descent;
587 : mBoundingMetrics.leftBearing =
588 0 : NS_MIN(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing);
589 : mBoundingMetrics.rightBearing =
590 0 : NS_MAX(dxBase + bmBase.rightBearing, dxOver + bmOver.rightBearing);
591 :
592 : //////////
593 : // pass 2, do what <munder> does: attach the underscript on the previous
594 : // result. We conceptually view the previous result as an "anynomous base"
595 : // from where to attach the underscript. Hence if the underscript is empty,
596 : // we should end up like <mover>. If the overscript is empty, we should
597 : // end up like <munder>.
598 :
599 0 : nsBoundingMetrics bmAnonymousBase = mBoundingMetrics;
600 : nscoord ascentAnonymousBase =
601 : NS_MAX(mBoundingMetrics.ascent + overDelta2,
602 0 : overSize.ascent + bmOver.descent + overDelta1 + bmBase.ascent);
603 0 : ascentAnonymousBase = NS_MAX(ascentAnonymousBase, baseSize.ascent);
604 :
605 : // Width of non-spacing marks is zero so use left and right bearing.
606 0 : nscoord underWidth = bmUnder.width;
607 0 : if (!underWidth) {
608 0 : underWidth = bmUnder.rightBearing - bmUnder.leftBearing;
609 0 : dxUnder = -bmUnder.leftBearing;
610 : }
611 :
612 0 : nscoord maxWidth = NS_MAX(bmAnonymousBase.width, underWidth);
613 0 : if (alignPosition == center &&
614 0 : !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {
615 0 : GetItalicCorrection(bmAnonymousBase, correction);
616 0 : dxUnder += -correction/2;
617 : }
618 0 : nscoord dxAnonymousBase = 0;
619 0 : if (alignPosition == center) {
620 0 : dxUnder += (maxWidth - underWidth)/2;
621 0 : dxAnonymousBase = (maxWidth - bmAnonymousBase.width)/2;
622 0 : } else if (alignPosition == right) {
623 0 : dxUnder += maxWidth - underWidth;
624 0 : dxAnonymousBase = maxWidth - bmAnonymousBase.width;
625 : }
626 :
627 : // adjust the offsets of the real base and overscript since their
628 : // final offsets should be relative to us...
629 0 : dxOver += dxAnonymousBase;
630 0 : dxBase += dxAnonymousBase;
631 :
632 : mBoundingMetrics.width =
633 0 : NS_MAX(dxAnonymousBase + bmAnonymousBase.width, dxUnder + bmUnder.width);
634 : // At this point, mBoundingMetrics.ascent = bmAnonymousBase.ascent
635 : mBoundingMetrics.descent =
636 0 : bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + bmUnder.descent;
637 : mBoundingMetrics.leftBearing =
638 0 : NS_MIN(dxAnonymousBase + bmAnonymousBase.leftBearing, dxUnder + bmUnder.leftBearing);
639 : mBoundingMetrics.rightBearing =
640 0 : NS_MAX(dxAnonymousBase + bmAnonymousBase.rightBearing, dxUnder + bmUnder.rightBearing);
641 :
642 0 : aDesiredSize.ascent = ascentAnonymousBase;
643 : aDesiredSize.height = aDesiredSize.ascent +
644 : NS_MAX(mBoundingMetrics.descent + underDelta2,
645 : bmAnonymousBase.descent + underDelta1 + bmUnder.ascent +
646 0 : underSize.height - underSize.ascent);
647 : aDesiredSize.height = NS_MAX(aDesiredSize.height,
648 : aDesiredSize.ascent +
649 0 : baseSize.height - baseSize.ascent);
650 0 : aDesiredSize.width = mBoundingMetrics.width;
651 0 : aDesiredSize.mBoundingMetrics = mBoundingMetrics;
652 :
653 0 : mReference.x = 0;
654 0 : mReference.y = aDesiredSize.ascent;
655 :
656 0 : if (aPlaceOrigin) {
657 : nscoord dy;
658 : // place overscript
659 0 : if (overFrame) {
660 : dy = aDesiredSize.ascent - mBoundingMetrics.ascent + bmOver.ascent
661 0 : - overSize.ascent;
662 0 : FinishReflowChild (overFrame, PresContext(), nsnull, overSize, dxOver, dy, 0);
663 : }
664 : // place base
665 0 : dy = aDesiredSize.ascent - baseSize.ascent;
666 0 : FinishReflowChild (baseFrame, PresContext(), nsnull, baseSize, dxBase, dy, 0);
667 : // place underscript
668 0 : if (underFrame) {
669 : dy = aDesiredSize.ascent + mBoundingMetrics.descent - bmUnder.descent
670 0 : - underSize.ascent;
671 : FinishReflowChild (underFrame, PresContext(), nsnull, underSize,
672 0 : dxUnder, dy, 0);
673 : }
674 : }
675 0 : return NS_OK;
676 : }
|