1999-09-21 02:12:01 +00:00
|
|
|
/*
|
|
|
|
* 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/
|
2000-03-28 09:38:24 +00:00
|
|
|
*
|
1999-09-21 02:12:01 +00:00
|
|
|
* 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.
|
2000-03-28 09:38:24 +00:00
|
|
|
*
|
1999-09-21 02:12:01 +00:00
|
|
|
* The Original Code is Mozilla MathML Project.
|
2000-03-28 09:38:24 +00:00
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is The University Of
|
1999-09-21 02:12:01 +00:00
|
|
|
* Queensland. Portions created by The University Of Queensland are
|
|
|
|
* Copyright (C) 1999 The University Of Queensland. All Rights Reserved.
|
2000-03-28 09:38:24 +00:00
|
|
|
*
|
|
|
|
* Contributor(s):
|
1999-09-21 02:12:01 +00:00
|
|
|
* Roger B. Sidje <rbs@maths.uq.edu.au>
|
1999-11-17 00:49:37 +00:00
|
|
|
* Shyjan Mahamud <mahamud@cs.cmu.edu>
|
1999-09-21 02:12:01 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "nsCOMPtr.h"
|
|
|
|
#include "nsFrame.h"
|
|
|
|
#include "nsIPresContext.h"
|
|
|
|
#include "nsUnitConversion.h"
|
|
|
|
#include "nsIStyleContext.h"
|
|
|
|
#include "nsStyleConsts.h"
|
|
|
|
#include "nsString.h"
|
2002-02-19 22:49:12 +00:00
|
|
|
#include "nsUnicharUtils.h"
|
1999-09-21 02:12:01 +00:00
|
|
|
#include "nsIRenderingContext.h"
|
|
|
|
#include "nsIFontMetrics.h"
|
|
|
|
|
2002-11-22 11:29:31 +00:00
|
|
|
#include "nsIPrefBranch.h"
|
|
|
|
#include "nsIPrefService.h"
|
|
|
|
#include "nsISupportsPrimitives.h"
|
2001-02-02 09:40:53 +00:00
|
|
|
#include "nsIComponentManager.h"
|
|
|
|
#include "nsIPersistentProperties2.h"
|
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "nsIObserverService.h"
|
|
|
|
#include "nsIObserver.h"
|
|
|
|
#include "nsNetUtil.h"
|
|
|
|
#include "nsIURI.h"
|
|
|
|
|
2002-04-02 04:15:22 +00:00
|
|
|
#include "nsILookAndFeel.h"
|
2000-03-28 09:38:24 +00:00
|
|
|
#include "nsIDeviceContext.h"
|
|
|
|
#include "nsCSSRendering.h"
|
2001-02-02 09:40:53 +00:00
|
|
|
#include "prprf.h" // For PR_snprintf()
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2002-02-28 22:39:43 +00:00
|
|
|
#include "nsIDOMWindow.h"
|
|
|
|
#include "nsIDialogParamBlock.h"
|
2002-01-11 02:57:03 +00:00
|
|
|
#include "nsIWindowWatcher.h"
|
2002-03-08 23:34:27 +00:00
|
|
|
#include "nsIStringBundle.h"
|
2002-01-11 02:57:03 +00:00
|
|
|
|
1999-09-21 02:12:01 +00:00
|
|
|
#include "nsMathMLOperators.h"
|
1999-10-13 15:10:14 +00:00
|
|
|
#include "nsMathMLChar.h"
|
1999-09-21 02:12:01 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
//#define SHOW_BORDERS 1
|
|
|
|
//#define NOISY_SEARCH 1
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// -----------------------------------------------------------------------------------
|
2001-03-23 09:46:24 +00:00
|
|
|
static const PRUnichar kSqrChar = PRUnichar(0x221A);
|
|
|
|
static const PRUnichar kSpaceCh = PRUnichar(' ');
|
|
|
|
static const nsGlyphCode kNullGlyph = {0, 0};
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// -----------------------------------------------------------------------------------
|
2000-03-28 09:38:24 +00:00
|
|
|
// 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.
|
2001-03-23 09:46:24 +00:00
|
|
|
// An instance of nsGlyphTable is associated to one primary font. Extra glyphs
|
|
|
|
// can be taken in other additional fonts when stretching certain characters.
|
|
|
|
// These supplementary fonts are referred to as "external" fonts to the table.
|
1999-09-21 02:12:01 +00:00
|
|
|
//
|
2000-03-28 09:38:24 +00:00
|
|
|
// 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.
|
2001-02-02 09:40:53 +00:00
|
|
|
//
|
2000-03-28 09:38:24 +00:00
|
|
|
// 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.
|
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
// General format of MathFont Property Files from which glyph data are retrieved:
|
2001-02-02 09:40:53 +00:00
|
|
|
// -----------------------------------------------------------------------------------
|
|
|
|
// Each font should have its set of glyph data. For example, the glyph data for
|
|
|
|
// the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties"
|
|
|
|
// and "mathfontMTExtra.properties", respectively. The mathfont property file is a
|
|
|
|
// set of all the stretchy MathML characters that can be rendered with that font
|
|
|
|
// using larger and/or partial glyphs. The entry of each stretchy character in the
|
|
|
|
// mathfont property file gives, in that order, the 4 partial glyphs: Top (or Left),
|
|
|
|
// Middle, Bottom (or Right), Glue; and the variants of bigger sizes (if any).
|
|
|
|
// A position that is not relevant to a particular character is indicated there
|
|
|
|
// with the UNICODE REPLACEMENT CHARACTER 0xFFFD.
|
|
|
|
// Characters that need to be built recursively from other characters are said
|
|
|
|
// to be composite. For example, chars like over/underbrace in CMEX10 have to
|
|
|
|
// be built from two half stretchy chars and joined in the middle (TeXbook, p.225).
|
|
|
|
// Such chars are handled in a special manner by the nsMathMLChar class, which allows
|
|
|
|
// several (2 or more) child chars to be composed in order to render another char.
|
|
|
|
// To specify such chars, their list of glyphs in the property file should be given
|
|
|
|
// as space-separated segments of glyphs. Each segment gives the 4 partial
|
|
|
|
// glyphs with which to build the child char that will be joined with its other
|
|
|
|
// siblings. In this code, when this situation happens (see the detailed description
|
|
|
|
// of Stretch() below), the original char (referred to as "parent") creates a
|
|
|
|
// singly-linked list of child chars, asking them to stretch in an equally divided
|
|
|
|
// space. The nsGlyphTable embeds the necessary logic to guarantee correctness in a
|
|
|
|
// recursive stretch (and in the use of TopOf(), GlueOf(), etc) on these child chars.
|
|
|
|
// -----------------------------------------------------------------------------------
|
2000-03-28 09:38:24 +00:00
|
|
|
|
|
|
|
#define NS_TABLE_TYPE_UNICODE 0
|
|
|
|
#define NS_TABLE_TYPE_GLYPH_INDEX 1
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
#define NS_TABLE_STATE_ERROR -1
|
|
|
|
#define NS_TABLE_STATE_EMPTY 0
|
|
|
|
#define NS_TABLE_STATE_READY 1
|
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
// Hook to resolve common assignments to the PUA
|
|
|
|
static nsCOMPtr<nsIPersistentProperties> gPUAProperties;
|
|
|
|
|
|
|
|
// helper to check if a font is installed
|
|
|
|
static PRBool
|
2002-01-11 02:57:03 +00:00
|
|
|
CheckFontExistence(nsIPresContext* aPresContext, const nsString& aFontName)
|
2001-03-23 09:46:24 +00:00
|
|
|
{
|
|
|
|
PRBool aliased;
|
|
|
|
nsAutoString localName;
|
|
|
|
nsCOMPtr<nsIDeviceContext> deviceContext;
|
|
|
|
aPresContext->GetDeviceContext(getter_AddRefs(deviceContext));
|
|
|
|
deviceContext->GetLocalFontName(aFontName, localName, aliased);
|
|
|
|
PRBool rv = (aliased || (NS_OK == deviceContext->CheckFontExistence(localName)));
|
|
|
|
// (see bug 35824 for comments about the aliased localName)
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2002-01-11 02:57:03 +00:00
|
|
|
// alert the user if some of the needed MathML fonts are not installed.
|
2002-02-28 22:39:43 +00:00
|
|
|
// it is non-modal (i.e., it doesn't wait for input from the user)
|
2002-01-11 05:08:11 +00:00
|
|
|
static void
|
2002-01-11 02:57:03 +00:00
|
|
|
AlertMissingFonts(nsString& aMissingFonts)
|
|
|
|
{
|
2002-03-08 23:34:27 +00:00
|
|
|
nsCOMPtr<nsIStringBundleService> sbs(do_GetService(NS_STRINGBUNDLE_CONTRACTID));
|
|
|
|
if (!sbs)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIStringBundle> sb;
|
|
|
|
sbs->CreateBundle("resource:/res/fonts/mathfont.properties", getter_AddRefs(sb));
|
|
|
|
if (!sb)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsXPIDLString title, message;
|
|
|
|
const PRUnichar* strings[] = { aMissingFonts.get() };
|
|
|
|
sb->GetStringFromName(NS_LITERAL_STRING("mathfont_missing_dialog_title").get(), getter_Copies(title));
|
|
|
|
sb->FormatStringFromName(NS_LITERAL_STRING("mathfont_missing_dialog_message").get(),
|
|
|
|
strings, 1, getter_Copies(message));
|
|
|
|
|
2002-08-06 05:56:22 +00:00
|
|
|
nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
|
2002-03-08 23:34:27 +00:00
|
|
|
if (!wwatch)
|
|
|
|
return;
|
|
|
|
|
2002-08-14 12:39:03 +00:00
|
|
|
nsCOMPtr<nsIDialogParamBlock> paramBlock(do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID));
|
2002-03-08 23:34:27 +00:00
|
|
|
if (!paramBlock)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// copied from nsICommonDialogs.idl which curiously isn't part of the build
|
|
|
|
// (mozilla/xpfe/appshell/public/nsICommonDialogs.idl)
|
|
|
|
enum {eMsg=0, eCheckboxMsg=1, eIconClass=2, eTitleMessage=3, eEditfield1Msg=4,
|
|
|
|
eEditfield2Msg=5, eEditfield1Value=6, eEditfield2Value=7, eButton0Text=8,
|
|
|
|
eButton1Text=9, eButton2Text=10, eButton3Text=11,eDialogTitle=12};
|
|
|
|
enum {eButtonPressed=0, eCheckboxState=1, eNumberButtons=2, eNumberEditfields=3,
|
|
|
|
eEditField1Password=4};
|
|
|
|
|
|
|
|
paramBlock->SetInt(eNumberButtons, 1);
|
|
|
|
paramBlock->SetString(eIconClass, NS_LITERAL_STRING("alert-icon").get());
|
|
|
|
paramBlock->SetString(eDialogTitle, title.get());
|
|
|
|
paramBlock->SetString(eMsg, message.get());
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMWindow> parent;
|
|
|
|
wwatch->GetActiveWindow(getter_AddRefs(parent));
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMWindow> dialog;
|
|
|
|
wwatch->OpenWindow(parent, "chrome://global/content/commonDialog.xul", "_blank",
|
|
|
|
"dependent,centerscreen,chrome,titlebar", paramBlock,
|
|
|
|
getter_AddRefs(dialog));
|
2002-01-11 02:57:03 +00:00
|
|
|
}
|
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
// helper to trim off comments from data in a MathFont Property File
|
|
|
|
static void
|
|
|
|
Clean(nsString& aValue)
|
|
|
|
{
|
|
|
|
// chop the trailing # comment portion if any ...
|
|
|
|
PRInt32 comment = aValue.RFindChar('#');
|
|
|
|
if (comment > 0) aValue.Truncate(comment);
|
|
|
|
aValue.CompressWhitespace();
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper to load a MathFont Property File
|
|
|
|
static nsresult
|
|
|
|
LoadProperties(const nsString& aName,
|
|
|
|
nsCOMPtr<nsIPersistentProperties>& aProperties)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
nsAutoString uriStr;
|
2001-03-30 02:39:38 +00:00
|
|
|
uriStr.Assign(NS_LITERAL_STRING("resource:/res/fonts/mathfont"));
|
2001-03-23 09:46:24 +00:00
|
|
|
uriStr.Append(aName);
|
|
|
|
uriStr.StripWhitespace(); // that may come from aName
|
2001-03-30 02:39:38 +00:00
|
|
|
uriStr.Append(NS_LITERAL_STRING(".properties"));
|
2001-03-23 09:46:24 +00:00
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
rv = NS_NewURI(getter_AddRefs(uri), uriStr);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsCOMPtr<nsIInputStream> in;
|
|
|
|
rv = NS_OpenURI(getter_AddRefs(in), uri);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = nsComponentManager::
|
|
|
|
CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID, nsnull,
|
|
|
|
NS_GET_IID(nsIPersistentProperties),
|
|
|
|
getter_AddRefs(aProperties));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
return aProperties->Load(in);
|
|
|
|
}
|
|
|
|
|
2001-03-30 02:39:38 +00:00
|
|
|
// helper to get the stretchy direction of a char
|
|
|
|
static nsStretchDirection
|
|
|
|
GetStretchyDirection(PRUnichar aChar)
|
|
|
|
{
|
|
|
|
PRInt32 k = nsMathMLOperators::FindStretchyOperator(aChar);
|
|
|
|
return (k == kNotFound)
|
|
|
|
? NS_STRETCH_DIRECTION_UNSUPPORTED
|
|
|
|
: nsMathMLOperators::GetStretchyDirectionAt(k);
|
|
|
|
}
|
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
// -----------------------------------------------------------------------------------
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
MOZ_DECL_CTOR_COUNTER(nsGlyphTable)
|
2000-03-28 09:38:24 +00:00
|
|
|
|
|
|
|
class nsGlyphTable {
|
|
|
|
public:
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphTable(const nsString& aPrimaryFontName)
|
2001-03-23 09:46:24 +00:00
|
|
|
{
|
2001-02-02 09:40:53 +00:00
|
|
|
MOZ_COUNT_CTOR(nsGlyphTable);
|
2001-03-23 09:46:24 +00:00
|
|
|
mFontName.AppendString(aPrimaryFontName);
|
2001-02-02 09:40:53 +00:00
|
|
|
mType = NS_TABLE_TYPE_UNICODE;
|
|
|
|
mState = NS_TABLE_STATE_EMPTY;
|
|
|
|
mCharCache = 0;
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~nsGlyphTable() // not a virtual destructor: this class is not intended to be subclassed
|
|
|
|
{
|
2001-02-02 09:40:53 +00:00
|
|
|
MOZ_COUNT_DTOR(nsGlyphTable);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
void GetPrimaryFontName(nsString& aPrimaryFontName) {
|
|
|
|
mFontName.StringAt(0, aPrimaryFontName);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// True if this table contains some glyphs (variants and/or parts)
|
|
|
|
// or contains child chars that can be used to render this char
|
2001-04-28 07:39:20 +00:00
|
|
|
PRBool Has(nsIPresContext* aPresContext, nsMathMLChar* aChar);
|
|
|
|
PRBool Has(nsIPresContext* aPresContext, PRUnichar aChar);
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
// True if this table contains variants of larger sizes to render this char
|
2001-04-28 07:39:20 +00:00
|
|
|
PRBool HasVariantsOf(nsIPresContext* aPresContext, nsMathMLChar* aChar);
|
|
|
|
PRBool HasVariantsOf(nsIPresContext* aPresContext, PRUnichar aChar);
|
2001-03-23 09:46:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// True if this table contains parts (or composite parts) to render this char
|
2001-04-28 07:39:20 +00:00
|
|
|
PRBool HasPartsOf(nsIPresContext* aPresContext, nsMathMLChar* aChar);
|
|
|
|
PRBool HasPartsOf(nsIPresContext* aPresContext, PRUnichar aChar);
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// True if aChar is to be assembled from other child chars in this table
|
2001-04-28 07:39:20 +00:00
|
|
|
PRBool IsComposite(nsIPresContext* aPresContext, nsMathMLChar* aChar);
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// The number of child chars to assemble in order to render aChar
|
2001-04-28 07:39:20 +00:00
|
|
|
PRInt32 ChildCountOf(nsIPresContext* aPresContext, nsMathMLChar* aChar);
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// Getters for the parts
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphCode TopOf(nsIPresContext* aPresContext, nsMathMLChar* aChar) {
|
|
|
|
return ElementAt(aPresContext, aChar, 0);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphCode MiddleOf(nsIPresContext* aPresContext, nsMathMLChar* aChar) {
|
|
|
|
return ElementAt(aPresContext, aChar, 1);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphCode BottomOf(nsIPresContext* aPresContext, nsMathMLChar* aChar) {
|
|
|
|
return ElementAt(aPresContext, aChar, 2);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphCode GlueOf(nsIPresContext* aPresContext, nsMathMLChar* aChar) {
|
|
|
|
return ElementAt(aPresContext, aChar, 3);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphCode BigOf(nsIPresContext* aPresContext, nsMathMLChar* aChar, PRInt32 aSize) {
|
|
|
|
return ElementAt(aPresContext, aChar, 4 + aSize);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphCode LeftOf(nsIPresContext* aPresContext, nsMathMLChar* aChar) {
|
|
|
|
return ElementAt(aPresContext, aChar, 0);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphCode RightOf(nsIPresContext* aPresContext, nsMathMLChar* aChar) {
|
|
|
|
return ElementAt(aPresContext, aChar, 2);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// use these to measure/render a glyph that comes from this table
|
2001-03-23 09:46:24 +00:00
|
|
|
nsresult
|
|
|
|
GetBoundingMetrics(nsIRenderingContext& aRenderingContext,
|
|
|
|
nsFont& aFont,
|
|
|
|
nsGlyphCode& aGlyphCode,
|
|
|
|
nsBoundingMetrics& aBoundingMetrics);
|
2000-03-28 09:38:24 +00:00
|
|
|
void
|
|
|
|
DrawGlyph(nsIRenderingContext& aRenderingContext,
|
2001-03-23 09:46:24 +00:00
|
|
|
nsFont& aFont,
|
|
|
|
nsGlyphCode& aGlyphCode,
|
2000-03-28 09:38:24 +00:00
|
|
|
nscoord aX,
|
|
|
|
nscoord aY,
|
|
|
|
nsRect* aClipRect = nsnull);
|
|
|
|
|
|
|
|
private:
|
2002-08-30 17:13:34 +00:00
|
|
|
char GetAnnotation(nsMathMLChar* aChar, PRInt32 aPosition);
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphCode ElementAt(nsIPresContext* aPresContext, nsMathMLChar* aChar, PRUint32 aPosition);
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
// The type is either NS_TABLE_TYPE_UNICODE or NS_TABLE_TYPE_GLYPH_INDEX
|
|
|
|
PRInt32 mType;
|
|
|
|
|
|
|
|
// mFontName[0] is the primary font associated to this table. The others
|
|
|
|
// are possible "external" fonts for glyphs not in the primary font
|
|
|
|
// but which are needed to stretch certain characters in the table
|
|
|
|
nsStringArray mFontName;
|
|
|
|
|
|
|
|
// Tri-state variable for error/empty/ready
|
|
|
|
PRInt32 mState;
|
|
|
|
|
|
|
|
// The set of glyph data in this table, as provided by the MathFont Property File
|
|
|
|
nsCOMPtr<nsIPersistentProperties> mGlyphProperties;
|
|
|
|
|
|
|
|
// For speedy re-use, we always cache the last data used in the table.
|
|
|
|
// mCharCache is the Unicode point of the last char that was queried in this
|
|
|
|
// table. mGlyphCache is a buffer containing the glyph data associated to
|
|
|
|
// that char. For a property line 'key = value' in the MathFont Property File,
|
|
|
|
// mCharCache will retain the 'key' -- which is a Unicode point, while mGlyphCache
|
|
|
|
// will retain the 'value', which is a consecutive list of nsGlyphCodes, i.e.,
|
|
|
|
// the pairs of 'code@font' needed by the char -- in which 'code@0' can be specified
|
|
|
|
// without the optional '@0'. However, to ease subsequent processing, mGlyphCache
|
|
|
|
// excludes the '@' symbol and explicitly inserts all optional '0' that indicates
|
|
|
|
// the primary font identifier. Specifically therefore, the k-th glyph is
|
|
|
|
// characterized by :
|
|
|
|
// 1) mGlyphCache[2*k] : its Unicode point (or glyph index -- depending on mType),
|
|
|
|
// 2) mGlyphCache[2*k+1] : the numeric identifier of the font where it comes from.
|
|
|
|
// A font identifier of '0' means the default primary font associated to this
|
|
|
|
// table. Other digits map to the "external" fonts that may have been specified
|
|
|
|
// in the MathFont Property File.
|
|
|
|
nsString mGlyphCache;
|
|
|
|
PRUnichar mCharCache;
|
2001-02-02 09:40:53 +00:00
|
|
|
};
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2002-08-30 17:13:34 +00:00
|
|
|
char
|
2001-03-23 09:46:24 +00:00
|
|
|
nsGlyphTable::GetAnnotation(nsMathMLChar* aChar, PRInt32 aPosition)
|
2001-02-02 09:40:53 +00:00
|
|
|
{
|
2001-03-30 02:39:38 +00:00
|
|
|
NS_ASSERTION(aChar->mDirection == NS_STRETCH_DIRECTION_VERTICAL ||
|
|
|
|
aChar->mDirection == NS_STRETCH_DIRECTION_HORIZONTAL,
|
|
|
|
"invalid call");
|
2001-03-23 09:46:24 +00:00
|
|
|
static const char* kVertical = "TMBG";
|
|
|
|
static const char* kHorizontal = "LMRG";
|
|
|
|
if (aPosition >= 4) {
|
|
|
|
// return an ASCII digit for the size=0,1,2,...
|
|
|
|
return PRUnichar('0' + aPosition - 4);
|
|
|
|
}
|
2002-08-30 17:13:34 +00:00
|
|
|
return (aChar->mDirection == NS_STRETCH_DIRECTION_VERTICAL) ?
|
|
|
|
kVertical[aPosition] :
|
|
|
|
kHorizontal[aPosition];
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsGlyphCode
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphTable::ElementAt(nsIPresContext* aPresContext, nsMathMLChar* aChar, PRUint32 aPosition)
|
2001-02-02 09:40:53 +00:00
|
|
|
{
|
2001-03-23 09:46:24 +00:00
|
|
|
if (mState == NS_TABLE_STATE_ERROR) return kNullGlyph;
|
2001-02-02 09:40:53 +00:00
|
|
|
// Load glyph properties if this is the first time we have been here
|
|
|
|
if (mState == NS_TABLE_STATE_EMPTY) {
|
2001-04-28 07:39:20 +00:00
|
|
|
if (!CheckFontExistence(aPresContext, *mFontName[0])) {
|
|
|
|
return kNullGlyph;
|
|
|
|
}
|
2001-03-23 09:46:24 +00:00
|
|
|
nsresult rv = LoadProperties(*mFontName[0], mGlyphProperties);
|
2001-02-02 09:40:53 +00:00
|
|
|
#ifdef NS_DEBUG
|
2001-10-16 03:53:44 +00:00
|
|
|
nsCAutoString uriStr;
|
|
|
|
uriStr.Assign(NS_LITERAL_CSTRING("resource:/res/fonts/mathfont"));
|
|
|
|
uriStr.Append(NS_LossyConvertUCS2toASCII(*mFontName[0]));
|
2001-02-02 09:40:53 +00:00
|
|
|
uriStr.StripWhitespace(); // that may come from mFontName
|
2001-10-16 03:53:44 +00:00
|
|
|
uriStr.Append(NS_LITERAL_CSTRING(".properties"));
|
|
|
|
printf("Loading %s ... %s\n",
|
|
|
|
uriStr.get(),
|
|
|
|
(NS_FAILED(rv)) ? "Failed" : "Done");
|
2001-02-02 09:40:53 +00:00
|
|
|
#endif
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
mState = NS_TABLE_STATE_ERROR; // never waste time with this table again
|
2001-03-23 09:46:24 +00:00
|
|
|
return kNullGlyph;
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
mState = NS_TABLE_STATE_READY;
|
2001-03-23 09:46:24 +00:00
|
|
|
|
|
|
|
// see if there are external fonts needed for certain chars in this table
|
2002-08-30 17:13:34 +00:00
|
|
|
nsCAutoString key;
|
|
|
|
nsAutoString value;
|
2001-03-23 09:46:24 +00:00
|
|
|
for (PRInt32 i = 1; ; i++) {
|
2002-08-30 17:13:34 +00:00
|
|
|
key.Assign(NS_LITERAL_CSTRING("external."));
|
2001-03-23 09:46:24 +00:00
|
|
|
key.AppendInt(i, 10);
|
2001-03-30 02:39:38 +00:00
|
|
|
rv = mGlyphProperties->GetStringProperty(key, value);
|
2001-03-23 09:46:24 +00:00
|
|
|
if (NS_FAILED(rv)) break;
|
|
|
|
Clean(value);
|
|
|
|
mFontName.AppendString(value); // i.e., mFontName[i] holds this font name
|
|
|
|
}
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// If aChar is a child char to be used by a parent composite char, make
|
|
|
|
// sure that it is really attached to this table
|
2001-03-23 09:46:24 +00:00
|
|
|
if (aChar->mParent && (aChar->mGlyphTable != this)) return kNullGlyph;
|
2001-02-02 09:40:53 +00:00
|
|
|
|
|
|
|
// Update our cache if it is not associated to this character
|
2001-03-23 09:46:24 +00:00
|
|
|
PRUnichar uchar = aChar->mData[0];
|
|
|
|
if (mCharCache != uchar) {
|
2002-08-30 17:13:34 +00:00
|
|
|
// The key in the property file is interpreted as ASCII and kept
|
|
|
|
// as such ...
|
2001-03-23 09:46:24 +00:00
|
|
|
char cbuf[10]; PR_snprintf(cbuf, sizeof(cbuf), "\\u%04X", uchar);
|
2002-08-30 17:13:34 +00:00
|
|
|
nsDependentCString key(cbuf);
|
|
|
|
|
|
|
|
nsAutoString value;
|
2001-02-02 09:40:53 +00:00
|
|
|
nsresult rv = mGlyphProperties->GetStringProperty(key, value);
|
2001-03-23 09:46:24 +00:00
|
|
|
if (NS_FAILED(rv)) return kNullGlyph;
|
|
|
|
Clean(value);
|
|
|
|
// See if this char uses external fonts; e.g., if the 2nd glyph is taken from the
|
|
|
|
// external font '1', the property line looks like \uNNNN = \uNNNN\uNNNN@1\uNNNN.
|
|
|
|
// This is where mGlyphCache is pre-processed to explicitly store all glyph codes
|
|
|
|
// as combined pairs of 'code@font', excluding the '@' separator. This means that
|
|
|
|
// mGlyphCache[2*k] will later be rendered with mFontName[mGlyphCache[2*k+1]-'0']
|
|
|
|
// Note: font identifier is internally an ASCII digit to avoid the null char issue
|
2002-08-30 17:13:34 +00:00
|
|
|
nsAutoString buffer, puaValue;
|
|
|
|
nsCAutoString puaKey;
|
2001-03-23 09:46:24 +00:00
|
|
|
PRInt32 length = value.Length();
|
|
|
|
for (PRInt32 i = 0, j = 0; i < length; i++, j++) {
|
|
|
|
PRUnichar code = value[i];
|
|
|
|
PRUnichar font = PRUnichar('0');
|
2001-05-08 08:36:02 +00:00
|
|
|
// see if we are at the beginning of a child char
|
2001-03-23 09:46:24 +00:00
|
|
|
if (code == kSpaceCh) {
|
|
|
|
// reset the annotation indicator to be 0 for the next code point
|
|
|
|
j = -1;
|
|
|
|
}
|
|
|
|
// see if this code point is an *indirect reference* to
|
|
|
|
// the PUA, and lookup "key.[TLMBRG1-9]" in the PUA
|
|
|
|
else if (code == PRUnichar(0xF8FF)) {
|
|
|
|
puaKey.Assign(key);
|
2002-08-30 17:13:34 +00:00
|
|
|
puaKey.Append('.');
|
2001-03-23 09:46:24 +00:00
|
|
|
puaKey.Append(GetAnnotation(aChar, j));
|
|
|
|
rv = gPUAProperties->GetStringProperty(puaKey, puaValue);
|
|
|
|
if (NS_FAILED(rv) || !puaValue.Length()) return kNullGlyph;
|
|
|
|
code = puaValue[0];
|
|
|
|
}
|
|
|
|
// see if this code point is a *direct reference* to
|
|
|
|
// the PUA, and lookup "code.[TLMBRG1-9]" in the PUA
|
|
|
|
else if ((i+2 < length) && (value[i+1] == PRUnichar('.'))) {
|
|
|
|
i += 2;
|
|
|
|
PR_snprintf(cbuf, sizeof(cbuf), "\\u%04X", code);
|
2002-08-30 17:13:34 +00:00
|
|
|
puaKey.Assign(cbuf);
|
|
|
|
puaKey.Append('.');
|
|
|
|
puaKey.Append(char(value[i])); // safe cast, it's ascii
|
2001-03-23 09:46:24 +00:00
|
|
|
rv = gPUAProperties->GetStringProperty(puaKey, puaValue);
|
|
|
|
if (NS_FAILED(rv) || !puaValue.Length()) return kNullGlyph;
|
|
|
|
code = puaValue[0];
|
|
|
|
}
|
2001-05-08 08:36:02 +00:00
|
|
|
// see if an external font is needed for the code point
|
2001-03-23 09:46:24 +00:00
|
|
|
if ((i+2 < length) && (value[i+1] == PRUnichar('@')) &&
|
|
|
|
(value[i+2] >= PRUnichar('0')) && (value[i+2] <= PRUnichar('9'))) {
|
|
|
|
i += 2;
|
|
|
|
font = value[i];
|
|
|
|
// The char cannot be handled if this font is not installed
|
2001-04-28 07:39:20 +00:00
|
|
|
nsAutoString fontName;
|
|
|
|
mFontName.StringAt(font-'0', fontName);
|
|
|
|
if (!fontName.Length() || !CheckFontExistence(aPresContext, fontName)) {
|
|
|
|
return kNullGlyph;
|
|
|
|
}
|
2001-03-23 09:46:24 +00:00
|
|
|
}
|
|
|
|
buffer.Append(code);
|
|
|
|
buffer.Append(font);
|
|
|
|
}
|
|
|
|
// update our cache with the new settings
|
|
|
|
mGlyphCache.Assign(buffer);
|
|
|
|
mCharCache = uchar;
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// If aChar is a composite char, only its children are allowed
|
|
|
|
// to use its glyphs in this table, i.e., the parent char itself
|
|
|
|
// is disabled and cannot be stretched directly with these glyphs.
|
|
|
|
// This guarantees a coherent behavior in Stretch().
|
|
|
|
if (!aChar->mParent && (kNotFound != mGlyphCache.FindChar(kSpaceCh))) {
|
2001-03-23 09:46:24 +00:00
|
|
|
return kNullGlyph;
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If aChar is a child char, the index of the glyph is relative to
|
|
|
|
// the offset of the list of glyphs corresponding to the child char
|
|
|
|
PRUint32 offset = 0;
|
|
|
|
PRUint32 length = mGlyphCache.Length();
|
|
|
|
if (aChar->mParent) {
|
|
|
|
nsMathMLChar* child = aChar->mParent->mSibling;
|
2001-03-23 09:46:24 +00:00
|
|
|
while (child && (child != aChar)) {
|
|
|
|
offset += 5; // skip the 4 partial glyphs + the whitespace separator
|
2001-02-02 09:40:53 +00:00
|
|
|
child = child->mSibling;
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
2001-03-23 09:46:24 +00:00
|
|
|
length = 2*(offset + 4); // stay confined in the 4 partial glyphs of this child
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
2001-03-23 09:46:24 +00:00
|
|
|
PRUint32 index = 2*(offset + aPosition); // 2* is to account for the code@font pairs
|
|
|
|
if (index+1 >= length) return kNullGlyph;
|
|
|
|
nsGlyphCode ch;
|
|
|
|
ch.code = mGlyphCache.CharAt(index);
|
|
|
|
ch.font = mGlyphCache.CharAt(index + 1) - '0'; // the ASCII trick is kept internal...
|
|
|
|
return (ch == PRUnichar(0xFFFD)) ? kNullGlyph : ch;
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphTable::IsComposite(nsIPresContext* aPresContext, nsMathMLChar* aChar)
|
2001-02-02 09:40:53 +00:00
|
|
|
{
|
|
|
|
// there is only one level of recursion in our model. a child
|
|
|
|
// cannot be composite because it cannot have its own children
|
|
|
|
if (aChar->mParent) return PR_FALSE;
|
|
|
|
// shortcut to sync the cache with this char...
|
2001-04-28 07:39:20 +00:00
|
|
|
mCharCache = 0; mGlyphCache.Truncate(); ElementAt(aPresContext, aChar, 0);
|
2001-02-02 09:40:53 +00:00
|
|
|
// the cache remained empty if the char wasn't found in this table
|
2001-03-23 09:46:24 +00:00
|
|
|
if (8 >= mGlyphCache.Length()) return PR_FALSE;
|
2001-02-02 09:40:53 +00:00
|
|
|
// the lists of glyphs of a composite char are space-separated
|
2001-03-23 09:46:24 +00:00
|
|
|
return (kSpaceCh == mGlyphCache.CharAt(8));
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphTable::ChildCountOf(nsIPresContext* aPresContext, nsMathMLChar* aChar)
|
2001-02-02 09:40:53 +00:00
|
|
|
{
|
|
|
|
// this will sync the cache as well ...
|
2001-04-28 07:39:20 +00:00
|
|
|
if (!IsComposite(aPresContext, aChar)) return 0;
|
2001-02-02 09:40:53 +00:00
|
|
|
// the lists of glyphs of a composite char are space-separated
|
|
|
|
return 1 + mGlyphCache.CountChar(kSpaceCh);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphTable::Has(nsIPresContext* aPresContext, nsMathMLChar* aChar)
|
2001-02-02 09:40:53 +00:00
|
|
|
{
|
2001-05-08 08:36:02 +00:00
|
|
|
return HasVariantsOf(aPresContext, aChar) || HasPartsOf(aPresContext, aChar);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
PRBool
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphTable::Has(nsIPresContext* aPresContext, PRUnichar aChar)
|
2001-02-02 09:40:53 +00:00
|
|
|
{
|
|
|
|
nsMathMLChar tmp;
|
|
|
|
tmp.mData = aChar;
|
2001-03-30 02:39:38 +00:00
|
|
|
tmp.mDirection = GetStretchyDirection(aChar);
|
|
|
|
return (tmp.mDirection == NS_STRETCH_DIRECTION_UNSUPPORTED)
|
|
|
|
? PR_FALSE
|
2001-04-28 07:39:20 +00:00
|
|
|
: Has(aPresContext, &tmp);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
PRBool
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphTable::HasVariantsOf(nsIPresContext* aPresContext, nsMathMLChar* aChar)
|
2001-03-23 09:46:24 +00:00
|
|
|
{
|
2001-05-08 08:36:02 +00:00
|
|
|
return BigOf(aPresContext, aChar, 1) != 0;
|
2001-03-23 09:46:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphTable::HasVariantsOf(nsIPresContext* aPresContext, PRUnichar aChar)
|
2001-03-23 09:46:24 +00:00
|
|
|
{
|
|
|
|
nsMathMLChar tmp;
|
|
|
|
tmp.mData = aChar;
|
2001-03-30 02:39:38 +00:00
|
|
|
tmp.mDirection = GetStretchyDirection(aChar);
|
|
|
|
return (tmp.mDirection == NS_STRETCH_DIRECTION_UNSUPPORTED)
|
|
|
|
? PR_FALSE
|
2001-04-28 07:39:20 +00:00
|
|
|
: HasVariantsOf(aPresContext, &tmp);
|
2001-03-23 09:46:24 +00:00
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
PRBool
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphTable::HasPartsOf(nsIPresContext* aPresContext, nsMathMLChar* aChar)
|
2001-02-02 09:40:53 +00:00
|
|
|
{
|
2001-04-28 07:39:20 +00:00
|
|
|
return GlueOf(aPresContext, aChar) || TopOf(aPresContext, aChar) ||
|
|
|
|
BottomOf(aPresContext, aChar) || MiddleOf(aPresContext, aChar) ||
|
|
|
|
IsComposite(aPresContext, aChar);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphTable::HasPartsOf(nsIPresContext* aPresContext, PRUnichar aChar)
|
2001-02-02 09:40:53 +00:00
|
|
|
{
|
|
|
|
nsMathMLChar tmp;
|
|
|
|
tmp.mData = aChar;
|
2001-03-30 02:39:38 +00:00
|
|
|
tmp.mDirection = GetStretchyDirection(aChar);
|
|
|
|
return (tmp.mDirection == NS_STRETCH_DIRECTION_UNSUPPORTED)
|
|
|
|
? PR_FALSE
|
2001-04-28 07:39:20 +00:00
|
|
|
: HasPartsOf(aPresContext, &tmp);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
// Get the bounding box of a glyph.
|
|
|
|
// Our primary font is assumed to be the current font in the rendering context
|
|
|
|
nsresult
|
|
|
|
nsGlyphTable::GetBoundingMetrics(nsIRenderingContext& aRenderingContext,
|
|
|
|
nsFont& aFont,
|
|
|
|
nsGlyphCode& aGlyphCode,
|
|
|
|
nsBoundingMetrics& aBoundingMetrics)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
if (aGlyphCode.font) {
|
|
|
|
// glyph not associated to our primary font, it comes from an external font
|
|
|
|
mFontName.StringAt(aGlyphCode.font, aFont.name);
|
2002-05-24 20:11:14 +00:00
|
|
|
aRenderingContext.SetFont(aFont, nsnull);
|
2001-03-23 09:46:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//if (mType == NS_TABLE_TYPE_UNICODE)
|
|
|
|
rv = aRenderingContext.GetBoundingMetrics((PRUnichar*)&aGlyphCode.code, PRUint32(1), aBoundingMetrics);
|
|
|
|
//else mType == NS_TABLE_TYPE_GLYPH_INDEX
|
|
|
|
//return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
//rv = aRenderingContext.GetBoundingMetricsI((PRUint16*)&aGlyphCode.code, PRUint32(1), aBoundingMetrics);
|
|
|
|
|
|
|
|
if (aGlyphCode.font) {
|
|
|
|
// restore our primary font in the rendering context
|
|
|
|
mFontName.StringAt(0, aFont.name);
|
2002-05-24 20:11:14 +00:00
|
|
|
aRenderingContext.SetFont(aFont, nsnull);
|
2001-03-23 09:46:24 +00:00
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// Draw a glyph in a clipped area so that we don't have hairy chars pending outside
|
2001-03-23 09:46:24 +00:00
|
|
|
// Our primary font is assumed to be the current font in the rendering context
|
2000-03-28 09:38:24 +00:00
|
|
|
void
|
|
|
|
nsGlyphTable::DrawGlyph(nsIRenderingContext& aRenderingContext,
|
2001-03-23 09:46:24 +00:00
|
|
|
nsFont& aFont,
|
|
|
|
nsGlyphCode& aGlyphCode,
|
2000-03-28 09:38:24 +00:00
|
|
|
nscoord aX,
|
|
|
|
nscoord aY,
|
|
|
|
nsRect* aClipRect)
|
|
|
|
{
|
|
|
|
PRBool clipState;
|
|
|
|
if (aClipRect) {
|
|
|
|
aRenderingContext.PushState();
|
|
|
|
aRenderingContext.SetClipRect(*aClipRect, nsClipCombine_kIntersect, clipState);
|
|
|
|
}
|
2001-03-23 09:46:24 +00:00
|
|
|
if (aGlyphCode.font) {
|
|
|
|
// glyph not associated to our primary font, it comes from an external font
|
|
|
|
mFontName.StringAt(aGlyphCode.font, aFont.name);
|
2002-05-24 20:11:14 +00:00
|
|
|
aRenderingContext.SetFont(aFont, nsnull);
|
2001-03-23 09:46:24 +00:00
|
|
|
}
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2000-07-13 04:18:35 +00:00
|
|
|
//if (mType == NS_TABLE_TYPE_UNICODE)
|
2001-03-23 09:46:24 +00:00
|
|
|
aRenderingContext.DrawString((PRUnichar*)&aGlyphCode.code, PRUint32(1), aX, aY);
|
2000-07-13 04:18:35 +00:00
|
|
|
//else
|
2001-03-23 09:46:24 +00:00
|
|
|
//NS_ASSERTION(0, "Error *** Not yet implemented");
|
|
|
|
//aRenderingContext.DrawStringI((PRUint16*)&aGlyphCode.code, PRUint32(1), aX, aY);
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
if (aGlyphCode.font) {
|
|
|
|
// restore our primary font in the rendering context
|
|
|
|
mFontName.StringAt(0, aFont.name);
|
2002-05-24 20:11:14 +00:00
|
|
|
aRenderingContext.SetFont(aFont, nsnull);
|
2001-03-23 09:46:24 +00:00
|
|
|
}
|
2000-03-28 09:38:24 +00:00
|
|
|
if (aClipRect) {
|
|
|
|
aRenderingContext.PopState(clipState);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------------
|
2001-02-02 09:40:53 +00:00
|
|
|
// This is the list of all the applicable glyph tables.
|
2001-05-08 08:36:02 +00:00
|
|
|
// We will maintain a single global instance that will only reveal those
|
2001-02-02 09:40:53 +00:00
|
|
|
// glyph tables that are associated to fonts currently installed on the
|
|
|
|
// user' system. The class is an XPCOM shutdown observer to allow us to
|
|
|
|
// free its allocated data at shutdown
|
|
|
|
|
|
|
|
MOZ_DECL_CTOR_COUNTER(nsGlyphTableList)
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
class nsGlyphTableList : public nsIObserver
|
|
|
|
{
|
2000-05-08 07:18:10 +00:00
|
|
|
public:
|
2001-02-02 09:40:53 +00:00
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
NS_DECL_NSIOBSERVER
|
|
|
|
|
|
|
|
// These are placeholders used to cache the indices (in mTableList) of
|
2001-03-30 02:39:38 +00:00
|
|
|
// the preferred extension tables for the particular chars.
|
|
|
|
// Each stretchy char can have a preferred ordered list of fonts to
|
|
|
|
// be used for its parts, and/or another preferred ordered list of
|
|
|
|
// fonts to be used for its variants of larger sizes.
|
|
|
|
// Several levels of indirection are used to store this information.
|
|
|
|
// The stretchy chars are collated in an array in nsMathMLOperators.
|
|
|
|
// If 'index' is the rank of a stretchy char in that array, then
|
|
|
|
// mTableList[gParts[index]] is the first preferred table to be used for
|
|
|
|
// the parts of that stretchy char, mTableList[gParts[index]+1] is the
|
|
|
|
// second table, etc. The same reasoning applies with gVariants[index].
|
2001-02-02 09:40:53 +00:00
|
|
|
static PRInt32* gParts;
|
|
|
|
static PRInt32* gVariants;
|
|
|
|
|
|
|
|
nsGlyphTableList()
|
2000-03-28 09:38:24 +00:00
|
|
|
{
|
2001-02-02 09:40:53 +00:00
|
|
|
MOZ_COUNT_CTOR(nsGlyphTableList);
|
2001-03-30 02:39:38 +00:00
|
|
|
mDefaultCount = 0;
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
|
2001-02-23 16:10:51 +00:00
|
|
|
virtual ~nsGlyphTableList()
|
2000-03-28 09:38:24 +00:00
|
|
|
{
|
2001-02-02 09:40:53 +00:00
|
|
|
MOZ_COUNT_DTOR(nsGlyphTableList);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
nsresult Initialize();
|
|
|
|
nsresult Finalize();
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2002-01-08 02:15:23 +00:00
|
|
|
nsGlyphTable* TableAt(PRInt32 aIndex) {
|
|
|
|
return NS_STATIC_CAST(nsGlyphTable*, mTableList.ElementAt(aIndex));
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
2002-01-08 02:15:23 +00:00
|
|
|
PRInt32 Count(PRBool aEverything = PR_FALSE) {
|
2001-03-30 02:39:38 +00:00
|
|
|
return (aEverything) ? mTableList.Count() : mDefaultCount;
|
|
|
|
}
|
|
|
|
|
2002-01-08 02:15:23 +00:00
|
|
|
nsGlyphTable* AdditionalTableAt(PRInt32 aIndex) {
|
|
|
|
return NS_STATIC_CAST(nsGlyphTable*, mAdditionalTableList.ElementAt(aIndex));
|
|
|
|
}
|
|
|
|
PRInt32 AdditionalCount() {
|
|
|
|
return mAdditionalTableList.Count();
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool AppendTable(nsGlyphTable* aGlyphTable) {
|
2001-03-30 02:39:38 +00:00
|
|
|
return mTableList.AppendElement(aGlyphTable);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2002-01-08 02:15:23 +00:00
|
|
|
// Add a glyph table in the list, return the new table that was added
|
|
|
|
nsGlyphTable*
|
|
|
|
AddGlyphTable(const nsString& aPrimaryFontName);
|
|
|
|
nsGlyphTable*
|
|
|
|
AddAdditionalGlyphTable(const nsString& aPrimaryFontName);
|
2000-05-08 07:18:10 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// Find a glyph table in the list that has a glyph for the given char
|
|
|
|
nsGlyphTable*
|
2001-04-28 07:39:20 +00:00
|
|
|
GetGlyphTableFor(nsIPresContext* aPresContext,
|
|
|
|
nsMathMLChar* aChar);
|
2001-02-02 09:40:53 +00:00
|
|
|
|
|
|
|
// Find the subset of glyph tables that are applicable to the given char,
|
2001-04-28 07:39:20 +00:00
|
|
|
// knowing that the stretchy style context of the char has the given font.
|
2001-02-02 09:40:53 +00:00
|
|
|
nsresult
|
2001-02-02 15:31:03 +00:00
|
|
|
GetListFor(nsIPresContext* aPresContext,
|
|
|
|
nsMathMLChar* aChar,
|
|
|
|
nsFont* aFont,
|
|
|
|
nsVoidArray* aGlyphTableList);
|
2001-03-30 02:39:38 +00:00
|
|
|
|
|
|
|
// Retrieve the subset of preferred glyph tables that start at the given index
|
2001-04-28 07:39:20 +00:00
|
|
|
// Return the number of installed fonts that are retrieved or 0 if none is found.
|
|
|
|
// If at least one font is found, the preferred fonts become active and
|
|
|
|
// take precedence (i.e., whatever was in the existing aGlyphTableList is
|
|
|
|
// cleared). But if it turns out that no preferred font is actually installed,
|
|
|
|
// the code behaves as if no preferred font was specified at all (i.e., whatever
|
|
|
|
// was in aGlyphTableList is retained).
|
2001-03-30 02:39:38 +00:00
|
|
|
nsresult
|
2001-04-28 07:39:20 +00:00
|
|
|
GetPreferredListAt(nsIPresContext* aPresContext,
|
|
|
|
PRInt32 aStartingIndex,
|
|
|
|
nsVoidArray* aGlyphTableList,
|
|
|
|
PRInt32* aCount);
|
2001-03-30 02:39:38 +00:00
|
|
|
|
2000-05-08 07:18:10 +00:00
|
|
|
private:
|
2001-03-30 02:39:38 +00:00
|
|
|
// Ordered list of glyph tables subdivided in several null-separated segments.
|
|
|
|
// The first segment contains mDefaultCount entries which are the default
|
|
|
|
// fonts as provided in the mathfont.properties file. The remainder of the
|
|
|
|
// list is used to store the preferred tables for the particular chars
|
|
|
|
// as explained above.
|
|
|
|
PRInt32 mDefaultCount;
|
2002-01-08 02:15:23 +00:00
|
|
|
nsVoidArray mTableList;
|
|
|
|
// Users can prefer certain fonts for a character, but without wanting those
|
|
|
|
// fonts to be used for other characters. mAdditionalTableList is a list of
|
|
|
|
// preferred fonts that are not meant to be used as a default sharable list by
|
|
|
|
// all characters. Note that mTableList[0..mDefaultCount-1] and mAdditionalTableList
|
|
|
|
// are kept mutually exclusive since there is no need to load the same table twice.
|
|
|
|
nsVoidArray mAdditionalTableList;
|
2000-03-28 09:38:24 +00:00
|
|
|
};
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
NS_IMPL_ISUPPORTS1(nsGlyphTableList, nsIObserver);
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------------
|
|
|
|
// Here is the global list of applicable glyph tables that we will be using
|
|
|
|
static nsGlyphTableList* gGlyphTableList = nsnull;
|
|
|
|
PRInt32* nsGlyphTableList::gParts = nsnull;
|
|
|
|
PRInt32* nsGlyphTableList::gVariants = nsnull;
|
|
|
|
|
|
|
|
static PRBool gInitialized = PR_FALSE;
|
|
|
|
|
|
|
|
// XPCOM shutdown observer
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsGlyphTableList::Observe(nsISupports* aSubject,
|
2001-10-19 22:30:02 +00:00
|
|
|
const char* aTopic,
|
2001-02-02 09:40:53 +00:00
|
|
|
const PRUnichar* someData)
|
2000-03-28 09:38:24 +00:00
|
|
|
{
|
2001-02-02 09:40:53 +00:00
|
|
|
Finalize();
|
2001-03-23 09:46:24 +00:00
|
|
|
// destroy the PUA
|
|
|
|
gPUAProperties = nsnull;
|
2001-02-02 09:40:53 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add an observer to XPCOM shutdown so that we can free our data at shutdown
|
|
|
|
nsresult
|
|
|
|
nsGlyphTableList::Initialize()
|
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
2001-07-25 07:54:28 +00:00
|
|
|
nsCOMPtr<nsIObserverService> obs =
|
2001-10-22 22:01:27 +00:00
|
|
|
do_GetService("@mozilla.org/observer-service;1", &rv);
|
2001-02-02 09:40:53 +00:00
|
|
|
if (NS_SUCCEEDED(rv)) {
|
2001-10-19 20:52:59 +00:00
|
|
|
rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// Remove our observer and free the memory that were allocated for us
|
|
|
|
nsresult
|
|
|
|
nsGlyphTableList::Finalize()
|
|
|
|
{
|
|
|
|
// Remove our observer from the observer service
|
|
|
|
nsresult rv = NS_OK;
|
2001-07-25 07:54:28 +00:00
|
|
|
nsCOMPtr<nsIObserverService> obs =
|
2001-10-22 22:01:27 +00:00
|
|
|
do_GetService("@mozilla.org/observer-service;1", &rv);
|
2001-02-02 09:40:53 +00:00
|
|
|
if (NS_SUCCEEDED(rv)) {
|
2001-10-19 22:30:02 +00:00
|
|
|
rv = obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
// delete the glyph tables
|
2002-01-08 02:15:23 +00:00
|
|
|
PRInt32 i;
|
|
|
|
for (i = Count() - 1; i >= 0; i--) {
|
|
|
|
nsGlyphTable* glyphTable = TableAt(i);
|
|
|
|
delete glyphTable;
|
|
|
|
}
|
|
|
|
for (i = AdditionalCount() - 1; i >= 0; i--) {
|
|
|
|
nsGlyphTable* glyphTable = AdditionalTableAt(i);
|
2001-02-02 09:40:53 +00:00
|
|
|
delete glyphTable;
|
|
|
|
}
|
|
|
|
// delete the other variables
|
|
|
|
if (gParts) delete gParts;
|
|
|
|
if (gVariants) delete gVariants;
|
|
|
|
gParts = gVariants = nsnull;
|
|
|
|
gInitialized = PR_FALSE;
|
|
|
|
// our oneself will be destroyed when our |Release| is called by the observer
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2002-01-08 02:15:23 +00:00
|
|
|
nsGlyphTable*
|
|
|
|
nsGlyphTableList::AddGlyphTable(const nsString& aPrimaryFontName)
|
2001-02-02 09:40:53 +00:00
|
|
|
{
|
2001-04-28 07:39:20 +00:00
|
|
|
// allocate a table to be deleted at shutdown
|
|
|
|
nsGlyphTable* glyphTable = new nsGlyphTable(aPrimaryFontName);
|
2002-01-08 02:15:23 +00:00
|
|
|
if (!glyphTable) return nsnull;
|
2001-04-28 07:39:20 +00:00
|
|
|
mTableList.AppendElement(glyphTable);
|
|
|
|
mDefaultCount++;
|
2002-01-08 02:15:23 +00:00
|
|
|
return glyphTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsGlyphTable*
|
|
|
|
nsGlyphTableList::AddAdditionalGlyphTable(const nsString& aPrimaryFontName)
|
|
|
|
{
|
|
|
|
// allocate a table to be deleted at shutdown
|
|
|
|
nsGlyphTable* glyphTable = new nsGlyphTable(aPrimaryFontName);
|
|
|
|
if (!glyphTable) return nsnull;
|
|
|
|
mAdditionalTableList.AppendElement(glyphTable);
|
|
|
|
return glyphTable;
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsGlyphTable*
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphTableList::GetGlyphTableFor(nsIPresContext* aPresContext,
|
|
|
|
nsMathMLChar* aChar)
|
2000-03-28 09:38:24 +00:00
|
|
|
{
|
2002-01-08 02:15:23 +00:00
|
|
|
PRInt32 i;
|
|
|
|
for (i = 0; i < Count(); i++) {
|
|
|
|
nsGlyphTable* glyphTable = TableAt(i);
|
|
|
|
if (glyphTable->Has(aPresContext, aChar)) {
|
|
|
|
return glyphTable;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < AdditionalCount(); i++) {
|
|
|
|
nsGlyphTable* glyphTable = AdditionalTableAt(i);
|
2001-04-28 07:39:20 +00:00
|
|
|
if (glyphTable->Has(aPresContext, aChar)) {
|
2000-03-28 09:38:24 +00:00
|
|
|
return glyphTable;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
struct StretchyFontEnumContext {
|
2001-04-28 07:39:20 +00:00
|
|
|
nsIPresContext* mPresContext;
|
|
|
|
nsMathMLChar* mChar;
|
|
|
|
nsVoidArray* mGlyphTableList;
|
2001-02-02 09:40:53 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// check if the current font is associated to a known glyph table, if so the
|
|
|
|
// glyph table is added to the list of tables that can be used for the char
|
2002-01-28 23:24:17 +00:00
|
|
|
static PRBool
|
2001-02-02 09:40:53 +00:00
|
|
|
StretchyFontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
|
2000-05-08 07:18:10 +00:00
|
|
|
{
|
2002-01-08 02:15:23 +00:00
|
|
|
if (aGeneric) return PR_FALSE; // stop now
|
2001-02-02 09:40:53 +00:00
|
|
|
StretchyFontEnumContext* context = (StretchyFontEnumContext*)aData;
|
2001-04-28 07:39:20 +00:00
|
|
|
nsIPresContext* currPresContext = context->mPresContext;
|
2001-02-02 09:40:53 +00:00
|
|
|
nsMathMLChar* currChar = context->mChar;
|
|
|
|
nsVoidArray* currList = context->mGlyphTableList;
|
|
|
|
// check if the current font is associated to a known glyph table
|
|
|
|
for (PRInt32 i = 0; i < gGlyphTableList->Count(); i++) {
|
2002-01-08 02:15:23 +00:00
|
|
|
nsGlyphTable* glyphTable = gGlyphTableList->TableAt(i);
|
|
|
|
nsAutoString fontName;
|
2001-03-30 02:39:38 +00:00
|
|
|
glyphTable->GetPrimaryFontName(fontName);
|
2002-02-19 22:49:12 +00:00
|
|
|
if (fontName.Equals(aFamily, nsCaseInsensitiveStringComparator()) &&
|
2001-04-28 07:39:20 +00:00
|
|
|
glyphTable->Has(currPresContext, currChar)) {
|
2001-03-30 02:39:38 +00:00
|
|
|
currList->AppendElement(glyphTable); // the table is retained
|
2002-01-08 02:15:23 +00:00
|
|
|
return PR_TRUE; // don't stop
|
2000-05-08 07:18:10 +00:00
|
|
|
}
|
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
return PR_TRUE; // don't stop
|
2000-05-08 07:18:10 +00:00
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
nsresult
|
|
|
|
nsGlyphTableList::GetListFor(nsIPresContext* aPresContext,
|
|
|
|
nsMathMLChar* aChar,
|
|
|
|
nsFont* aFont,
|
|
|
|
nsVoidArray* aGlyphTableList)
|
|
|
|
{
|
2002-01-08 02:15:23 +00:00
|
|
|
// @see the documentation of -moz-math-font-style-stretchy in mathml.css
|
|
|
|
// for how this work
|
|
|
|
aGlyphTableList->Clear();
|
2001-02-02 09:40:53 +00:00
|
|
|
PRBool useDocumentFonts = PR_TRUE;
|
|
|
|
aPresContext->GetCachedBoolPref(kPresContext_UseDocumentFonts, useDocumentFonts);
|
2002-01-08 02:15:23 +00:00
|
|
|
// Check to honor the pref("browser.display.use_document_fonts", 0)
|
|
|
|
// Only include fonts from CSS if the pref to disallow authors' fonts isn't set
|
2001-02-02 09:40:53 +00:00
|
|
|
if (useDocumentFonts) {
|
|
|
|
// Convert the list of fonts in aFont (from -moz-math-font-style-stretchy)
|
|
|
|
// to an ordered list of corresponding glyph extension tables
|
2001-04-28 07:39:20 +00:00
|
|
|
StretchyFontEnumContext context = {aPresContext, aChar, aGlyphTableList};
|
2001-02-02 09:40:53 +00:00
|
|
|
aFont->EnumerateFamilies(StretchyFontEnumCallback, &context);
|
|
|
|
}
|
2002-01-08 02:15:23 +00:00
|
|
|
if (!aGlyphTableList->Count()) {
|
|
|
|
// No font was retained, fallback to our default tables
|
|
|
|
PRInt32 count = Count();
|
|
|
|
for (PRInt32 i = 0; i < count; i++) {
|
|
|
|
nsGlyphTable* glyphTable = TableAt(i);
|
|
|
|
if (glyphTable->Has(aPresContext, aChar)) {
|
|
|
|
aGlyphTableList->AppendElement(glyphTable);
|
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2000-04-17 04:19:54 +00:00
|
|
|
|
2001-03-30 02:39:38 +00:00
|
|
|
nsresult
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphTableList::GetPreferredListAt(nsIPresContext* aPresContext,
|
|
|
|
PRInt32 aStartingIndex,
|
|
|
|
nsVoidArray* aGlyphTableList,
|
|
|
|
PRInt32* aCount)
|
2001-03-30 02:39:38 +00:00
|
|
|
{
|
2001-04-28 07:39:20 +00:00
|
|
|
*aCount = 0;
|
|
|
|
if (aStartingIndex == kNotFound) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
nsAutoString fontName;
|
2001-03-30 02:39:38 +00:00
|
|
|
PRInt32 index = aStartingIndex;
|
2001-04-28 07:39:20 +00:00
|
|
|
NS_ASSERTION(index < Count(PR_TRUE), "invalid call");
|
2002-01-08 02:15:23 +00:00
|
|
|
nsGlyphTable* glyphTable = TableAt(index);
|
2001-03-30 02:39:38 +00:00
|
|
|
while (glyphTable) {
|
2001-04-28 07:39:20 +00:00
|
|
|
glyphTable->GetPrimaryFontName(fontName);
|
|
|
|
if (CheckFontExistence(aPresContext, fontName)) {
|
2001-03-30 02:39:38 +00:00
|
|
|
#ifdef NOISY_SEARCH
|
2001-04-28 07:39:20 +00:00
|
|
|
char str[50];
|
|
|
|
fontName.ToCString(str, sizeof(str));
|
|
|
|
printf("Found preferreed font %s\n", str);
|
2001-03-30 02:39:38 +00:00
|
|
|
#endif
|
2001-04-28 07:39:20 +00:00
|
|
|
if (index == aStartingIndex) {
|
|
|
|
// At least one font is found, clear aGlyphTableList
|
|
|
|
aGlyphTableList->Clear();
|
|
|
|
}
|
|
|
|
aGlyphTableList->AppendElement(glyphTable);
|
2002-02-27 01:35:27 +00:00
|
|
|
++*aCount;
|
2001-04-28 07:39:20 +00:00
|
|
|
}
|
2002-01-08 02:15:23 +00:00
|
|
|
glyphTable = TableAt(++index);
|
2001-03-30 02:39:38 +00:00
|
|
|
}
|
|
|
|
// XXX append other tables if UseDocumentFonts is set?
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-03-28 09:38:24 +00:00
|
|
|
// -----------------------------------------------------------------------------------
|
|
|
|
|
2001-03-30 02:39:38 +00:00
|
|
|
struct PreferredFontEnumContext {
|
|
|
|
PRInt32 mCharIndex;
|
|
|
|
PRBool mIsFontForParts;
|
|
|
|
PRInt32 mFontCount;
|
|
|
|
};
|
|
|
|
|
2002-01-08 02:15:23 +00:00
|
|
|
// mark a glyph table as a preferred table that can be used for a char
|
2002-01-28 23:24:17 +00:00
|
|
|
static PRBool
|
2001-03-30 02:39:38 +00:00
|
|
|
PreferredFontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
|
|
|
|
{
|
2002-01-08 02:15:23 +00:00
|
|
|
PRInt32 i;
|
2001-03-30 02:39:38 +00:00
|
|
|
nsAutoString fontName;
|
2002-02-18 22:52:15 +00:00
|
|
|
nsGlyphTable* glyphTable = nsnull;
|
2001-03-30 02:39:38 +00:00
|
|
|
PreferredFontEnumContext* context = (PreferredFontEnumContext*)aData;
|
2002-01-08 02:15:23 +00:00
|
|
|
// see if the table already exists in mTableList[0..mDefaultCount-1]
|
|
|
|
PRBool found = PR_FALSE;
|
2001-03-30 02:39:38 +00:00
|
|
|
PRInt32 count = gGlyphTableList->Count();
|
2002-01-08 02:15:23 +00:00
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
glyphTable = gGlyphTableList->TableAt(i);
|
2001-03-30 02:39:38 +00:00
|
|
|
glyphTable->GetPrimaryFontName(fontName);
|
2002-02-19 22:49:12 +00:00
|
|
|
if (fontName.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
|
2002-01-08 02:15:23 +00:00
|
|
|
found = PR_TRUE;
|
2001-03-30 02:39:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2002-01-08 02:15:23 +00:00
|
|
|
if (!found) {
|
|
|
|
// the table wasn't found in the default sharable list,
|
|
|
|
// see if it exists in the additional list
|
|
|
|
count = gGlyphTableList->AdditionalCount();
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
glyphTable = gGlyphTableList->AdditionalTableAt(i);
|
|
|
|
glyphTable->GetPrimaryFontName(fontName);
|
2002-02-19 22:49:12 +00:00
|
|
|
if (fontName.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
|
2002-01-08 02:15:23 +00:00
|
|
|
found = PR_TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
// the table wasn't found in the additional list either, add it now
|
|
|
|
glyphTable = gGlyphTableList->AddAdditionalGlyphTable(aFamily);
|
|
|
|
if (!glyphTable)
|
|
|
|
return PR_FALSE; // stop in low-memory situations
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the table to the list of preferred extension tables for this char
|
|
|
|
if (!context->mFontCount) {
|
|
|
|
// this is the first font to be retained, remember
|
|
|
|
// the starting index where the first glyphTable was appended
|
|
|
|
PRInt32 startingIndex = gGlyphTableList->Count(PR_TRUE);
|
|
|
|
if (context->mIsFontForParts) {
|
|
|
|
NS_ASSERTION(nsGlyphTableList::gParts[context->mCharIndex] == -1,
|
|
|
|
"remove duplicate property in mathfont.properties");
|
|
|
|
nsGlyphTableList::gParts[context->mCharIndex] = startingIndex;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
NS_ASSERTION(nsGlyphTableList::gVariants[context->mCharIndex] == -1,
|
|
|
|
"remove duplicate property in mathfont.properties");
|
|
|
|
nsGlyphTableList::gVariants[context->mCharIndex] = startingIndex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gGlyphTableList->AppendTable(glyphTable);
|
|
|
|
++context->mFontCount;
|
|
|
|
|
2001-03-30 02:39:38 +00:00
|
|
|
return PR_TRUE; // don't stop
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store the list of preferred extension fonts for this char
|
2001-02-02 09:40:53 +00:00
|
|
|
static void
|
2002-08-30 17:13:34 +00:00
|
|
|
SetPreferredTableList(PRUnichar aChar, nsACString& aExtension, nsString& aFamilyList)
|
2001-02-02 09:40:53 +00:00
|
|
|
{
|
|
|
|
PRBool isFontForParts;
|
2002-08-30 17:13:34 +00:00
|
|
|
if (aExtension.Equals(NS_LITERAL_CSTRING(".parts")))
|
2001-02-02 09:40:53 +00:00
|
|
|
isFontForParts = PR_TRUE;
|
2002-08-30 17:13:34 +00:00
|
|
|
else if (aExtension.Equals(NS_LITERAL_CSTRING(".variants")))
|
2001-02-02 09:40:53 +00:00
|
|
|
isFontForParts = PR_FALSE;
|
|
|
|
else return; // input is not applicable
|
|
|
|
|
|
|
|
// Ensure that this is a valid stretchy operator
|
|
|
|
PRInt32 k = nsMathMLOperators::FindStretchyOperator(aChar);
|
|
|
|
if (k != kNotFound) {
|
2001-03-30 02:39:38 +00:00
|
|
|
// We just want to iterate over the font-family list using the
|
|
|
|
// callback mechanism that nsFont has...
|
|
|
|
nsFont font(aFamilyList, 0, 0, 0, 0, 0);
|
2001-04-28 07:39:20 +00:00
|
|
|
PreferredFontEnumContext context = {k, isFontForParts, 0};
|
2001-03-30 02:39:38 +00:00
|
|
|
font.EnumerateFamilies(PreferredFontEnumCallback, &context);
|
|
|
|
if (context.mFontCount) { // at least one font was retained
|
|
|
|
// Append a null separator
|
2002-01-08 02:15:23 +00:00
|
|
|
gGlyphTableList->AppendTable(nsnull);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2000-05-26 05:56:23 +00:00
|
|
|
|
2002-01-11 02:57:03 +00:00
|
|
|
struct MathFontEnumContext {
|
|
|
|
nsIPresContext* mPresContext;
|
|
|
|
nsString* mMissingFamilyList;
|
|
|
|
};
|
|
|
|
|
2002-01-28 23:24:17 +00:00
|
|
|
static PRBool
|
2002-01-08 02:15:23 +00:00
|
|
|
MathFontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
|
|
|
|
{
|
2002-01-11 02:57:03 +00:00
|
|
|
// check if the font is missing
|
|
|
|
MathFontEnumContext* context = (MathFontEnumContext*)aData;
|
|
|
|
nsIPresContext* presContext = context->mPresContext;
|
|
|
|
nsString* missingFamilyList = context->mMissingFamilyList;
|
|
|
|
if (!CheckFontExistence(presContext, aFamily)) {
|
|
|
|
//#ifndef _WIN32
|
|
|
|
// XXX In principle, the mathfont-family list in the mathfont.properties file
|
|
|
|
// is customizable depending on the platform. For now, this is here since there
|
2002-01-11 05:08:11 +00:00
|
|
|
// is no need to alert Linux users about TrueType fonts specific to Windows.
|
2002-01-11 02:57:03 +00:00
|
|
|
if (aFamily.EqualsIgnoreCase("MT Extra"))
|
|
|
|
return PR_TRUE; // continue to try other fonts
|
|
|
|
//#endif
|
|
|
|
if (!missingFamilyList->IsEmpty()) {
|
|
|
|
missingFamilyList->Append(NS_LITERAL_STRING(", "));
|
|
|
|
}
|
|
|
|
missingFamilyList->Append(aFamily);
|
|
|
|
}
|
|
|
|
|
2002-01-08 02:15:23 +00:00
|
|
|
if (!gGlyphTableList->AddGlyphTable(aFamily))
|
|
|
|
return PR_FALSE; // stop in low-memory situations
|
|
|
|
return PR_TRUE; // don't stop
|
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
static nsresult
|
2002-01-11 02:57:03 +00:00
|
|
|
InitGlobals(nsIPresContext* aPresContext)
|
2000-05-26 05:56:23 +00:00
|
|
|
{
|
2001-02-02 09:40:53 +00:00
|
|
|
NS_ASSERTION(!gInitialized, "Error -- already initialized");
|
2000-05-26 05:56:23 +00:00
|
|
|
gInitialized = PR_TRUE;
|
2001-02-02 09:40:53 +00:00
|
|
|
PRInt32 count = nsMathMLOperators::CountStretchyOperator();
|
|
|
|
if (!count) {
|
|
|
|
// nothing to stretch, so why bother...
|
|
|
|
return NS_OK;
|
2000-05-26 05:56:23 +00:00
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
// Allocate the placeholders for the preferred parts and variants
|
|
|
|
nsGlyphTableList::gParts = new PRInt32[count];
|
2001-10-30 22:58:00 +00:00
|
|
|
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;
|
2000-05-26 05:56:23 +00:00
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
delete nsGlyphTableList::gParts;
|
|
|
|
delete nsGlyphTableList::gVariants;
|
|
|
|
if (gGlyphTableList) delete gGlyphTableList;
|
|
|
|
gGlyphTableList = nsnull;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
else
|
|
|
|
The gGlyphTableList has been successfully registered as a shutdown observer.
|
|
|
|
It will be deleted at shutdown, even if a failure happens below.
|
|
|
|
*/
|
|
|
|
|
2002-08-30 17:13:34 +00:00
|
|
|
nsCAutoString key;
|
|
|
|
nsAutoString value;
|
2001-03-23 09:46:24 +00:00
|
|
|
nsCOMPtr<nsIPersistentProperties> mathfontProp;
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// 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
|
|
|
|
// be loaded lazily (see nsGlyphTable::ElementAt()).
|
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
// Load the "mathfont.properties" file
|
|
|
|
value.Truncate();
|
|
|
|
rv = LoadProperties(value, mathfontProp);
|
2001-02-02 09:40:53 +00:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
2001-03-23 09:46:24 +00:00
|
|
|
|
|
|
|
// Load the "mathfontPUA.properties" file
|
2001-03-30 02:39:38 +00:00
|
|
|
value.Assign(NS_LITERAL_STRING("PUA"));
|
2001-03-23 09:46:24 +00:00
|
|
|
rv = LoadProperties(value, gPUAProperties);
|
2001-02-02 09:40:53 +00:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
2002-01-08 02:15:23 +00:00
|
|
|
// Get the default list of mathfonts to be used for stretchy characters
|
|
|
|
nsFont font(nsnull, 0, 0, 0, 0, 0);
|
2002-11-22 11:29:31 +00:00
|
|
|
nsAutoString familyList;
|
|
|
|
nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
|
|
|
|
if (prefBranch) {
|
2002-01-08 02:15:23 +00:00
|
|
|
// first, try to see if the user has a value in the pref
|
2002-11-22 11:29:31 +00:00
|
|
|
nsCOMPtr<nsISupportsString> prefString;
|
|
|
|
prefBranch->GetComplexValue("font.mathfont-family",
|
|
|
|
NS_GET_IID(nsISupportsString),
|
|
|
|
getter_AddRefs(prefString));
|
|
|
|
if (prefString) {
|
|
|
|
prefString->GetData(familyList);
|
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
2002-01-08 02:15:23 +00:00
|
|
|
if (familyList.IsEmpty()) {
|
|
|
|
// fallback to the default list
|
2002-08-30 17:13:34 +00:00
|
|
|
rv = mathfontProp->GetStringProperty(NS_LITERAL_CSTRING("mathfont-family"),
|
|
|
|
value);
|
2002-01-08 02:15:23 +00:00
|
|
|
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
|
2002-01-11 02:57:03 +00:00
|
|
|
nsAutoString missingFamilyList;
|
|
|
|
MathFontEnumContext context = {aPresContext, &missingFamilyList};
|
|
|
|
font.EnumerateFamilies(MathFontEnumCallback, &context);
|
2001-03-30 02:39:38 +00:00
|
|
|
// Append a null separator
|
2002-01-08 02:15:23 +00:00
|
|
|
gGlyphTableList->AppendTable(nsnull);
|
2001-02-02 09:40:53 +00:00
|
|
|
|
2002-01-11 02:57:03 +00:00
|
|
|
// alert the user if some of the expected fonts are missing
|
|
|
|
if (!missingFamilyList.IsEmpty()) {
|
|
|
|
AlertMissingFonts(missingFamilyList);
|
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// Let the particular characters have their preferred extension tables
|
|
|
|
nsCOMPtr<nsISimpleEnumerator> iterator;
|
2002-08-30 17:13:34 +00:00
|
|
|
if (NS_SUCCEEDED(mathfontProp->Enumerate(getter_AddRefs(iterator)))) {
|
2001-02-02 09:40:53 +00:00
|
|
|
PRBool more;
|
|
|
|
while ((NS_SUCCEEDED(iterator->HasMoreElements(&more))) && more) {
|
|
|
|
nsCOMPtr<nsIPropertyElement> element;
|
|
|
|
if (NS_SUCCEEDED(iterator->GetNext(getter_AddRefs(element)))) {
|
2002-08-30 17:13:34 +00:00
|
|
|
if (NS_SUCCEEDED(element->GetKey(key)) &&
|
|
|
|
NS_SUCCEEDED(element->GetValue(value))) {
|
2002-01-08 02:15:23 +00:00
|
|
|
// expected key: "mathfont-family.\uNNNN.parts" or
|
|
|
|
// "mathfont-family.\uNNNN.variants"
|
|
|
|
if ((22 <= key.Length()) && (0 == key.Find("mathfont-family.\\u"))) {
|
2001-02-02 09:40:53 +00:00
|
|
|
PRInt32 error = 0;
|
2002-01-08 02:15:23 +00:00
|
|
|
key.Cut(0, 18); // 18 is the length of "mathfont-family.\\u";
|
2001-02-02 09:40:53 +00:00
|
|
|
PRUnichar uchar = key.ToInteger(&error, 16);
|
|
|
|
if (error) continue;
|
2002-01-08 02:15:23 +00:00
|
|
|
key.Cut(0, 4); // the digits of the unicode point ("NNNN")
|
2001-03-23 09:46:24 +00:00
|
|
|
Clean(value);
|
2001-03-30 02:39:38 +00:00
|
|
|
SetPreferredTableList(uchar, key, value);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2000-05-26 05:56:23 +00:00
|
|
|
}
|
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
return rv;
|
2000-05-26 05:56:23 +00:00
|
|
|
}
|
2000-03-29 23:15:07 +00:00
|
|
|
|
2000-03-28 09:38:24 +00:00
|
|
|
// -----------------------------------------------------------------------------------
|
|
|
|
// And now the implementation of nsMathMLChar
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsMathMLChar::GetStyleContext(nsIStyleContext** aStyleContext) const
|
|
|
|
{
|
2001-02-02 09:40:53 +00:00
|
|
|
NS_ASSERTION(!mParent, "invalid call - not allowed for child chars");
|
2000-03-28 09:38:24 +00:00
|
|
|
NS_PRECONDITION(aStyleContext, "null OUT ptr");
|
|
|
|
NS_ASSERTION(mStyleContext, "chars shoud always have style context");
|
|
|
|
*aStyleContext = mStyleContext;
|
2001-10-24 21:40:27 +00:00
|
|
|
NS_IF_ADDREF(*aStyleContext);
|
2000-03-28 09:38:24 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-12-10 13:02:23 +00:00
|
|
|
|
2000-03-28 09:38:24 +00:00
|
|
|
nsresult
|
|
|
|
nsMathMLChar::SetStyleContext(nsIStyleContext* aStyleContext)
|
|
|
|
{
|
2001-02-02 09:40:53 +00:00
|
|
|
NS_ASSERTION(!mParent, "invalid call - not allowed for child chars");
|
2000-03-28 09:38:24 +00:00
|
|
|
NS_PRECONDITION(aStyleContext, "null ptr");
|
|
|
|
if (aStyleContext != mStyleContext) {
|
|
|
|
NS_IF_RELEASE(mStyleContext);
|
|
|
|
if (aStyleContext) {
|
|
|
|
mStyleContext = aStyleContext;
|
|
|
|
NS_ADDREF(aStyleContext);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-11-17 00:49:37 +00:00
|
|
|
|
|
|
|
void
|
2000-03-28 09:38:24 +00:00
|
|
|
nsMathMLChar::SetData(nsIPresContext* aPresContext,
|
|
|
|
nsString& aData)
|
|
|
|
{
|
2001-02-02 09:40:53 +00:00
|
|
|
NS_ASSERTION(!mParent, "invalid call - not allowed for child chars");
|
2000-05-26 05:56:23 +00:00
|
|
|
if (!gInitialized) {
|
2002-01-11 02:57:03 +00:00
|
|
|
InitGlobals(aPresContext);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
1999-12-10 13:02:23 +00:00
|
|
|
mData = aData;
|
2001-02-02 09:40:53 +00:00
|
|
|
// some assumptions until proven otherwise
|
2000-03-28 09:38:24 +00:00
|
|
|
// note that mGlyph is not initialized
|
2002-02-08 22:48:38 +00:00
|
|
|
mOperator = -1;
|
1999-12-10 13:02:23 +00:00
|
|
|
mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
|
2000-01-07 14:49:46 +00:00
|
|
|
mBoundingMetrics.Clear();
|
2000-03-28 09:38:24 +00:00
|
|
|
mGlyphTable = nsnull;
|
2001-02-02 09:40:53 +00:00
|
|
|
// check if stretching is applicable ...
|
|
|
|
if (gGlyphTableList && (1 == mData.Length())) {
|
2002-02-08 22:48:38 +00:00
|
|
|
mOperator = nsMathMLOperators::FindStretchyOperator(mData[0]);
|
|
|
|
if (mOperator >= 0) {
|
|
|
|
mDirection = nsMathMLOperators::GetStretchyDirectionAt(mOperator);
|
2001-02-02 09:40:53 +00:00
|
|
|
// default tentative table (not the one that is necessarily going to be used)
|
2001-04-28 07:39:20 +00:00
|
|
|
mGlyphTable = gGlyphTableList->GetGlyphTableFor(aPresContext, this);
|
2002-01-08 02:15:23 +00:00
|
|
|
// commom case: we won't bother with the stretching if there is
|
|
|
|
// no glyph table for us...
|
2000-03-28 09:38:24 +00:00
|
|
|
if (!mGlyphTable) {
|
2001-02-02 09:40:53 +00:00
|
|
|
// never try to stretch this operator again
|
2002-02-08 22:48:38 +00:00
|
|
|
nsMathMLOperators::DisableStretchyOperatorAt(mOperator);
|
2002-02-12 06:18:04 +00:00
|
|
|
mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
|
|
|
|
mOperator = -1;
|
1999-12-10 13:02:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// -----------------------------------------------------------------------------------
|
1999-11-17 00:49:37 +00:00
|
|
|
/*
|
|
|
|
The Stretch:
|
|
|
|
@param aContainerSize - suggested size for the stretched char
|
2001-02-02 09:40:53 +00:00
|
|
|
@param aDesiredStretchSize - OUT parameter. The desired size
|
|
|
|
after stretching. If no stretching is done, the output will
|
|
|
|
simply give the base size.
|
1999-11-17 00:49:37 +00:00
|
|
|
|
|
|
|
How it works?
|
2001-02-02 09:40:53 +00:00
|
|
|
Summary:-
|
1999-11-17 00:49:37 +00:00
|
|
|
The Stretch() method first looks for a glyph of appropriate
|
2001-02-02 09:40:53 +00:00
|
|
|
size; If a glyph is found, it is cached by this object and
|
|
|
|
its size is returned in aDesiredStretchSize. The cached
|
1999-11-17 00:49:37 +00:00
|
|
|
glyph will then be used at the painting stage.
|
2001-02-02 09:40:53 +00:00
|
|
|
If no glyph of appropriate size is found, a search is made
|
|
|
|
to see if the char can be built by parts.
|
|
|
|
|
|
|
|
Details:-
|
|
|
|
A character gets stretched through the following pipeline :
|
|
|
|
|
|
|
|
1) If the base size of the char is sufficient to cover the
|
|
|
|
container' size, we use that. If not, it will still be
|
|
|
|
used as a fallback if the other stages in the pipeline fail.
|
|
|
|
Issues :
|
|
|
|
a) The base size, the parts and the variants of a char can
|
|
|
|
be in different fonts. For eg., the base size for '(' should
|
|
|
|
come from a normal ascii font if CMEX10 is used, since CMEX10
|
|
|
|
only contains the stretched versions. Hence, there are two
|
|
|
|
style contexts in use throughout the process. The leaf style
|
|
|
|
context of the char holds fonts with which to try to stretch
|
|
|
|
the char. The parent style context of the char contains fonts
|
|
|
|
for normal rendering. So the parent context is the one used
|
|
|
|
to get the initial base size at the start of the pipeline.
|
|
|
|
b) For operators that can be largeop's in display mode,
|
|
|
|
we will skip the base size even if it fits, so that
|
|
|
|
the next stage in the pipeline is given a chance to find
|
|
|
|
a largeop variant. If the next stage fails, we fallback
|
|
|
|
to the base size.
|
|
|
|
|
|
|
|
2) We search for the first larger variant of the char that fits the
|
|
|
|
container' size. We search fonts for larger variants in the order
|
|
|
|
specified in the list of stretchy fonts held by the leaf style
|
|
|
|
context (from -moz-math-font-style-stretchy in mathml.css).
|
|
|
|
Issues :
|
|
|
|
a) the largeop and display settings determine the starting
|
|
|
|
size when we do the above search, regardless of whether
|
|
|
|
smaller variants already fit the container' size.
|
|
|
|
b) if it is a largeopOnly request (i.e., a displaystyle operator
|
|
|
|
with largeop=true and stretchy=false), we break after finding
|
|
|
|
the first starting variant, regardless of whether that
|
|
|
|
variant fits the container's size.
|
|
|
|
|
|
|
|
3) If a variant of appropriate size wasn't found, we see if the char
|
|
|
|
can be built by parts. We search the ordered list of stretchy fonts
|
|
|
|
for the first font which has parts that fit the container' size.
|
|
|
|
Issues:
|
|
|
|
a) Certain chars like over/underbrace in CMEX10 have to be built
|
|
|
|
from two half stretchy chars and joined in the middle. Such
|
|
|
|
chars are handled in a special manner. When this situation is
|
|
|
|
detected, the initial char (referred to as "parent") creates a
|
|
|
|
singly-linked list of child chars, asking them to stretch in
|
|
|
|
a divided space. A convention is used in the the setup of
|
|
|
|
nsGlyphTable to express that a composite parent char can be built
|
|
|
|
from child chars.
|
|
|
|
b) There are some chars that have no middle and glue glyphs. For
|
|
|
|
such chars, the parts need to be joined using the rule.
|
|
|
|
By convention (TeXbook p.225), the descent of the parts is
|
|
|
|
zero while their ascent gives the thickness of the rule that
|
|
|
|
should be used to join them.
|
|
|
|
|
|
|
|
Of note:
|
|
|
|
When the pipeline completes successfully, the desired size of the
|
|
|
|
stretched char can actually be slighthly larger or smaller than
|
|
|
|
aContainerSize. But it is the responsibility of the caller to
|
|
|
|
account for the spacing when setting aContainerSize, and to leave
|
|
|
|
any extra margin when placing the stretched char.
|
1999-11-17 00:49:37 +00:00
|
|
|
*/
|
2001-02-02 09:40:53 +00:00
|
|
|
// -----------------------------------------------------------------------------------
|
1999-11-17 00:49:37 +00:00
|
|
|
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// plain TeX settings (TeXbook p.152)
|
|
|
|
#define NS_MATHML_DELIMITER_FACTOR 0.901f
|
|
|
|
#define NS_MATHML_DELIMITER_SHORTFALL NSFloatPointsToTwips(5.0f)
|
|
|
|
|
2000-03-28 09:38:24 +00:00
|
|
|
static PRBool
|
2001-02-02 09:40:53 +00:00
|
|
|
IsSizeOK(nscoord a, nscoord b, PRUint32 aHint)
|
2000-03-28 09:38:24 +00:00
|
|
|
{
|
2001-02-02 09:40:53 +00:00
|
|
|
// Normal: True if 'a' is around +/-10% of the target 'b' (10% is
|
|
|
|
// 1-DelimiterFactor). This often gives a chance to the base size to
|
2001-03-23 09:46:24 +00:00
|
|
|
// win, especially in the context of <mfenced> without tall elements
|
2001-02-02 09:40:53 +00:00
|
|
|
// or in sloppy markups without protective <mrow></mrow>
|
|
|
|
PRBool isNormal =
|
|
|
|
(aHint & NS_STRETCH_NORMAL)
|
|
|
|
&& PRBool(float(PR_ABS(a - b))
|
|
|
|
< (1.0f - NS_MATHML_DELIMITER_FACTOR) * float(b));
|
|
|
|
// Nearer: True if 'a' is around max{ +/-10% of 'b' , 'b' - 5pt },
|
|
|
|
// as documented in The TeXbook, Ch.17, p.152.
|
|
|
|
PRBool isNearer = PR_FALSE;
|
|
|
|
if (aHint & (NS_STRETCH_NEARER | NS_STRETCH_LARGEOP)) {
|
|
|
|
float c = PR_MAX(float(b) * NS_MATHML_DELIMITER_FACTOR,
|
|
|
|
float(b) - NS_MATHML_DELIMITER_SHORTFALL);
|
|
|
|
isNearer = PRBool(float(PR_ABS(b - a)) <= (float(b) - c));
|
|
|
|
}
|
|
|
|
// Smaller: Mainly for transitory use, to compare two candidate
|
|
|
|
// choices
|
|
|
|
PRBool isSmaller =
|
|
|
|
(aHint & NS_STRETCH_SMALLER)
|
|
|
|
&& PRBool((float(a) >= (NS_MATHML_DELIMITER_FACTOR * float(b)))
|
|
|
|
&& (a <= b));
|
|
|
|
// Larger: Critical to the sqrt code to ensure that the radical
|
|
|
|
// size is tall enough
|
|
|
|
PRBool isLarger =
|
|
|
|
(aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
|
|
|
|
&& PRBool(a >= b);
|
|
|
|
return (isNormal || isSmaller || isNearer || isLarger);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static PRBool
|
2001-02-02 09:40:53 +00:00
|
|
|
IsSizeBetter(nscoord a, nscoord olda, nscoord b, PRUint32 aHint)
|
2000-03-28 09:38:24 +00:00
|
|
|
{
|
2001-02-02 09:40:53 +00:00
|
|
|
if (0 == olda) return PR_TRUE;
|
|
|
|
if (PR_ABS(a - b) < PR_ABS(olda - b)) {
|
|
|
|
if (aHint & (NS_STRETCH_NORMAL | NS_STRETCH_NEARER))
|
2000-03-28 09:38:24 +00:00
|
|
|
return PR_TRUE;
|
2001-02-02 09:40:53 +00:00
|
|
|
if (aHint & NS_STRETCH_SMALLER)
|
|
|
|
return PRBool(a < olda);
|
|
|
|
if (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
|
|
|
|
return PRBool(a > olda);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
// We want to place the glyphs even when they don't fit at their
|
|
|
|
// full extent, i.e., we may clip to tolerate a small amount of
|
|
|
|
// overlap between the parts. This is important to cater for fonts
|
|
|
|
// with long glues.
|
|
|
|
static nscoord
|
2001-02-02 09:40:53 +00:00
|
|
|
ComputeSizeFromParts(nsGlyphCode* aGlyphs,
|
|
|
|
nscoord* aSizes,
|
|
|
|
nscoord aTargetSize,
|
|
|
|
PRUint32 aHint)
|
|
|
|
{
|
2001-03-23 09:46:24 +00:00
|
|
|
enum {first, middle, last, glue};
|
|
|
|
float flex[] = {0.901f, 0.901f, 0.901f};
|
2001-02-02 09:40:53 +00:00
|
|
|
// refine the flexibility depending on whether some parts can be left out
|
2001-03-23 09:46:24 +00:00
|
|
|
if (aGlyphs[glue] == aGlyphs[middle]) flex[middle] = 0.0f;
|
|
|
|
if (aGlyphs[glue] == aGlyphs[first]) flex[first] = 0.0f;
|
|
|
|
if (aGlyphs[glue] == aGlyphs[last]) flex[last] = 0.0f;
|
2001-02-02 09:40:53 +00:00
|
|
|
|
|
|
|
// get the minimum allowable size
|
2001-03-23 09:46:24 +00:00
|
|
|
nscoord computedSize = nscoord(flex[first] * aSizes[first] +
|
|
|
|
flex[middle] * aSizes[middle] +
|
|
|
|
flex[last] * aSizes[last]);
|
2001-02-02 09:40:53 +00:00
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
if (computedSize <= aTargetSize) {
|
2001-02-02 09:40:53 +00:00
|
|
|
// if we can afford more room, the default is to fill-up the target area
|
2001-03-23 09:46:24 +00:00
|
|
|
return aTargetSize;
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
2001-03-23 09:46:24 +00:00
|
|
|
if (IsSizeOK(computedSize, aTargetSize, aHint)) {
|
2001-02-02 09:40:53 +00:00
|
|
|
// settle with the size, and let Paint() do the rest
|
2001-03-23 09:46:24 +00:00
|
|
|
return computedSize;
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
// reject these parts
|
2001-03-23 09:46:24 +00:00
|
|
|
return 0;
|
2000-04-17 04:19:54 +00:00
|
|
|
}
|
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
// Put aFamily in the first position of aFont to guarantee that our
|
|
|
|
// desired font is the one that the GFX font sub-system will use
|
|
|
|
inline void
|
2001-02-02 09:40:53 +00:00
|
|
|
SetFirstFamily(nsFont& aFont, const nsString& aFamily)
|
2000-04-17 04:19:54 +00:00
|
|
|
{
|
|
|
|
// overwrite the old value of font-family:
|
2001-03-23 09:46:24 +00:00
|
|
|
aFont.name.Assign(aFamily);
|
2000-04-17 04:19:54 +00:00
|
|
|
}
|
|
|
|
|
2000-03-28 09:38:24 +00:00
|
|
|
nsresult
|
1999-11-24 06:03:41 +00:00
|
|
|
nsMathMLChar::Stretch(nsIPresContext* aPresContext,
|
1999-11-17 00:49:37 +00:00
|
|
|
nsIRenderingContext& aRenderingContext,
|
|
|
|
nsStretchDirection aStretchDirection,
|
2000-03-28 09:38:24 +00:00
|
|
|
nsBoundingMetrics& aContainerSize,
|
|
|
|
nsBoundingMetrics& aDesiredStretchSize,
|
2001-02-02 09:40:53 +00:00
|
|
|
PRUint32 aStretchHint)
|
1999-09-21 02:12:01 +00:00
|
|
|
{
|
1999-11-17 00:49:37 +00:00
|
|
|
nsresult rv = NS_OK;
|
2001-02-02 09:40:53 +00:00
|
|
|
nsStretchDirection direction = aStretchDirection;
|
1999-12-10 13:02:23 +00:00
|
|
|
|
2002-02-08 22:48:38 +00:00
|
|
|
// if we have been called before, and we didn't actually stretch, our
|
|
|
|
// direction may have been set to NS_STRETCH_DIRECTION_UNSUPPORTED.
|
|
|
|
// So first set our direction back to its instrinsic value
|
|
|
|
if (mOperator >= 0) {
|
|
|
|
// mOperator is initialized in SetData() and remains unchanged
|
|
|
|
mDirection = nsMathMLOperators::GetStretchyDirectionAt(mOperator);
|
|
|
|
}
|
|
|
|
|
2000-03-28 09:38:24 +00:00
|
|
|
// if no specified direction, attempt to stretch in our preferred direction
|
2001-02-02 09:40:53 +00:00
|
|
|
if (direction == NS_STRETCH_DIRECTION_DEFAULT) {
|
|
|
|
direction = mDirection;
|
1999-12-10 13:02:23 +00:00
|
|
|
}
|
1999-11-17 00:49:37 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// Set default font and get the default bounding metrics
|
|
|
|
// mStyleContext is a leaf context used only when stretching happens.
|
|
|
|
// For the base size, the default font should come from the parent context
|
|
|
|
nsAutoString fontName;
|
2002-10-09 01:46:15 +00:00
|
|
|
nsCOMPtr<nsIStyleContext> parentContext(mStyleContext->GetParent());
|
2001-05-31 22:19:43 +00:00
|
|
|
const nsStyleFont *font = NS_STATIC_CAST(const nsStyleFont*,
|
|
|
|
parentContext->GetStyleData(eStyleStruct_Font));
|
|
|
|
nsFont theFont(font->mFont);
|
2002-01-08 02:15:23 +00:00
|
|
|
// XXXrbs get rid of this hardcoding - bug 118600
|
2001-02-02 09:40:53 +00:00
|
|
|
PRUnichar uchar = mData[0];
|
2001-03-30 02:39:38 +00:00
|
|
|
if (kSqrChar == uchar) { // Special to the sqrt char. Due to
|
2002-07-09 01:41:52 +00:00
|
|
|
fontName.Assign(NS_LITERAL_STRING("CMSY10,Math2")); // assumptions in the sqrt code, we need
|
2001-05-31 22:19:43 +00:00
|
|
|
SetFirstFamily(theFont, fontName); // to force precedence on this TeX font
|
2000-04-17 04:19:54 +00:00
|
|
|
}
|
2002-05-24 20:11:14 +00:00
|
|
|
aRenderingContext.SetFont(theFont, nsnull);
|
2001-06-30 11:02:25 +00:00
|
|
|
rv = aRenderingContext.GetBoundingMetrics(mData.get(),
|
2000-03-28 09:38:24 +00:00
|
|
|
PRUint32(mData.Length()),
|
|
|
|
mBoundingMetrics);
|
|
|
|
if (NS_FAILED(rv)) {
|
2001-02-23 16:10:51 +00:00
|
|
|
NS_WARNING("GetBoundingMetrics failed");
|
2000-05-26 05:56:23 +00:00
|
|
|
// ensure that the char later behaves like a normal char
|
2002-02-08 22:48:38 +00:00
|
|
|
// (will be reset back to its intrinsic value in case of dynamic updates)
|
2002-01-08 02:15:23 +00:00
|
|
|
mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
|
2000-03-28 09:38:24 +00:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// set the default desired metrics in case stretching doesn't happen
|
|
|
|
aDesiredStretchSize = mBoundingMetrics;
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// quick return if there is nothing special about this char
|
|
|
|
if (!mGlyphTable || (mDirection != direction)) {
|
2000-03-28 09:38:24 +00:00
|
|
|
// ensure that the char later behaves like a normal char
|
2002-02-08 22:48:38 +00:00
|
|
|
// (will be reset back to its intrinsic value in case of dynamic updates)
|
|
|
|
mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
|
1999-11-17 00:49:37 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// see if this is a particular largeop or largeopOnly request
|
2001-03-30 02:39:38 +00:00
|
|
|
PRBool largeop = (NS_STRETCH_LARGEOP & aStretchHint) != 0;
|
2001-02-02 09:40:53 +00:00
|
|
|
PRBool largeopOnly = (NS_STRETCH_LARGEOP == aStretchHint); // (==, not mask!)
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// 1. Check the common situations where stretching is not actually needed
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
nscoord targetSize, charSize;
|
2001-02-02 09:40:53 +00:00
|
|
|
PRBool isVertical = (direction == NS_STRETCH_DIRECTION_VERTICAL);
|
|
|
|
if (isVertical) {
|
|
|
|
charSize = aDesiredStretchSize.ascent + aDesiredStretchSize.descent;
|
|
|
|
targetSize = aContainerSize.ascent + aContainerSize.descent;
|
1999-11-17 00:49:37 +00:00
|
|
|
}
|
2000-03-28 09:38:24 +00:00
|
|
|
else {
|
2001-02-02 09:40:53 +00:00
|
|
|
charSize = aDesiredStretchSize.width;
|
|
|
|
targetSize = aContainerSize.width;
|
|
|
|
}
|
|
|
|
// if we are not a largeop in display mode, return if size fits
|
2002-02-15 14:39:50 +00:00
|
|
|
if (targetSize <= 0 || (!largeop && IsSizeOK(charSize, targetSize, aStretchHint))) {
|
2001-02-02 09:40:53 +00:00
|
|
|
// ensure that the char later behaves like a normal char
|
2002-02-08 22:48:38 +00:00
|
|
|
// (will be reset back to its intrinsic value in case of dynamic updates)
|
|
|
|
mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
|
2000-03-28 09:38:24 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// 2. Try to search if there is a glyph of appropriate size
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
2000-03-28 09:38:24 +00:00
|
|
|
|
|
|
|
PRInt32 size;
|
2001-02-02 09:40:53 +00:00
|
|
|
nsGlyphTable* glyphTable;
|
2000-03-28 09:38:24 +00:00
|
|
|
nsBoundingMetrics bm;
|
2001-03-23 09:46:24 +00:00
|
|
|
nsGlyphCode startingGlyph = {uchar, 0}; // code@font
|
2000-03-28 09:38:24 +00:00
|
|
|
nsGlyphCode ch;
|
|
|
|
|
|
|
|
// this will be the best glyph that we encounter during the search...
|
2001-03-23 09:46:24 +00:00
|
|
|
nsGlyphCode bestGlyph = startingGlyph;
|
2000-03-28 09:38:24 +00:00
|
|
|
nsGlyphTable* bestGlyphTable = mGlyphTable;
|
|
|
|
nsBoundingMetrics bestbm = mBoundingMetrics;
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// use our stretchy style context now that stretching is in progress
|
2001-05-31 22:19:43 +00:00
|
|
|
font = NS_STATIC_CAST(const nsStyleFont*,
|
|
|
|
mStyleContext->GetStyleData(eStyleStruct_Font));
|
|
|
|
theFont = font->mFont;
|
2001-02-02 09:40:53 +00:00
|
|
|
|
|
|
|
// initialize the search list for this char
|
2001-04-28 07:39:20 +00:00
|
|
|
PRBool alreadyCSS = PR_FALSE;
|
2001-02-02 09:40:53 +00:00
|
|
|
nsAutoVoidArray tableList;
|
2001-04-28 07:39:20 +00:00
|
|
|
// see if there are user-specified preferred tables for the variants of this char
|
2002-02-18 22:52:15 +00:00
|
|
|
PRInt32 count, t = nsGlyphTableList::gVariants[mOperator];
|
2001-04-28 07:39:20 +00:00
|
|
|
gGlyphTableList->GetPreferredListAt(aPresContext, t, &tableList, &count);
|
|
|
|
if (!count) {
|
2001-02-02 09:40:53 +00:00
|
|
|
// get a list that attempts to honor the css font-family
|
2001-05-31 22:19:43 +00:00
|
|
|
gGlyphTableList->GetListFor(aPresContext, this, &theFont, &tableList);
|
2001-04-28 07:39:20 +00:00
|
|
|
alreadyCSS = PR_TRUE;
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
|
2001-03-23 09:46:24 +00:00
|
|
|
#ifdef NOISY_SEARCH
|
|
|
|
printf("Searching in %d fonts for a glyph of appropriate size for: 0x%04X:%c\n",
|
|
|
|
tableList.Count(), uchar, uchar&0x00FF);
|
|
|
|
#endif
|
|
|
|
|
2001-04-28 07:39:20 +00:00
|
|
|
count = tableList.Count();
|
2001-03-30 02:39:38 +00:00
|
|
|
for (t = 0; t < count; t++) {
|
2001-02-02 09:40:53 +00:00
|
|
|
// see if this table has a glyph that matches the container
|
2001-04-28 07:39:20 +00:00
|
|
|
glyphTable = NS_STATIC_CAST(nsGlyphTable*, tableList.ElementAt(t));
|
2001-02-02 09:40:53 +00:00
|
|
|
// figure out the starting size : if this is a largeop, start at 2 else 1
|
|
|
|
size = 1; // size=0 is the char at its normal size
|
2001-04-28 07:39:20 +00:00
|
|
|
if (largeop && glyphTable->BigOf(aPresContext, this, 2)) {
|
2001-02-02 09:40:53 +00:00
|
|
|
size = 2;
|
|
|
|
}
|
2001-03-23 09:46:24 +00:00
|
|
|
glyphTable->GetPrimaryFontName(fontName);
|
2001-05-31 22:19:43 +00:00
|
|
|
SetFirstFamily(theFont, fontName);
|
2002-05-24 20:11:14 +00:00
|
|
|
aRenderingContext.SetFont(theFont, nsnull);
|
2000-03-28 09:38:24 +00:00
|
|
|
#ifdef NOISY_SEARCH
|
2001-02-02 09:40:53 +00:00
|
|
|
char str[50];
|
|
|
|
fontName.ToCString(str, sizeof(str));
|
|
|
|
printf(" searching in %s ...\n", str);
|
2000-03-28 09:38:24 +00:00
|
|
|
#endif
|
2001-04-28 07:39:20 +00:00
|
|
|
ch = glyphTable->BigOf(aPresContext, this, size++);
|
2001-02-02 09:40:53 +00:00
|
|
|
while (ch) {
|
|
|
|
NS_ASSERTION(ch != uchar, "glyph table incorrectly set -- duplicate found");
|
2001-05-31 22:19:43 +00:00
|
|
|
rv = glyphTable->GetBoundingMetrics(aRenderingContext, theFont, ch, bm);
|
2001-02-02 09:40:53 +00:00
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
charSize = (isVertical)
|
|
|
|
? bm.ascent + bm.descent
|
|
|
|
: bm.rightBearing - bm.leftBearing;
|
|
|
|
// always break when largeopOnly is set
|
|
|
|
if (IsSizeOK(charSize, targetSize, aStretchHint) || largeopOnly) {
|
2000-03-28 09:38:24 +00:00
|
|
|
#ifdef NOISY_SEARCH
|
2001-02-02 09:40:53 +00:00
|
|
|
printf(" size:%d OK!\n", size-1);
|
2000-03-28 09:38:24 +00:00
|
|
|
#endif
|
2001-02-02 09:40:53 +00:00
|
|
|
bestbm = bm;
|
|
|
|
bestGlyphTable = glyphTable;
|
|
|
|
bestGlyph = ch;
|
|
|
|
goto done; // get out...
|
|
|
|
}
|
2001-03-23 09:46:24 +00:00
|
|
|
nscoord oldSize = (isVertical)
|
|
|
|
? bestbm.ascent + bestbm.descent
|
|
|
|
: bestbm.rightBearing - bestbm.leftBearing;
|
2001-02-02 09:40:53 +00:00
|
|
|
if (IsSizeBetter(charSize, oldSize, targetSize, aStretchHint)) {
|
|
|
|
bestGlyphTable = glyphTable;
|
|
|
|
bestGlyph = ch;
|
|
|
|
bestbm = bm;
|
2000-03-28 09:38:24 +00:00
|
|
|
#ifdef NOISY_SEARCH
|
2001-02-02 09:40:53 +00:00
|
|
|
printf(" size:%d Current best\n", size-1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
printf(" size:%d Rejected!\n", size-1);
|
2000-03-28 09:38:24 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
// if largeopOnly is set, break now
|
|
|
|
if (largeopOnly) break;
|
2001-04-28 07:39:20 +00:00
|
|
|
ch = glyphTable->BigOf(aPresContext, this, size++);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
if (largeopOnly) goto done; // the user doesn't want to stretch
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Build by parts. If no glyph of appropriate size was found, see if we can
|
2001-03-30 02:39:38 +00:00
|
|
|
// build the char by parts. If there are preferred tables, they are used. Otherwise,
|
2001-02-02 09:40:53 +00:00
|
|
|
// search for the first table with suitable parts for this char
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-04-28 07:39:20 +00:00
|
|
|
// see if there are user-specified preferred tables for the parts of this char
|
2002-02-18 22:52:15 +00:00
|
|
|
t = nsGlyphTableList::gParts[mOperator];
|
2001-04-28 07:39:20 +00:00
|
|
|
gGlyphTableList->GetPreferredListAt(aPresContext, t, &tableList, &count);
|
|
|
|
if (!count && !alreadyCSS) {
|
2001-03-30 02:39:38 +00:00
|
|
|
// we didn't do this earlier... so we need to do it here:
|
|
|
|
// get a list that attempts to honor the css font-family
|
2001-05-31 22:19:43 +00:00
|
|
|
gGlyphTableList->GetListFor(aPresContext, this, &theFont, &tableList);
|
2001-03-30 02:39:38 +00:00
|
|
|
}
|
|
|
|
|
2000-03-28 09:38:24 +00:00
|
|
|
#ifdef NOISY_SEARCH
|
2001-03-30 02:39:38 +00:00
|
|
|
printf("Searching in %d fonts for the first font with suitable parts for: 0x%04X:%c\n",
|
|
|
|
tableList.Count(), uchar, uchar&0x00FF);
|
2000-03-28 09:38:24 +00:00
|
|
|
#endif
|
2001-03-30 02:39:38 +00:00
|
|
|
|
|
|
|
count = tableList.Count();
|
|
|
|
for (t = 0; t < count; t++) {
|
2001-04-28 07:39:20 +00:00
|
|
|
glyphTable = NS_STATIC_CAST(nsGlyphTable*, tableList.ElementAt(t));
|
|
|
|
if (!glyphTable->HasPartsOf(aPresContext, this)) continue; // to next table
|
2001-02-02 09:40:53 +00:00
|
|
|
|
|
|
|
// See if this is a composite character //////////////////////////////////////////
|
2001-04-28 07:39:20 +00:00
|
|
|
if (glyphTable->IsComposite(aPresContext, this)) {
|
2001-02-02 09:40:53 +00:00
|
|
|
// let the child chars do the job
|
|
|
|
nsBoundingMetrics compositeSize;
|
|
|
|
rv = ComposeChildren(aPresContext, aRenderingContext, glyphTable,
|
|
|
|
aContainerSize, compositeSize, aStretchHint);
|
2000-03-28 09:38:24 +00:00
|
|
|
#ifdef NOISY_SEARCH
|
|
|
|
char str[50];
|
|
|
|
fontName.ToCString(str, sizeof(str));
|
2001-02-02 09:40:53 +00:00
|
|
|
printf(" Composing %d chars in font %s %s!\n",
|
2001-04-28 07:39:20 +00:00
|
|
|
glyphTable->ChildCountOf(aPresContext, this), str,
|
2001-02-02 09:40:53 +00:00
|
|
|
NS_SUCCEEDED(rv)? "OK" : "Rejected");
|
2000-03-28 09:38:24 +00:00
|
|
|
#endif
|
2001-02-23 16:10:51 +00:00
|
|
|
if (NS_FAILED(rv)) continue; // to next table
|
|
|
|
|
|
|
|
// all went well, painting will be delegated from now on to children
|
2001-03-23 09:46:24 +00:00
|
|
|
mGlyph = kNullGlyph; // this will tell paint to build by parts
|
2001-02-23 16:10:51 +00:00
|
|
|
mGlyphTable = glyphTable;
|
|
|
|
mBoundingMetrics = compositeSize;
|
|
|
|
aDesiredStretchSize = compositeSize;
|
|
|
|
return NS_OK; // get out ...
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
// See if the parts of this table fit in the desired space ///////////////////////
|
2001-03-23 09:46:24 +00:00
|
|
|
glyphTable->GetPrimaryFontName(fontName);
|
2001-05-31 22:19:43 +00:00
|
|
|
SetFirstFamily(theFont, fontName);
|
2002-05-24 20:11:14 +00:00
|
|
|
aRenderingContext.SetFont(theFont, nsnull);
|
2001-02-02 09:40:53 +00:00
|
|
|
// Compute the bounding metrics of all partial glyphs
|
1999-11-17 00:49:37 +00:00
|
|
|
PRInt32 i;
|
2000-05-04 04:44:36 +00:00
|
|
|
nsGlyphCode chdata[4];
|
2000-01-18 04:35:37 +00:00
|
|
|
nsBoundingMetrics bmdata[4];
|
2001-02-02 09:40:53 +00:00
|
|
|
nscoord computedSize, sizedata[4];
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphCode glue = glyphTable->GlueOf(aPresContext, this);
|
2000-01-18 04:35:37 +00:00
|
|
|
for (i = 0; i < 4; i++) {
|
2000-03-28 09:38:24 +00:00
|
|
|
switch (i) {
|
2001-04-28 07:39:20 +00:00
|
|
|
case 0: ch = glyphTable->TopOf(aPresContext, this); break;
|
|
|
|
case 1: ch = glyphTable->MiddleOf(aPresContext, this); break;
|
|
|
|
case 2: ch = glyphTable->BottomOf(aPresContext, this); break;
|
|
|
|
case 3: ch = glue; break;
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
// empty slots are filled with the glue if it is not null
|
2001-04-06 00:49:50 +00:00
|
|
|
if (!ch) ch = glue;
|
2001-02-02 09:40:53 +00:00
|
|
|
if (!ch) { // glue is null, set bounding metrics to 0
|
|
|
|
bm.Clear();
|
|
|
|
}
|
|
|
|
else {
|
2001-05-31 22:19:43 +00:00
|
|
|
rv = glyphTable->GetBoundingMetrics(aRenderingContext, theFont, ch, bm);
|
2001-02-02 09:40:53 +00:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
// stop if we failed to compute the bounding metrics of a part.
|
2001-02-23 16:10:51 +00:00
|
|
|
NS_WARNING("GetBoundingMetrics failed");
|
2001-02-02 09:40:53 +00:00
|
|
|
break;
|
|
|
|
}
|
2000-01-18 04:35:37 +00:00
|
|
|
}
|
2000-05-04 04:44:36 +00:00
|
|
|
chdata[i] = ch;
|
2000-01-18 04:35:37 +00:00
|
|
|
bmdata[i] = bm;
|
2001-02-02 09:40:53 +00:00
|
|
|
sizedata[i] = (isVertical)
|
|
|
|
? bm.ascent + bm.descent
|
|
|
|
: bm.rightBearing - bm.leftBearing;
|
2000-01-18 04:35:37 +00:00
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
if (NS_FAILED(rv)) continue; // to next table
|
|
|
|
|
|
|
|
// Build by parts if we have successfully computed the
|
|
|
|
// bounding metrics of all parts.
|
2001-03-23 09:46:24 +00:00
|
|
|
computedSize = ComputeSizeFromParts(chdata, sizedata, targetSize, aStretchHint);
|
2001-02-02 09:40:53 +00:00
|
|
|
#ifdef NOISY_SEARCH
|
|
|
|
char str[50];
|
|
|
|
fontName.ToCString(str, sizeof(str));
|
|
|
|
printf(" Font %s %s!\n", str, (computedSize) ? "OK" : "Rejected");
|
|
|
|
#endif
|
|
|
|
if (!computedSize) continue; // to next table
|
|
|
|
|
|
|
|
// the computed size is suitable for the available space...
|
|
|
|
// now is the time to compute and cache our bounding metrics
|
|
|
|
if (isVertical) {
|
|
|
|
nscoord lbearing = bmdata[0].leftBearing;
|
|
|
|
nscoord rbearing = bmdata[0].rightBearing;
|
|
|
|
nscoord width = bmdata[0].width;
|
|
|
|
for (i = 1; i < 4; i++) {
|
|
|
|
bm = bmdata[i];
|
|
|
|
if (width < bm.width) width = bm.width;
|
|
|
|
if (lbearing > bm.leftBearing) lbearing = bm.leftBearing;
|
|
|
|
if (rbearing < bm.rightBearing) rbearing = bm.rightBearing;
|
1999-11-17 00:49:37 +00:00
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
bestbm.width = width;
|
2001-02-23 16:10:51 +00:00
|
|
|
bestbm.ascent = bmdata[0].ascent; // Yes top, so that it works with TeX sqrt!
|
2001-02-02 09:40:53 +00:00
|
|
|
bestbm.descent = computedSize - bestbm.ascent;
|
|
|
|
bestbm.leftBearing = lbearing;
|
|
|
|
bestbm.rightBearing = rbearing;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nscoord ascent = bmdata[0].ascent;
|
|
|
|
nscoord descent = bmdata[0].descent;
|
|
|
|
for (i = 1; i < 4; i++) {
|
|
|
|
bm = bmdata[i];
|
|
|
|
if (ascent < bm.ascent) ascent = bm.ascent;
|
|
|
|
if (descent < bm.descent) descent = bm.descent;
|
1999-11-17 00:49:37 +00:00
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
bestbm.width = computedSize;
|
|
|
|
bestbm.ascent = ascent;
|
|
|
|
bestbm.descent = descent;
|
|
|
|
bestbm.leftBearing = 0;
|
|
|
|
bestbm.rightBearing = computedSize;
|
1999-11-17 00:49:37 +00:00
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
// reset
|
2001-03-23 09:46:24 +00:00
|
|
|
bestGlyph = kNullGlyph; // this will tell paint to build by parts
|
2001-02-02 09:40:53 +00:00
|
|
|
bestGlyphTable = glyphTable;
|
|
|
|
goto done; // get out...
|
1999-09-21 02:12:01 +00:00
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
#ifdef NOISY_SEARCH
|
|
|
|
printf(" No font with suitable parts found\n");
|
|
|
|
#endif
|
|
|
|
// if sum of parts doesn't fit in the space... we will use a single
|
|
|
|
// glyph -- the base size or the best glyph encountered during the search
|
1999-09-21 02:12:01 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
done:
|
2001-03-23 09:46:24 +00:00
|
|
|
if (bestGlyph == startingGlyph) { // nothing happened
|
2000-03-28 09:38:24 +00:00
|
|
|
// ensure that the char behaves like a normal char
|
2002-02-08 22:48:38 +00:00
|
|
|
// (will be reset back to its intrinsic value in case of dynamic updates)
|
|
|
|
mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// will stretch
|
2001-03-23 09:46:24 +00:00
|
|
|
mGlyph = bestGlyph; // note that this can be null to tell paint to build by parts
|
2000-03-28 09:38:24 +00:00
|
|
|
mGlyphTable = bestGlyphTable;
|
|
|
|
mBoundingMetrics = bestbm;
|
|
|
|
aDesiredStretchSize = bestbm;
|
|
|
|
}
|
1999-09-21 02:12:01 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
nsresult
|
|
|
|
nsMathMLChar::ComposeChildren(nsIPresContext* aPresContext,
|
|
|
|
nsIRenderingContext& aRenderingContext,
|
|
|
|
nsGlyphTable* aGlyphTable,
|
|
|
|
nsBoundingMetrics& aContainerSize,
|
|
|
|
nsBoundingMetrics& aCompositeSize,
|
|
|
|
PRUint32 aStretchHint)
|
|
|
|
{
|
|
|
|
PRInt32 i = 0;
|
|
|
|
nsMathMLChar* child;
|
2001-04-28 07:39:20 +00:00
|
|
|
PRInt32 count = aGlyphTable->ChildCountOf(aPresContext, this);
|
2001-02-02 09:40:53 +00:00
|
|
|
NS_ASSERTION(count, "something is wrong somewhere");
|
|
|
|
if (!count) return NS_ERROR_FAILURE;
|
|
|
|
// if we haven't been here before, create the linked list of children now
|
|
|
|
// otherwise, use what we have, adding more children as needed or deleting the extra
|
|
|
|
nsMathMLChar* last = this;
|
|
|
|
while ((i < count) && last->mSibling) {
|
|
|
|
i++;
|
|
|
|
last = last->mSibling;
|
|
|
|
}
|
|
|
|
while (i < count) {
|
|
|
|
child = new nsMathMLChar(this);
|
|
|
|
if (!child) {
|
|
|
|
if (mSibling) delete mSibling; // don't leave a dangling list ...
|
|
|
|
mSibling = nsnull;
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
last->mSibling = child;
|
|
|
|
last = child;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if (last->mSibling) {
|
|
|
|
delete last->mSibling;
|
|
|
|
last->mSibling = nsnull;
|
|
|
|
}
|
|
|
|
// let children stretch in an equal space
|
|
|
|
nsBoundingMetrics splitSize(aContainerSize);
|
|
|
|
if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
|
|
|
|
splitSize.width /= count;
|
|
|
|
else {
|
|
|
|
splitSize.ascent = ((splitSize.ascent + splitSize.descent) / count) / 2;
|
|
|
|
splitSize.descent = splitSize.ascent;
|
|
|
|
}
|
|
|
|
nscoord dx = 0, dy = 0;
|
|
|
|
for (i = 0, child = mSibling; child; child = child->mSibling, i++) {
|
|
|
|
// child chars should just inherit our values - which may change between calls...
|
|
|
|
child->mData = mData;
|
2002-02-18 22:52:15 +00:00
|
|
|
child->mOperator = mOperator;
|
2001-02-02 09:40:53 +00:00
|
|
|
child->mDirection = mDirection;
|
|
|
|
child->mStyleContext = mStyleContext;
|
|
|
|
child->mGlyphTable = aGlyphTable; // the child is associated to this table
|
|
|
|
// there goes the Stretch() ...
|
|
|
|
nsBoundingMetrics childSize;
|
|
|
|
nsresult rv = child->Stretch(aPresContext, aRenderingContext, mDirection,
|
|
|
|
splitSize, childSize, aStretchHint);
|
|
|
|
// check if something went wrong or the child couldn't fit in the alloted space
|
|
|
|
if (NS_FAILED(rv) || (NS_STRETCH_DIRECTION_UNSUPPORTED == child->mDirection)) {
|
|
|
|
delete mSibling; // don't leave a dangling list behind ...
|
|
|
|
mSibling = nsnull;
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
child->SetRect(nsRect(dx, dy, childSize.width, childSize.ascent+childSize.descent));
|
|
|
|
if (0 == i)
|
|
|
|
aCompositeSize = childSize;
|
|
|
|
else {
|
|
|
|
if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
|
|
|
|
aCompositeSize += childSize;
|
|
|
|
else {
|
|
|
|
aCompositeSize.descent += childSize.ascent + childSize.descent;
|
|
|
|
if (aCompositeSize.leftBearing > childSize.leftBearing)
|
|
|
|
aCompositeSize.leftBearing = childSize.leftBearing;
|
|
|
|
if (aCompositeSize.rightBearing < childSize.rightBearing)
|
|
|
|
aCompositeSize.rightBearing = childSize.rightBearing;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
|
|
|
|
dx += childSize.width;
|
|
|
|
else
|
|
|
|
dy += childSize.ascent + childSize.descent;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-03-28 09:38:24 +00:00
|
|
|
nsresult
|
1999-11-24 06:03:41 +00:00
|
|
|
nsMathMLChar::Paint(nsIPresContext* aPresContext,
|
1999-09-21 02:12:01 +00:00
|
|
|
nsIRenderingContext& aRenderingContext,
|
2000-03-28 09:38:24 +00:00
|
|
|
const nsRect& aDirtyRect,
|
|
|
|
nsFramePaintLayer aWhichLayer,
|
2002-04-02 04:15:22 +00:00
|
|
|
nsIFrame* aForFrame,
|
|
|
|
const nsRect* aSelectedRect)
|
1999-09-21 02:12:01 +00:00
|
|
|
{
|
2001-02-02 09:40:53 +00:00
|
|
|
nsresult rv = NS_OK;
|
2002-10-09 01:46:15 +00:00
|
|
|
nsCOMPtr<nsIStyleContext> parentContext(mStyleContext->GetParent());
|
2001-02-02 09:40:53 +00:00
|
|
|
nsIStyleContext* styleContext = mStyleContext;
|
|
|
|
|
|
|
|
if (NS_STRETCH_DIRECTION_UNSUPPORTED == mDirection) {
|
|
|
|
// normal drawing if there is nothing special about this char
|
2001-02-23 16:10:51 +00:00
|
|
|
// Set default context to the parent context
|
|
|
|
styleContext = parentContext;
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
|
2001-05-31 22:19:43 +00:00
|
|
|
const nsStyleVisibility *visib = NS_STATIC_CAST(const nsStyleVisibility*,
|
|
|
|
styleContext->GetStyleData(eStyleStruct_Visibility));
|
2002-04-02 04:15:22 +00:00
|
|
|
if (!visib->IsVisible())
|
|
|
|
return NS_OK;
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2002-02-27 01:35:27 +00:00
|
|
|
// if the leaf style context that we use for stretchy chars has a background
|
|
|
|
// color we use it -- this feature is mostly used for testing and debugging
|
|
|
|
// purposes. Normally, users will set the background on the container frame.
|
2002-04-02 04:15:22 +00:00
|
|
|
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
|
|
|
|
// paint the selection background -- beware MathML frames overlap a lot
|
|
|
|
if (aSelectedRect && !aSelectedRect->IsEmpty()) {
|
|
|
|
// get color to use for selection from the look&feel object
|
|
|
|
nsCOMPtr<nsILookAndFeel> lf;
|
|
|
|
aPresContext->GetLookAndFeel(getter_AddRefs(lf));
|
|
|
|
if (lf) {
|
|
|
|
nscolor bgColor = NS_RGB(0, 0, 0);
|
|
|
|
lf->GetColor(nsILookAndFeel::eColor_TextSelectBackground, bgColor);
|
|
|
|
aRenderingContext.SetColor(bgColor);
|
|
|
|
aRenderingContext.FillRect(*aSelectedRect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (mRect.width && mRect.height) {
|
2001-05-31 22:19:43 +00:00
|
|
|
const nsStyleBorder *border = NS_STATIC_CAST(const nsStyleBorder*,
|
|
|
|
styleContext->GetStyleData(eStyleStruct_Border));
|
2002-10-08 10:24:53 +00:00
|
|
|
const nsStylePadding *padding = NS_STATIC_CAST(const nsStylePadding*,
|
|
|
|
styleContext->GetStyleData(eStyleStruct_Padding));
|
2002-01-15 07:48:10 +00:00
|
|
|
const nsStyleBackground *backg = NS_STATIC_CAST(const nsStyleBackground*,
|
|
|
|
styleContext->GetStyleData(eStyleStruct_Background));
|
2001-02-02 09:40:53 +00:00
|
|
|
nsRect rect(mRect); //0, 0, mRect.width, mRect.height);
|
2002-02-27 01:35:27 +00:00
|
|
|
if (styleContext != parentContext.get() &&
|
|
|
|
0 == (backg->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT))
|
2002-01-25 06:08:03 +00:00
|
|
|
nsCSSRendering::PaintBackgroundWithSC(aPresContext, aRenderingContext, aForFrame,
|
2002-10-08 10:24:53 +00:00
|
|
|
aDirtyRect, rect, *backg, *border, *padding,
|
|
|
|
0, 0);
|
2002-02-27 01:35:27 +00:00
|
|
|
//else
|
|
|
|
// our container frame will take care of painting its background
|
|
|
|
// nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, aForFrame,
|
2002-10-08 10:24:53 +00:00
|
|
|
// aDirtyRect, rect, *border, *padding, 0, 0);
|
2002-02-27 01:35:27 +00:00
|
|
|
#if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
|
|
|
|
// for visual debug
|
|
|
|
PRIntn skipSides = 0; //aForFrame->GetSkipSides();
|
|
|
|
const nsStyleOutline *outline = NS_STATIC_CAST(const nsStyleOutline*,
|
|
|
|
styleContext->GetStyleData(eStyleStruct_Outline));
|
2000-03-28 09:38:24 +00:00
|
|
|
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, aForFrame,
|
2001-05-31 22:19:43 +00:00
|
|
|
aDirtyRect, rect, *border, styleContext, skipSides);
|
2000-03-28 09:38:24 +00:00
|
|
|
nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, aForFrame,
|
2001-05-31 22:19:43 +00:00
|
|
|
aDirtyRect, rect, *border, *outline, styleContext, 0);
|
2002-02-27 01:35:27 +00:00
|
|
|
#endif
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
}
|
1999-09-21 02:12:01 +00:00
|
|
|
|
2002-04-02 04:15:22 +00:00
|
|
|
if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
|
2001-02-02 09:40:53 +00:00
|
|
|
// Set color ...
|
2001-05-31 22:19:43 +00:00
|
|
|
const nsStyleColor *color = NS_STATIC_CAST(const nsStyleColor*,
|
|
|
|
styleContext->GetStyleData(eStyleStruct_Color));
|
2002-04-02 04:15:22 +00:00
|
|
|
nscolor fgColor = color->mColor;
|
|
|
|
if (aSelectedRect && !aSelectedRect->IsEmpty()) {
|
|
|
|
// get color to use for selection from the look&feel object
|
|
|
|
nsCOMPtr<nsILookAndFeel> lf;
|
|
|
|
aPresContext->GetLookAndFeel(getter_AddRefs(lf));
|
|
|
|
if (lf) {
|
|
|
|
lf->GetColor(nsILookAndFeel::eColor_TextSelectForeground, fgColor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
aRenderingContext.SetColor(fgColor);
|
2000-04-17 04:19:54 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
nsAutoString fontName;
|
2001-05-31 22:19:43 +00:00
|
|
|
const nsStyleFont *font = NS_STATIC_CAST(const nsStyleFont*,
|
|
|
|
styleContext->GetStyleData(eStyleStruct_Font));
|
|
|
|
nsFont theFont(font->mFont);
|
2000-03-28 09:38:24 +00:00
|
|
|
|
2001-02-02 09:40:53 +00:00
|
|
|
if (NS_STRETCH_DIRECTION_UNSUPPORTED == mDirection) {
|
2000-03-28 09:38:24 +00:00
|
|
|
// normal drawing if there is nothing special about this char ...
|
2001-02-02 09:40:53 +00:00
|
|
|
// Set the default font and grab some metrics to adjust the placements ...
|
|
|
|
PRUint32 len = PRUint32(mData.Length());
|
|
|
|
PRUnichar uchar = mData[0];
|
2001-03-30 02:39:38 +00:00
|
|
|
if ((1 == len) && (kSqrChar == uchar)) { // Special to the sqrt char. Due to
|
2002-07-09 01:41:52 +00:00
|
|
|
fontName.Assign(NS_LITERAL_STRING("CMSY10,Math2")); // assumptions in the sqrt code, we need
|
2001-05-31 22:19:43 +00:00
|
|
|
SetFirstFamily(theFont, fontName); // to force precedence on this TeX font
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
2002-05-24 20:11:14 +00:00
|
|
|
aRenderingContext.SetFont(theFont, nsnull);
|
2001-02-02 09:40:53 +00:00
|
|
|
//printf("Painting %04X like a normal char\n", mData[0]);
|
2000-03-28 22:31:43 +00:00
|
|
|
//aRenderingContext.SetColor(NS_RGB(255,0,0));
|
2001-10-30 22:58:00 +00:00
|
|
|
aRenderingContext.DrawString(mData.get(), len, mRect.x, mRect.y + mBoundingMetrics.ascent);
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
else {
|
|
|
|
// Set the stretchy font and grab some metrics to adjust the placements ...
|
2001-03-23 09:46:24 +00:00
|
|
|
mGlyphTable->GetPrimaryFontName(fontName);
|
2001-05-31 22:19:43 +00:00
|
|
|
SetFirstFamily(theFont, fontName);
|
2002-05-24 20:11:14 +00:00
|
|
|
aRenderingContext.SetFont(theFont, nsnull);
|
2001-02-02 09:40:53 +00:00
|
|
|
// if there is a glyph of appropriate size, paint that glyph
|
2001-03-23 09:46:24 +00:00
|
|
|
if (mGlyph) {
|
2001-02-02 09:40:53 +00:00
|
|
|
//printf("Painting %04X with a glyph of appropriate size\n", mData[0]);
|
2000-03-28 22:31:43 +00:00
|
|
|
//aRenderingContext.SetColor(NS_RGB(0,0,255));
|
2001-10-30 22:58:00 +00:00
|
|
|
mGlyphTable->DrawGlyph(aRenderingContext, theFont, mGlyph,
|
|
|
|
mRect.x, mRect.y + mBoundingMetrics.ascent);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
else { // paint by parts
|
|
|
|
// see if this is a composite char and let children paint themselves
|
|
|
|
if (!mParent && mSibling) { // only a "root" having child chars can enter here
|
|
|
|
for (nsMathMLChar* child = mSibling; child; child = child->mSibling) {
|
|
|
|
//if (!mStyleContext->Equals(child->mStyleContext))
|
|
|
|
// printf("char contexts are out of sync\n");
|
|
|
|
child->Paint(aPresContext, aRenderingContext,
|
2002-04-02 04:15:22 +00:00
|
|
|
aDirtyRect, aWhichLayer, aForFrame, aSelectedRect);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
return NS_OK; // that's all folks
|
|
|
|
}
|
2000-03-28 22:31:43 +00:00
|
|
|
//aRenderingContext.SetColor(NS_RGB(0,255,0));
|
2001-02-02 09:40:53 +00:00
|
|
|
if (NS_STRETCH_DIRECTION_VERTICAL == mDirection)
|
2001-10-30 22:58:00 +00:00
|
|
|
rv = PaintVertically(aPresContext, aRenderingContext, theFont, styleContext,
|
|
|
|
mGlyphTable, this, mRect);
|
2001-02-02 09:40:53 +00:00
|
|
|
else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
|
2001-10-30 22:58:00 +00:00
|
|
|
rv = PaintHorizontally(aPresContext, aRenderingContext, theFont, styleContext,
|
|
|
|
mGlyphTable, this, mRect);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
1999-11-21 22:10:45 +00:00
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
return rv;
|
1999-11-21 22:10:45 +00:00
|
|
|
}
|
1999-11-17 00:49:37 +00:00
|
|
|
|
2000-03-28 09:38:24 +00:00
|
|
|
/* =================================================================================
|
1999-11-21 22:10:45 +00:00
|
|
|
And now the helper routines that actually do the job of painting the char by parts
|
|
|
|
*/
|
1999-11-17 00:49:37 +00:00
|
|
|
|
1999-11-21 22:10:45 +00:00
|
|
|
// paint a stretchy char by assembling glyphs vertically
|
|
|
|
nsresult
|
1999-11-24 06:03:41 +00:00
|
|
|
nsMathMLChar::PaintVertically(nsIPresContext* aPresContext,
|
1999-11-21 22:10:45 +00:00
|
|
|
nsIRenderingContext& aRenderingContext,
|
2001-03-23 09:46:24 +00:00
|
|
|
nsFont& aFont,
|
1999-11-21 22:10:45 +00:00
|
|
|
nsIStyleContext* aStyleContext,
|
2000-03-28 09:38:24 +00:00
|
|
|
nsGlyphTable* aGlyphTable,
|
2001-02-02 09:40:53 +00:00
|
|
|
nsMathMLChar* aChar,
|
2001-03-23 09:46:24 +00:00
|
|
|
nsRect& aRect)
|
1999-11-21 22:10:45 +00:00
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
nsRect clipRect;
|
2000-05-08 07:18:10 +00:00
|
|
|
nscoord dx, dy;
|
1999-11-21 22:10:45 +00:00
|
|
|
|
|
|
|
float p2t;
|
1999-11-24 06:03:41 +00:00
|
|
|
aPresContext->GetScaledPixelsToTwips(&p2t);
|
2000-03-28 09:38:24 +00:00
|
|
|
nscoord onePixel = NSIntPixelsToTwips(1, p2t);
|
1999-11-21 22:10:45 +00:00
|
|
|
|
|
|
|
// get metrics data to be re-used later
|
|
|
|
PRInt32 i;
|
2000-03-28 09:38:24 +00:00
|
|
|
nsGlyphCode ch, chdata[4];
|
1999-11-21 22:10:45 +00:00
|
|
|
nsBoundingMetrics bm, bmdata[4];
|
2002-02-18 22:52:15 +00:00
|
|
|
nscoord stride = 0, offset[3], start[3], end[3];
|
2001-03-23 09:46:24 +00:00
|
|
|
nscoord width = aRect.width;
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphCode glue = aGlyphTable->GlueOf(aPresContext, aChar);
|
1999-11-21 22:10:45 +00:00
|
|
|
for (i = 0; i < 4; i++) {
|
2000-03-28 09:38:24 +00:00
|
|
|
switch (i) {
|
2001-04-28 07:39:20 +00:00
|
|
|
case 0: ch = aGlyphTable->TopOf(aPresContext, aChar); break;
|
|
|
|
case 1: ch = aGlyphTable->MiddleOf(aPresContext, aChar); break;
|
|
|
|
case 2: ch = aGlyphTable->BottomOf(aPresContext, aChar); break;
|
|
|
|
case 3: ch = glue; break;
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
// empty slots are filled with the glue if it is not null
|
2001-04-06 00:49:50 +00:00
|
|
|
if (!ch) ch = glue;
|
2001-02-02 09:40:53 +00:00
|
|
|
if (!ch) {
|
|
|
|
bm.Clear(); // glue is null, set bounding metrics to 0
|
|
|
|
}
|
|
|
|
else {
|
2001-03-23 09:46:24 +00:00
|
|
|
rv = aGlyphTable->GetBoundingMetrics(aRenderingContext, aFont, ch, bm);
|
2001-02-02 09:40:53 +00:00
|
|
|
if (NS_FAILED(rv)) {
|
2001-02-23 16:10:51 +00:00
|
|
|
NS_WARNING("GetBoundingMetrics failed");
|
2001-02-02 09:40:53 +00:00
|
|
|
return rv;
|
|
|
|
}
|
2001-03-23 09:46:24 +00:00
|
|
|
if (width < bm.rightBearing) width = bm.rightBearing;
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
chdata[i] = ch;
|
1999-11-21 22:10:45 +00:00
|
|
|
bmdata[i] = bm;
|
|
|
|
}
|
2000-05-08 07:18:10 +00:00
|
|
|
dx = aRect.x;
|
1999-11-21 22:10:45 +00:00
|
|
|
for (i = 0; i < 3; i++) {
|
2000-03-28 09:38:24 +00:00
|
|
|
ch = chdata[i];
|
1999-11-21 22:10:45 +00:00
|
|
|
bm = bmdata[i];
|
|
|
|
if (0 == i) { // top
|
2001-10-30 22:58:00 +00:00
|
|
|
dy = aRect.y + bm.ascent;
|
1999-11-17 00:49:37 +00:00
|
|
|
}
|
1999-11-21 22:10:45 +00:00
|
|
|
else if (1 == i) { // middle
|
2001-10-30 22:58:00 +00:00
|
|
|
dy = aRect.y + bm.ascent + (aRect.height - (bm.ascent + bm.descent))/2;
|
1999-11-21 22:10:45 +00:00
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
else { // bottom
|
2001-10-30 22:58:00 +00:00
|
|
|
dy = aRect.y + aRect.height - bm.descent;
|
1999-11-21 22:10:45 +00:00
|
|
|
}
|
2000-03-28 09:38:24 +00:00
|
|
|
// abcissa passed to DrawString
|
1999-11-21 22:10:45 +00:00
|
|
|
offset[i] = dy;
|
|
|
|
// *exact* abcissa where the *top-most* pixel of the glyph is painted
|
2001-10-30 22:58:00 +00:00
|
|
|
start[i] = dy - bm.ascent;
|
1999-11-21 22:10:45 +00:00
|
|
|
// *exact* abcissa where the *bottom-most* pixel of the glyph is painted
|
2001-10-30 22:58:00 +00:00
|
|
|
end[i] = dy + bm.descent; // end = start + height
|
1999-11-21 22:10:45 +00:00
|
|
|
}
|
1999-09-21 02:12:01 +00:00
|
|
|
|
1999-11-21 22:10:45 +00:00
|
|
|
/////////////////////////////////////
|
|
|
|
// draw top, middle, bottom
|
|
|
|
for (i = 0; i < 3; i++) {
|
2000-03-28 09:38:24 +00:00
|
|
|
ch = chdata[i];
|
2002-02-28 19:28:11 +00:00
|
|
|
// glue can be null, and other parts could have been set to glue
|
|
|
|
if (ch) {
|
2002-02-28 22:39:43 +00:00
|
|
|
#ifdef SHOW_BORDERS
|
|
|
|
// bounding box of the part
|
|
|
|
aRenderingContext.SetColor(NS_RGB(0,0,0));
|
|
|
|
aRenderingContext.DrawRect(nsRect(dx,start[i],width+30*(i+1),end[i]-start[i]));
|
|
|
|
#endif
|
2001-02-02 09:40:53 +00:00
|
|
|
dy = offset[i];
|
2002-02-28 22:39:43 +00:00
|
|
|
if (0 == i) { // top
|
2001-03-23 09:46:24 +00:00
|
|
|
clipRect.SetRect(dx, aRect.y, width, aRect.height);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
2002-02-28 22:39:43 +00:00
|
|
|
else if (1 == i) { // middle
|
2001-03-23 09:46:24 +00:00
|
|
|
clipRect.SetRect(dx, end[0], width, start[2]-end[0]);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
2002-02-28 22:39:43 +00:00
|
|
|
else { // bottom
|
2001-03-23 09:46:24 +00:00
|
|
|
clipRect.SetRect(dx, start[2], width, end[2]-start[2]);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
|
|
|
if (!clipRect.IsEmpty()) {
|
|
|
|
clipRect.Inflate(onePixel, onePixel);
|
2001-10-30 22:58:00 +00:00
|
|
|
aGlyphTable->DrawGlyph(aRenderingContext, aFont, ch, dx, dy, &clipRect);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
1999-11-21 22:10:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////
|
|
|
|
// fill the gap between top and middle, and between middle and bottom.
|
2001-04-06 00:49:50 +00:00
|
|
|
if (!glue) { // null glue : draw a rule
|
2001-02-02 09:40:53 +00:00
|
|
|
// figure out the dimensions of the rule to be drawn :
|
|
|
|
// set lbearing to rightmost lbearing among the two current successive parts.
|
|
|
|
// set rbearing to leftmost rbearing among the two current successive parts.
|
|
|
|
// this not only satisfies the convention used for over/underbraces
|
|
|
|
// in TeX, but also takes care of broken fonts like the stretchy integral
|
|
|
|
// in Symbol for small font sizes in unix.
|
|
|
|
nscoord lbearing, rbearing;
|
|
|
|
PRInt32 first = 0, last = 2;
|
|
|
|
if (chdata[1]) { // middle part exists
|
|
|
|
last = 1;
|
|
|
|
}
|
|
|
|
while (last <= 2) {
|
|
|
|
if (chdata[last]) {
|
|
|
|
lbearing = bmdata[last].leftBearing;
|
|
|
|
rbearing = bmdata[last].rightBearing;
|
|
|
|
if (chdata[first]) {
|
|
|
|
if (lbearing < bmdata[first].leftBearing)
|
|
|
|
lbearing = bmdata[first].leftBearing;
|
|
|
|
if (rbearing > bmdata[first].rightBearing)
|
|
|
|
rbearing = bmdata[first].rightBearing;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (chdata[first]) {
|
|
|
|
lbearing = bmdata[first].leftBearing;
|
|
|
|
rbearing = bmdata[first].rightBearing;
|
|
|
|
}
|
|
|
|
else {
|
2002-02-27 01:35:27 +00:00
|
|
|
NS_ERROR("Cannot stretch - All parts missing");
|
2001-02-02 09:40:53 +00:00
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
// paint the rule between the parts
|
2002-02-28 22:39:43 +00:00
|
|
|
nsRect rule(aRect.x + lbearing, end[first] - onePixel,
|
|
|
|
rbearing - lbearing, start[last] - end[first] + 2*onePixel);
|
|
|
|
if (!rule.IsEmpty())
|
|
|
|
aRenderingContext.FillRect(rule);
|
2001-02-02 09:40:53 +00:00
|
|
|
first = last;
|
|
|
|
last++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { // glue is present
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
PRInt32 count = 0;
|
|
|
|
dy = offset[i];
|
2001-03-23 09:46:24 +00:00
|
|
|
clipRect.SetRect(dx, end[i], width, start[i+1]-end[i]);
|
2001-02-02 09:40:53 +00:00
|
|
|
clipRect.Inflate(onePixel, onePixel);
|
1999-11-17 00:49:37 +00:00
|
|
|
#ifdef SHOW_BORDERS
|
2001-02-02 09:40:53 +00:00
|
|
|
// exact area to fill
|
|
|
|
aRenderingContext.SetColor(NS_RGB(255,0,0));
|
|
|
|
aRenderingContext.DrawRect(clipRect);
|
1999-11-17 00:49:37 +00:00
|
|
|
#endif
|
2001-04-06 00:49:50 +00:00
|
|
|
PRBool clipState;
|
|
|
|
aRenderingContext.PushState();
|
|
|
|
aRenderingContext.SetClipRect(clipRect, nsClipCombine_kIntersect, clipState);
|
2001-02-02 09:40:53 +00:00
|
|
|
bm = bmdata[i];
|
2001-10-30 22:58:00 +00:00
|
|
|
while (dy + bm.descent < start[i+1]) {
|
2001-02-02 09:40:53 +00:00
|
|
|
if (2 > count) {
|
|
|
|
stride = bm.descent;
|
|
|
|
bm = bmdata[3]; // glue
|
|
|
|
stride += bm.ascent;
|
|
|
|
}
|
|
|
|
count++;
|
|
|
|
dy += stride;
|
2001-10-30 22:58:00 +00:00
|
|
|
aGlyphTable->DrawGlyph(aRenderingContext, aFont, glue, dx, dy);
|
2002-02-27 01:35:27 +00:00
|
|
|
NS_ASSERTION(1000 != count, "something is probably wrong somewhere");
|
2001-02-02 09:40:53 +00:00
|
|
|
if (1000 == count) return NS_ERROR_UNEXPECTED;
|
1999-11-17 00:49:37 +00:00
|
|
|
}
|
2001-04-06 00:49:50 +00:00
|
|
|
aRenderingContext.PopState(clipState);
|
1999-11-21 22:10:45 +00:00
|
|
|
#ifdef SHOW_BORDERS
|
2001-02-02 09:40:53 +00:00
|
|
|
// 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));
|
2001-10-30 22:58:00 +00:00
|
|
|
aRenderingContext.DrawRect(nsRect(dx, dy-bm.ascent, width, height));
|
1999-11-21 22:10:45 +00:00
|
|
|
#endif
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
1999-11-21 22:10:45 +00:00
|
|
|
}
|
2000-03-28 09:38:24 +00:00
|
|
|
return NS_OK;
|
1999-11-21 22:10:45 +00:00
|
|
|
}
|
1999-11-17 00:49:37 +00:00
|
|
|
|
1999-11-21 22:10:45 +00:00
|
|
|
// paint a stretchy char by assembling glyphs horizontally
|
|
|
|
nsresult
|
1999-11-24 06:03:41 +00:00
|
|
|
nsMathMLChar::PaintHorizontally(nsIPresContext* aPresContext,
|
1999-11-21 22:10:45 +00:00
|
|
|
nsIRenderingContext& aRenderingContext,
|
2001-03-23 09:46:24 +00:00
|
|
|
nsFont& aFont,
|
1999-11-21 22:10:45 +00:00
|
|
|
nsIStyleContext* aStyleContext,
|
2000-03-28 09:38:24 +00:00
|
|
|
nsGlyphTable* aGlyphTable,
|
2001-02-02 09:40:53 +00:00
|
|
|
nsMathMLChar* aChar,
|
2001-03-23 09:46:24 +00:00
|
|
|
nsRect& aRect)
|
1999-11-21 22:10:45 +00:00
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
2000-03-28 09:38:24 +00:00
|
|
|
nsRect clipRect;
|
2000-05-08 07:18:10 +00:00
|
|
|
nscoord dx, dy;
|
1999-11-21 22:10:45 +00:00
|
|
|
|
|
|
|
float p2t;
|
1999-11-24 06:03:41 +00:00
|
|
|
aPresContext->GetScaledPixelsToTwips(&p2t);
|
2000-03-28 09:38:24 +00:00
|
|
|
nscoord onePixel = NSIntPixelsToTwips(1, p2t);
|
1999-11-21 22:10:45 +00:00
|
|
|
|
|
|
|
// get metrics data to be re-used later
|
|
|
|
PRInt32 i;
|
2000-03-28 09:38:24 +00:00
|
|
|
nsGlyphCode ch, chdata[4];
|
1999-11-21 22:10:45 +00:00
|
|
|
nsBoundingMetrics bm, bmdata[4];
|
2002-02-18 22:52:15 +00:00
|
|
|
nscoord stride = 0, offset[3], start[3], end[3];
|
2001-10-30 22:58:00 +00:00
|
|
|
dy = aRect.y;
|
2001-04-28 07:39:20 +00:00
|
|
|
nsGlyphCode glue = aGlyphTable->GlueOf(aPresContext, aChar);
|
1999-11-21 22:10:45 +00:00
|
|
|
for (i = 0; i < 4; i++) {
|
2000-03-28 09:38:24 +00:00
|
|
|
switch (i) {
|
2001-04-28 07:39:20 +00:00
|
|
|
case 0: ch = aGlyphTable->LeftOf(aPresContext, aChar); break;
|
|
|
|
case 1: ch = aGlyphTable->MiddleOf(aPresContext, aChar); break;
|
|
|
|
case 2: ch = aGlyphTable->RightOf(aPresContext, aChar); break;
|
|
|
|
case 3: ch = glue; break;
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
// empty slots are filled with the glue if it is not null
|
2001-04-06 00:49:50 +00:00
|
|
|
if (!ch) ch = glue;
|
2001-02-02 09:40:53 +00:00
|
|
|
if (!ch) {
|
|
|
|
bm.Clear(); // glue is null, set bounding metrics to 0
|
|
|
|
}
|
|
|
|
else {
|
2001-03-23 09:46:24 +00:00
|
|
|
rv = aGlyphTable->GetBoundingMetrics(aRenderingContext, aFont, ch, bm);
|
2001-02-02 09:40:53 +00:00
|
|
|
if (NS_FAILED(rv)) {
|
2001-02-23 16:10:51 +00:00
|
|
|
NS_WARNING("GetBoundingMetrics failed");
|
2001-02-02 09:40:53 +00:00
|
|
|
return rv;
|
|
|
|
}
|
2001-10-30 22:58:00 +00:00
|
|
|
if (dy < aRect.y + bm.ascent) {
|
|
|
|
dy = aRect.y + bm.ascent;
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
2000-03-28 09:38:24 +00:00
|
|
|
}
|
|
|
|
chdata[i] = ch;
|
1999-11-21 22:10:45 +00:00
|
|
|
bmdata[i] = bm;
|
|
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
2000-03-28 09:38:24 +00:00
|
|
|
ch = chdata[i];
|
1999-11-21 22:10:45 +00:00
|
|
|
bm = bmdata[i];
|
2000-03-28 09:38:24 +00:00
|
|
|
if (0 == i) { // left
|
1999-11-21 22:10:45 +00:00
|
|
|
dx = aRect.x - bm.leftBearing;
|
|
|
|
}
|
|
|
|
else if (1 == i) { // middle
|
|
|
|
dx = aRect.x + (aRect.width - bm.width)/2;
|
|
|
|
}
|
2001-02-02 09:40:53 +00:00
|
|
|
else { // right
|
1999-11-21 22:10:45 +00:00
|
|
|
dx = aRect.x + aRect.width - bm.rightBearing;
|
1999-11-17 00:49:37 +00:00
|
|
|
}
|
1999-11-21 22:10:45 +00:00
|
|
|
// abcissa that DrawString used
|
|
|
|
offset[i] = dx;
|
|
|
|
// *exact* abcissa where the *left-most* pixel of the glyph is painted
|
2000-03-28 09:38:24 +00:00
|
|
|
start[i] = dx + bm.leftBearing;
|
1999-11-21 22:10:45 +00:00
|
|
|
// *exact* abcissa where the *right-most* pixel of the glyph is painted
|
|
|
|
end[i] = dx + bm.rightBearing; // note: end = start + width
|
|
|
|
}
|
1999-11-17 00:49:37 +00:00
|
|
|
|
1999-11-21 22:10:45 +00:00
|
|
|
///////////////////////////
|
|
|
|
// draw left, middle, right
|
|
|
|
for (i = 0; i < 3; i++) {
|
2000-03-28 09:38:24 +00:00
|
|
|
ch = chdata[i];
|
2001-02-02 09:40:53 +00:00
|
|
|
// glue can be null, and other parts could have been set to glue
|
|
|
|
if (ch) {
|
1999-11-17 00:49:37 +00:00
|
|
|
#ifdef SHOW_BORDERS
|
2001-02-02 09:40:53 +00:00
|
|
|
aRenderingContext.SetColor(NS_RGB(255,0,0));
|
2001-10-30 22:58:00 +00:00
|
|
|
aRenderingContext.DrawRect(nsRect(start[i], dy - bmdata[i].ascent,
|
2001-02-02 09:40:53 +00:00
|
|
|
end[i] - start[i], bmdata[i].ascent + bmdata[i].descent));
|
1999-11-17 00:49:37 +00:00
|
|
|
#endif
|
2001-02-02 09:40:53 +00:00
|
|
|
dx = offset[i];
|
2002-02-28 22:39:43 +00:00
|
|
|
if (0 == i) { // left
|
2001-02-02 09:40:53 +00:00
|
|
|
clipRect.SetRect(dx, aRect.y, aRect.width, aRect.height);
|
|
|
|
}
|
2002-02-28 22:39:43 +00:00
|
|
|
else if (1 == i) { // middle
|
2001-02-02 09:40:53 +00:00
|
|
|
clipRect.SetRect(end[0], aRect.y, start[2]-end[0], aRect.height);
|
|
|
|
}
|
2002-02-28 22:39:43 +00:00
|
|
|
else { // right
|
2001-02-02 09:40:53 +00:00
|
|
|
clipRect.SetRect(start[2], aRect.y, end[2]-start[2], aRect.height);
|
|
|
|
}
|
|
|
|
if (!clipRect.IsEmpty()) {
|
|
|
|
clipRect.Inflate(onePixel, onePixel);
|
2001-10-30 22:58:00 +00:00
|
|
|
aGlyphTable->DrawGlyph(aRenderingContext, aFont, ch, dx, dy, &clipRect);
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
1999-11-21 22:10:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////
|
|
|
|
// fill the gap between left and middle, and between middle and right.
|
2001-04-06 00:49:50 +00:00
|
|
|
if (!glue) { // null glue : draw a rule
|
2001-02-02 09:40:53 +00:00
|
|
|
// figure out the dimensions of the rule to be drawn :
|
|
|
|
// set ascent to lowest ascent among the two current successive parts.
|
|
|
|
// set descent to highest descent among the two current successive parts.
|
|
|
|
// this satisfies the convention used for over/underbraces, and helps
|
|
|
|
// fix broken fonts.
|
|
|
|
nscoord ascent, descent;
|
|
|
|
PRInt32 first = 0, last = 2;
|
|
|
|
if (chdata[1]) { // middle part exists
|
|
|
|
last = 1;
|
|
|
|
}
|
|
|
|
while (last <= 2) {
|
|
|
|
if (chdata[last]) {
|
|
|
|
ascent = bmdata[last].ascent;
|
|
|
|
descent = bmdata[last].descent;
|
|
|
|
if (chdata[first]) {
|
|
|
|
if (ascent > bmdata[first].ascent)
|
|
|
|
ascent = bmdata[first].ascent;
|
|
|
|
if (descent > bmdata[first].descent)
|
|
|
|
descent = bmdata[first].descent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (chdata[first]) {
|
|
|
|
ascent = bmdata[first].ascent;
|
|
|
|
descent = bmdata[first].descent;
|
|
|
|
}
|
|
|
|
else {
|
2002-02-27 01:35:27 +00:00
|
|
|
NS_ERROR("Cannot stretch - All parts missing");
|
2001-02-02 09:40:53 +00:00
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
// paint the rule between the parts
|
2002-02-28 22:39:43 +00:00
|
|
|
nsRect rule(end[first] - onePixel, dy - ascent,
|
|
|
|
start[last] - end[first] + 2*onePixel, ascent + descent);
|
|
|
|
if (!rule.IsEmpty())
|
|
|
|
aRenderingContext.FillRect(rule);
|
2001-02-02 09:40:53 +00:00
|
|
|
first = last;
|
|
|
|
last++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { // glue is present
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
PRInt32 count = 0;
|
|
|
|
dx = offset[i];
|
|
|
|
clipRect.SetRect(end[i], aRect.y, start[i+1]-end[i], aRect.height);
|
|
|
|
clipRect.Inflate(onePixel, onePixel);
|
1999-11-17 00:49:37 +00:00
|
|
|
#ifdef SHOW_BORDERS
|
2001-02-02 09:40:53 +00:00
|
|
|
// rectangles in-between that are to be filled
|
|
|
|
aRenderingContext.SetColor(NS_RGB(255,0,0));
|
|
|
|
aRenderingContext.DrawRect(clipRect);
|
1999-11-17 00:49:37 +00:00
|
|
|
#endif
|
2001-04-06 00:49:50 +00:00
|
|
|
PRBool clipState;
|
|
|
|
aRenderingContext.PushState();
|
|
|
|
aRenderingContext.SetClipRect(clipRect, nsClipCombine_kIntersect, clipState);
|
2001-02-02 09:40:53 +00:00
|
|
|
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;
|
2001-10-30 22:58:00 +00:00
|
|
|
aGlyphTable->DrawGlyph(aRenderingContext, aFont, glue, dx, dy);
|
2002-02-27 01:35:27 +00:00
|
|
|
NS_ASSERTION(1000 != count, "something is probably wrong somewhere");
|
2001-02-02 09:40:53 +00:00
|
|
|
if (1000 == count) return NS_ERROR_UNEXPECTED;
|
1999-11-17 00:49:37 +00:00
|
|
|
}
|
2001-04-06 00:49:50 +00:00
|
|
|
aRenderingContext.PopState(clipState);
|
1999-11-17 00:49:37 +00:00
|
|
|
#ifdef SHOW_BORDERS
|
2001-02-02 09:40:53 +00:00
|
|
|
// 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));
|
1999-11-17 00:49:37 +00:00
|
|
|
#endif
|
2001-02-02 09:40:53 +00:00
|
|
|
}
|
1999-11-17 00:49:37 +00:00
|
|
|
}
|
2000-03-28 09:38:24 +00:00
|
|
|
return NS_OK;
|
1999-11-17 00:49:37 +00:00
|
|
|
}
|