From 9c15c8d486069cb9f4909a27fe603312b5b7b54f Mon Sep 17 00:00:00 2001 From: "rbs%maths.uq.edu.au" Date: Tue, 22 Apr 2003 03:32:06 +0000 Subject: [PATCH] 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 git-svn-id: svn://10.0.0.236/trunk@141571 18797224-902f-48f8-a5cc-f745e15eee43 --- .../mathml/base/src/mathfont.properties | 23 +- .../layout/mathml/base/src/nsMathMLChar.cpp | 203 ++++++++++++------ .../mathml/base/src/nsMathMLmoFrame.cpp | 2 + .../mathml/base/src/nsMathMLmrootFrame.cpp | 11 +- .../mathml/base/src/nsMathMLmsqrtFrame.cpp | 11 +- 5 files changed, 178 insertions(+), 72 deletions(-) diff --git a/mozilla/layout/mathml/base/src/mathfont.properties b/mozilla/layout/mathml/base/src/mathfont.properties index 085cb073067..004bf9d0991 100644 --- a/mozilla/layout/mathml/base/src/mathfont.properties +++ b/mozilla/layout/mathml/base/src/mathfont.properties @@ -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. diff --git a/mozilla/layout/mathml/base/src/nsMathMLChar.cpp b/mozilla/layout/mathml/base/src/nsMathMLChar.cpp index 4d4f57dfe9f..f583dc2efd9 100644 --- a/mozilla/layout/mathml/base/src/nsMathMLChar.cpp +++ b/mozilla/layout/mathml/base/src/nsMathMLChar.cpp @@ -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 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 mathfontProp; + nsCOMPtr 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 prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID)); - if (prefBranch) { - // first, try to see if the user has a value in the pref - nsCOMPtr 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 iterator; if (NS_SUCCEEDED(mathfontProp->Enumerate(getter_AddRefs(iterator)))) { PRBool more; while ((NS_SUCCEEDED(iterator->HasMoreElements(&more))) && more) { nsCOMPtr 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]); diff --git a/mozilla/layout/mathml/base/src/nsMathMLmoFrame.cpp b/mozilla/layout/mathml/base/src/nsMathMLmoFrame.cpp index c9bd3275792..e2dd5cf4996 100644 --- a/mozilla/layout/mathml/base/src/nsMathMLmoFrame.cpp +++ b/mozilla/layout/mathml/base/src/nsMathMLmoFrame.cpp @@ -256,6 +256,8 @@ nsMathMLmoFrame::ProcessTextData(nsIPresContext* aPresContext) if (1 == length) { PRUnichar ch = data[0]; if ((ch == '+') || (ch == '=') || (ch == '*') || + (ch == 0x2264) || // ≤ + (ch == 0x2265) || // ≥ (ch == 0x00D7)) { // × mFlags |= NS_MATHML_OPERATOR_CENTERED; } diff --git a/mozilla/layout/mathml/base/src/nsMathMLmrootFrame.cpp b/mozilla/layout/mathml/base/src/nsMathMLmrootFrame.cpp index bbef79a6a59..6b6709d2e20 100644 --- a/mozilla/layout/mathml/base/src/nsMathMLmrootFrame.cpp +++ b/mozilla/layout/mathml/base/src/nsMathMLmrootFrame.cpp @@ -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; diff --git a/mozilla/layout/mathml/base/src/nsMathMLmsqrtFrame.cpp b/mozilla/layout/mathml/base/src/nsMathMLmsqrtFrame.cpp index bddfa4ea8fe..f797b6275fa 100644 --- a/mozilla/layout/mathml/base/src/nsMathMLmsqrtFrame.cpp +++ b/mozilla/layout/mathml/base/src/nsMathMLmsqrtFrame.cpp @@ -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;