Mozilla/mozilla/layout/mathml/base/src/nsMathMLChar.cpp
2000-05-08 07:18:10 +00:00

1484 lines
50 KiB
C++

/*
* 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 University Of Queensland are
* Copyright (C) 1999 The University Of Queensland. All Rights Reserved.
*
* Contributor(s):
* Roger B. Sidje <rbs@maths.uq.edu.au>
* Shyjan Mahamud <mahamud@cs.cmu.edu>
*/
#include "nsCOMPtr.h"
#include "nsHTMLParts.h"
#include "nsIHTMLContent.h"
#include "nsFrame.h"
#include "nsLineLayout.h"
#include "nsHTMLIIDs.h"
#include "nsIPresContext.h"
#include "nsHTMLAtoms.h"
#include "nsUnitConversion.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
#include "nsINameSpaceManager.h"
#include "nsString.h"
#include "nsIRenderingContext.h"
#include "nsIFontMetrics.h"
#include "nsStyleUtil.h"
#include "nsIDeviceContext.h"
#include "nsCSSRendering.h"
#include "nsQuickSort.h"
#include "nsMathMLOperators.h"
#include "nsMathMLChar.h"
// List of all stretchy MathML chars with their attributes --------------
// ----------------------------------------------------------------------
struct nsCharInfo {
PRUnichar mUnicode;
PRInt32 mDirection;
nsGlyphTable* mGlyphTable; // placeholder to cache the table with the smallest glue
};
nsCharInfo gCharInfo[] = {
#define WANT_CHAR_INFO
#include "nsMathMLCharList.h"
#undef WANT_CHAR_INFO
};
// ----------------------------------------------------------------------
// nsGlyphTable is a class that provides an interface for accessing glyphs
// of stretchy chars. It acts like a table that stores the variants of bigger
// sizes (if any) and the partial glyphs needed to build extensible symbols.
// An instance of nsGlyphTable is associated to one font.
//
// A char for which nsGlyphTable::Has(aChar) is true means that the table
// contains some glyphs (bigger and/or partial) that can be used to render
// the char. Bigger sizes (if any) of the char can then be retrieved with
// BigOf(aSize). Partial glyphs can be retrieved with TopOf(), GlueOf(), etc.
// ----------------------------------------------------------------------
// A table consists of "nsGlyphCode"s which are viewed either as Unicode
// points or as direct glyph indices, depending on the type of the table.
// XXX The latter is not yet supported.
// XXX There are many assertions in the code to ensure that callers
// write correct/efficient/speedy code. As a rule of thumb, you should
// ckeck for Has(aChar) prior to using a char within a glyphtable.
#define NS_TABLE_TYPE_UNICODE 0
#define NS_TABLE_TYPE_GLYPH_INDEX 1
struct nsCharData {
nsMathMLCharEnum mEnum;
PRInt32 mIndex;
};
class nsGlyphTable {
public:
nsGlyphTable(PRInt32 aType,
char* aFontName,
nsCharData* aCharArray,
PRInt32 aCharCount,
nsGlyphCode* aGlyphArray,
PRInt32 aGlyphCount)
{
mType = aType;
mFontName.Assign(aFontName);
mCharArray = aCharArray;
mCharCount = aCharCount;
mGlyphArray = aGlyphArray;
mGlyphCount = aGlyphCount;
mNextTable = nsnull;
mIsSorted = PR_FALSE;
mCache = mCharArray;
}
~nsGlyphTable() // not a virtual destructor: this class is not intended to be subclassed
{
}
void GetFontName(nsString& aFontName)
{
aFontName.Assign(mFontName);
}
nsGlyphTable* GetNextTable(void)
{
return mNextTable;
}
void SetNextTable(nsGlyphTable* aNextTable)
{
mNextTable = aNextTable;
}
PRBool Has(nsMathMLCharEnum aEnum)
{
return PRBool(IndexOf(aEnum) < mGlyphCount);
}
#ifdef NS_DEBUG
// a linear search function for debug mode only
PRBool Has(nsGlyphCode aGlyphCode)
{
NS_ASSERTION(aGlyphCode, "bad usage of a glyph table");
for (PRInt32 i = 0; i < mGlyphCount; i++)
if (mGlyphArray[i] == aGlyphCode) return PR_TRUE;
return PR_FALSE;
}
#endif
nsGlyphCode TopOf(nsMathMLCharEnum aEnum)
{
NS_ASSERTION(Has(aEnum), "bad usage of a glyph table");
return mGlyphArray[IndexOf(aEnum)];
}
nsGlyphCode MiddleOf(nsMathMLCharEnum aEnum)
{
NS_ASSERTION(IndexOf(aEnum) + 1 < mGlyphCount, "bad usage of a glyph table");
return mGlyphArray[IndexOf(aEnum) + 1];
}
nsGlyphCode BottomOf(nsMathMLCharEnum aEnum)
{
NS_ASSERTION(IndexOf(aEnum) + 2 < mGlyphCount, "bad usage of a glyph table");
return mGlyphArray[IndexOf(aEnum) + 2];
}
nsGlyphCode GlueOf(nsMathMLCharEnum aEnum)
{
NS_ASSERTION(IndexOf(aEnum) + 3 < mGlyphCount, "bad usage of a glyph table");
return mGlyphArray[IndexOf(aEnum) + 3];
}
nsGlyphCode BigOf(nsMathMLCharEnum aEnum, PRInt32 aSize)
{
NS_ASSERTION(aSize>=0 && IndexOf(aEnum) + 4 + aSize < mGlyphCount, "bad usage of a glyph table");
return mGlyphArray[IndexOf(aEnum) + 4 + aSize];
}
nsGlyphCode LeftOf(nsMathMLCharEnum aEnum)
{
NS_ASSERTION(Has(aEnum), "bad usage of a glyph table");
return mGlyphArray[IndexOf(aEnum)];
}
nsGlyphCode RightOf(nsMathMLCharEnum aEnum)
{
NS_ASSERTION(IndexOf(aEnum) + 2 < mGlyphCount, "bad usage of a glyph table");
return mGlyphArray[IndexOf(aEnum) + 2];
}
// use these to measure/render a glyph that comes from this table
nsresult GetBoundingMetrics(nsIRenderingContext& aRenderingContext,
nsGlyphCode aGlyphCode,
nsBoundingMetrics& aBoundingMetrics)
{
#ifdef NS_DEBUG
NS_ASSERTION(Has(aGlyphCode), "bad usage of a glyph table");
#endif
if (mType == NS_TABLE_TYPE_UNICODE)
return aRenderingContext.GetBoundingMetrics((PRUnichar*)&aGlyphCode, PRUint32(1), aBoundingMetrics);
// else mType == NS_TABLE_TYPE_GLYPH_INDEX
return NS_ERROR_NOT_IMPLEMENTED;
// return aRenderingContext.GetBoundingMetricsI((PRUint16*)&aGlyphCode, PRUint32(1), aBoundingMetrics);
}
void
DrawGlyph(nsIRenderingContext& aRenderingContext,
nsGlyphCode aGlyphCode,
nscoord aX,
nscoord aY,
nsRect* aClipRect = nsnull);
private:
PRInt32 mType; // NS_TABLE_TYPE_UNICODE
// NS_TABLE_TYPE_GLYPH_INDEX
nsCString mFontName; // The name of the font associated to this table.
PRInt32 mCharCount;
nsCharData* mCharArray; // All the stretchy chars supported by the font associated to
// this table.
PRInt32 mGlyphCount;
nsGlyphCode* mGlyphArray; // For each stretchy char in this font, this array contains
// the unicode points (or glyph-indices) of the partial glyphs
// needed to build extensible symbols, and of the readily-available
// variants of bigger sizes (if any) that the font already contains.
nsGlyphTable* mNextTable; // Pointer to another table --to enable walking/trying several fonts.
PRBool mIsSorted; // mCharArray is kept sorted so that we can speedily lookup
// the index of a char using binary search.
nsCharData* mCache; // For speedy re-use, always cache the last char used in IndexOf() ...
// ---------------------------
// helper functions
// compare function for doing a quick sort of our chars in ascending order
static int compare( const void *a, const void *b , void *unused)
{
return (PRInt32)(((nsCharData*)a)->mEnum) - (PRInt32)(((nsCharData*)b)->mEnum);
}
// we use this a lot... to lookup the starting index (in mGlyphArray) of the
// list of glyphs corresponding to a char. This list consists of the 4 partial
// glyphs: top (or left), middle, bottom (or right), glue, followed by the
// bigger sizes (if any): size0, ..., size{N-1}, *and* the null separator.
// (Note: retrieval with BigOf(aSize) is 0-based, BigOf(0) gives the first
// glyph size which is the normal size.)
PRInt32 IndexOf(nsMathMLCharEnum aEnum);
};
PRInt32
nsGlyphTable::IndexOf(nsMathMLCharEnum aEnum)
{
// quick return if it is identical with our cache ...
if (mCache->mEnum == aEnum) return mCache->mIndex;
// quick sort if this is the first call ...
if (!mIsSorted) {
NS_QuickSort(mCharArray, mCharCount, sizeof(nsCharData), compare, nsnull);
mIsSorted = PR_TRUE;
}
// binary search ...
PRInt32 low = 0;
PRInt32 high = mCharCount-1;
while (low <= high) {
PRInt32 middle = (low + high) >> 1;
PRInt32 result = (PRInt32)aEnum - (PRInt32)mCharArray[middle].mEnum;
if (result == 0) {
mCache = &mCharArray[middle]; // update our cache ...
return mCache->mIndex;
}
if (result > 0)
low = middle + 1;
else
high = middle - 1;
}
return mGlyphCount;
}
// draw a glyph in a clipped area so that we don't have hairy chars pending outside
void
nsGlyphTable::DrawGlyph(nsIRenderingContext& aRenderingContext,
nsGlyphCode aGlyphCode,
nscoord aX,
nscoord aY,
nsRect* aClipRect)
{
#ifdef NS_DEBUG
NS_ASSERTION(Has(aGlyphCode), "bad usage of a glyph table");
#endif
PRBool clipState;
if (aClipRect) {
aRenderingContext.PushState();
aRenderingContext.SetClipRect(*aClipRect, nsClipCombine_kIntersect, clipState);
}
if (mType == NS_TABLE_TYPE_UNICODE)
aRenderingContext.DrawString((PRUnichar*)&aGlyphCode, PRUint32(1), aX, aY);
else
NS_ASSERTION(0, "Error *** Not yet implemented");
//aRenderingContext.DrawStringI((PRUint16*)&aGlyphCode, PRUint32(1), aX, aY);
if (aClipRect) {
aRenderingContext.PopState(clipState);
}
}
// Class used to walk/try all the the glyph tables that we have.
// Glyph tables are singly linked together through their next-table pointer.
// -----------------------------------------------------------------------------------
class nsGlyphTableList {
public:
nsGlyphTableList(void)
{
mFirstTable = nsnull;
mIsInitialized = PR_FALSE;
}
PRBool IsInitialized(void)
{
return mIsInitialized;
}
nsGlyphTable*
FirstTable(void)
{
return mFirstTable;
}
// Create a list of glyph tables. The list will only select glyph tables that
// are associated to <b>fonts currently installed on the system</b>...
void Init(nsIPresContext* aPresContext,
nsGlyphTable** aGlyphTables);
// Find a glyph table that has a glyph for a char.
nsGlyphTable*
FindTableFor(nsMathMLCharEnum aCharEnum);
// Check for existence of a glyph table.
PRBool
Has(nsGlyphTable* aGlyphTable);
private:
nsGlyphTable* mFirstTable;
PRBool mIsInitialized;
};
void
nsGlyphTableList::Init(nsIPresContext* aPresContext,
nsGlyphTable** aGlyphTables)
{
NS_ASSERTION(!mIsInitialized, "glyph table list is already initialized");
mIsInitialized = PR_TRUE;
nsCOMPtr<nsIDeviceContext> deviceContext;
aPresContext->GetDeviceContext(getter_AddRefs(deviceContext));
PRBool aliased;
nsAutoString fontName, localName;
nsGlyphTable* lastTable = nsnull;
nsGlyphTable** glyphTable = aGlyphTables;
while (*glyphTable) {
// check for existence of the font
(*glyphTable)->GetFontName(fontName);
deviceContext->GetLocalFontName(fontName, localName, aliased);
if (aliased || (NS_OK == deviceContext->CheckFontExistence(localName)))
{
if (mFirstTable == nsnull) {
mFirstTable = lastTable = *glyphTable;
}
else {
lastTable->SetNextTable(*glyphTable);
lastTable = *glyphTable;
}
}
else { // the font is not installed
#ifdef NS_DEBUG
char str[50];
localName.ToCString(str, sizeof(str));
printf("WARNING *** Missing the %s font to stretch MathML symbols!\n", str);
printf(" Why don't you install the %s font on your system for a better MathML experience!\n", str);
#endif
}
glyphTable++;
}
}
nsGlyphTable*
nsGlyphTableList::FindTableFor(nsMathMLCharEnum aCharEnum)
{
NS_ASSERTION(mIsInitialized, "glyph table list must be initialized first");
nsGlyphTable* glyphTable = mFirstTable;
while (glyphTable) {
if (glyphTable->Has(aCharEnum)) {
return glyphTable;
}
glyphTable = glyphTable->GetNextTable();
}
return nsnull;
}
PRBool
nsGlyphTableList::Has(nsGlyphTable* aGlyphTable)
{
NS_ASSERTION(mIsInitialized, "glyph table list must be initialized first");
nsGlyphTable* glyphTable = mFirstTable;
while (glyphTable) {
if (glyphTable == aGlyphTable) {
return PR_TRUE;
}
glyphTable = glyphTable->GetNextTable();
}
return PR_FALSE;
}
// -----------------------------------------------------------------------------------
// Here is the global list of glyph tables that we will be using ...
nsGlyphTableList gGlyphTableList;
// And this is a dummy glyph table that we will use as a flag for gCharInfo[].mGlyphTable
nsGlyphTable gGlyphTableUNDEFINED =
nsGlyphTable(NS_TABLE_TYPE_UNICODE, "UNDEFINED",
nsnull, 0, nsnull, 0);
// Data sets that we have ...
// -----------------------------------------------------------------------------------
// Data for strecthy chars that are supported by the Symbol font ---------------------
static nsCharData gCharDataSymbol[] = {
#define WANT_SYMBOL_DATA
#define WANT_CHAR_DATA
#include "nsMathMLCharList.h"
#undef WANT_CHAR_DATA
#undef WANT_SYMBOL_DATA
};
static nsGlyphCode gGlyphCodeSymbol[] = {
#define WANT_SYMBOL_DATA
#define WANT_GLYPH_DATA
#include "nsMathMLCharList.h"
#undef WANT_GLYPH_DATA
#undef WANT_SYMBOL_DATA
};
nsGlyphTable gGlyphTableSymbol =
nsGlyphTable(NS_TABLE_TYPE_UNICODE, "Symbol",
gCharDataSymbol, sizeof(gCharDataSymbol) / sizeof(gCharDataSymbol[0]),
gGlyphCodeSymbol, sizeof(gGlyphCodeSymbol) / sizeof(gGlyphCodeSymbol[0]));
// -----------------------------------------------------------------------------------
// Data for strecthy chars that are supported by the MT Extra font -------------------
static nsCharData gCharDataMTExtra[] = {
#define WANT_MTEXTRA_DATA
#define WANT_CHAR_DATA
#include "nsMathMLCharList.h"
#undef WANT_CHAR_DATA
#undef WANT_MTEXTRA_DATA
};
static nsGlyphCode gGlyphCodeMTExtra[] = {
#define WANT_MTEXTRA_DATA
#define WANT_GLYPH_DATA
#include "nsMathMLCharList.h"
#undef WANT_GLYPH_DATA
#undef WANT_MTEXTRA_DATA
};
nsGlyphTable gGlyphTableMTExtra =
nsGlyphTable(NS_TABLE_TYPE_UNICODE, "MT Extra",
gCharDataMTExtra, sizeof(gCharDataMTExtra) / sizeof(gCharDataMTExtra[0]),
gGlyphCodeMTExtra, sizeof(gGlyphCodeMTExtra) / sizeof(gGlyphCodeMTExtra[0]));
// -----------------------------------------------------------------------------------
// Data for strecthy chars that are supported by TeX's CMEX font ---------------------
static nsCharData gCharDataCMEX[] = {
#define WANT_CMEX_DATA
#define WANT_CHAR_DATA
#include "nsMathMLCharList.h"
#undef WANT_CHAR_DATA
#undef WANT_CMEX_DATA
};
static nsGlyphCode gGlyphCodeCMEX[] = {
#define WANT_CMEX_DATA
#define WANT_GLYPH_DATA
#include "nsMathMLCharList.h"
#undef WANT_GLYPH_DATA
#undef WANT_CMEX_DATA
};
nsGlyphTable gGlyphTableCMEX =
nsGlyphTable(NS_TABLE_TYPE_UNICODE, "CMEX10",
gCharDataCMEX, sizeof(gCharDataCMEX) / sizeof(gCharDataCMEX[0]),
gGlyphCodeCMEX, sizeof(gGlyphCodeCMEX) / sizeof(gGlyphCodeCMEX[0]));
// -----------------------------------------------------------------------------------
// Data for strecthy chars that are supported by TeX's CMSY font ---------------------
static nsCharData gCharDataCMSY[] = {
#define WANT_CMSY_DATA
#define WANT_CHAR_DATA
#include "nsMathMLCharList.h"
#undef WANT_CHAR_DATA
#undef WANT_CMSY_DATA
};
static nsGlyphCode gGlyphCodeCMSY[] = {
#define WANT_CMSY_DATA
#define WANT_GLYPH_DATA
#include "nsMathMLCharList.h"
#undef WANT_GLYPH_DATA
#undef WANT_CMSY_DATA
};
nsGlyphTable gGlyphTableCMSY =
nsGlyphTable(NS_TABLE_TYPE_UNICODE, "CMSY10",
gCharDataCMSY, sizeof(gCharDataCMSY) / sizeof(gCharDataCMSY[0]),
gGlyphCodeCMSY, sizeof(gGlyphCodeCMSY) / sizeof(gGlyphCodeCMSY[0]));
// -----------------------------------------------------------------------------------
// Data for strecthy chars that are supported by the Math4 font ----------------------
static nsCharData gCharDataMath4[] = {
#define WANT_MATH4_DATA
#define WANT_CHAR_DATA
#include "nsMathMLCharList.h"
#undef WANT_CHAR_DATA
#undef WANT_MATH4_DATA
};
static nsGlyphCode gGlyphCodeMath4[] = {
#define WANT_MATH4_DATA
#define WANT_GLYPH_DATA
#include "nsMathMLCharList.h"
#undef WANT_GLYPH_DATA
#undef WANT_MATH4_DATA
};
nsGlyphTable gGlyphTableMath4 =
nsGlyphTable(NS_TABLE_TYPE_UNICODE, "Math4",
gCharDataMath4, sizeof(gCharDataMath4) / sizeof(gCharDataMath4[0]),
gGlyphCodeMath4, sizeof(gGlyphCodeMath4) / sizeof(gGlyphCodeMath4[0]));
// -----------------------------------------------------------------------------------
// All the glyph tables in order of preference ...
nsGlyphTable* gAllGlyphTables[] = {
&gGlyphTableCMSY, // should be first to pick the single sqrt glyph therein
&gGlyphTableCMEX,
&gGlyphTableMTExtra,
&gGlyphTableMath4,
&gGlyphTableSymbol,
nsnull
};
// -----------------------------------------------------------------------------------
// And now the implementation of nsMathMLChar
nsresult
nsMathMLChar::GetStyleContext(nsIStyleContext** aStyleContext) const
{
NS_PRECONDITION(aStyleContext, "null OUT ptr");
NS_ASSERTION(mStyleContext, "chars shoud always have style context");
NS_IF_ADDREF(mStyleContext);
*aStyleContext = mStyleContext;
return NS_OK;
}
nsresult
nsMathMLChar::SetStyleContext(nsIStyleContext* aStyleContext)
{
NS_PRECONDITION(aStyleContext, "null ptr");
if (aStyleContext != mStyleContext) {
NS_IF_RELEASE(mStyleContext);
if (aStyleContext) {
mStyleContext = aStyleContext;
NS_ADDREF(aStyleContext);
}
}
return NS_OK;
}
// If you call SetData(), it will lookup the enum
// of the char and set it for you.
void
nsMathMLChar::SetData(nsIPresContext* aPresContext,
nsString& aData)
{
if (!gGlyphTableList.IsInitialized()) {
gGlyphTableList.Init(aPresContext, gAllGlyphTables);
for (PRInt32 i = 0; i < eMathMLChar_COUNT; i++) {
gCharInfo[i].mGlyphTable = &gGlyphTableUNDEFINED;
}
// let some particular chars have their preferred extension tables
if (gGlyphTableList.Has(&gGlyphTableMTExtra)) {
gCharInfo[eMathMLChar_OverCurlyBracket].mGlyphTable = &gGlyphTableMTExtra;
gCharInfo[eMathMLChar_UnderCurlyBracket].mGlyphTable = &gGlyphTableMTExtra;
}
#ifdef NS_DEBUG
// sanity check
for (PRInt32 j = 0; j < eMathMLChar_COUNT; j++) {
PRUnichar ci = gCharInfo[i].mUnicode;
for (PRInt32 k = 0; k < eMathMLChar_COUNT; k++) {
// hitting this assertion?
// check nsMathMLCharList to ensure that the same Unicode point
// is not associated to different enums
PRUnichar ck = gCharInfo[k].mUnicode;
NS_ASSERTION(!(ci == ck && i != k), "Duplicate Unicode point found");
}
}
#endif
}
mData = aData;
// some assumptions until proven otherwise!
// note that mGlyph is not initialized
mEnum = eMathMLChar_DONT_STRETCH;
mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
mBoundingMetrics.Clear();
mGlyphTable = nsnull;
// lookup the enum ...
if (1 == mData.Length()) {
PRUnichar ch = mData[0];
for (PRInt32 i = 0; i < eMathMLChar_COUNT; i++) {
if (ch == gCharInfo[i].mUnicode) {
// found ...
mEnum = nsMathMLCharEnum(i);
mDirection = gCharInfo[i].mDirection;
if (mDirection != NS_STRETCH_DIRECTION_UNSUPPORTED) {
// ... now see if there is a glyph table for us
mGlyphTable = gGlyphTableList.FindTableFor(mEnum);
// don't bother with the streching if there is no glyph table for us...
if (!mGlyphTable) {
gCharInfo[i].mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED; // update to never try again
mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
mEnum = eMathMLChar_DONT_STRETCH;
}
#ifdef NS_DEBUG
// hitting this assertion?
// check nsMathMLCharList to ensure that enum and Unicode (of size0) match in MATHML_CHAR(index, enum, ...)
else NS_ASSERTION(mGlyphTable->Has(nsGlyphCode(mData[0])), "Something is wrong somewhere");
#endif
}
return;
}
}
}
}
// If you call SetEnum(), it will lookup the actual value
// of the char and set it for you.
void
nsMathMLChar::SetEnum(nsIPresContext* aPresContext,
nsMathMLCharEnum aEnum)
{
NS_ASSERTION(aEnum < eMathMLChar_COUNT, "Something is wrong somewhere");
if (!gGlyphTableList.IsInitialized()) {
gGlyphTableList.Init(aPresContext, gAllGlyphTables);
for (PRInt32 i = 0; i < eMathMLChar_COUNT; i++) {
gCharInfo[i].mGlyphTable = &gGlyphTableUNDEFINED;
}
// let some particular chars have their preferred extension tables
if (gGlyphTableList.Has(&gGlyphTableMTExtra)) {
gCharInfo[eMathMLChar_OverCurlyBracket].mGlyphTable = &gGlyphTableMTExtra;
gCharInfo[eMathMLChar_UnderCurlyBracket].mGlyphTable = &gGlyphTableMTExtra;
}
#ifdef NS_DEBUG
// sanity check
for (PRInt32 j = 0; j < eMathMLChar_COUNT; j++) {
PRUnichar ci = gCharInfo[i].mUnicode;
for (PRInt32 k = 0; k < eMathMLChar_COUNT; k++) {
// hitting this assertion?
// check nsMathMLCharList to ensure that the same Unicode point
// is not associated to different enums
PRUnichar ck = gCharInfo[k].mUnicode;
NS_ASSERTION(!(ci == ck && i != k), "Duplicate Unicode point found");
}
}
#endif
}
mEnum = aEnum;
// some assumptions until proven otherwise!
// note that mGlyph is not initialized
mData = nsAutoString();
mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
mBoundingMetrics.Clear();
mGlyphTable = nsnull;
if (mEnum != eMathMLChar_DONT_STRETCH) {
mData = gCharInfo[mEnum].mUnicode;
mDirection = gCharInfo[mEnum].mDirection;
if (mDirection != NS_STRETCH_DIRECTION_UNSUPPORTED) {
// ... now see if there is a glyph table for us
mGlyphTable = gGlyphTableList.FindTableFor(mEnum);
// don't bother with the streching if there is no glyph table for us...
if (!mGlyphTable) {
gCharInfo[mEnum].mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED; // update to never try again
mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
mEnum = eMathMLChar_DONT_STRETCH;
}
#ifdef NS_DEBUG
// hitting this assertion?
// check nsMathMLCharList to ensure that enum and Unicode (of size0) match in MATHML_CHAR(index, enum, ...)
else NS_ASSERTION(mGlyphTable->Has(nsGlyphCode(mData[0])), "Something is wrong somewhere");
#endif
}
NS_ASSERTION(mData.Length(), "Something is wrong somewhere");
}
}
/*
The Stretch:
@param aContainerSize - suggested size for the stretched char
@param aDesiredStretchSize - IN/OUT parameter. On input
our current size or zero if current size is unknown, on output
the size after stretching. If no stretching is done, and the
input was zero, the output will simply give the default size.
How it works?
The Stretch() method first looks for a glyph of appropriate
size; If a glyph is found, it is cached by this object
and its size is returned in aDesiredStretchSize. The cached
glyph will then be used at the painting stage.
If no glyph of appropriate size is found, aDesiredStretchSize
is set to aContainerSize and the char is later built by
parts, at the painting stage, to fit the size.
* When a glyph is found, its size can actually be slighthly
larger or smaller than aContainerSize.
* When built by parts, the char is stretched to *exactly*
fit aContainerSize.
In either case, it is the responsibility of the caller
to account for the spacing when setting aContainerSize, and
to leave the extra spacing when placing the stretched char.
*/
//#define SHOW_BORDERS 1
//#define NOISY_SEARCH 1
// **********************************************************************
// **********************************************************************
// **********************************************************************
#undef PR_ABS
#define PR_ABS(x) ((x) < 0 ? -(x) : (x))
static PRBool
IsSizeOK(nscoord a, nscoord b, PRInt32 aHint)
{
if (aHint == NS_STRETCH_NORMAL)
return PRBool(float(PR_ABS(a - b)) < 0.15f * float(b));
else if (aHint == NS_STRETCH_SMALLER)
return PRBool((0.85f * float(b) <= float(a)) && (a <= b));
else if (aHint == NS_STRETCH_LARGER)
return PRBool(a >= b);
else
return PR_FALSE;
}
static PRBool
IsSizeBetter(nscoord a, nscoord olda, nscoord b, PRInt32 aHint)
{
if (0 == olda)
return PR_TRUE;
else if (PR_ABS(a - b) < PR_ABS(olda - b))
{
if (aHint == NS_STRETCH_NORMAL)
return PR_TRUE;
else if (aHint == NS_STRETCH_SMALLER)
return PRBool(a <= olda);
else if (aHint == NS_STRETCH_LARGER)
return PRBool(a >= olda);
}
return PR_FALSE;
}
static PRBool
FontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
{
nsAutoString* familyList = (nsAutoString*)aData;
// XXX unreliable if aFamily is a substring of another family already in the list
if (familyList->Find(aFamily, PR_TRUE) == kNotFound) {
familyList->Append(',');
// XXX could enclose in quotes if weird font problems develop
familyList->Append(aFamily);
}
return PR_TRUE; // don't stop
}
// re-order the font-family list of aFont to put aFamily in first position
static void SetFirstFamily(nsFont& aFont, const nsString& aFamily)
{
// put aFamily in first position
nsAutoString familyList(aFamily);
// XXX hack to force CMSY10 to be at least the second best choice !
if (!aFamily.EqualsIgnoreCase("CMSY10"))
familyList.AppendWithConversion(",CMSY10");
// loop over font-family: (skipping aFamily if present)
aFont.EnumerateFamilies(FontEnumCallback, &familyList);
// overwrite the old value of font-family:
aFont.name.Assign(familyList);
}
nsresult
nsMathMLChar::Stretch(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
nsStretchDirection aStretchDirection,
nsBoundingMetrics& aContainerSize,
nsBoundingMetrics& aDesiredStretchSize,
PRInt32 aStretchHint)
{
nsresult rv = NS_OK;
nsStretchDirection aDirection = aStretchDirection;
////////////////
// if no specified direction, attempt to stretch in our preferred direction
if (aDirection == NS_STRETCH_DIRECTION_DEFAULT) {
aDirection = mDirection;
}
///////////////
// Set font
nsAutoString fontName;
nsStyleFont font;
mStyleContext->GetStyle(eStyleStruct_Font, font);
if (mGlyphTable) {
mGlyphTable->GetFontName(fontName);
SetFirstFamily(font.mFont, fontName); // force precedence on this font
}
aRenderingContext.SetFont(font.mFont);
//////////////
// set the default bounding metrics
rv = aRenderingContext.GetBoundingMetrics(mData.GetUnicode(),
PRUint32(mData.Length()),
mBoundingMetrics);
if (NS_FAILED(rv)) {
printf ("GetBoundingMetrics failed\n");
return rv;
}
//////////////
// set the default desired metrics for special cases where
// the caller doesn't know the default size
if ((0 == aDesiredStretchSize.width) ||
(0 == aDesiredStretchSize.ascent && 0 == aDesiredStretchSize.descent))
{
aDesiredStretchSize = mBoundingMetrics;
}
///////////////
// quickly return if there is nothing special about this char
if (!mGlyphTable ||
eMathMLChar_DONT_STRETCH == mEnum ||
aDirection != mDirection) {
// ensure that the char later behaves like a normal char
mEnum = eMathMLChar_DONT_STRETCH; // XXX need to reset in dynamic updates
return NS_OK;
}
// hitting these assertions?
// make sure to clobber-build the MathML directory if nsMathMLCharList.h has changed
NS_ASSERTION(gCharInfo[mEnum].mUnicode == mData[0], "Mystery!");
NS_ASSERTION(gCharInfo[mEnum].mDirection == mDirection, "Mystery!");
NS_ASSERTION(gCharInfo[mEnum].mDirection != NS_STRETCH_DIRECTION_UNSUPPORTED, "Mystery!");
////////////////
// check the common situations where stretching is not actually needed
if (aDirection == NS_STRETCH_DIRECTION_VERTICAL) {
if (IsSizeOK(aDesiredStretchSize.ascent + aDesiredStretchSize.descent,
aContainerSize.ascent + aContainerSize.descent,
aStretchHint)) {
// ensure that the char later behaves like a normal char
mEnum = eMathMLChar_DONT_STRETCH; // XXX need to reset in dynamic updates
return NS_OK;
}
}
else if (aDirection == NS_STRETCH_DIRECTION_HORIZONTAL) {
if (IsSizeOK(aDesiredStretchSize.width,
aContainerSize.width,
aStretchHint)) {
// ensure that the char later behaves like a normal char
mEnum = eMathMLChar_DONT_STRETCH; // XXX need to reset in dynamic updates
return NS_OK;
}
}
else {
NS_ASSERTION(0, "What are you doing here?");
return NS_OK;
}
//////////////////
// try first to search if there is a glyph of appropriate size ...
PRInt32 size;
PRBool sizeOK = PR_FALSE;
nsBoundingMetrics bm;
nsGlyphCode ch;
// this will be the best glyph that we encounter during the search...
nsGlyphCode bestGlyph = mData[0]; // XXX need to also take care of the glyph index case
nsGlyphTable* bestGlyphTable = mGlyphTable;
nsBoundingMetrics bestbm = mBoundingMetrics;
#ifdef NOISY_SEARCH
printf("Searching a font with a glyph of appropriate size for: 0x%04X:%c\n",
mData[0], mData[0]&0x00FF);
#endif
nsGlyphTable* glyphTable = gGlyphTableList.FirstTable();
while (glyphTable && !sizeOK) {
if (glyphTable->Has(mEnum) && glyphTable->BigOf(mEnum, 1)) {
// see if this table has a glyph that matches the size
glyphTable->GetFontName(fontName);
SetFirstFamily(font.mFont, fontName); // force precedence on this font
aRenderingContext.SetFont(font.mFont);
#ifdef NOISY_SEARCH
char str[50];
fontName.ToCString(str, sizeof(str));
printf(" searching in %s ...\n", str);
#endif
size = 1; // size=0 is the char at its normal size
ch = glyphTable->BigOf(mEnum, size++);
while (ch) {
NS_ASSERTION(ch != mData[0], "glyph table incorrectly set -- duplicate found");
rv = glyphTable->GetBoundingMetrics(aRenderingContext, ch, bm);
if (NS_SUCCEEDED(rv)) {
nscoord h = bm.ascent + bm.descent;
nscoord w = bm.rightBearing - bm.leftBearing;
if ((aDirection == NS_STRETCH_DIRECTION_VERTICAL &&
IsSizeOK(h, aContainerSize.ascent + aContainerSize.descent, aStretchHint)) ||
(aDirection == NS_STRETCH_DIRECTION_HORIZONTAL &&
IsSizeOK(w, aContainerSize.width, aStretchHint)))
{
#ifdef NOISY_SEARCH
printf(" size:%d OK!\n", size-1);
#endif
bestbm = bm;
bestGlyphTable = glyphTable;
bestGlyph = ch;
sizeOK = PR_TRUE;
break; // get out...
}
if ((aDirection == NS_STRETCH_DIRECTION_VERTICAL &&
IsSizeBetter(h, bestbm.ascent + bestbm.descent,
aContainerSize.ascent + aContainerSize.descent, aStretchHint)) ||
(aDirection == NS_STRETCH_DIRECTION_HORIZONTAL &&
IsSizeBetter(w, bestbm.rightBearing - bestbm.leftBearing,
aContainerSize.width, aStretchHint)))
{
#ifdef NOISY_SEARCH
printf(" size:%d Current best\n", size-1);
#endif
bestGlyphTable = glyphTable;
bestGlyph = ch;
bestbm = bm;
}
#ifdef NOISY_SEARCH
else {
printf(" size:%d Rejected!\n", size-1);
}
#endif
}
ch = glyphTable->BigOf(mEnum, size++);
}
}
glyphTable = glyphTable->GetNextTable();
}
//////////////////
// if no glyph of appropriate size, see if we can build by parts...
// search for the table with the smallest glue
if (!sizeOK) {
#ifdef NOISY_SEARCH
printf(" searching for the font with the smallest glue\n");
#endif
if (gCharInfo[mEnum].mGlyphTable == &gGlyphTableUNDEFINED) { // first time
// gCharInfo[mEnum].mGlyphTable is not yet initialized, scan the global list
nscoord lengthGlue = 0;
gCharInfo[mEnum].mGlyphTable = nsnull; // clear the first time flag
glyphTable = gGlyphTableList.FirstTable();
while (glyphTable) {
if (glyphTable->Has(mEnum) && glyphTable->GlueOf(mEnum)) {
glyphTable->GetFontName(fontName);
SetFirstFamily(font.mFont, fontName); // force precedence on this font
aRenderingContext.SetFont(font.mFont);
ch = glyphTable->GlueOf(mEnum);
rv = glyphTable->GetBoundingMetrics(aRenderingContext, ch, bm);
if (NS_SUCCEEDED(rv)) {
nscoord length = (aDirection == NS_STRETCH_DIRECTION_VERTICAL)
? bm.ascent + bm.descent
: bm.rightBearing - bm.leftBearing;
NS_ASSERTION(length, "glue should never be zero *** Erroneous glue in glyph table!");
if (IsSizeBetter(length, lengthGlue, 0, NS_STRETCH_SMALLER)) {
// current glyph table is the one with the smallest glue, update the cache...
gCharInfo[mEnum].mGlyphTable = glyphTable;
lengthGlue = length;
#ifdef NOISY_SEARCH
char str[50];
fontName.ToCString(str, sizeof(str));
printf(" %s glue:%d Current best\n", str, lengthGlue);
#endif
}
#ifdef NOISY_SEARCH
else {
char str[50];
fontName.ToCString(str, sizeof(str));
printf(" %s glue:%d Rejected!\n", str, length);
}
#endif
}
}
glyphTable = glyphTable->GetNextTable();
}
#ifdef NOISY_SEARCH
if (gCharInfo[mEnum].mGlyphTable) {
gCharInfo[mEnum].mGlyphTable->GetFontName(fontName);
char str[50];
fontName.ToCString(str, sizeof(str));
printf(" Found %s in the global list\n", str);
}
#endif
}
#ifdef NOISY_SEARCH
if (gCharInfo[mEnum].mGlyphTable) {
gCharInfo[mEnum].mGlyphTable->GetFontName(fontName);
char str[50];
fontName.ToCString(str, sizeof(str));
printf(" Found %s in the cache\n", str);
}
else {
printf(" no font found\n");
}
#endif
}
if (sizeOK || !gCharInfo[mEnum].mGlyphTable) {
// if we enter here, it means either that the best glyph encountered
// is of appropriate size, or that it is not possible to build by
// parts (i.e., a font with a glue wasn't found).
// so return the best glyph that we have
}
else {
// we are going to build by parts...
// using the glyph table with the smallest glue
glyphTable = gCharInfo[mEnum].mGlyphTable;
glyphTable->GetFontName(fontName);
SetFirstFamily(font.mFont, fontName); // force precedence on this font
aRenderingContext.SetFont(font.mFont);
PRInt32 i;
// XXX hack!
float flex[3] = {0.7f, 0.3f, 0.7f};
nsGlyphCode chdata[4];
nsBoundingMetrics bmdata[4];
for (i = 0; i < 4; i++) {
switch (i) {
case 0: ch = glyphTable->TopOf(mEnum); break;
case 1: ch = glyphTable->MiddleOf(mEnum); break;
case 2: ch = glyphTable->BottomOf(mEnum); break;
case 3: ch = glyphTable->GlueOf(mEnum); break;
}
if (!ch) ch = glyphTable->GlueOf(mEnum); // empty slots are filled with the glue
rv = glyphTable->GetBoundingMetrics(aRenderingContext, ch, bm);
if (NS_FAILED(rv)) {
printf("GetBoundingMetrics failed for %04X:%c\n", ch, ch&0x00FF);
return rv;
}
chdata[i] = ch;
bmdata[i] = bm;
}
// refine the flexibility depending on whether some parts are no there
if ((chdata[1] == chdata[0]) || // mid == top (or mid == left)
(chdata[1] == chdata[2]) || // mid == bot (or mid == right)
(chdata[1] == chdata[3])) // mid == glue
{
flex[0] = 0.5f;
flex[1] = 0.0f;
flex[2] = 0.5f;
}
if (aDirection == NS_STRETCH_DIRECTION_VERTICAL) {
// default is to fill-up the area given to us
nscoord lbearing = bmdata[0].leftBearing;
nscoord rbearing = bmdata[0].rightBearing;
nscoord h = 0;
nscoord w = bmdata[0].width;
for (i = 0; i < 4; i++) {
bm = bmdata[i];
if (w < bm.width) w = bm.width;
if (i < 3) {
h += nscoord(flex[i]*(bm.ascent + bm.descent)); // sum heights of the parts...
// compute and cache our bounding metrics (vertical stacking!)
if (lbearing > bm.leftBearing) lbearing = bm.leftBearing;
if (rbearing < bm.rightBearing) rbearing = bm.rightBearing;
}
}
if (h <= aContainerSize.ascent + aContainerSize.descent) {
// can nicely fit in the available space...
bestbm.width = w;
bestbm.ascent = aContainerSize.ascent;
bestbm.descent = aContainerSize.descent;
bestbm.leftBearing = lbearing;
bestbm.rightBearing = rbearing;
// reset
bestGlyph = 0; // this will tell paint to build by parts
bestGlyphTable = glyphTable;
}
else {
// sum of parts doesn't fit in the space... will use a single glyph
// will use the best glyph encountered earlier
}
}
else if (aDirection == NS_STRETCH_DIRECTION_HORIZONTAL) {
// default is to fill-up the area given to us
nscoord a = bmdata[0].ascent;
nscoord d = bmdata[0].descent;
nscoord w = 0;
for (i = 0; i < 4; i++) {
bm = bmdata[i];
if (a < bm.ascent) a = bm.ascent;
if (d < bm.descent) d = bm.descent;
if (i < 3) {
w += nscoord(flex[i]*(bm.rightBearing - bm.leftBearing)); // sum widths of the parts...
}
}
if (w <= aContainerSize.width) {
// can nicely fit in the available space...
bestbm.width = aContainerSize.width;
bestbm.ascent = a;
bestbm.descent = d;
bestbm.leftBearing = 0;
bestbm.rightBearing = aContainerSize.width;
// reset
bestGlyph = 0; // this will tell paint to build by parts
bestGlyphTable = glyphTable;
}
else {
// sum of parts doesn't fit in the space... will use a single glyph
// will use the best glyph encountered earlier
}
}
}
if (bestGlyph == mData[0]) { // nothing happened
// ensure that the char behaves like a normal char
mEnum = eMathMLChar_DONT_STRETCH; // XXX need to reset in dynamic updates
}
else {
// will stretch
mGlyph = bestGlyph; // note that this can be 0 in order to tell paint to build by parts
mGlyphTable = bestGlyphTable;
mBoundingMetrics = bestbm;
aDesiredStretchSize = bestbm;
}
return NS_OK;
}
nsresult
nsMathMLChar::Paint(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
nsFramePaintLayer aWhichLayer,
nsIFrame* aForFrame)
{
nsStyleDisplay display;
nsStyleColor color;
mStyleContext->GetStyle(eStyleStruct_Display, display);
mStyleContext->GetStyle(eStyleStruct_Color, color);
if (display.IsVisible() && NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)
{
if (mRect.width && mRect.height) {
// Paint our background and border
PRIntn skipSides = 0; //aForFrame->GetSkipSides();
nsStyleSpacing spacing;
mStyleContext->GetStyle(eStyleStruct_Spacing, spacing);
nsRect rect(mRect); //0, 0, mRect.width, mRect.height);
nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, aForFrame,
aDirtyRect, rect, color, spacing, 0, 0);
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, aForFrame,
aDirtyRect, rect, spacing, mStyleContext, skipSides);
nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, aForFrame,
aDirtyRect, rect, spacing, mStyleContext, 0);
}
}
if (display.IsVisible() && NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer)
{
// Set color and font ...
nsStyleFont font;
mStyleContext->GetStyle(eStyleStruct_Font, font);
aRenderingContext.SetColor(color.mColor);
if (mGlyphTable) {
nsAutoString fontName;
mGlyphTable->GetFontName(fontName);
SetFirstFamily(font.mFont, fontName); // force precedence on this font
}
aRenderingContext.SetFont(font.mFont);
// grab some metrics that will help to adjust the placements ...
nscoord fontAscent;
nsCOMPtr<nsIFontMetrics> fm;
aRenderingContext.GetFontMetrics(*getter_AddRefs(fm));
fm->GetMaxAscent(fontAscent);
if (eMathMLChar_DONT_STRETCH == mEnum || NS_STRETCH_DIRECTION_UNSUPPORTED == mDirection) {
// normal drawing if there is nothing special about this char ...
//printf("Painting %04X like a normal char\n", mData[0]);
//aRenderingContext.SetColor(NS_RGB(255,0,0));
aRenderingContext.DrawString(mData.GetUnicode(), PRUint32(mData.Length()),
mRect.x,
mRect.y - (fontAscent - mBoundingMetrics.ascent));
}
else if (0 < mGlyph) { // wow, there is a glyph of appropriate size!
//printf("Painting %04X with a glyph of appropriate size\n", mData[0]);
//aRenderingContext.SetColor(NS_RGB(0,0,255));
mGlyphTable->DrawGlyph(aRenderingContext, mGlyph,
mRect.x,
mRect.y - (fontAscent - mBoundingMetrics.ascent));
}
else { // paint by parts
//aRenderingContext.SetColor(NS_RGB(0,255,0));
if (NS_STRETCH_DIRECTION_VERTICAL == mDirection)
return PaintVertically(aPresContext, aRenderingContext, fontAscent,
mStyleContext, mGlyphTable, mEnum, mRect);
else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
return PaintHorizontally(aPresContext, aRenderingContext, fontAscent,
mStyleContext, mGlyphTable, mEnum, mRect);
}
}
return NS_OK;
}
/* =================================================================================
And now the helper routines that actually do the job of painting the char by parts
*/
// paint a stretchy char by assembling glyphs vertically
nsresult
nsMathMLChar::PaintVertically(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
nscoord aFontAscent,
nsIStyleContext* aStyleContext,
nsGlyphTable* aGlyphTable,
nsMathMLCharEnum aCharEnum,
nsRect aRect)
{
nsresult rv = NS_OK;
nsRect clipRect;
nscoord dx, dy;
float p2t;
aPresContext->GetScaledPixelsToTwips(&p2t);
nscoord onePixel = NSIntPixelsToTwips(1, p2t);
// get metrics data to be re-used later
PRInt32 i;
nsGlyphCode ch, chdata[4];
nsBoundingMetrics bm, bmdata[4];
nscoord stride, offset[3], start[3], end[3];
for (i = 0; i < 4; i++) {
switch (i) {
case 0: ch = aGlyphTable->TopOf(aCharEnum); break;
case 1: ch = aGlyphTable->MiddleOf(aCharEnum); break;
case 2: ch = aGlyphTable->BottomOf(aCharEnum); break;
case 3: ch = aGlyphTable->GlueOf(aCharEnum); break;
}
if (!ch) ch = aGlyphTable->GlueOf(aCharEnum);
rv = aGlyphTable->GetBoundingMetrics(aRenderingContext, ch, bm);
if (NS_FAILED(rv)) {
printf("GetBoundingMetrics failed for %04X:%c\n", ch, ch&0x00FF);
return rv;
}
chdata[i] = ch;
bmdata[i] = bm;
}
dx = aRect.x;
for (i = 0; i < 3; i++) {
ch = chdata[i];
bm = bmdata[i];
if (0 == i) { // top
dy = aRect.y - aFontAscent + bm.ascent;
}
else if (1 == i) { // middle
dy = aRect.y - aFontAscent + bm.ascent +
(aRect.height - (bm.ascent + bm.descent))/2;
}
else if (2 == i) { // bottom
dy = aRect.y - aFontAscent + aRect.height - bm.descent;
}
// abcissa passed to DrawString
offset[i] = dy;
// *exact* abcissa where the *top-most* pixel of the glyph is painted
start[i] = dy + aFontAscent - bm.ascent;
// *exact* abcissa where the *bottom-most* pixel of the glyph is painted
end[i] = dy + aFontAscent + bm.descent; // end = start + height
}
/////////////////////////////////////
// draw top, middle, bottom
for (i = 0; i < 3; i++) {
ch = chdata[i];
#ifdef SHOW_BORDERS
// bounding box of the part
aRenderingContext.SetColor(NS_RGB(0,0,0));
aRenderingContext.DrawRect(nsRect(dx,start[i],aRect.width+30*(i+1),end[i]-start[i]));
#endif
dy = offset[i];
if (i==0) clipRect = nsRect(dx, aRect.y, aRect.width, aRect.height);
else if (i==1) clipRect = nsRect(dx, end[0], aRect.width, start[2]-end[0]);
else if (i==2) clipRect = nsRect(dx, start[2], aRect.width, end[2]-start[2]);
if (!clipRect.IsEmpty()) {
clipRect.Inflate(onePixel, onePixel);
aGlyphTable->DrawGlyph(aRenderingContext, ch, dx, dy, &clipRect);
}
}
///////////////
// fill the gap between top and middle, and between middle and bottom.
ch = aGlyphTable->GlueOf(aCharEnum);
for (i = 0; i < 2; i++) {
PRInt32 count = 0;
dy = offset[i];
clipRect = nsRect(dx, end[i], aRect.width, start[i+1]-end[i]);
clipRect.Inflate(onePixel, onePixel);
#ifdef SHOW_BORDERS
// exact area to fill
aRenderingContext.SetColor(NS_RGB(255,0,0));
aRenderingContext.DrawRect(clipRect);
#endif
bm = bmdata[i];
while (dy + aFontAscent + bm.descent < start[i+1]) {
if (2 > count) {
stride = bm.descent;
bm = bmdata[3]; // glue
stride += bm.ascent;
}
count++;
dy += stride;
aGlyphTable->DrawGlyph(aRenderingContext, ch, dx, dy, &clipRect);
// NS_ASSERTION(5000 == count, "Error - glyph table is incorrectly set");
if (1000 == count) return NS_ERROR_UNEXPECTED;
}
#ifdef SHOW_BORDERS
// last glyph that may cross past its boundary and collide with the next
nscoord height = bm.ascent + bm.descent;
aRenderingContext.SetColor(NS_RGB(0,255,0));
aRenderingContext.DrawRect(nsRect(dx, dy+aFontAscent-bm.ascent, aRect.width, height));
#endif
}
return NS_OK;
}
// paint a stretchy char by assembling glyphs horizontally
nsresult
nsMathMLChar::PaintHorizontally(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
nscoord aFontAscent,
nsIStyleContext* aStyleContext,
nsGlyphTable* aGlyphTable,
nsMathMLCharEnum aCharEnum,
nsRect aRect)
{
nsresult rv = NS_OK;
nsRect clipRect;
nscoord dx, dy;
float p2t;
aPresContext->GetScaledPixelsToTwips(&p2t);
nscoord onePixel = NSIntPixelsToTwips(1, p2t);
// get metrics data to be re-used later
PRInt32 i;
nsGlyphCode ch, chdata[4];
nsBoundingMetrics bm, bmdata[4];
nscoord ascent, stride, offset[3], start[3], end[3];
for (i = 0; i < 4; i++) {
switch (i) {
case 0: ch = aGlyphTable->LeftOf(aCharEnum); break;
case 1: ch = aGlyphTable->MiddleOf(aCharEnum); break;
case 2: ch = aGlyphTable->RightOf(aCharEnum); break;
case 3: ch = aGlyphTable->GlueOf(aCharEnum); break;
}
if (!ch) ch = aGlyphTable->GlueOf(aCharEnum);
rv = aGlyphTable->GetBoundingMetrics(aRenderingContext, ch, bm);
if (NS_FAILED(rv)) {
printf("GetBoundingMetrics failed for %04X:%c\n", ch, ch&0x00FF);
return rv;
}
chdata[i] = ch;
bmdata[i] = bm;
if (0 == i || ascent < bm.ascent) ascent = bm.ascent;
}
dy = aRect.y - aFontAscent + ascent;
for (i = 0; i < 3; i++) {
ch = chdata[i];
bm = bmdata[i];
if (0 == i) { // left
dx = aRect.x - bm.leftBearing;
}
else if (1 == i) { // middle
dx = aRect.x + (aRect.width - bm.width)/2;
}
else if (2 == i) { // right
dx = aRect.x + aRect.width - bm.rightBearing;
}
// abcissa that DrawString used
offset[i] = dx;
// *exact* abcissa where the *left-most* pixel of the glyph is painted
start[i] = dx + bm.leftBearing;
// *exact* abcissa where the *right-most* pixel of the glyph is painted
end[i] = dx + bm.rightBearing; // note: end = start + width
}
///////////////////////////
// draw left, middle, right
for (i = 0; i < 3; i++) {
ch = chdata[i];
#ifdef SHOW_BORDERS
aRenderingContext.SetColor(NS_RGB(0,0,0));
aRenderingContext.DrawRect(nsRect(start[i], aRect.y, end[i]-start[i], aRect.height+30*(i+1)));
#endif
dx = offset[i];
if (i==0) clipRect = nsRect(dx, aRect.y, aRect.width, aRect.height);
else if (i==1) clipRect = nsRect(end[0], aRect.y, start[2]-end[0], aRect.height);
else if (i==2) clipRect = nsRect(start[2], aRect.y, end[2]-start[2], aRect.height);
if (!clipRect.IsEmpty()) {
clipRect.Inflate(onePixel, onePixel);
aGlyphTable->DrawGlyph(aRenderingContext, ch, dx, dy, &clipRect);
}
}
////////////////
// fill the gap between left and middle, and between middle and right.
ch = aGlyphTable->GlueOf(aCharEnum);
for (i = 0; i < 2; i++) {
PRInt32 count = 0;
dx = offset[i];
clipRect = nsRect(end[i], aRect.y, start[i+1]-end[i], aRect.height);
clipRect.Inflate(onePixel, onePixel);
#ifdef SHOW_BORDERS
// rectangles in-between that are to be filled
aRenderingContext.SetColor(NS_RGB(255,0,0));
aRenderingContext.DrawRect(clipRect);
#endif
bm = bmdata[i];
while (dx + bm.rightBearing < start[i+1]) {
if (2 > count) {
stride = bm.rightBearing;
bm = bmdata[3]; // glue
stride -= bm.leftBearing;
}
count++;
dx += stride;
aGlyphTable->DrawGlyph(aRenderingContext, ch, dx, dy, &clipRect);
// NS_ASSERTION(5000 == count, "Error - glyph table is incorrectly set");
if (1000 == count) return NS_ERROR_UNEXPECTED;
}
#ifdef SHOW_BORDERS
// last glyph that may cross past its boundary and collide with the next
nscoord width = bm.rightBearing - bm.leftBearing;
aRenderingContext.SetColor(NS_RGB(0,255,0));
aRenderingContext.DrawRect(nsRect(dx + bm.leftBearing, aRect.y, width, aRect.height));
#endif
}
return NS_OK;
}