Parameterize the handling of base fonts (i.e., fonts used for certain characters at their base/non-stretchy size). Use that to set base fonts for sqrt (which used to be hard-coded), and fix the ≤ problem by setting the safer Symbol font as its base font. Also while here, emulate \mathstrut in sqrt for extra beautification. b=118600, r+sr=roc+moz

This commit is contained in:
rbs%maths.uq.edu.au 2003-04-22 03:32:06 +00:00
parent 3304c29388
commit 1bbb3daf7f
5 changed files with 178 additions and 72 deletions

View File

@ -35,7 +35,7 @@ mathfont_missing_dialog_message = To properly display the MathML on this page yo
# setting the pref("font.mathfont-family", "CMSY10, CMEX10, ...") or by
# setting the font-family list in :-moz-math-font-style-stretchy in mathml.css.
mathfont-family = CMSY10, CMEX10, Math1, Math2, Math4, MT Extra, Symbol
font.mathfont-family = CMSY10, CMEX10, Math1, Math2, Math4, MT Extra, Symbol
# Style invariant chars (these chars have their own intrinsic predefined styles)
# XXX complete these...
@ -71,13 +71,24 @@ mathvariant.double-struck = \u2102\u210D\u2115\u2119\u211A\u211D\u2124
# the comma-separated list of fonts like in the font-family property of CSS.
# A character can request different fonts for its variants and for its parts.
# CMEX10 or Math2 are preferred for Sqrt
mathfont-family.\u221A.variants = CMEX10, Math2
mathfont-family.\u221A.parts = CMEX10, Math2
# Use the safer Symbol font for ≤ And also for ≥ for the sake of uniformity
# @see http://bugzilla.mozilla.org/show_bug.cgi?id=120198#c27
# Note: setting base fonts for non-stretchy characters only works
# for operators that are ultimately handled by nsMathMLChar.
# @see how |useMathMLChar| is set in nsMathMLmoFrame::Stretch() & Paint().
font.mathfont-family.\u2264.base = Symbol
font.mathfont-family.\u2265.base = Symbol
# CMSY10 or Math2 are preferred for Sqrt at its base size
font.mathfont-family.\u221A.base = CMSY10, Math2
# CMEX10 or Math2 are preferred for Sqrt when stretching
font.mathfont-family.\u221A.variants = CMEX10, Math2
font.mathfont-family.\u221A.parts = CMEX10, Math2
# CMEX10 is preferred for OverBrace and UnderBrace
mathfont-family.\uFE37.parts = CMEX10
mathfont-family.\uFE38.parts = CMEX10
font.mathfont-family.\uFE37.parts = CMEX10
font.mathfont-family.\uFE38.parts = CMEX10
# Operator Dictionary indexed on the "form" (i.e., infix, prefix, or suffix).
# Each entry lists the attributes of the operator, using its Unicode format.

View File

