mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
2c717f398d
This changes a number of callers that were previously bypassing the use of the correct language (with either no language or the charset-detected language on the pres context via nsPresContext::GetMetricsFor) and/or the correct user font set to pass the correct values, which should improve the correctness of the behavior of whatever they were using the fonts for, and also reduce the number of unique sets of font metrics requested (which helps nsFontCache effectiveness).
445 lines
16 KiB
C++
445 lines
16 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Mozilla MathML Project.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* The University Of Queensland.
|
|
* Portions created by the Initial Developer are Copyright (C) 1999
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Roger B. Sidje <rbs@maths.uq.edu.au>
|
|
* David J. Fiddes <D.J.Fiddes@hw.ac.uk>
|
|
* Vilya Harvey <vilya@nag.co.uk>
|
|
* Shyjan Mahamud <mahamud@cs.cmu.edu>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsFrame.h"
|
|
#include "nsPresContext.h"
|
|
#include "nsStyleContext.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsRenderingContext.h"
|
|
|
|
#include "nsMathMLmrootFrame.h"
|
|
|
|
//
|
|
// <msqrt> and <mroot> -- form a radical - implementation
|
|
//
|
|
|
|
//NOTE:
|
|
// The code assumes that TeX fonts are picked.
|
|
// There is no fall-back to draw the branches of the sqrt explicitly
|
|
// in the case where TeX fonts are not there. In general, there are no
|
|
// fall-back(s) in MathML when some (freely-downloadable) fonts are missing.
|
|
// Otherwise, this will add much work and unnecessary complexity to the core
|
|
// MathML engine. Assuming that authors have the free fonts is part of the
|
|
// deal. We are not responsible for cases of misconfigurations out there.
|
|
|
|
// additional style context to be used by our MathMLChar.
|
|
#define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0
|
|
|
|
static const PRUnichar kSqrChar = PRUnichar(0x221A);
|
|
|
|
nsIFrame*
|
|
NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
|
{
|
|
return new (aPresShell) nsMathMLmrootFrame(aContext);
|
|
}
|
|
|
|
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame)
|
|
|
|
nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext* aContext) :
|
|
nsMathMLContainerFrame(aContext),
|
|
mSqrChar(),
|
|
mBarRect()
|
|
{
|
|
}
|
|
|
|
nsMathMLmrootFrame::~nsMathMLmrootFrame()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLmrootFrame::Init(nsIContent* aContent,
|
|
nsIFrame* aParent,
|
|
nsIFrame* aPrevInFlow)
|
|
{
|
|
nsresult rv = nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
|
|
|
|
nsPresContext *presContext = PresContext();
|
|
|
|
// No need to track the style context given to our MathML char.
|
|
// The Style System will use Get/SetAdditionalStyleContext() to keep it
|
|
// up-to-date if dynamic changes arise.
|
|
nsAutoString sqrChar; sqrChar.Assign(kSqrChar);
|
|
mSqrChar.SetData(presContext, sqrChar);
|
|
ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mSqrChar, PR_TRUE);
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLmrootFrame::TransmitAutomaticData()
|
|
{
|
|
// 1. The REC says:
|
|
// The <mroot> element increments scriptlevel by 2, and sets displaystyle to
|
|
// "false", within index, but leaves both attributes unchanged within base.
|
|
// 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
|
|
UpdatePresentationDataFromChildAt(1, 1,
|
|
~NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED,
|
|
NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED);
|
|
UpdatePresentationDataFromChildAt(0, 0,
|
|
NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|
const nsRect& aDirtyRect,
|
|
const nsDisplayListSet& aLists)
|
|
{
|
|
/////////////
|
|
// paint the content we are square-rooting
|
|
nsresult rv = nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
/////////////
|
|
// paint the sqrt symbol
|
|
if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
|
|
rv = mSqrChar.Display(aBuilder, this, aLists);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = DisplayBar(aBuilder, this, mBarRect, aLists);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
#if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
|
|
// for visual debug
|
|
nsRect rect;
|
|
mSqrChar.GetRect(rect);
|
|
nsBoundingMetrics bm;
|
|
mSqrChar.GetBoundingMetrics(bm);
|
|
rv = DisplayBoundingMetrics(aBuilder, this, rect.TopLeft(), bm, aLists);
|
|
#endif
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
static void
|
|
GetRadicalXOffsets(nscoord aIndexWidth, nscoord aSqrWidth,
|
|
nsFontMetrics* aFontMetrics,
|
|
nscoord* aIndexOffset, nscoord* aSqrOffset)
|
|
{
|
|
// The index is tucked in closer to the radical while making sure
|
|
// that the kern does not make the index and radical collide
|
|
nscoord dxIndex, dxSqr;
|
|
nscoord xHeight = aFontMetrics->XHeight();
|
|
nscoord indexRadicalKern = NSToCoordRound(1.35f * xHeight);
|
|
if (indexRadicalKern > aIndexWidth) {
|
|
dxIndex = indexRadicalKern - aIndexWidth;
|
|
dxSqr = 0;
|
|
}
|
|
else {
|
|
dxIndex = 0;
|
|
dxSqr = aIndexWidth - indexRadicalKern;
|
|
}
|
|
// avoid collision by leaving a minimum space between index and radical
|
|
nscoord minimumClearance = aSqrWidth/2;
|
|
if (dxIndex + aIndexWidth + minimumClearance > dxSqr + aSqrWidth) {
|
|
if (aIndexWidth + minimumClearance < aSqrWidth) {
|
|
dxIndex = aSqrWidth - (aIndexWidth + minimumClearance);
|
|
dxSqr = 0;
|
|
}
|
|
else {
|
|
dxIndex = 0;
|
|
dxSqr = (aIndexWidth + minimumClearance) - aSqrWidth;
|
|
}
|
|
}
|
|
|
|
if (aIndexOffset)
|
|
*aIndexOffset = dxIndex;
|
|
if (aSqrOffset)
|
|
*aSqrOffset = dxSqr;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
|
|
nsReflowStatus childStatus;
|
|
|
|
aDesiredSize.width = aDesiredSize.height = 0;
|
|
aDesiredSize.ascent = 0;
|
|
|
|
nsBoundingMetrics bmSqr, bmBase, bmIndex;
|
|
nsRenderingContext& renderingContext = *aReflowState.rendContext;
|
|
|
|
//////////////////
|
|
// Reflow Children
|
|
|
|
PRInt32 count = 0;
|
|
nsIFrame* baseFrame = nsnull;
|
|
nsIFrame* indexFrame = nsnull;
|
|
nsHTMLReflowMetrics baseSize;
|
|
nsHTMLReflowMetrics indexSize;
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
// ask our children to compute their bounding metrics
|
|
nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mFlags
|
|
| NS_REFLOW_CALC_BOUNDING_METRICS);
|
|
nsHTMLReflowState childReflowState(aPresContext, aReflowState,
|
|
childFrame, availSize);
|
|
rv = ReflowChild(childFrame, aPresContext,
|
|
childDesiredSize, childReflowState, childStatus);
|
|
//NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
|
|
if (NS_FAILED(rv)) {
|
|
// Call DidReflow() for the child frames we successfully did reflow.
|
|
DidReflowChildren(mFrames.FirstChild(), childFrame);
|
|
return rv;
|
|
}
|
|
if (0 == count) {
|
|
// base
|
|
baseFrame = childFrame;
|
|
baseSize = childDesiredSize;
|
|
bmBase = childDesiredSize.mBoundingMetrics;
|
|
}
|
|
else if (1 == count) {
|
|
// index
|
|
indexFrame = childFrame;
|
|
indexSize = childDesiredSize;
|
|
bmIndex = childDesiredSize.mBoundingMetrics;
|
|
}
|
|
count++;
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
if (2 != count) {
|
|
// report an error, encourage people to get their markups in order
|
|
rv = ReflowError(renderingContext, aDesiredSize);
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
|
|
// Call DidReflow() for the child frames we successfully did reflow.
|
|
DidReflowChildren(mFrames.FirstChild(), childFrame);
|
|
return rv;
|
|
}
|
|
|
|
////////////
|
|
// Prepare the radical symbol and the overline bar
|
|
|
|
nsRefPtr<nsFontMetrics> fm;
|
|
nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
|
|
renderingContext.SetFont(fm);
|
|
|
|
// For radical glyphs from TeX fonts and some of the radical glyphs from
|
|
// Mathematica fonts, the thickness of the overline can be obtained from the
|
|
// ascent of the glyph. Most fonts however have radical glyphs above the
|
|
// baseline so no assumption can be made about the meaning of the ascent.
|
|
nscoord ruleThickness, leading, em;
|
|
GetRuleThickness(renderingContext, fm, ruleThickness);
|
|
|
|
PRUnichar one = '1';
|
|
nsBoundingMetrics bmOne = renderingContext.GetBoundingMetrics(&one, 1);
|
|
|
|
// get the leading to be left at the top of the resulting frame
|
|
// this seems more reliable than using fm->GetLeading() on suspicious fonts
|
|
GetEmHeight(fm, em);
|
|
leading = nscoord(0.2f * em);
|
|
|
|
// Rule 11, App. G, TeXbook
|
|
// psi = clearance between rule and content
|
|
nscoord phi = 0, psi = 0;
|
|
if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags))
|
|
phi = fm->XHeight();
|
|
else
|
|
phi = ruleThickness;
|
|
psi = ruleThickness + phi/4;
|
|
|
|
// built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
|
|
if (bmOne.ascent > bmBase.ascent)
|
|
psi += bmOne.ascent - bmBase.ascent;
|
|
|
|
// make sure that the rule appears on on screen
|
|
nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
|
|
if (ruleThickness < onePixel) {
|
|
ruleThickness = onePixel;
|
|
}
|
|
|
|
// adjust clearance psi to get an exact number of pixels -- this
|
|
// gives a nicer & uniform look on stacked radicals (bug 130282)
|
|
nscoord delta = psi % onePixel;
|
|
if (delta)
|
|
psi += onePixel - delta; // round up
|
|
|
|
// Stretch the radical symbol to the appropriate height if it is not big enough.
|
|
nsBoundingMetrics contSize = bmBase;
|
|
contSize.descent = bmBase.ascent + bmBase.descent + psi;
|
|
contSize.ascent = ruleThickness;
|
|
|
|
// height(radical) should be >= height(base) + psi + ruleThickness
|
|
nsBoundingMetrics radicalSize;
|
|
mSqrChar.Stretch(aPresContext, renderingContext,
|
|
NS_STRETCH_DIRECTION_VERTICAL,
|
|
contSize, radicalSize,
|
|
NS_STRETCH_LARGER);
|
|
// radicalSize have changed at this point, and should match with
|
|
// the bounding metrics of the char
|
|
mSqrChar.GetBoundingMetrics(bmSqr);
|
|
|
|
// Update the desired size for the container (like msqrt, index is not yet included)
|
|
// the baseline will be that of the base.
|
|
mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness;
|
|
mBoundingMetrics.descent =
|
|
NS_MAX(bmBase.descent,
|
|
(bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
|
|
mBoundingMetrics.width = bmSqr.width + bmBase.width;
|
|
mBoundingMetrics.leftBearing = bmSqr.leftBearing;
|
|
mBoundingMetrics.rightBearing = bmSqr.width +
|
|
NS_MAX(bmBase.width, bmBase.rightBearing); // take also care of the rule
|
|
|
|
aDesiredSize.ascent = mBoundingMetrics.ascent + leading;
|
|
aDesiredSize.height = aDesiredSize.ascent +
|
|
NS_MAX(baseSize.height - baseSize.ascent,
|
|
mBoundingMetrics.descent + ruleThickness);
|
|
aDesiredSize.width = mBoundingMetrics.width;
|
|
|
|
/////////////
|
|
// Re-adjust the desired size to include the index.
|
|
|
|
// the index is raised by some fraction of the height
|
|
// of the radical, see \mroot macro in App. B, TexBook
|
|
nscoord raiseIndexDelta = NSToCoordRound(0.6f * (bmSqr.ascent + bmSqr.descent));
|
|
nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical
|
|
- (bmSqr.ascent + bmSqr.descent) // to bottom of radical
|
|
+ raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index
|
|
|
|
nscoord indexClearance = 0;
|
|
if (mBoundingMetrics.ascent < indexRaisedAscent) {
|
|
indexClearance =
|
|
indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index
|
|
mBoundingMetrics.ascent = indexRaisedAscent;
|
|
nscoord descent = aDesiredSize.height - aDesiredSize.ascent;
|
|
aDesiredSize.ascent = mBoundingMetrics.ascent + leading;
|
|
aDesiredSize.height = aDesiredSize.ascent + descent;
|
|
}
|
|
|
|
nscoord dxIndex, dxSqr;
|
|
GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr);
|
|
|
|
// place the index
|
|
nscoord dx = dxIndex;
|
|
nscoord dy = aDesiredSize.ascent - (indexRaisedAscent + indexSize.ascent - bmIndex.ascent);
|
|
FinishReflowChild(indexFrame, aPresContext, nsnull, indexSize, dx, dy, 0);
|
|
|
|
// place the radical symbol and the radical bar
|
|
dx = dxSqr;
|
|
dy = indexClearance + leading; // leave a leading at the top
|
|
mSqrChar.SetRect(nsRect(dx, dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
|
|
dx += bmSqr.width;
|
|
mBarRect.SetRect(dx, dy, bmBase.width, ruleThickness);
|
|
|
|
// place the base
|
|
dy = aDesiredSize.ascent - baseSize.ascent;
|
|
FinishReflowChild(baseFrame, aPresContext, nsnull, baseSize, dx, dy, 0);
|
|
|
|
mReference.x = 0;
|
|
mReference.y = aDesiredSize.ascent;
|
|
|
|
mBoundingMetrics.width = dx + bmBase.width;
|
|
mBoundingMetrics.leftBearing =
|
|
NS_MIN(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
|
|
mBoundingMetrics.rightBearing = dx +
|
|
NS_MAX(bmBase.width, bmBase.rightBearing);
|
|
|
|
aDesiredSize.width = mBoundingMetrics.width;
|
|
aDesiredSize.mBoundingMetrics = mBoundingMetrics;
|
|
GatherAndStoreOverflow(&aDesiredSize);
|
|
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
|
|
return NS_OK;
|
|
}
|
|
|
|
/* virtual */ nscoord
|
|
nsMathMLmrootFrame::GetIntrinsicWidth(nsRenderingContext* aRenderingContext)
|
|
{
|
|
nsIFrame* baseFrame = mFrames.FirstChild();
|
|
nsIFrame* indexFrame = nsnull;
|
|
if (baseFrame)
|
|
indexFrame = baseFrame->GetNextSibling();
|
|
if (!indexFrame || indexFrame->GetNextSibling()) {
|
|
nsHTMLReflowMetrics desiredSize;
|
|
ReflowError(*aRenderingContext, desiredSize);
|
|
return desiredSize.width;
|
|
}
|
|
|
|
nscoord baseWidth =
|
|
nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame,
|
|
nsLayoutUtils::PREF_WIDTH);
|
|
nscoord indexWidth =
|
|
nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame,
|
|
nsLayoutUtils::PREF_WIDTH);
|
|
nscoord sqrWidth = mSqrChar.GetMaxWidth(PresContext(), *aRenderingContext);
|
|
|
|
nscoord dxSqr;
|
|
GetRadicalXOffsets(indexWidth, sqrWidth, aRenderingContext->FontMetrics(),
|
|
nsnull, &dxSqr);
|
|
|
|
return dxSqr + sqrWidth + baseWidth;
|
|
}
|
|
|
|
// ----------------------
|
|
// the Style System will use these to pass the proper style context to our MathMLChar
|
|
nsStyleContext*
|
|
nsMathMLmrootFrame::GetAdditionalStyleContext(PRInt32 aIndex) const
|
|
{
|
|
switch (aIndex) {
|
|
case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
|
|
return mSqrChar.GetStyleContext();
|
|
break;
|
|
default:
|
|
return nsnull;
|
|
}
|
|
}
|
|
|
|
void
|
|
nsMathMLmrootFrame::SetAdditionalStyleContext(PRInt32 aIndex,
|
|
nsStyleContext* aStyleContext)
|
|
{
|
|
switch (aIndex) {
|
|
case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
|
|
mSqrChar.SetStyleContext(aStyleContext);
|
|
break;
|
|
}
|
|
}
|