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 "nsMathMLmmultiscriptsFrame.h"
50 :
51 : //
52 : // <mmultiscripts> -- attach prescripts and tensor indices to a base - implementation
53 : //
54 :
55 : nsIFrame*
56 0 : NS_NewMathMLmmultiscriptsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
57 : {
58 0 : return new (aPresShell) nsMathMLmmultiscriptsFrame(aContext);
59 : }
60 :
61 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmmultiscriptsFrame)
62 :
63 0 : nsMathMLmmultiscriptsFrame::~nsMathMLmmultiscriptsFrame()
64 : {
65 0 : }
66 :
67 : NS_IMETHODIMP
68 0 : nsMathMLmmultiscriptsFrame::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 : // The REC says:
75 : // The <mmultiscripts> element increments scriptlevel by 1, and sets
76 : // displaystyle to "false", within each of its arguments except base
77 : UpdatePresentationDataFromChildAt(1, -1,
78 0 : ~NS_MATHML_DISPLAYSTYLE, NS_MATHML_DISPLAYSTYLE);
79 :
80 : // The TeXbook (Ch 17. p.141) says the superscript inherits the compression
81 : // while the subscript is compressed. So here we collect subscripts and set
82 : // the compression flag in them.
83 0 : PRInt32 count = 0;
84 0 : bool isSubScript = false;
85 0 : nsAutoTArray<nsIFrame*, 8> subScriptFrames;
86 0 : nsIFrame* childFrame = mFrames.FirstChild();
87 0 : while (childFrame) {
88 0 : if (childFrame->GetContent()->Tag() == nsGkAtoms::mprescripts_) {
89 : // mprescripts frame
90 : }
91 0 : else if (0 == count) {
92 : // base frame
93 : }
94 : else {
95 : // super/subscript block
96 0 : if (isSubScript) {
97 : // subscript
98 0 : subScriptFrames.AppendElement(childFrame);
99 : }
100 : else {
101 : // superscript
102 : }
103 0 : isSubScript = !isSubScript;
104 : }
105 0 : count++;
106 0 : childFrame = childFrame->GetNextSibling();
107 : }
108 0 : for (PRInt32 i = subScriptFrames.Length() - 1; i >= 0; i--) {
109 0 : childFrame = subScriptFrames[i];
110 : PropagatePresentationDataFor(childFrame,
111 0 : NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
112 : }
113 :
114 0 : return NS_OK;
115 : }
116 :
117 : void
118 0 : nsMathMLmmultiscriptsFrame::ProcessAttributes()
119 : {
120 0 : mSubScriptShift = 0;
121 0 : mSupScriptShift = 0;
122 :
123 : // check if the subscriptshift attribute is there
124 0 : nsAutoString value;
125 : GetAttribute(mContent, mPresentationData.mstyle,
126 0 : nsGkAtoms::subscriptshift_, value);
127 0 : if (!value.IsEmpty()) {
128 0 : nsCSSValue cssValue;
129 0 : if (ParseNumericValue(value, cssValue) && cssValue.IsLengthUnit()) {
130 0 : mSubScriptShift = CalcLength(PresContext(), mStyleContext, cssValue);
131 : }
132 : }
133 : // check if the superscriptshift attribute is there
134 : GetAttribute(mContent, mPresentationData.mstyle,
135 0 : nsGkAtoms::superscriptshift_, value);
136 0 : if (!value.IsEmpty()) {
137 0 : nsCSSValue cssValue;
138 0 : if (ParseNumericValue(value, cssValue) && cssValue.IsLengthUnit()) {
139 0 : mSupScriptShift = CalcLength(PresContext(), mStyleContext, cssValue);
140 : }
141 : }
142 0 : }
143 :
144 : /* virtual */ nsresult
145 0 : nsMathMLmmultiscriptsFrame::Place(nsRenderingContext& aRenderingContext,
146 : bool aPlaceOrigin,
147 : nsHTMLReflowMetrics& aDesiredSize)
148 : {
149 : ////////////////////////////////////
150 : // Get the children's desired sizes
151 :
152 : nscoord minShiftFromXHeight, subDrop, supDrop;
153 :
154 : ////////////////////////////////////////
155 : // Initialize super/sub shifts that
156 : // depend only on the current font
157 : ////////////////////////////////////////
158 :
159 0 : ProcessAttributes();
160 :
161 : // get x-height (an ex)
162 0 : const nsStyleFont* font = GetStyleFont();
163 0 : nsRefPtr<nsFontMetrics> fm;
164 0 : nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
165 0 : aRenderingContext.SetFont(fm);
166 :
167 0 : nscoord xHeight = fm->XHeight();
168 :
169 : nscoord ruleSize;
170 0 : GetRuleThickness (aRenderingContext, fm, ruleSize);
171 :
172 : // scriptspace from TeX for extra spacing after sup/subscript (0.5pt in plain TeX)
173 : // forced to be at least 1 pixel here
174 0 : nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
175 0 : nscoord scriptSpace = NS_MAX(nsPresContext::CSSPointsToAppUnits(0.5f), onePixel);
176 :
177 : /////////////////////////////////////
178 : // first the shift for the subscript
179 :
180 : // subScriptShift{1,2}
181 : // = minimum amount to shift the subscript down
182 : // = sub{1,2} in TeXbook
183 : // subScriptShift1 = subscriptshift attribute * x-height
184 : nscoord subScriptShift1, subScriptShift2;
185 :
186 : // Get subScriptShift{1,2} default from font
187 0 : GetSubScriptShifts (fm, subScriptShift1, subScriptShift2);
188 0 : if (0 < mSubScriptShift) {
189 : // the user has set the subscriptshift attribute
190 0 : float scaler = ((float) subScriptShift2) / subScriptShift1;
191 0 : subScriptShift1 = NS_MAX(subScriptShift1, mSubScriptShift);
192 0 : subScriptShift2 = NSToCoordRound(scaler * subScriptShift1);
193 : }
194 : // the font dependent shift
195 0 : nscoord subScriptShift = NS_MAX(subScriptShift1,subScriptShift2);
196 :
197 : /////////////////////////////////////
198 : // next the shift for the superscript
199 :
200 : // supScriptShift{1,2,3}
201 : // = minimum amount to shift the supscript up
202 : // = sup{1,2,3} in TeX
203 : // supScriptShift1 = superscriptshift attribute * x-height
204 : // Note that there are THREE values for supscript shifts depending
205 : // on the current style
206 : nscoord supScriptShift1, supScriptShift2, supScriptShift3;
207 : // Set supScriptShift{1,2,3} default from font
208 0 : GetSupScriptShifts (fm, supScriptShift1, supScriptShift2, supScriptShift3);
209 0 : if (0 < mSupScriptShift) {
210 : // the user has set the superscriptshift attribute
211 0 : float scaler2 = ((float) supScriptShift2) / supScriptShift1;
212 0 : float scaler3 = ((float) supScriptShift3) / supScriptShift1;
213 0 : supScriptShift1 = NS_MAX(supScriptShift1, mSupScriptShift);
214 0 : supScriptShift2 = NSToCoordRound(scaler2 * supScriptShift1);
215 0 : supScriptShift3 = NSToCoordRound(scaler3 * supScriptShift1);
216 : }
217 :
218 : // get sup script shift depending on current script level and display style
219 : // Rule 18c, App. G, TeXbook
220 : nscoord supScriptShift;
221 0 : if ( font->mScriptLevel == 0 &&
222 : NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags) &&
223 0 : !NS_MATHML_IS_COMPRESSED(mPresentationData.flags)) {
224 : // Style D in TeXbook
225 0 : supScriptShift = supScriptShift1;
226 : }
227 0 : else if (NS_MATHML_IS_COMPRESSED(mPresentationData.flags)) {
228 : // Style C' in TeXbook = D',T',S',SS'
229 0 : supScriptShift = supScriptShift3;
230 : }
231 : else {
232 : // everything else = T,S,SS
233 0 : supScriptShift = supScriptShift2;
234 : }
235 :
236 : ////////////////////////////////////
237 : // Get the children's sizes
238 : ////////////////////////////////////
239 :
240 0 : nscoord width = 0, prescriptsWidth = 0, rightBearing = 0;
241 0 : nsIFrame* mprescriptsFrame = nsnull; // frame of <mprescripts/>, if there.
242 0 : bool isSubScript = false;
243 0 : nscoord minSubScriptShift = 0, minSupScriptShift = 0;
244 0 : nscoord trySubScriptShift = subScriptShift;
245 0 : nscoord trySupScriptShift = supScriptShift;
246 0 : nscoord maxSubScriptShift = subScriptShift;
247 0 : nscoord maxSupScriptShift = supScriptShift;
248 0 : PRInt32 count = 0;
249 0 : nsHTMLReflowMetrics baseSize;
250 0 : nsHTMLReflowMetrics subScriptSize;
251 0 : nsHTMLReflowMetrics supScriptSize;
252 0 : nsIFrame* baseFrame = nsnull;
253 0 : nsIFrame* subScriptFrame = nsnull;
254 0 : nsIFrame* supScriptFrame = nsnull;
255 :
256 0 : bool firstPrescriptsPair = false;
257 0 : nsBoundingMetrics bmBase, bmSubScript, bmSupScript;
258 0 : nscoord italicCorrection = 0;
259 :
260 0 : mBoundingMetrics.width = 0;
261 0 : mBoundingMetrics.ascent = mBoundingMetrics.descent = -0x7FFFFFFF;
262 0 : nscoord ascent = -0x7FFFFFFF, descent = -0x7FFFFFFF;
263 0 : aDesiredSize.width = aDesiredSize.height = 0;
264 :
265 0 : nsIFrame* childFrame = mFrames.FirstChild();
266 0 : while (childFrame) {
267 0 : if (childFrame->GetContent()->Tag() == nsGkAtoms::mprescripts_) {
268 0 : if (mprescriptsFrame) {
269 : // duplicate <mprescripts/> found
270 : // report an error, encourage people to get their markups in order
271 0 : return ReflowError(aRenderingContext, aDesiredSize);
272 : }
273 0 : mprescriptsFrame = childFrame;
274 0 : firstPrescriptsPair = true;
275 : }
276 : else {
277 0 : if (0 == count) {
278 : // base
279 0 : baseFrame = childFrame;
280 0 : GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
281 0 : GetItalicCorrection(bmBase, italicCorrection);
282 : // for the superscript, we always add "a little to spare"
283 0 : italicCorrection += onePixel;
284 :
285 : // we update mBoundingMetrics.{ascent,descent} with that
286 : // of the baseFrame only after processing all the sup/sub pairs
287 : // XXX need italic correction only *if* there are postscripts ?
288 0 : mBoundingMetrics.width = bmBase.width + italicCorrection;
289 0 : mBoundingMetrics.rightBearing = bmBase.rightBearing;
290 0 : mBoundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten
291 : }
292 : else {
293 : // super/subscript block
294 0 : if (isSubScript) {
295 : // subscript
296 0 : subScriptFrame = childFrame;
297 0 : GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
298 : // get the subdrop from the subscript font
299 0 : GetSubDropFromChild (subScriptFrame, subDrop);
300 : // parameter v, Rule 18a, App. G, TeXbook
301 0 : minSubScriptShift = bmBase.descent + subDrop;
302 0 : trySubScriptShift = NS_MAX(minSubScriptShift,subScriptShift);
303 : mBoundingMetrics.descent =
304 0 : NS_MAX(mBoundingMetrics.descent,bmSubScript.descent);
305 0 : descent = NS_MAX(descent,subScriptSize.height - subScriptSize.ascent);
306 0 : width = bmSubScript.width + scriptSpace;
307 0 : rightBearing = bmSubScript.rightBearing;
308 : }
309 : else {
310 : // supscript
311 0 : supScriptFrame = childFrame;
312 0 : GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
313 : // get the supdrop from the supscript font
314 0 : GetSupDropFromChild (supScriptFrame, supDrop);
315 : // parameter u, Rule 18a, App. G, TeXbook
316 0 : minSupScriptShift = bmBase.ascent - supDrop;
317 : // get min supscript shift limit from x-height
318 : // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook
319 : minShiftFromXHeight = NSToCoordRound
320 0 : ((bmSupScript.descent + (1.0f/4.0f) * xHeight));
321 : trySupScriptShift =
322 0 : NS_MAX(minSupScriptShift,NS_MAX(minShiftFromXHeight,supScriptShift));
323 : mBoundingMetrics.ascent =
324 0 : NS_MAX(mBoundingMetrics.ascent,bmSupScript.ascent);
325 0 : ascent = NS_MAX(ascent,supScriptSize.ascent);
326 0 : width = NS_MAX(width, bmSupScript.width + scriptSpace);
327 0 : rightBearing = NS_MAX(rightBearing, bmSupScript.rightBearing);
328 :
329 0 : if (!mprescriptsFrame) { // we are still looping over base & postscripts
330 0 : mBoundingMetrics.rightBearing = mBoundingMetrics.width + rightBearing;
331 0 : mBoundingMetrics.width += width;
332 : }
333 : else {
334 0 : prescriptsWidth += width;
335 0 : if (firstPrescriptsPair) {
336 0 : firstPrescriptsPair = false;
337 : mBoundingMetrics.leftBearing =
338 0 : NS_MIN(bmSubScript.leftBearing, bmSupScript.leftBearing);
339 : }
340 : }
341 0 : width = rightBearing = 0;
342 :
343 : // negotiate between the various shifts so that
344 : // there is enough gap between the sup and subscripts
345 : // Rule 18e, App. G, TeXbook
346 : nscoord gap =
347 : (trySupScriptShift - bmSupScript.descent) -
348 0 : (bmSubScript.ascent - trySubScriptShift);
349 0 : if (gap < 4.0f * ruleSize) {
350 : // adjust trySubScriptShift to get a gap of (4.0 * ruleSize)
351 0 : trySubScriptShift += NSToCoordRound ((4.0f * ruleSize) - gap);
352 : }
353 :
354 : // next we want to ensure that the bottom of the superscript
355 : // will be > (4/5) * x-height above baseline
356 : gap = NSToCoordRound ((4.0f/5.0f) * xHeight -
357 0 : (trySupScriptShift - bmSupScript.descent));
358 0 : if (gap > 0.0f) {
359 0 : trySupScriptShift += gap;
360 0 : trySubScriptShift -= gap;
361 : }
362 :
363 0 : maxSubScriptShift = NS_MAX(maxSubScriptShift, trySubScriptShift);
364 0 : maxSupScriptShift = NS_MAX(maxSupScriptShift, trySupScriptShift);
365 :
366 0 : trySubScriptShift = subScriptShift;
367 0 : trySupScriptShift = supScriptShift;
368 : }
369 : }
370 :
371 0 : isSubScript = !isSubScript;
372 : }
373 0 : count++;
374 0 : childFrame = childFrame->GetNextSibling();
375 : }
376 : // note: width=0 if all sup-sub pairs match correctly
377 0 : if ((0 != width) || !baseFrame || !subScriptFrame || !supScriptFrame) {
378 : // report an error, encourage people to get their markups in order
379 0 : return ReflowError(aRenderingContext, aDesiredSize);
380 : }
381 :
382 : // we left out the width of prescripts, so ...
383 0 : mBoundingMetrics.rightBearing += prescriptsWidth;
384 0 : mBoundingMetrics.width += prescriptsWidth;
385 :
386 : // we left out the base during our bounding box updates, so ...
387 : mBoundingMetrics.ascent =
388 0 : NS_MAX(mBoundingMetrics.ascent+maxSupScriptShift,bmBase.ascent);
389 : mBoundingMetrics.descent =
390 0 : NS_MAX(mBoundingMetrics.descent+maxSubScriptShift,bmBase.descent);
391 :
392 : // get the reflow metrics ...
393 : aDesiredSize.ascent =
394 0 : NS_MAX(ascent+maxSupScriptShift,baseSize.ascent);
395 : aDesiredSize.height = aDesiredSize.ascent +
396 0 : NS_MAX(descent+maxSubScriptShift,baseSize.height - baseSize.ascent);
397 0 : aDesiredSize.width = mBoundingMetrics.width;
398 0 : aDesiredSize.mBoundingMetrics = mBoundingMetrics;
399 :
400 0 : mReference.x = 0;
401 0 : mReference.y = aDesiredSize.ascent;
402 :
403 : //////////////////
404 : // Place Children
405 :
406 : // Place prescripts, followed by base, and then postscripts.
407 : // The list of frames is in the order: {base} {postscripts} {prescripts}
408 : // We go over the list in a circular manner, starting at <prescripts/>
409 :
410 0 : if (aPlaceOrigin) {
411 0 : nscoord dx = 0, dy = 0;
412 :
413 0 : count = 0;
414 0 : childFrame = mprescriptsFrame;
415 0 : do {
416 0 : if (!childFrame) { // end of prescripts,
417 : // place the base ...
418 0 : childFrame = baseFrame;
419 0 : dy = aDesiredSize.ascent - baseSize.ascent;
420 : FinishReflowChild (baseFrame, PresContext(), nsnull, baseSize,
421 : MirrorIfRTL(aDesiredSize.width,
422 : baseSize.width,
423 : dx),
424 0 : dy, 0);
425 0 : dx += bmBase.width + italicCorrection;
426 : }
427 0 : else if (mprescriptsFrame != childFrame) {
428 : // process each sup/sub pair
429 0 : if (0 == count) {
430 0 : subScriptFrame = childFrame;
431 0 : count = 1;
432 : }
433 0 : else if (1 == count) {
434 0 : supScriptFrame = childFrame;
435 0 : count = 0;
436 :
437 : // get the ascent/descent of sup/subscripts stored in their rects
438 : // rect.x = descent, rect.y = ascent
439 0 : GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
440 0 : GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
441 :
442 : // center w.r.t. largest width
443 0 : width = NS_MAX(subScriptSize.width, supScriptSize.width);
444 :
445 : dy = aDesiredSize.ascent - subScriptSize.ascent +
446 0 : maxSubScriptShift;
447 : FinishReflowChild (subScriptFrame, PresContext(), nsnull,
448 : subScriptSize,
449 : MirrorIfRTL(aDesiredSize.width,
450 : subScriptSize.width,
451 : dx + (width-subScriptSize.width)/2),
452 0 : dy, 0);
453 :
454 : dy = aDesiredSize.ascent - supScriptSize.ascent -
455 0 : maxSupScriptShift;
456 : FinishReflowChild (supScriptFrame, PresContext(), nsnull,
457 : supScriptSize,
458 : MirrorIfRTL(aDesiredSize.width,
459 : supScriptSize.width,
460 : dx + (width-supScriptSize.width)/2),
461 0 : dy, 0);
462 :
463 0 : dx += width + scriptSpace;
464 : }
465 : }
466 0 : childFrame = childFrame->GetNextSibling();
467 : } while (mprescriptsFrame != childFrame);
468 : }
469 :
470 0 : return NS_OK;
471 : }
|