@ -51,6 +51,7 @@
#include "nsIDialogParamBlock.h"
#include "nsIWindowWatcher.h"
#include "nsIStringBundle.h"
#include "nsDoubleHashtable.h"
#include "nsMathMLOperators.h"
#include "nsMathMLChar.h"
@ -59,7 +60,6 @@
//#define NOISY_SEARCH 1
// -----------------------------------------------------------------------------------
static const PRUnichar kSqrChar = PRUnichar(0x221A);
static const PRUnichar kSpaceCh = PRUnichar(' ');
static const nsGlyphCode kNullGlyph = {0, 0};
@ -638,6 +638,21 @@ nsGlyphTable::DrawGlyph(nsIRenderingContext& aRenderingContext,
}
}
// class to map a Unicode point to a string (used to store the list of
// fonts preferred for the base size of certain characters, i.e., when
// stretching doesn't happen with a char, we use its preferred base fonts)
class nsBaseFontEntry : public PLDHashInt32Entry
{
public:
nsBaseFontEntry(const void* aKey) : PLDHashInt32Entry(aKey) { }
~nsBaseFontEntry() { };
nsString mFontFamily; // a font-family list a-la CSS
};
DECL_DHASH_WRAPPER(nsBaseFontHashtable, nsBaseFontEntry, PRInt32)
DHASH_WRAPPER(nsBaseFontHashtable, nsBaseFontEntry, PRInt32)
// -----------------------------------------------------------------------------------
// This is the list of all the applicable glyph tables.
// We will maintain a single global instance that will only reveal those
@ -653,6 +668,9 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
// Hashtable to cache the preferred fonts of some chars at their base size
static nsBaseFontHashtable gBaseFonts;
// These are placeholders used to cache the indices (in mTableList) of
// the preferred extension tables for the particular chars.
// Each stretchy char can have a preferred ordered list of fonts to
@ -752,6 +770,7 @@ NS_IMPL_ISUPPORTS1(nsGlyphTableList, nsIObserver);
// -----------------------------------------------------------------------------------
// Here is the global list of applicable glyph tables that we will be using
static nsGlyphTableList* gGlyphTableList = nsnull;
nsBaseFontHashtable nsGlyphTableList::gBaseFonts;
PRInt32* nsGlyphTableList::gParts = nsnull;
PRInt32* nsGlyphTableList::gVariants = nsnull;
@ -804,8 +823,8 @@ nsGlyphTableList::Finalize()
delete glyphTable;
}
// delete the other variables
if (gParts) delete gParts;
if (gVariants) delete gVariants;
delete gParts;
delete gVariants;
gParts = gVariants = nsnull;
gInitialized = PR_FALSE;
// our oneself will be destroyed when our |Release| is called by the observer
@ -1020,14 +1039,46 @@ PreferredFontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
return PR_TRUE; // don't stop
}
// retrieve a pref value set by the user
static PRBool
GetPrefValue(nsIPrefBranch* aPrefBranch, const char* aPrefKey, nsString& aPrefValue)
{
aPrefValue.Truncate();
if (aPrefBranch) {
nsCOMPtr<nsISupportsString> prefString;
aPrefBranch->GetComplexValue(aPrefKey,
NS_GET_IID(nsISupportsString),
getter_AddRefs(prefString));
if (prefString) {
prefString->GetData(aPrefValue);
}
}
return !aPrefValue.IsEmpty();
}
// Store the list of preferred extension fonts for this char
static void
SetPreferredTableList(PRUnichar aChar, nsACString& aExtension, nsString& aFamilyList)
SetPreferredFonts(PRUnichar aChar, const char* aExtension, nsString& aFamilyList)
{
#ifdef DEBUG_rbs
char str[50];
aFamilyList.ToCString(str, sizeof(str));
printf("Setting preferred fonts for \\u%04X%s: %s\n", aChar, aExtension, str);
#endif
if (!strcmp(aExtension, ".base")) {
// fonts to be used for the base size of the char (i.e., no stretching)
nsBaseFontEntry* entry = nsGlyphTableList::gBaseFonts.AddEntry(aChar);
if (entry) {
entry->mFontFamily = aFamilyList;
}
return;
}
PRBool isFontForParts;
if (aExtension.Equals(NS_LITERAL_CSTRING(".parts")))
if (!strcmp(aExtension, ".parts"))
isFontForParts = PR_TRUE;
else if (aExtension.Equals(NS_LITERAL_CSTRING(".variants")))
else if (!strcmp(aExtension, ".variants"))
isFontForParts = PR_FALSE;
else return; // input is not applicable
@ -1082,36 +1133,32 @@ InitGlobals(nsIPresContext* aPresContext)
{
NS_ASSERTION(!gInitialized, "Error -- already initialized");
gInitialized = PR_TRUE;
PRInt32 count = nsMathMLOperators::CountStretchyOperator();
PRUint32 count = nsMathMLOperators::CountStretchyOperator();
if (!count) {
// nothing to stretch, so why bother...
return NS_OK;
}
// Allocate the placeholders for the preferred parts and variants
nsGlyphTableList::gParts = new PRInt32[count];
if (!nsGlyphTableList::gParts)
return NS_ERROR_OUT_OF_MEMORY;
nsGlyphTableList::gVariants = new PRInt32[count];
if (!nsGlyphTableList::gVariants) {
delete nsGlyphTableList::gParts;
return NS_ERROR_OUT_OF_MEMORY;
}
PRInt32 i;
for (i = 0; i < count; i++) {
nsGlyphTableList::gParts[i] = kNotFound; // i.e., -1
nsGlyphTableList::gVariants[i] = kNotFound; // i.e., -1
}
// Allocate gGlyphTableList
nsresult rv = NS_ERROR_OUT_OF_MEMORY;
gGlyphTableList = new nsGlyphTableList();
if (gGlyphTableList) {
rv = gGlyphTableList->Initialize();
nsGlyphTableList::gParts = new PRInt32[count];
nsGlyphTableList::gVariants = new PRInt32[count];
if (nsGlyphTableList::gParts && nsGlyphTableList::gVariants) {
rv = gGlyphTableList->Initialize();
}
}
if (NS_FAILED(rv)) {
if (NS_FAILED(rv) ||
!gGlyphTableList ||
!nsGlyphTableList::gParts ||
!nsGlyphTableList::gVariants) {
delete gGlyphTableList;
delete nsGlyphTableList::gParts;
delete nsGlyphTableList::gVariants;
if (gGlyphTableList) delete gGlyphTableList;
gGlyphTableList = nsnull;
nsGlyphTableList::gParts = nsnull;
nsGlyphTableList::gVariants = nsnull;
return rv;
}
/*
@ -1120,9 +1167,17 @@ InitGlobals(nsIPresContext* aPresContext)
It will be deleted at shutdown, even if a failure happens below.
*/
PRUint32 i;
for (i = 0; i < count; i++) {
nsGlyphTableList::gParts[i] = kNotFound; // i.e., -1
nsGlyphTableList::gVariants[i] = kNotFound; // i.e., -1
}
nsGlyphTableList::gBaseFonts.Init(5);
nsCAutoString key;
nsAutoString value;
nsCOMPtr<nsIPersistentProperties> mathfontProp;
nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
// Add the math fonts in the gGlyphTableList in order of preference ...
// Note: we only load font-names at this stage. The actual glyph tables will
@ -1140,27 +1195,11 @@ InitGlobals(nsIPresContext* aPresContext)
// Get the default list of mathfonts to be used for stretchy characters
nsFont font(nsnull, 0, 0, 0, 0, 0);
nsAutoString familyList;
nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
if (prefBranch) {
// first, try to see if the user has a value in the pref
nsCOMPtr<nsISupportsString> prefString;
prefBranch->GetComplexValue("font.mathfont-family",
NS_GET_IID(nsISupportsString),
getter_AddRefs(prefString));
if (prefString) {
prefString->GetData(familyList);
}
}
if (familyList.IsEmpty()) {
// fallback to the default list
rv = mathfontProp->GetStringProperty(NS_LITERAL_CSTRING("mathfont-family"),
value);
NS_NAMED_LITERAL_CSTRING(defaultKey, "font.mathfont-family");
if (!GetPrefValue(prefBranch, defaultKey.get(), font.name)) {
// fallback to the internal default list
rv = mathfontProp->GetStringProperty(defaultKey, font.name);
if (NS_FAILED(rv)) return rv;
font.name.Assign(value);
}
else {
font.name.Assign(familyList);
}
// Parse the font list and append an entry for each family to gGlyphTableList
@ -1175,25 +1214,57 @@ InitGlobals(nsIPresContext* aPresContext)
AlertMissingFonts(missingFamilyList);
}
// Let the particular characters have their preferred extension tables
// Let the particular characters have their preferred fonts
// First, look the prefs of the user
char **allKey = nsnull;
prefBranch->GetChildList("font.mathfont-family.", &count, &allKey);
for (i = 0; i < count; ++i) {
#ifdef DEBUG_rbs
char str[50];
GetPrefValue(prefBranch, allKey[i], value);
value.ToCString(str, sizeof(str));
printf("Found user pref %s: %s\n", allKey[i], str);
#endif
if ((30 < strlen(allKey[i])) &&
GetPrefValue(prefBranch, allKey[i], value)) {
// expected key:
// "font.mathfont-family.\uNNNN.base" -- fonts for the base size
// "font.mathfont-family.\uNNNN.parts" -- fonts for partial glyphs
// "font.mathfont-family.\uNNNN.variants" -- fonts for larger glyphs
PRInt32 error = 0;
// 22 is to skip "font.mathfont-family.\\u";
PRUnichar uchar = nsCAutoString(allKey[i]+22).ToInteger(&error, 16);
if (error) continue;
// 27 is to skip "font.mathfont-family.\\uNNNN"
SetPreferredFonts(uchar, allKey[i]+27, value);
}
}
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, allKey);
// Next, look our internal settings
nsCOMPtr<nsISimpleEnumerator> iterator;
if (NS_SUCCEEDED(mathfontProp->Enumerate(getter_AddRefs(iterator)))) {
PRBool more;
while ((NS_SUCCEEDED(iterator->HasMoreElements(&more))) && more) {
nsCOMPtr<nsIPropertyElement> element;
if (NS_SUCCEEDED(iterator->GetNext(getter_AddRefs(element)))) {
if (NS_SUCCEEDED(element->GetKey(key)) &&
NS_SUCCEEDED(element->GetValue(value))) {
// expected key: "mathfont-family.\uNNNN.parts" or
// "mathfont-family.\uNNNN.variants"
if ((22 <= key.Length()) && (0 == key.Find("mathfont-family.\\u"))) {
if (NS_SUCCEEDED(element->GetKey(key))) {
// expected key:
// "font.mathfont-family.\uNNNN.base" -- fonts for the base size
// "font.mathfont-family.\uNNNN.parts" -- fonts for partial glyphs
// "font.mathfont-family.\uNNNN.variants" -- fonts for larger glyphs
if ((30 < key.Length()) &&
(0 == key.Find("font.mathfont-family.\\u")) &&
!GetPrefValue(prefBranch, key.get(), value) && // priority to user
NS_SUCCEEDED(element->GetValue(value))) {
PRInt32 error = 0;
key.Cut(0, 18); // 18 is the length of "mathfont-family.\\u";
key.Cut(0, 23); // 23 is the length of "font.mathfont-family.\\u";
PRUnichar uchar = key.ToInteger(&error, 16);
if (error) continue;
key.Cut(0, 4); // the digits of the unicode point ("NNNN")
Clean(value);
SetPreferredTableList(uchar, key, value);
SetPreferredFonts(uchar, key.get(), value);
}
}
}
@ -1202,6 +1273,18 @@ InitGlobals(nsIPresContext* aPresContext)
return rv;
}
// helper to override CSS and set the default font-family list to be used
// for the base size of a particular character (i.e., in the situation where
// stretching doesn't happen).
static void
SetBaseFamily(PRUnichar aChar, nsFont& aFont)
{
nsBaseFontEntry* entry = nsGlyphTableList::gBaseFonts.GetEntry(aChar);
if (entry) {
aFont.name.Assign(entry->mFontFamily);
}
}
// -----------------------------------------------------------------------------------
// And now the implementation of nsMathMLChar
@ -1470,12 +1553,10 @@ nsMathMLChar::Stretch(nsIPresContext* aPresContext,
const nsStyleFont *font = NS_STATIC_CAST(const nsStyleFont*,
parentContext->GetStyleData(eStyleStruct_Font));
nsFont theFont(font->mFont);
// XXXrbs get rid of this hardcoding - bug 118600
// Override with specific fonts if applicable for this character
PRUnichar uchar = mData[0];
if (kSqrChar == uchar) { // Special to the sqrt char. Due to
fontName.Assign(NS_LITERAL_STRING("CMSY10,Math2")); // assumptions in the sqrt code, we need
SetFirstFamily(theFont, fontName); // to force precedence on this TeX font
}
SetBaseFamily(uchar, theFont);
aRenderingContext.SetFont(theFont, nsnull);
rv = aRenderingContext.GetBoundingMetrics(mData.get(),
PRUint32(mData.Length()),
@ -1588,7 +1669,7 @@ nsMathMLChar::Stretch(nsIPresContext* aPresContext,
? bm.ascent + bm.descent
: bm.rightBearing - bm.leftBearing;
// always break when largeopOnly is set
if (IsSizeOK(charSize, targetSize, aStretchHint) || largeopOnly) {
if (largeopOnly || IsSizeOK(charSize, targetSize, aStretchHint)) {
#ifdef NOISY_SEARCH
printf(" size:%d OK!\n", size-1);
#endif
@ -1950,10 +2031,8 @@ nsMathMLChar::Paint(nsIPresContext* aPresContext,
// normal drawing if there is nothing special about this char ...
// Set the default font and grab some metrics to adjust the placements ...
PRUint32 len = PRUint32(mData.Length());
PRUnichar uchar = mData[0];
if ((1 == len) && (kSqrChar == uchar)) { // Special to the sqrt char. Due to
fontName.Assign(NS_LITERAL_STRING("CMSY10,Math2")); // assumptions in the sqrt code, we need
SetFirstFamily(theFont, fontName); // to force precedence on this TeX font
if (1 == len) {
SetBaseFamily(mData[0], theFont);
}
aRenderingContext.SetFont(theFont, nsnull);
//printf("Painting %04X like a normal char\n", mData[0]);

View File

@ -256,6 +256,8 @@ nsMathMLmoFrame::ProcessTextData(nsIPresContext* aPresContext)
if (1 == length) {
PRUnichar ch = data[0];
if ((ch == '+') || (ch == '=') || (ch == '*') ||
(ch == 0x2264) || // &le;
(ch == 0x2265) || // &ge;
(ch == 0x00D7)) { // &times;
mFlags |= NS_MATHML_OPERATOR_CENTERED;
}

View File

@ -130,7 +130,7 @@ nsMathMLmrootFrame::Paint(nsIPresContext* aPresContext,
mSqrChar.Paint(aPresContext, aRenderingContext,
aDirtyRect, aWhichLayer, this);
if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer && !mBarRect.IsEmpty()) {
// paint the overline bar
const nsStyleColor *color = NS_STATIC_CAST(const nsStyleColor*,
mStyleContext->GetStyleData(eStyleStruct_Color));
@ -231,6 +231,9 @@ nsMathMLmrootFrame::Reflow(nsIPresContext* aPresContext,
nscoord ruleThickness, leading, em;
GetRuleThickness(renderingContext, fm, ruleThickness);
nsBoundingMetrics bmOne;
renderingContext.GetBoundingMetrics(NS_LITERAL_STRING("1").get(), 1, bmOne);
// 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);
@ -244,7 +247,11 @@ nsMathMLmrootFrame::Reflow(nsIPresContext* aPresContext,
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;
// Stretch the radical symbol to the appropriate height if it is not big enough.
nsBoundingMetrics contSize = bmBase;
contSize.descent = bmBase.ascent + bmBase.descent + psi;

View File

@ -140,7 +140,7 @@ nsMathMLmsqrtFrame::Paint(nsIPresContext* aPresContext,
mSqrChar.Paint(aPresContext, aRenderingContext,
aDirtyRect, aWhichLayer, this);
if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer && !mBarRect.IsEmpty()) {
// paint the overline bar
const nsStyleColor *color = NS_STATIC_CAST(const nsStyleColor*,
mStyleContext->GetStyleData(eStyleStruct_Color));
@ -200,6 +200,9 @@ nsMathMLmsqrtFrame::Reflow(nsIPresContext* aPresContext,
nscoord ruleThickness, leading, em;
GetRuleThickness(renderingContext, fm, ruleThickness);
nsBoundingMetrics bmOne;
renderingContext.GetBoundingMetrics(NS_LITERAL_STRING("1").get(), 1, bmOne);
// 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);
@ -213,7 +216,11 @@ nsMathMLmsqrtFrame::Reflow(nsIPresContext* aPresContext,
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;
// Stretch the radical symbol to the appropriate height if it is not big enough.
nsBoundingMetrics contSize = bmBase;
contSize.ascent = ruleThickness;