Back to index

lightning-sunbird  0.9+nobinonly
nsMathMLmmultiscriptsFrame.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla MathML Project.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * The University Of Queensland.
00018  * Portions created by the Initial Developer are Copyright (C) 1999
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Roger B. Sidje <rbs@maths.uq.edu.au>
00023  *   David J. Fiddes <D.J.Fiddes@hw.ac.uk>
00024  *   Shyjan Mahamud <mahamud@cs.cmu.edu> (added TeX rendering rules)
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 
00041 #include "nsCOMPtr.h"
00042 #include "nsFrame.h"
00043 #include "nsPresContext.h"
00044 #include "nsUnitConversion.h"
00045 #include "nsStyleContext.h"
00046 #include "nsStyleConsts.h"
00047 #include "nsIRenderingContext.h"
00048 #include "nsIFontMetrics.h"
00049 
00050 #include "nsMathMLmmultiscriptsFrame.h"
00051 
00052 //
00053 // <mmultiscripts> -- attach prescripts and tensor indices to a base - implementation
00054 //
00055 
00056 nsresult
00057 NS_NewMathMLmmultiscriptsFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
00058 {
00059   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00060   if (nsnull == aNewFrame) {
00061     return NS_ERROR_NULL_POINTER;
00062   }
00063   nsMathMLmmultiscriptsFrame* it = new (aPresShell) nsMathMLmmultiscriptsFrame;
00064   if (nsnull == it) {
00065     return NS_ERROR_OUT_OF_MEMORY;
00066   }
00067   *aNewFrame = it;
00068   return NS_OK;
00069 }
00070 
00071 nsMathMLmmultiscriptsFrame::nsMathMLmmultiscriptsFrame()
00072 {
00073 }
00074 
00075 nsMathMLmmultiscriptsFrame::~nsMathMLmmultiscriptsFrame()
00076 {
00077 }
00078 
00079 NS_IMETHODIMP
00080 nsMathMLmmultiscriptsFrame::TransmitAutomaticData()
00081 {
00082   // if our base is an embellished operator, let its state bubble to us
00083   mPresentationData.baseFrame = mFrames.FirstChild();
00084   GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData);
00085 
00086   // The REC says:
00087   // The <mmultiscripts> element increments scriptlevel by 1, and sets
00088   // displaystyle to "false", within each of its arguments except base
00089   UpdatePresentationDataFromChildAt(1, -1, 1,
00090     ~NS_MATHML_DISPLAYSTYLE, NS_MATHML_DISPLAYSTYLE);
00091 
00092   // The TeXbook (Ch 17. p.141) says the superscript inherits the compression
00093   // while the subscript is compressed. So here we collect subscripts and set
00094   // the compression flag in them.
00095   PRInt32 count = 0;
00096   PRBool isSubScript = PR_FALSE;
00097   nsAutoVoidArray subScriptFrames;
00098   nsIFrame* childFrame = mFrames.FirstChild();
00099   while (childFrame) {
00100     if (childFrame->GetContent()->Tag() == nsMathMLAtoms::mprescripts_) {
00101       // mprescripts frame
00102     }
00103     else if (0 == count) {
00104       // base frame
00105     }
00106     else {
00107       // super/subscript block
00108       if (isSubScript) {
00109         // subscript
00110         subScriptFrames.AppendElement(childFrame);
00111       }
00112       else {
00113         // superscript
00114       }
00115       isSubScript = !isSubScript;
00116     }
00117     count++;
00118     childFrame = childFrame->GetNextSibling();
00119   }
00120   for (PRInt32 i = subScriptFrames.Count() - 1; i >= 0; i--) {
00121     childFrame = (nsIFrame*)subScriptFrames[i];
00122     PropagatePresentationDataFor(childFrame, 0,
00123       NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
00124   }
00125 
00126   return NS_OK;
00127 }
00128 
00129 void
00130 nsMathMLmmultiscriptsFrame::ProcessAttributes()
00131 {
00132   mSubScriptShift = 0;
00133   mSupScriptShift = 0;
00134 
00135   // check if the subscriptshift attribute is there
00136   nsAutoString value;
00137   if (NS_CONTENT_ATTR_HAS_VALUE == GetAttribute(mContent, mPresentationData.mstyle,
00138                    nsMathMLAtoms::subscriptshift_, value)) {
00139     nsCSSValue cssValue;
00140     if (ParseNumericValue(value, cssValue) && cssValue.IsLengthUnit()) {
00141       mSubScriptShift = CalcLength(GetPresContext(), mStyleContext, cssValue);
00142     }
00143   }
00144   // check if the superscriptshift attribute is there
00145   if (NS_CONTENT_ATTR_HAS_VALUE == GetAttribute(mContent, mPresentationData.mstyle,
00146                    nsMathMLAtoms::superscriptshift_, value)) {
00147     nsCSSValue cssValue;
00148     if (ParseNumericValue(value, cssValue) && cssValue.IsLengthUnit()) {
00149       mSupScriptShift = CalcLength(GetPresContext(), mStyleContext, cssValue);
00150     }
00151   }
00152 }
00153 
00154 NS_IMETHODIMP
00155 nsMathMLmmultiscriptsFrame::Place(nsIRenderingContext& aRenderingContext,
00156                                   PRBool               aPlaceOrigin,
00157                                   nsHTMLReflowMetrics& aDesiredSize)
00158 {
00160   // Get the children's desired sizes
00161 
00162   nscoord minShiftFromXHeight, subDrop, supDrop;
00163 
00165   // Initialize super/sub shifts that
00166   // depend only on the current font
00168 
00169   ProcessAttributes();
00170 
00171   // get x-height (an ex)
00172   aRenderingContext.SetFont(GetStyleFont()->mFont, nsnull);
00173   nsCOMPtr<nsIFontMetrics> fm;
00174   aRenderingContext.GetFontMetrics(*getter_AddRefs(fm));
00175 
00176   nscoord xHeight;
00177   fm->GetXHeight (xHeight);
00178 
00179   nscoord ruleSize;
00180   GetRuleThickness (aRenderingContext, fm, ruleSize);
00181 
00182   // scriptspace from TeX for extra spacing after sup/subscript (0.5pt in plain TeX)
00183   // forced to be at least 1 pixel here
00184   nscoord onePixel = GetPresContext()->IntScaledPixelsToTwips(1);
00185   nscoord scriptSpace = PR_MAX(NSFloatPointsToTwips(0.5f), onePixel);
00186 
00188   // first the shift for the subscript
00189 
00190   // subScriptShift{1,2}
00191   // = minimum amount to shift the subscript down
00192   // = sub{1,2} in TeXbook
00193   // subScriptShift1 = subscriptshift attribute * x-height
00194   nscoord subScriptShift1, subScriptShift2;
00195 
00196   // Get subScriptShift{1,2} default from font
00197   GetSubScriptShifts (fm, subScriptShift1, subScriptShift2);
00198   if (0 < mSubScriptShift) {
00199     // the user has set the subscriptshift attribute
00200     float scaler = ((float) subScriptShift2) / subScriptShift1;
00201     subScriptShift1 = PR_MAX(subScriptShift1, mSubScriptShift);
00202     subScriptShift2 = NSToCoordRound(scaler * subScriptShift1);
00203   }
00204   // the font dependent shift
00205   nscoord subScriptShift = PR_MAX(subScriptShift1,subScriptShift2);
00206 
00208   // next the shift for the superscript
00209 
00210   // supScriptShift{1,2,3}
00211   // = minimum amount to shift the supscript up
00212   // = sup{1,2,3} in TeX
00213   // supScriptShift1 = superscriptshift attribute * x-height
00214   // Note that there are THREE values for supscript shifts depending
00215   // on the current style
00216   nscoord supScriptShift1, supScriptShift2, supScriptShift3;
00217   // Set supScriptShift{1,2,3} default from font
00218   GetSupScriptShifts (fm, supScriptShift1, supScriptShift2, supScriptShift3);
00219   if (0 < mSupScriptShift) {
00220     // the user has set the superscriptshift attribute
00221     float scaler2 = ((float) supScriptShift2) / supScriptShift1;
00222     float scaler3 = ((float) supScriptShift3) / supScriptShift1;
00223     supScriptShift1 = PR_MAX(supScriptShift1, mSupScriptShift);
00224     supScriptShift2 = NSToCoordRound(scaler2 * supScriptShift1);
00225     supScriptShift3 = NSToCoordRound(scaler3 * supScriptShift1);
00226   }
00227 
00228   // get sup script shift depending on current script level and display style
00229   // Rule 18c, App. G, TeXbook
00230   nscoord supScriptShift;
00231   if ( mPresentationData.scriptLevel == 0 &&
00232        NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags) &&
00233       !NS_MATHML_IS_COMPRESSED(mPresentationData.flags)) {
00234     // Style D in TeXbook
00235     supScriptShift = supScriptShift1;
00236   }
00237   else if (NS_MATHML_IS_COMPRESSED(mPresentationData.flags)) {
00238     // Style C' in TeXbook = D',T',S',SS'
00239     supScriptShift = supScriptShift3;
00240   }
00241   else {
00242     // everything else = T,S,SS
00243     supScriptShift = supScriptShift2;
00244   }
00245 
00247   // Get the children's sizes
00249 
00250   nscoord width = 0, prescriptsWidth = 0, rightBearing = 0;
00251   nsIFrame* mprescriptsFrame = nsnull; // frame of <mprescripts/>, if there.
00252   PRBool isSubScript = PR_FALSE;
00253   nscoord minSubScriptShift = 0, minSupScriptShift = 0;
00254   nscoord trySubScriptShift = subScriptShift;
00255   nscoord trySupScriptShift = supScriptShift;
00256   nscoord maxSubScriptShift = subScriptShift;
00257   nscoord maxSupScriptShift = supScriptShift;
00258   PRInt32 count = 0;
00259   nsHTMLReflowMetrics baseSize (nsnull);
00260   nsHTMLReflowMetrics subScriptSize (nsnull);
00261   nsHTMLReflowMetrics supScriptSize (nsnull);
00262   nsIFrame* baseFrame = nsnull;
00263   nsIFrame* subScriptFrame = nsnull;
00264   nsIFrame* supScriptFrame = nsnull;
00265 
00266   PRBool firstPrescriptsPair = PR_FALSE;
00267   nsBoundingMetrics bmBase, bmSubScript, bmSupScript;
00268   nscoord italicCorrection = 0;
00269 
00270   mBoundingMetrics.width = 0;
00271   mBoundingMetrics.ascent = mBoundingMetrics.descent = -0x7FFFFFFF;
00272   aDesiredSize.ascent = aDesiredSize.descent = -0x7FFFFFFF;
00273   aDesiredSize.width = aDesiredSize.height = 0;
00274 
00275   nsIFrame* childFrame = mFrames.FirstChild();
00276   while (childFrame) {
00277     if (childFrame->GetContent()->Tag() == nsMathMLAtoms::mprescripts_) {
00278       if (mprescriptsFrame) {
00279         // duplicate <mprescripts/> found
00280         // report an error, encourage people to get their markups in order
00281         NS_WARNING("invalid markup");
00282         return ReflowError(aRenderingContext, aDesiredSize);
00283       }
00284       mprescriptsFrame = childFrame;
00285       firstPrescriptsPair = PR_TRUE;
00286     }
00287     else {
00288       if (0 == count) {
00289         // base
00290         baseFrame = childFrame;
00291         GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
00292         GetItalicCorrection(bmBase, italicCorrection);
00293         // for the superscript, we always add "a little to spare"
00294         italicCorrection += onePixel;
00295 
00296         // we update mBoundingMetrics.{ascent,descent} with that
00297         // of the baseFrame only after processing all the sup/sub pairs
00298         // XXX need italic correction only *if* there are postscripts ?
00299         mBoundingMetrics.width = bmBase.width + italicCorrection;
00300         mBoundingMetrics.rightBearing = bmBase.rightBearing;
00301         mBoundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten
00302       }
00303       else {
00304         // super/subscript block
00305         if (isSubScript) {
00306           // subscript
00307           subScriptFrame = childFrame;
00308           GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
00309           // get the subdrop from the subscript font
00310           GetSubDropFromChild (subScriptFrame, subDrop);
00311           // parameter v, Rule 18a, App. G, TeXbook
00312           minSubScriptShift = bmBase.descent + subDrop;
00313           trySubScriptShift = PR_MAX(minSubScriptShift,subScriptShift);
00314           mBoundingMetrics.descent =
00315             PR_MAX(mBoundingMetrics.descent,bmSubScript.descent);
00316           aDesiredSize.descent =
00317             PR_MAX(aDesiredSize.descent,subScriptSize.descent);
00318           width = bmSubScript.width + scriptSpace;
00319           rightBearing = bmSubScript.rightBearing;
00320         }
00321         else {
00322           // supscript
00323           supScriptFrame = childFrame;
00324           GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
00325           // get the supdrop from the supscript font
00326           GetSupDropFromChild (supScriptFrame, supDrop);
00327           // parameter u, Rule 18a, App. G, TeXbook
00328           minSupScriptShift = bmBase.ascent - supDrop;
00329           // get min supscript shift limit from x-height
00330           // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook
00331           minShiftFromXHeight = NSToCoordRound
00332             ((bmSupScript.descent + (1.0f/4.0f) * xHeight));
00333           trySupScriptShift =
00334             PR_MAX(minSupScriptShift,PR_MAX(minShiftFromXHeight,supScriptShift));
00335           mBoundingMetrics.ascent =
00336             PR_MAX(mBoundingMetrics.ascent,bmSupScript.ascent);
00337           aDesiredSize.ascent =
00338             PR_MAX(aDesiredSize.ascent,supScriptSize.ascent);
00339           width = PR_MAX(width, bmSupScript.width + scriptSpace);
00340           rightBearing = PR_MAX(rightBearing, bmSupScript.rightBearing);
00341 
00342           if (!mprescriptsFrame) { // we are still looping over base & postscripts
00343             mBoundingMetrics.rightBearing = mBoundingMetrics.width + rightBearing;
00344             mBoundingMetrics.width += width;
00345           }
00346           else {
00347             prescriptsWidth += width;
00348             if (firstPrescriptsPair) {
00349               firstPrescriptsPair = PR_FALSE;
00350               mBoundingMetrics.leftBearing =
00351                 PR_MIN(bmSubScript.leftBearing, bmSupScript.leftBearing);
00352             }
00353           }
00354           width = rightBearing = 0;
00355 
00356           // negotiate between the various shifts so that
00357           // there is enough gap between the sup and subscripts
00358           // Rule 18e, App. G, TeXbook
00359           nscoord gap =
00360             (trySupScriptShift - bmSupScript.descent) -
00361             (bmSubScript.ascent - trySubScriptShift);
00362           if (gap < 4.0f * ruleSize) {
00363             // adjust trySubScriptShift to get a gap of (4.0 * ruleSize)
00364             trySubScriptShift += NSToCoordRound ((4.0f * ruleSize) - gap);
00365           }
00366 
00367           // next we want to ensure that the bottom of the superscript
00368           // will be > (4/5) * x-height above baseline
00369           gap = NSToCoordRound ((4.0f/5.0f) * xHeight -
00370                   (trySupScriptShift - bmSupScript.descent));
00371           if (gap > 0.0f) {
00372             trySupScriptShift += gap;
00373             trySubScriptShift -= gap;
00374           }
00375           
00376           maxSubScriptShift = PR_MAX(maxSubScriptShift, trySubScriptShift);
00377           maxSupScriptShift = PR_MAX(maxSupScriptShift, trySupScriptShift);
00378 
00379           trySubScriptShift = subScriptShift;
00380           trySupScriptShift = supScriptShift;
00381         }
00382       }
00383 
00384       isSubScript = !isSubScript;
00385     }
00386     count++;
00387     childFrame = childFrame->GetNextSibling();
00388   }
00389   // note: width=0 if all sup-sub pairs match correctly
00390   if ((0 != width) || !baseFrame || !subScriptFrame || !supScriptFrame) {
00391     // report an error, encourage people to get their markups in order
00392     NS_WARNING("invalid markup");
00393     return ReflowError(aRenderingContext, aDesiredSize);
00394   }
00395 
00396   // we left out the width of prescripts, so ...
00397   mBoundingMetrics.rightBearing += prescriptsWidth;
00398   mBoundingMetrics.width += prescriptsWidth;
00399 
00400   // we left out the base during our bounding box updates, so ...
00401   mBoundingMetrics.ascent =
00402     PR_MAX(mBoundingMetrics.ascent+maxSupScriptShift,bmBase.ascent);
00403   mBoundingMetrics.descent =
00404     PR_MAX(mBoundingMetrics.descent+maxSubScriptShift,bmBase.descent);
00405 
00406   // get the reflow metrics ...
00407   aDesiredSize.ascent =
00408     PR_MAX(aDesiredSize.ascent+maxSupScriptShift,baseSize.ascent);
00409   aDesiredSize.descent =
00410     PR_MAX(aDesiredSize.descent+maxSubScriptShift,baseSize.descent);
00411   aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
00412   aDesiredSize.width = mBoundingMetrics.width;
00413   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
00414 
00415   mReference.x = 0;
00416   mReference.y = aDesiredSize.ascent;
00417 
00419   // Place Children
00420 
00421   // Place prescripts, followed by base, and then postscripts.
00422   // The list of frames is in the order: {base} {postscripts} {prescripts}
00423   // We go over the list in a circular manner, starting at <prescripts/>
00424 
00425   if (aPlaceOrigin) {
00426     nscoord dx = 0, dy = 0;
00427 
00428     count = 0;
00429     childFrame = mprescriptsFrame;
00430     do {
00431       if (!childFrame) { // end of prescripts,
00432         // place the base ...
00433         childFrame = baseFrame;
00434         dy = aDesiredSize.ascent - baseSize.ascent;
00435         FinishReflowChild (baseFrame, GetPresContext(), nsnull, baseSize, dx, dy, 0);
00436         dx += bmBase.width + italicCorrection;
00437       }
00438       else if (mprescriptsFrame != childFrame) {
00439         // process each sup/sub pair
00440         if (0 == count) {
00441           subScriptFrame = childFrame;
00442           count = 1;
00443         }
00444         else if (1 == count) {
00445           supScriptFrame = childFrame;
00446           count = 0;
00447 
00448           // get the ascent/descent of sup/subscripts stored in their rects
00449           // rect.x = descent, rect.y = ascent
00450           GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
00451           GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
00452 
00453           // center w.r.t. largest width
00454           width = PR_MAX(subScriptSize.width, supScriptSize.width);
00455 
00456           dy = aDesiredSize.ascent - subScriptSize.ascent +
00457             maxSubScriptShift;
00458           FinishReflowChild (subScriptFrame, GetPresContext(), nsnull, subScriptSize,
00459                              dx + (width-subScriptSize.width)/2, dy, 0);
00460 
00461           dy = aDesiredSize.ascent - supScriptSize.ascent -
00462             maxSupScriptShift;
00463           FinishReflowChild (supScriptFrame, GetPresContext(), nsnull, supScriptSize,
00464                              dx + (width-supScriptSize.width)/2, dy, 0);
00465 
00466           dx += width + scriptSpace;
00467         }
00468       }
00469       childFrame = childFrame->GetNextSibling();
00470     } while (mprescriptsFrame != childFrame);
00471   }
00472 
00473   return NS_OK;
00474 }