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> (added TeX rendering rules)
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 :
42 : #include "nsCOMPtr.h"
43 : #include "nsFrame.h"
44 : #include "nsPresContext.h"
45 : #include "nsStyleContext.h"
46 : #include "nsStyleConsts.h"
47 : #include "nsRenderingContext.h"
48 :
49 : #include "nsMathMLmsubsupFrame.h"
50 :
51 : //
52 : // <msubsup> -- attach a subscript-superscript pair to a base - implementation
53 : //
54 :
55 : nsIFrame*
56 0 : NS_NewMathMLmsubsupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
57 : {
58 0 : return new (aPresShell) nsMathMLmsubsupFrame(aContext);
59 : }
60 :
61 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmsubsupFrame)
62 :
63 0 : nsMathMLmsubsupFrame::~nsMathMLmsubsupFrame()
64 : {
65 0 : }
66 :
67 : NS_IMETHODIMP
68 0 : nsMathMLmsubsupFrame::TransmitAutomaticData()
69 : {
70 : // if our base is an embellished operator, let its state bubble to us
71 0 : mPresentationData.baseFrame = mFrames.FirstChild();
72 0 : GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData);
73 :
74 : // 1. The REC says:
75 : // The <msubsup> element increments scriptlevel by 1, and sets displaystyle to
76 : // "false", within subscript and superscript, but leaves both attributes
77 : // unchanged within base.
78 : // 2. The TeXbook (Ch 17. p.141) says the superscript inherits the compression
79 : // while the subscript is compressed
80 : UpdatePresentationDataFromChildAt(1, -1,
81 : ~NS_MATHML_DISPLAYSTYLE,
82 0 : NS_MATHML_DISPLAYSTYLE);
83 : UpdatePresentationDataFromChildAt(1, 1,
84 : NS_MATHML_COMPRESSED,
85 0 : NS_MATHML_COMPRESSED);
86 :
87 0 : return NS_OK;
88 : }
89 :
90 : /* virtual */ nsresult
91 0 : nsMathMLmsubsupFrame::Place(nsRenderingContext& aRenderingContext,
92 : bool aPlaceOrigin,
93 : nsHTMLReflowMetrics& aDesiredSize)
94 : {
95 : // extra spacing between base and sup/subscript
96 0 : nscoord scriptSpace = 0;
97 :
98 : // check if the subscriptshift attribute is there
99 0 : nsAutoString value;
100 0 : nscoord subScriptShift = 0;
101 : GetAttribute(mContent, mPresentationData.mstyle,
102 0 : nsGkAtoms::subscriptshift_, value);
103 0 : if (!value.IsEmpty()) {
104 0 : nsCSSValue cssValue;
105 0 : if (ParseNumericValue(value, cssValue) && cssValue.IsLengthUnit()) {
106 0 : subScriptShift = CalcLength(PresContext(), mStyleContext, cssValue);
107 : }
108 : }
109 : // check if the superscriptshift attribute is there
110 0 : nscoord supScriptShift = 0;
111 : GetAttribute(mContent, mPresentationData.mstyle,
112 0 : nsGkAtoms::superscriptshift_, value);
113 0 : if (!value.IsEmpty()) {
114 0 : nsCSSValue cssValue;
115 0 : if (ParseNumericValue(value, cssValue) && cssValue.IsLengthUnit()) {
116 0 : supScriptShift = CalcLength(PresContext(), mStyleContext, cssValue);
117 : }
118 : }
119 :
120 : return nsMathMLmsubsupFrame::PlaceSubSupScript(PresContext(),
121 : aRenderingContext,
122 : aPlaceOrigin,
123 : aDesiredSize,
124 : this,
125 : subScriptShift,
126 : supScriptShift,
127 0 : scriptSpace);
128 : }
129 :
130 : // exported routine that both munderover and msubsup share.
131 : // munderover uses this when movablelimits is set.
132 : nsresult
133 0 : nsMathMLmsubsupFrame::PlaceSubSupScript(nsPresContext* aPresContext,
134 : nsRenderingContext& aRenderingContext,
135 : bool aPlaceOrigin,
136 : nsHTMLReflowMetrics& aDesiredSize,
137 : nsMathMLContainerFrame* aFrame,
138 : nscoord aUserSubScriptShift,
139 : nscoord aUserSupScriptShift,
140 : nscoord aScriptSpace)
141 : {
142 : // force the scriptSpace to be atleast 1 pixel
143 0 : nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
144 0 : aScriptSpace = NS_MAX(onePixel, aScriptSpace);
145 :
146 : ////////////////////////////////////
147 : // Get the children's desired sizes
148 :
149 0 : nsHTMLReflowMetrics baseSize;
150 0 : nsHTMLReflowMetrics subScriptSize;
151 0 : nsHTMLReflowMetrics supScriptSize;
152 0 : nsBoundingMetrics bmBase, bmSubScript, bmSupScript;
153 0 : nsIFrame* subScriptFrame = nsnull;
154 0 : nsIFrame* supScriptFrame = nsnull;
155 0 : nsIFrame* baseFrame = aFrame->GetFirstPrincipalChild();
156 0 : if (baseFrame)
157 0 : subScriptFrame = baseFrame->GetNextSibling();
158 0 : if (subScriptFrame)
159 0 : supScriptFrame = subScriptFrame->GetNextSibling();
160 0 : if (!baseFrame || !subScriptFrame || !supScriptFrame ||
161 0 : supScriptFrame->GetNextSibling()) {
162 : // report an error, encourage people to get their markups in order
163 0 : return aFrame->ReflowError(aRenderingContext, aDesiredSize);
164 : }
165 0 : GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
166 0 : GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
167 0 : GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
168 :
169 : // get the subdrop from the subscript font
170 : nscoord subDrop;
171 0 : GetSubDropFromChild(subScriptFrame, subDrop);
172 : // parameter v, Rule 18a, App. G, TeXbook
173 0 : nscoord minSubScriptShift = bmBase.descent + subDrop;
174 :
175 : // get the supdrop from the supscript font
176 : nscoord supDrop;
177 0 : GetSupDropFromChild(supScriptFrame, supDrop);
178 : // parameter u, Rule 18a, App. G, TeXbook
179 0 : nscoord minSupScriptShift = bmBase.ascent - supDrop;
180 :
181 : //////////////////
182 : // Place Children
183 : //////////////////
184 :
185 : //////////////////////////////////////////////////
186 : // Get subscript shift
187 : // slightly different from nsMathMLmsubFrame.cpp
188 : //////////////////////////////////////////////////
189 :
190 : // subScriptShift{1,2}
191 : // = minimum amount to shift the subscript down
192 : // = sub{1,2} in TeXbook
193 : // subScriptShift1 = subscriptshift attribute * x-height
194 : nscoord subScriptShift1, subScriptShift2;
195 :
196 0 : nsRefPtr<nsFontMetrics> fm;
197 0 : nsLayoutUtils::GetFontMetricsForFrame(baseFrame, getter_AddRefs(fm));
198 0 : aRenderingContext.SetFont(fm);
199 :
200 : // get x-height (an ex)
201 0 : nscoord xHeight = fm->XHeight();
202 :
203 : nscoord ruleSize;
204 0 : GetRuleThickness (aRenderingContext, fm, ruleSize);
205 :
206 : // Get subScriptShift{1,2} default from font
207 0 : GetSubScriptShifts (fm, subScriptShift1, subScriptShift2);
208 :
209 0 : if (0 < aUserSubScriptShift) {
210 : // the user has set the subscriptshift attribute
211 0 : float scaler = ((float) subScriptShift2) / subScriptShift1;
212 0 : subScriptShift1 = NS_MAX(subScriptShift1, aUserSubScriptShift);
213 0 : subScriptShift2 = NSToCoordRound(scaler * subScriptShift1);
214 : }
215 :
216 : // get a tentative value for subscriptshift
217 : // Rule 18d, App. G, TeXbook
218 : nscoord subScriptShift =
219 0 : NS_MAX(minSubScriptShift,NS_MAX(subScriptShift1,subScriptShift2));
220 :
221 : //////////////////////////////////////////////////
222 : // Get supscript shift
223 : // same code from nsMathMLmsupFrame.cpp
224 : //////////////////////////////////////////////////
225 :
226 : // get min supscript shift limit from x-height
227 : // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook
228 : nscoord minShiftFromXHeight = (nscoord)
229 0 : (bmSupScript.descent + (1.0f/4.0f) * xHeight);
230 :
231 : // supScriptShift{1,2,3}
232 : // = minimum amount to shift the supscript up
233 : // = sup{1,2,3} in TeX
234 : // supScriptShift1 = superscriptshift attribute * x-height
235 : // Note that there are THREE values for supscript shifts depending
236 : // on the current style
237 : nscoord supScriptShift1, supScriptShift2, supScriptShift3;
238 : // Set supScriptShift{1,2,3} default from font
239 0 : GetSupScriptShifts (fm, supScriptShift1, supScriptShift2, supScriptShift3);
240 0 : if (0 < aUserSupScriptShift) {
241 : // the user has set the superscriptshift attribute
242 0 : float scaler2 = ((float) supScriptShift2) / supScriptShift1;
243 0 : float scaler3 = ((float) supScriptShift3) / supScriptShift1;
244 0 : supScriptShift1 = NS_MAX(supScriptShift1, aUserSupScriptShift);
245 0 : supScriptShift2 = NSToCoordRound(scaler2 * supScriptShift1);
246 0 : supScriptShift3 = NSToCoordRound(scaler3 * supScriptShift1);
247 : }
248 :
249 : // get sup script shift depending on current script level and display style
250 : // Rule 18c, App. G, TeXbook
251 : nscoord supScriptShift;
252 0 : nsPresentationData presentationData;
253 0 : aFrame->GetPresentationData(presentationData);
254 0 : if ( aFrame->GetStyleFont()->mScriptLevel == 0 &&
255 : NS_MATHML_IS_DISPLAYSTYLE(presentationData.flags) &&
256 0 : !NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
257 : // Style D in TeXbook
258 0 : supScriptShift = supScriptShift1;
259 : }
260 0 : else if (NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
261 : // Style C' in TeXbook = D',T',S',SS'
262 0 : supScriptShift = supScriptShift3;
263 : }
264 : else {
265 : // everything else = T,S,SS
266 0 : supScriptShift = supScriptShift2;
267 : }
268 :
269 : // get tentative value for superscriptshift
270 : // Rule 18c, App. G, TeXbook
271 : supScriptShift =
272 0 : NS_MAX(minSupScriptShift,NS_MAX(supScriptShift,minShiftFromXHeight));
273 :
274 : //////////////////////////////////////////////////
275 : // Negotiate between supScriptShift and subScriptShift
276 : // so that there will be enough gap between them
277 : // Rule 18e, App. G, TeXbook
278 : //////////////////////////////////////////////////
279 :
280 : nscoord gap =
281 : (supScriptShift - bmSupScript.descent) -
282 0 : (bmSubScript.ascent - subScriptShift);
283 0 : if (gap < 4.0f * ruleSize) {
284 : // adjust subScriptShift to get a gap of (4.0 * ruleSize)
285 0 : subScriptShift += NSToCoordRound ((4.0f * ruleSize) - gap);
286 : }
287 :
288 : // next we want to ensure that the bottom of the superscript
289 : // will be > (4/5) * x-height above baseline
290 : gap = NSToCoordRound ((4.0f/5.0f) * xHeight -
291 0 : (supScriptShift - bmSupScript.descent));
292 0 : if (gap > 0) {
293 0 : supScriptShift += gap;
294 0 : subScriptShift -= gap;
295 : }
296 :
297 : //////////////////////////////////////////////////
298 : // Do the Placing
299 : //////////////////////////////////////////////////
300 :
301 : // get bounding box for base + subscript + superscript
302 0 : nsBoundingMetrics boundingMetrics;
303 : boundingMetrics.ascent =
304 0 : NS_MAX(bmBase.ascent, (bmSupScript.ascent + supScriptShift));
305 : boundingMetrics.descent =
306 0 : NS_MAX(bmBase.descent, (bmSubScript.descent + subScriptShift));
307 :
308 : // leave aScriptSpace after both super/subscript
309 : // add italicCorrection between base and superscript
310 : // add "a little to spare" as well (see TeXbook Ch.11, p.64), as we
311 : // estimate the italic creation ourselves and it isn't the same as TeX
312 : nscoord italicCorrection;
313 0 : GetItalicCorrection(bmBase, italicCorrection);
314 0 : italicCorrection += onePixel;
315 : boundingMetrics.width = bmBase.width + aScriptSpace +
316 0 : NS_MAX((italicCorrection + bmSupScript.width), bmSubScript.width);
317 0 : boundingMetrics.leftBearing = bmBase.leftBearing;
318 : boundingMetrics.rightBearing = bmBase.width +
319 0 : NS_MAX((italicCorrection + bmSupScript.rightBearing), bmSubScript.rightBearing);
320 0 : aFrame->SetBoundingMetrics(boundingMetrics);
321 :
322 : // reflow metrics
323 : aDesiredSize.ascent =
324 : NS_MAX(baseSize.ascent,
325 : NS_MAX(subScriptSize.ascent - subScriptShift,
326 0 : supScriptSize.ascent + supScriptShift));
327 : aDesiredSize.height = aDesiredSize.ascent +
328 : NS_MAX(baseSize.height - baseSize.ascent,
329 : NS_MAX(subScriptSize.height - subScriptSize.ascent + subScriptShift,
330 0 : supScriptSize.height - subScriptSize.ascent - supScriptShift));
331 0 : aDesiredSize.width = boundingMetrics.width;
332 0 : aDesiredSize.mBoundingMetrics = boundingMetrics;
333 :
334 0 : aFrame->SetReference(nsPoint(0, aDesiredSize.ascent));
335 :
336 0 : if (aPlaceOrigin) {
337 : nscoord dx, dy;
338 : // now place the base ...
339 0 : dx = aFrame->MirrorIfRTL(aDesiredSize.width, baseSize.width, 0);
340 0 : dy = aDesiredSize.ascent - baseSize.ascent;
341 : FinishReflowChild(baseFrame, aPresContext, nsnull,
342 0 : baseSize, dx, dy, 0);
343 : // ... and subscript
344 : dx = aFrame->MirrorIfRTL(aDesiredSize.width, subScriptSize.width,
345 0 : bmBase.width);
346 0 : dy = aDesiredSize.ascent - (subScriptSize.ascent - subScriptShift);
347 : FinishReflowChild(subScriptFrame, aPresContext, nsnull,
348 0 : subScriptSize, dx, dy, 0);
349 : // ... and the superscript
350 : dx = aFrame->MirrorIfRTL(aDesiredSize.width, supScriptSize.width,
351 0 : bmBase.width + italicCorrection);
352 0 : dy = aDesiredSize.ascent - (supScriptSize.ascent + supScriptShift);
353 : FinishReflowChild(supScriptFrame, aPresContext, nsnull,
354 0 : supScriptSize, dx, dy, 0);
355 : }
356 :
357 0 : return NS_OK;
358 : }
|