1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla MathML Project.
17 * The Initial Developer of the Original Code is
18 * The University Of Queensland.
19 * Portions created by the Initial Developer are Copyright (C) 1999
20 * the Initial Developer. All Rights Reserved.
23 * Roger B. Sidje <rbs@maths.uq.edu.au>
24 * Shyjan Mahamud <mahamud@cs.cmu.edu>
25 * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
43 #include "nsPresContext.h"
44 #include "nsStyleContext.h"
45 #include "nsStyleConsts.h"
47 #include "nsUnicharUtils.h"
48 #include "nsIRenderingContext.h"
49 #include "gfxPlatform.h"
50 #include "nsIFontMetrics.h"
52 #include "nsIPrefBranch.h"
53 #include "nsIPrefService.h"
54 #include "nsISupportsPrimitives.h"
55 #include "nsIComponentManager.h"
56 #include "nsIPersistentProperties2.h"
57 #include "nsIServiceManager.h"
58 #include "nsIObserverService.h"
59 #include "nsIObserver.h"
60 #include "nsNetUtil.h"
62 #include "nsILookAndFeel.h"
63 #include "nsIDeviceContext.h"
64 #include "nsCSSRendering.h"
65 #include "prprf.h" // For PR_snprintf()
67 #if ALERT_MISSING_FONTS
68 #include "nsIDOMWindow.h"
69 #include "nsINonBlockingAlertService.h"
70 #include "nsIWindowWatcher.h"
71 #include "nsIStringBundle.h"
73 #include "nsDisplayList.h"
75 #include "nsMathMLOperators.h"
76 #include "nsMathMLChar.h"
78 //#define SHOW_BORDERS 1
79 //#define NOISY_SEARCH 1
81 // -----------------------------------------------------------------------------------
82 static const PRUnichar kSpaceCh
= PRUnichar(' ');
83 static const nsGlyphCode kNullGlyph
= {0, 0};
84 typedef enum {eExtension_base
, eExtension_variants
, eExtension_parts
}
85 nsMathfontPrefExtension
;
87 // -----------------------------------------------------------------------------------
88 // nsGlyphTable is a class that provides an interface for accessing glyphs
89 // of stretchy chars. It acts like a table that stores the variants of bigger
90 // sizes (if any) and the partial glyphs needed to build extensible symbols.
91 // An instance of nsGlyphTable is associated to one primary font. Extra glyphs
92 // can be taken in other additional fonts when stretching certain characters.
93 // These supplementary fonts are referred to as "external" fonts to the table.
95 // A char for which nsGlyphTable::Has(aChar) is true means that the table
96 // contains some glyphs (bigger and/or partial) that can be used to render
97 // the char. Bigger sizes (if any) of the char can then be retrieved with
98 // BigOf(aSize). Partial glyphs can be retrieved with TopOf(), GlueOf(), etc.
100 // A table consists of "nsGlyphCode"s which are viewed either as Unicode
101 // points or as direct glyph indices, depending on the type of the table.
102 // XXX The latter is not yet supported.
104 // General format of MathFont Property Files from which glyph data are retrieved:
105 // -----------------------------------------------------------------------------------
106 // Each font should have its set of glyph data. For example, the glyph data for
107 // the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties"
108 // and "mathfontMTExtra.properties", respectively. The mathfont property file is a
109 // set of all the stretchy MathML characters that can be rendered with that font
110 // using larger and/or partial glyphs. The entry of each stretchy character in the
111 // mathfont property file gives, in that order, the 4 partial glyphs: Top (or Left),
112 // Middle, Bottom (or Right), Glue; and the variants of bigger sizes (if any).
113 // A position that is not relevant to a particular character is indicated there
114 // with the UNICODE REPLACEMENT CHARACTER 0xFFFD.
115 // Characters that need to be built recursively from other characters are said
116 // to be composite. For example, chars like over/underbrace in CMEX10 have to
117 // be built from two half stretchy chars and joined in the middle (TeXbook, p.225).
118 // Such chars are handled in a special manner by the nsMathMLChar class, which allows
119 // several (2 or more) child chars to be composed in order to render another char.
120 // To specify such chars, their list of glyphs in the property file should be given
121 // as space-separated segments of glyphs. Each segment gives the 4 partial
122 // glyphs with which to build the child char that will be joined with its other
123 // siblings. In this code, when this situation happens (see the detailed description
124 // of Stretch() below), the original char (referred to as "parent") creates a
125 // singly-linked list of child chars, asking them to stretch in an equally divided
126 // space. The nsGlyphTable embeds the necessary logic to guarantee correctness in a
127 // recursive stretch (and in the use of TopOf(), GlueOf(), etc) on these child chars.
128 // -----------------------------------------------------------------------------------
130 #define NS_TABLE_TYPE_UNICODE 0
131 #define NS_TABLE_TYPE_GLYPH_INDEX 1
133 #define NS_TABLE_STATE_ERROR -1
134 #define NS_TABLE_STATE_EMPTY 0
135 #define NS_TABLE_STATE_READY 1
137 // helper to check if a font is installed
139 CheckFontExistence(nsPresContext
* aPresContext
, const nsString
& aFontName
)
142 nsAutoString localName
;
143 nsIDeviceContext
*deviceContext
= aPresContext
->DeviceContext();
144 deviceContext
->GetLocalFontName(aFontName
, localName
, aliased
);
145 // XXXkt CheckFontExistence always returns NS_OK.
146 PRBool rv
= (aliased
|| (NS_OK
== deviceContext
->CheckFontExistence(localName
)));
147 // (see bug 35824 for comments about the aliased localName)
151 #if ALERT_MISSING_FONTS
152 // alert the user if some of the needed MathML fonts are not installed.
153 // it is non-modal (i.e., it doesn't wait for input from the user)
155 AlertMissingFonts(nsString
& aMissingFonts
)
157 nsCOMPtr
<nsIStringBundleService
> sbs(do_GetService(NS_STRINGBUNDLE_CONTRACTID
));
161 nsCOMPtr
<nsIStringBundle
> sb
;
162 sbs
->CreateBundle("resource://gre/res/fonts/mathfont.properties", getter_AddRefs(sb
));
166 nsXPIDLString title
, message
;
167 const PRUnichar
* strings
[] = { aMissingFonts
.get() };
168 sb
->GetStringFromName(NS_LITERAL_STRING("mathfont_missing_dialog_title").get(), getter_Copies(title
));
169 sb
->FormatStringFromName(NS_LITERAL_STRING("mathfont_missing_dialog_message").get(),
170 strings
, 1, getter_Copies(message
));
172 nsCOMPtr
<nsIWindowWatcher
> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID
));
176 nsCOMPtr
<nsIDOMWindow
> parent
;
177 wwatch
->GetActiveWindow(getter_AddRefs(parent
));
179 nsCOMPtr
<nsINonBlockingAlertService
> prompter
=
180 do_GetService("@mozilla.org/embedcomp/nbalert-service;1", &rv
);
182 if (prompter
&& parent
) {
183 prompter
->ShowNonBlockingAlert(parent
, title
.get(), message
.get());
188 // helper to trim off comments from data in a MathFont Property File
190 Clean(nsString
& aValue
)
192 // chop the trailing # comment portion if any ...
193 PRInt32 comment
= aValue
.RFindChar('#');
194 if (comment
> 0) aValue
.Truncate(comment
);
195 aValue
.CompressWhitespace();
198 // helper to load a MathFont Property File
200 LoadProperties(const nsString
& aName
,
201 nsCOMPtr
<nsIPersistentProperties
>& aProperties
)
204 uriStr
.AssignLiteral("resource://gre/res/fonts/mathfont");
205 uriStr
.Append(aName
);
206 uriStr
.StripWhitespace(); // that may come from aName
207 uriStr
.AppendLiteral(".properties");
208 return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties
),
209 NS_ConvertUTF16toUTF8(uriStr
));
212 // -----------------------------------------------------------------------------------
216 explicit nsGlyphTable(const nsString
& aPrimaryFontName
)
217 : mType(NS_TABLE_TYPE_UNICODE
),
218 mFontName(1), // ensure space for primary font name.
219 mState(NS_TABLE_STATE_EMPTY
),
222 MOZ_COUNT_CTOR(nsGlyphTable
);
223 mFontName
.AppendString(aPrimaryFontName
);
226 ~nsGlyphTable() // not a virtual destructor: this class is not intended to be subclassed
228 MOZ_COUNT_DTOR(nsGlyphTable
);
231 const nsAString
& PrimaryFontName() const
233 return *mFontName
.StringAt(0);
236 const nsAString
& FontNameFor(const nsGlyphCode
& aGlyphCode
) const
238 return *mFontName
.StringAt(aGlyphCode
.font
);
241 // True if this table contains some glyphs (variants and/or parts)
242 // or contains child chars that can be used to render this char
243 PRBool
Has(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
);
245 // True if this table contains variants of larger sizes to render this char
246 PRBool
HasVariantsOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
);
248 // True if this table contains parts (or composite parts) to render this char
249 PRBool
HasPartsOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
);
251 // True if aChar is to be assembled from other child chars in this table
252 PRBool
IsComposite(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
);
254 // The number of child chars to assemble in order to render aChar
255 PRInt32
ChildCountOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
);
257 // Getters for the parts
258 nsGlyphCode
TopOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
) {
259 return ElementAt(aPresContext
, aChar
, 0);
261 nsGlyphCode
MiddleOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
) {
262 return ElementAt(aPresContext
, aChar
, 1);
264 nsGlyphCode
BottomOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
) {
265 return ElementAt(aPresContext
, aChar
, 2);
267 nsGlyphCode
GlueOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
) {
268 return ElementAt(aPresContext
, aChar
, 3);
270 nsGlyphCode
BigOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
, PRInt32 aSize
) {
271 return ElementAt(aPresContext
, aChar
, 4 + aSize
);
273 nsGlyphCode
LeftOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
) {
274 return ElementAt(aPresContext
, aChar
, 0);
276 nsGlyphCode
RightOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
) {
277 return ElementAt(aPresContext
, aChar
, 2);
281 nsGlyphCode
ElementAt(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
, PRUint32 aPosition
);
283 // The type is either NS_TABLE_TYPE_UNICODE or NS_TABLE_TYPE_GLYPH_INDEX
286 // mFontName[0] is the primary font associated to this table. The others
287 // are possible "external" fonts for glyphs not in the primary font
288 // but which are needed to stretch certain characters in the table
289 nsStringArray mFontName
;
291 // Tri-state variable for error/empty/ready
294 // The set of glyph data in this table, as provided by the MathFont Property File
295 nsCOMPtr
<nsIPersistentProperties
> mGlyphProperties
;
297 // For speedy re-use, we always cache the last data used in the table.
298 // mCharCache is the Unicode point of the last char that was queried in this
299 // table. mGlyphCache is a buffer containing the glyph data associated to
300 // that char. For a property line 'key = value' in the MathFont Property File,
301 // mCharCache will retain the 'key' -- which is a Unicode point, while mGlyphCache
302 // will retain the 'value', which is a consecutive list of nsGlyphCodes, i.e.,
303 // the pairs of 'code@font' needed by the char -- in which 'code@0' can be specified
304 // without the optional '@0'. However, to ease subsequent processing, mGlyphCache
305 // excludes the '@' symbol and explicitly inserts all optional '0' that indicates
306 // the primary font identifier. Specifically therefore, the k-th glyph is
307 // characterized by :
308 // 1) mGlyphCache[2*k] : its Unicode point (or glyph index -- depending on mType),
309 // 2) mGlyphCache[2*k+1] : the numeric identifier of the font where it comes from.
310 // A font identifier of '0' means the default primary font associated to this
311 // table. Other digits map to the "external" fonts that may have been specified
312 // in the MathFont Property File.
313 nsString mGlyphCache
;
314 PRUnichar mCharCache
;
318 nsGlyphTable::ElementAt(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
, PRUint32 aPosition
)
320 if (mState
== NS_TABLE_STATE_ERROR
) return kNullGlyph
;
321 // Load glyph properties if this is the first time we have been here
322 if (mState
== NS_TABLE_STATE_EMPTY
) {
323 nsresult rv
= LoadProperties(*mFontName
[0], mGlyphProperties
);
325 nsCAutoString uriStr
;
326 uriStr
.AssignLiteral("resource://gre/res/fonts/mathfont");
327 LossyAppendUTF16toASCII(*mFontName
[0], uriStr
);
328 uriStr
.StripWhitespace(); // that may come from mFontName
329 uriStr
.AppendLiteral(".properties");
330 printf("Loading %s ... %s\n",
332 (NS_FAILED(rv
)) ? "Failed" : "Done");
335 mState
= NS_TABLE_STATE_ERROR
; // never waste time with this table again
338 mState
= NS_TABLE_STATE_READY
;
340 // see if there are external fonts needed for certain chars in this table
343 for (PRInt32 i
= 1; ; i
++) {
344 key
.AssignLiteral("external.");
345 key
.AppendInt(i
, 10);
346 rv
= mGlyphProperties
->GetStringProperty(key
, value
);
347 if (NS_FAILED(rv
)) break;
349 mFontName
.AppendString(value
); // i.e., mFontName[i] holds this font name
353 // If aChar is a child char to be used by a parent composite char, make
354 // sure that it is really attached to this table
355 if (aChar
->mParent
&& (aChar
->mGlyphTable
!= this)) return kNullGlyph
;
357 // Update our cache if it is not associated to this character
358 PRUnichar uchar
= aChar
->mData
[0];
359 if (mCharCache
!= uchar
) {
360 // The key in the property file is interpreted as ASCII and kept
362 char key
[10]; PR_snprintf(key
, sizeof(key
), "\\u%04X", uchar
);
364 nsresult rv
= mGlyphProperties
->GetStringProperty(nsDependentCString(key
), value
);
365 if (NS_FAILED(rv
)) return kNullGlyph
;
367 // See if this char uses external fonts; e.g., if the 2nd glyph is taken from the
368 // external font '1', the property line looks like \uNNNN = \uNNNN\uNNNN@1\uNNNN.
369 // This is where mGlyphCache is pre-processed to explicitly store all glyph codes
370 // as combined pairs of 'code@font', excluding the '@' separator. This means that
371 // mGlyphCache[2*k] will later be rendered with mFontName[mGlyphCache[2*k+1]]
372 // Note: font identifier is internally an ASCII digit to avoid the null char issue
374 PRInt32 length
= value
.Length();
375 PRInt32 i
= 0; // index in value
376 PRInt32 j
= 0; // part/variant index
378 PRUnichar code
= value
[i
];
381 // see if we are at the beginning of a child char
382 if (code
== kSpaceCh
) {
383 // reset the annotation indicator to be 0 for the next code point
386 #if 0 // If we want this then the nsGlyphTableList must be declared
387 // or the UnicodeTable could be made a global.
388 // See if this code point is an *indirect reference* to the Unicode
389 // table and lookup the code there.
390 else if (code
== PRUnichar(0xF8FF) && gGlyphTableList
&&
391 this != &gGlyphTableList
->mUnicodeTable
) {
392 code
= gGlyphTableList
->mUnicodeTable
.
393 ElementAt(aPresContext
, aChar
, aPosition
).code
;
395 // see if this code point is a *direct reference* to
396 // the Unicode table, and lookup the [TLMBRG1-9] position for code.
397 else if ((i
+1 < length
) && (value
[i
] == PRUnichar('.'))) {
399 // Need to implement this if we want it:
400 // Set (new) code from the value[i] position for (current) code.
406 // See if an external font is needed for the code point.
407 // Limit of 9 external fonts
408 if (i
+1 < length
&& value
[i
] == PRUnichar('@') &&
409 value
[i
+1] >= PRUnichar('0') && value
[i
+1] <= PRUnichar('9')) {
411 font
= value
[i
] - '0';
413 if (font
>= mFontName
.Count()) {
414 NS_ERROR("Non-existant font referenced in glyph table");
417 // The char cannot be handled if this font is not installed
418 nsAutoString fontName
;
419 mFontName
.StringAt(font
, fontName
);
420 if (!fontName
.Length() || !CheckFontExistence(aPresContext
, fontName
)) {
428 // update our cache with the new settings
429 mGlyphCache
.Assign(buffer
);
433 // If aChar is a composite char, only its children are allowed
434 // to use its glyphs in this table, i.e., the parent char itself
435 // is disabled and cannot be stretched directly with these glyphs.
436 // This guarantees a coherent behavior in Stretch().
437 if (!aChar
->mParent
&& (kNotFound
!= mGlyphCache
.FindChar(kSpaceCh
))) {
441 // If aChar is a child char, the index of the glyph is relative to
442 // the offset of the list of glyphs corresponding to the child char.
444 PRUint32 length
= mGlyphCache
.Length();
445 if (aChar
->mParent
) {
446 nsMathMLChar
* child
= aChar
->mParent
->mSibling
;
447 // XXXkt composite chars can't have size variants
448 while (child
&& (child
!= aChar
)) {
449 offset
+= 5; // skip the 4 partial glyphs + the whitespace separator
450 child
= child
->mSibling
;
452 length
= 2*(offset
+ 4); // stay confined in the 4 partial glyphs of this child
454 PRUint32 index
= 2*(offset
+ aPosition
); // 2* is to account for the code@font pairs
455 if (index
+1 >= length
) return kNullGlyph
;
457 ch
.code
= mGlyphCache
.CharAt(index
);
458 ch
.font
= mGlyphCache
.CharAt(index
+ 1);
459 return (ch
.code
== PRUnichar(0xFFFD)) ? kNullGlyph
: ch
;
463 nsGlyphTable::IsComposite(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
)
465 // there is only one level of recursion in our model. a child
466 // cannot be composite because it cannot have its own children
467 if (aChar
->mParent
) return PR_FALSE
;
468 // shortcut to sync the cache with this char...
469 mCharCache
= 0; mGlyphCache
.Truncate(); ElementAt(aPresContext
, aChar
, 0);
470 // the cache remained empty if the char wasn't found in this table
471 if (8 >= mGlyphCache
.Length()) return PR_FALSE
;
472 // the lists of glyphs of a composite char are space-separated
473 return (kSpaceCh
== mGlyphCache
.CharAt(8));
477 nsGlyphTable::ChildCountOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
)
479 // this will sync the cache as well ...
480 if (!IsComposite(aPresContext
, aChar
)) return 0;
481 // the lists of glyphs of a composite char are space-separated
482 return 1 + mGlyphCache
.CountChar(kSpaceCh
);
486 nsGlyphTable::Has(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
)
488 return HasVariantsOf(aPresContext
, aChar
) || HasPartsOf(aPresContext
, aChar
);
492 nsGlyphTable::HasVariantsOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
)
494 //XXXkt all variants must be in the same file as size 1
495 return BigOf(aPresContext
, aChar
, 1).Exists();
499 nsGlyphTable::HasPartsOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
)
501 return GlueOf(aPresContext
, aChar
).Exists() ||
502 TopOf(aPresContext
, aChar
).Exists() ||
503 BottomOf(aPresContext
, aChar
).Exists() ||
504 MiddleOf(aPresContext
, aChar
).Exists() ||
505 IsComposite(aPresContext
, aChar
);
508 // -----------------------------------------------------------------------------------
509 // This is the list of all the applicable glyph tables.
510 // We will maintain a single global instance that will only reveal those
511 // glyph tables that are associated to fonts currently installed on the
512 // user' system. The class is an XPCOM shutdown observer to allow us to
513 // free its allocated data at shutdown
515 class nsGlyphTableList
: public nsIObserver
521 nsGlyphTable mUnicodeTable
;
524 : mUnicodeTable(NS_LITERAL_STRING("Unicode"))
526 MOZ_COUNT_CTOR(nsGlyphTableList
);
529 virtual ~nsGlyphTableList()
531 MOZ_COUNT_DTOR(nsGlyphTableList
);
534 nsresult
Initialize();
537 // Add a glyph table in the list, return the new table that was added
539 AddGlyphTable(const nsString
& aPrimaryFontName
);
541 // Find a glyph table in the list that has a glyph for the given char
543 GetGlyphTableFor(nsPresContext
* aPresContext
,
544 nsMathMLChar
* aChar
);
546 // Find the glyph table in the list corresponding to the given font family.
548 GetGlyphTableFor(const nsAString
& aFamily
);
551 nsGlyphTable
* TableAt(PRInt32 aIndex
) {
552 return &mTableList
.ElementAt(aIndex
);
555 return mTableList
.Length();
558 // List of glyph tables;
559 nsTArray
<nsGlyphTable
> mTableList
;
562 NS_IMPL_ISUPPORTS1(nsGlyphTableList
, nsIObserver
)
564 // -----------------------------------------------------------------------------------
565 // Here is the global list of applicable glyph tables that we will be using
566 static nsGlyphTableList
* gGlyphTableList
= nsnull
;
568 static PRBool gInitialized
= PR_FALSE
;
570 // XPCOM shutdown observer
572 nsGlyphTableList::Observe(nsISupports
* aSubject
,
574 const PRUnichar
* someData
)
580 // Add an observer to XPCOM shutdown so that we can free our data at shutdown
582 nsGlyphTableList::Initialize()
585 nsCOMPtr
<nsIObserverService
> obs
=
586 do_GetService("@mozilla.org/observer-service;1", &rv
);
587 NS_ENSURE_SUCCESS(rv
, rv
);
589 rv
= obs
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, PR_FALSE
);
590 NS_ENSURE_SUCCESS(rv
, rv
);
595 // Remove our observer and free the memory that were allocated for us
597 nsGlyphTableList::Finalize()
599 // Remove our observer from the observer service
601 nsCOMPtr
<nsIObserverService
> obs
=
602 do_GetService("@mozilla.org/observer-service;1", &rv
);
603 if (NS_SUCCEEDED(rv
)) {
604 rv
= obs
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
606 gInitialized
= PR_FALSE
;
607 // our oneself will be destroyed when our |Release| is called by the observer
612 nsGlyphTableList::AddGlyphTable(const nsString
& aPrimaryFontName
)
614 // See if there is already a special table for this family.
615 nsGlyphTable
* glyphTable
= GetGlyphTableFor(aPrimaryFontName
);
616 if (glyphTable
!= &mUnicodeTable
)
620 glyphTable
= mTableList
.AppendElement(aPrimaryFontName
);
625 nsGlyphTableList::GetGlyphTableFor(nsPresContext
* aPresContext
,
628 if (mUnicodeTable
.Has(aPresContext
, aChar
))
629 return &mUnicodeTable
;
632 for (i
= 0; i
< Count(); i
++) {
633 nsGlyphTable
* glyphTable
= TableAt(i
);
634 if (glyphTable
->Has(aPresContext
, aChar
)) {
642 nsGlyphTableList::GetGlyphTableFor(const nsAString
& aFamily
)
644 for (PRInt32 i
= 0; i
< Count(); i
++) {
645 nsGlyphTable
* glyphTable
= TableAt(i
);
646 const nsAString
& fontName
= glyphTable
->PrimaryFontName();
647 // TODO: would be nice to consider StripWhitespace and other aliasing
648 if (fontName
.Equals(aFamily
, nsCaseInsensitiveStringComparator())) {
652 // Fall back to default Unicode table
653 return &mUnicodeTable
;
656 // -----------------------------------------------------------------------------------
658 // retrieve a pref value set by the user
660 GetPrefValue(nsIPrefBranch
* aPrefBranch
, const char* aPrefKey
, nsString
& aPrefValue
)
662 aPrefValue
.Truncate();
664 nsCOMPtr
<nsISupportsString
> prefString
;
665 aPrefBranch
->GetComplexValue(aPrefKey
,
666 NS_GET_IID(nsISupportsString
),
667 getter_AddRefs(prefString
));
669 prefString
->GetData(aPrefValue
);
672 return !aPrefValue
.IsEmpty();
675 // Lookup the preferences:
676 // "font.mathfont-family.\uNNNN.base" -- fonts for the base size
677 // "font.mathfont-family.\uNNNN.variants" -- fonts for larger glyphs
678 // "font.mathfont-family.\uNNNN.parts" -- fonts for partial glyphs
679 // Given the char code and mode of stretch, retrieve the preferred extension
682 GetFontExtensionPref(nsIPrefBranch
* aPrefBranch
, PRUnichar aChar
,
683 nsMathfontPrefExtension aExtension
, nsString
& aValue
)
685 // initialize OUT param
688 // We are going to try two keys because some users specify their pref as
689 // user_pref("font.mathfont-family.\uNNNN.base", "...") rather than
690 // user_pref("font.mathfont-family.\\uNNNN.base", "...").
691 // The \uNNNN in the former is interpreted as an UTF16 escape sequence by
692 // JavaScript and is converted to the internal UTF8 string that JavaScript uses.
693 // But clueless users who are not savvy of JavaScript have no idea as to what
694 // is going on and are baffled as to why their pref setting is not working.
695 // So to save countless explanations, we are going to support both keys.
697 static const char* kMathFontPrefix
= "font.mathfont-family.";
699 nsCAutoString extension
;
702 case eExtension_base
:
703 extension
.AssignLiteral(".base");
704 case eExtension_variants
:
705 extension
.AssignLiteral(".variants");
706 case eExtension_parts
:
707 extension
.AssignLiteral(".parts");
714 key
.AssignASCII(kMathFontPrefix
);
716 PR_snprintf(ustr
, sizeof(ustr
), "\\u%04X", aChar
);
718 key
.Append(extension
);
720 nsCAutoString alternateKey
;
721 alternateKey
.AssignASCII(kMathFontPrefix
);
722 NS_ConvertUTF16toUTF8
tmp(&aChar
, 1);
724 key
.Append(extension
);
726 return GetPrefValue(aPrefBranch
, key
.get(), aValue
) ||
727 GetPrefValue(aPrefBranch
, alternateKey
.get(), aValue
);
730 #if ALERT_MISSING_FONTS
731 struct MathFontEnumContext
{
732 nsPresContext
* mPresContext
;
733 nsString
* mMissingFamilyList
;
738 MathFontEnumCallback(const nsString
& aFamily
, PRBool aGeneric
, void *aData
)
740 #if ALERT_MISSING_FONTS
741 // check if the font is missing
742 MathFontEnumContext
* context
= (MathFontEnumContext
*)aData
;
743 nsPresContext
* presContext
= context
->mPresContext
;
744 nsString
* missingFamilyList
= context
->mMissingFamilyList
;
745 if (!CheckFontExistence(presContext
, aFamily
)) {
747 // XXX In principle, the mathfont-family list in the mathfont.properties file
748 // is customizable depending on the platform. For now, this is here since there
749 // is no need to alert Linux users about TrueType fonts specific to Windows.
750 if (aFamily
.LowerCaseEqualsLiteral("mt extra"))
751 return PR_TRUE
; // continue to try other fonts
753 if (!missingFamilyList
->IsEmpty()) {
754 missingFamilyList
->AppendLiteral(", ");
756 missingFamilyList
->Append(aFamily
);
760 if (!gGlyphTableList
->AddGlyphTable(aFamily
))
761 return PR_FALSE
; // stop in low-memory situations
762 return PR_TRUE
; // don't stop
766 InitGlobals(nsPresContext
* aPresContext
)
768 NS_ASSERTION(!gInitialized
, "Error -- already initialized");
769 gInitialized
= PR_TRUE
;
770 PRUint32 count
= nsMathMLOperators::CountStretchyOperator();
772 // nothing to stretch, so why bother...
776 // Allocate the placeholders for the preferred parts and variants
777 nsresult rv
= NS_ERROR_OUT_OF_MEMORY
;
778 gGlyphTableList
= new nsGlyphTableList();
779 if (gGlyphTableList
) {
780 rv
= gGlyphTableList
->Initialize();
783 delete gGlyphTableList
;
784 gGlyphTableList
= nsnull
;
789 The gGlyphTableList has been successfully registered as a shutdown observer.
790 It will be deleted at shutdown, even if a failure happens below.
795 nsCOMPtr
<nsIPersistentProperties
> mathfontProp
;
796 nsCOMPtr
<nsIPrefBranch
> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID
));
798 // Add the math fonts in the gGlyphTableList in order of preference ...
799 // Note: we only load font-names at this stage. The actual glyph tables will
800 // be loaded lazily (see nsGlyphTable::ElementAt()).
802 // Load the "mathfont.properties" file
804 rv
= LoadProperties(value
, mathfontProp
);
805 if (NS_FAILED(rv
)) return rv
;
807 // Get the list of mathfonts having special glyph tables to be used for
808 // stretchy characters.
809 // We just want to iterate over the font-family list using the
810 // callback mechanism that nsFont has...
811 nsFont
font("", 0, 0, 0, 0, 0);
812 NS_NAMED_LITERAL_CSTRING(defaultKey
, "font.mathfont-glyph-tables");
813 rv
= mathfontProp
->GetStringProperty(defaultKey
, font
.name
);
814 if (NS_FAILED(rv
)) return rv
;
816 // Parse the font list and append an entry for each family to gGlyphTableList
817 nsAutoString missingFamilyList
;
819 #if ALERT_MISSING_FONTS
820 // We don't really need all these fonts, so alerting on some missing is not
821 // right. The best place to alert would be Stretch when we notice that we
822 // can't get the char we want. In this way the user would not be alerted
823 // unnecessarily when the document contains only simple math. The alert
824 // also needs a "don't tell me again box".
825 MathFontEnumContext context
= {aPresContext
, &missingFamilyList
};
826 font
.EnumerateFamilies(MathFontEnumCallback
, &context
);
827 // alert the user if some of the expected fonts are missing
828 if (!missingFamilyList
.IsEmpty()) {
829 AlertMissingFonts(missingFamilyList
);
832 font
.EnumerateFamilies(MathFontEnumCallback
, nsnull
);
837 // -----------------------------------------------------------------------------------
838 // And now the implementation of nsMathMLChar
841 nsMathMLChar::GetStyleContext() const
843 NS_ASSERTION(!mParent
, "invalid call - not allowed for child chars");
844 NS_ASSERTION(mStyleContext
, "chars shoud always have style context");
845 return mStyleContext
;
850 nsMathMLChar::SetStyleContext(nsStyleContext
* aStyleContext
)
852 NS_ASSERTION(!mParent
, "invalid call - not allowed for child chars");
853 NS_PRECONDITION(aStyleContext
, "null ptr");
854 if (aStyleContext
!= mStyleContext
) {
856 mStyleContext
->Release();
858 mStyleContext
= aStyleContext
;
859 aStyleContext
->AddRef();
861 // Sync the pointers of child chars.
862 nsMathMLChar
* child
= mSibling
;
864 child
->mStyleContext
= mStyleContext
;
865 child
= child
->mSibling
;
872 nsMathMLChar::SetData(nsPresContext
* aPresContext
,
875 NS_ASSERTION(!mParent
, "invalid call - not allowed for child chars");
877 InitGlobals(aPresContext
);
880 // some assumptions until proven otherwise
881 // note that mGlyph is not initialized
883 mDirection
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
884 mBoundingMetrics
.Clear();
885 mGlyphTable
= nsnull
;
886 // check if stretching is applicable ...
887 if (gGlyphTableList
&& (1 == mData
.Length())) {
888 mOperator
= nsMathMLOperators::FindStretchyOperator(mData
[0]);
889 if (mOperator
>= 0) {
890 mDirection
= nsMathMLOperators::GetStretchyDirectionAt(mOperator
);
891 // default tentative table (not the one that is necessarily going to be used)
892 mGlyphTable
= gGlyphTableList
->GetGlyphTableFor(aPresContext
, this);
893 // commom case: we won't bother with the stretching if there is
894 // no glyph table for us...
895 if (!mGlyphTable
) { // TODO: consider scaling the base char
896 // never try to stretch this operator again
897 nsMathMLOperators::DisableStretchyOperatorAt(mOperator
);
898 mDirection
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
905 // -----------------------------------------------------------------------------------
908 @param aContainerSize - suggested size for the stretched char
909 @param aDesiredStretchSize - OUT parameter. The desired size
910 after stretching. If no stretching is done, the output will
911 simply give the base size.
915 The Stretch() method first looks for a glyph of appropriate
916 size; If a glyph is found, it is cached by this object and
917 its size is returned in aDesiredStretchSize. The cached
918 glyph will then be used at the painting stage.
919 If no glyph of appropriate size is found, a search is made
920 to see if the char can be built by parts.
923 A character gets stretched through the following pipeline :
925 1) If the base size of the char is sufficient to cover the
926 container' size, we use that. If not, it will still be
927 used as a fallback if the other stages in the pipeline fail.
929 a) The base size, the parts and the variants of a char can
930 be in different fonts. For eg., the base size for '(' should
931 come from a normal ascii font if CMEX10 is used, since CMEX10
932 only contains the stretched versions. Hence, there are two
933 style contexts in use throughout the process. The leaf style
934 context of the char holds fonts with which to try to stretch
935 the char. The parent style context of the char contains fonts
936 for normal rendering. So the parent context is the one used
937 to get the initial base size at the start of the pipeline.
938 b) For operators that can be largeop's in display mode,
939 we will skip the base size even if it fits, so that
940 the next stage in the pipeline is given a chance to find
941 a largeop variant. If the next stage fails, we fallback
944 2) We search for the first larger variant of the char that fits the
945 container' size. We first search for larger variants using the glyph
946 table corresponding to the first existing font specified in the list of
947 stretchy fonts held by the leaf style context (from -moz-math-stretchy in
948 mathml.css). Generic fonts are resolved by the preference
949 "font.mathfont-family".
951 a) the largeop and display settings determine the starting
952 size when we do the above search, regardless of whether
953 smaller variants already fit the container' size.
954 b) if it is a largeopOnly request (i.e., a displaystyle operator
955 with largeop=true and stretchy=false), we break after finding
956 the first starting variant, regardless of whether that
957 variant fits the container's size.
959 3) If a variant of appropriate size wasn't found, we see if the char
960 can be built by parts using the same glyph table.
962 a) Certain chars like over/underbrace in CMEX10 have to be built
963 from two half stretchy chars and joined in the middle. Such
964 chars are handled in a special manner. When this situation is
965 detected, the initial char (referred to as "parent") creates a
966 singly-linked list of child chars, asking them to stretch in
967 a divided space. A convention is used in the setup of
968 nsGlyphTable to express that a composite parent char can be built
970 b) There are some chars that have no middle and glue glyphs. For
971 such chars, the parts need to be joined using the rule.
972 By convention (TeXbook p.225), the descent of the parts is
973 zero while their ascent gives the thickness of the rule that
974 should be used to join them.
976 4) If a match was not found in that glyph table, repeat from 2 to search the
977 ordered list of stretchy fonts for the first font with a glyph table that
978 provides a fit to the container size. If no fit is found, the closest fit
982 When the pipeline completes successfully, the desired size of the
983 stretched char can actually be slighthly larger or smaller than
984 aContainerSize. But it is the responsibility of the caller to
985 account for the spacing when setting aContainerSize, and to leave
986 any extra margin when placing the stretched char.
988 // -----------------------------------------------------------------------------------
991 // plain TeX settings (TeXbook p.152)
992 #define NS_MATHML_DELIMITER_FACTOR 0.901f
993 #define NS_MATHML_DELIMITER_SHORTFALL_POINTS 5.0f
996 IsSizeOK(nsPresContext
* aPresContext
, nscoord a
, nscoord b
, PRUint32 aHint
)
998 // Normal: True if 'a' is around +/-10% of the target 'b' (10% is
999 // 1-DelimiterFactor). This often gives a chance to the base size to
1000 // win, especially in the context of <mfenced> without tall elements
1001 // or in sloppy markups without protective <mrow></mrow>
1003 (aHint
& NS_STRETCH_NORMAL
)
1004 && PRBool(float(PR_ABS(a
- b
))
1005 < (1.0f
- NS_MATHML_DELIMITER_FACTOR
) * float(b
));
1006 // Nearer: True if 'a' is around max{ +/-10% of 'b' , 'b' - 5pt },
1007 // as documented in The TeXbook, Ch.17, p.152.
1008 // i.e. within 10% and within 5pt
1009 PRBool isNearer
= PR_FALSE
;
1010 if (aHint
& (NS_STRETCH_NEARER
| NS_STRETCH_LARGEOP
)) {
1011 float c
= PR_MAX(float(b
) * NS_MATHML_DELIMITER_FACTOR
,
1012 float(b
) - aPresContext
->PointsToAppUnits(NS_MATHML_DELIMITER_SHORTFALL_POINTS
));
1013 isNearer
= PRBool(float(PR_ABS(b
- a
)) <= (float(b
) - c
));
1015 // Smaller: Mainly for transitory use, to compare two candidate
1018 (aHint
& NS_STRETCH_SMALLER
)
1019 && PRBool((float(a
) >= (NS_MATHML_DELIMITER_FACTOR
* float(b
)))
1021 // Larger: Critical to the sqrt code to ensure that the radical
1022 // size is tall enough
1024 (aHint
& (NS_STRETCH_LARGER
| NS_STRETCH_LARGEOP
))
1026 return (isNormal
|| isSmaller
|| isNearer
|| isLarger
);
1030 IsSizeBetter(nscoord a
, nscoord olda
, nscoord b
, PRUint32 aHint
)
1034 if (aHint
& (NS_STRETCH_LARGER
| NS_STRETCH_LARGEOP
))
1035 return (a
>= olda
) ? (olda
< b
) : (a
>= b
);
1036 if (aHint
& NS_STRETCH_SMALLER
)
1037 return (a
<= olda
) ? (olda
> b
) : (a
<= b
);
1039 // XXXkt prob want log scale here i.e. 1.5 is closer to 1 than 0.5
1040 return PR_ABS(a
- b
) < PR_ABS(olda
- b
);
1043 // We want to place the glyphs even when they don't fit at their
1044 // full extent, i.e., we may clip to tolerate a small amount of
1045 // overlap between the parts. This is important to cater for fonts
1048 ComputeSizeFromParts(nsPresContext
* aPresContext
,
1049 nsGlyphCode
* aGlyphs
,
1051 nscoord aTargetSize
)
1053 enum {first
, middle
, last
, glue
};
1054 // Add the parts that cannot be left out.
1056 for (PRInt32 i
= first
; i
<= last
; i
++) {
1057 if (aGlyphs
[i
] != aGlyphs
[glue
]) {
1062 // Determine how much is used in joins
1063 nscoord oneDevPixel
= aPresContext
->AppUnitsPerDevPixel();
1064 PRInt32 joins
= aGlyphs
[middle
] == aGlyphs
[glue
] ? 1 : 2;
1066 // Pick a maximum size using a maximum number of glue glyphs that we are
1067 // prepared to draw for one character.
1068 const PRInt32 maxGlyphs
= 1000;
1070 // This also takes into account the fact that, if the glue has no size,
1071 // then the character can't be lengthened.
1072 nscoord maxSize
= sum
- 2 * joins
* oneDevPixel
+ maxGlyphs
* aSizes
[glue
];
1073 if (maxSize
< aTargetSize
)
1074 return maxSize
; // settle with the maximum size
1076 // Get the minimum allowable size using some flex.
1077 nscoord minSize
= NSToCoordRound(NS_MATHML_DELIMITER_FACTOR
* sum
);
1079 if (minSize
> aTargetSize
)
1080 return minSize
; // settle with the minimum size
1082 // Fill-up the target area
1086 // Insert aFallbackFamilies before the first generic family in or at the end
1087 // of a CSS aFontName.
1089 AddFallbackFonts(nsAString
& aFontName
, const nsAString
& aFallbackFamilies
)
1091 if (aFallbackFamilies
.IsEmpty())
1094 if (aFontName
.IsEmpty()) {
1098 static const PRUnichar kSingleQuote
= PRUnichar('\'');
1099 static const PRUnichar kDoubleQuote
= PRUnichar('\"');
1100 static const PRUnichar kComma
= PRUnichar(',');
1102 const PRUnichar
*p_begin
, *p_end
;
1103 aFontName
.BeginReading(p_begin
);
1104 aFontName
.EndReading(p_end
);
1106 const PRUnichar
*p
= p_begin
;
1107 const PRUnichar
*p_name
= nsnull
;
1109 while (nsCRT::IsAsciiSpace(*p
))
1114 if (*p
== kSingleQuote
|| *p
== kDoubleQuote
) {
1115 // quoted font family
1116 PRUnichar quoteMark
= *p
;
1120 // XXX What about CSS character escapes?
1121 while (*p
!= quoteMark
)
1125 while (++p
!= p_end
&& *p
!= kComma
)
1129 // unquoted font family
1130 const PRUnichar
*nameStart
= p
;
1131 while (++p
!= p_end
&& *p
!= kComma
)
1134 nsAutoString family
;
1135 family
= Substring(nameStart
, p
);
1136 family
.CompressWhitespace(PR_FALSE
, PR_TRUE
);
1139 nsFont::GetGenericID(family
, &generic
);
1140 if (generic
!= kGenericFont_NONE
)
1144 ++p
; // may advance past p_end
1147 aFontName
.Append(NS_LITERAL_STRING(",") + aFallbackFamilies
);
1152 aFontName
.Insert(aFallbackFamilies
+ NS_LITERAL_STRING(","),
1155 else { // whitespace or empty
1156 aFontName
= aFallbackFamilies
;
1160 // Update the font and rendering context if there is a family change
1162 SetFontFamily(nsPresContext
* aPresContext
,
1163 nsIRenderingContext
& aRenderingContext
,
1165 const nsGlyphTable
* aGlyphTable
,
1166 const nsGlyphCode
& aGlyphCode
,
1167 const nsAString
& aDefaultFamily
)
1169 const nsAString
& family
=
1170 aGlyphCode
.font
? aGlyphTable
->FontNameFor(aGlyphCode
) : aDefaultFamily
;
1171 if (! family
.Equals(aFont
.name
)) {
1172 aFont
.name
= family
;
1173 aRenderingContext
.SetFont(aFont
, nsnull
, aPresContext
->GetUserFontSet());
1177 class nsMathMLChar::StretchEnumContext
{
1179 StretchEnumContext(nsMathMLChar
* aChar
,
1180 nsPresContext
* aPresContext
,
1181 nsIRenderingContext
& aRenderingContext
,
1182 nsStretchDirection aStretchDirection
,
1183 nscoord aTargetSize
,
1184 PRUint32 aStretchHint
,
1185 nsBoundingMetrics
& aStretchedMetrics
,
1186 const nsAString
& aFamilies
)
1188 mPresContext(aPresContext
),
1189 mRenderingContext(aRenderingContext
),
1190 mDirection(aStretchDirection
),
1191 mTargetSize(aTargetSize
),
1192 mStretchHint(aStretchHint
),
1193 mBoundingMetrics(aStretchedMetrics
),
1194 mFamilies(aFamilies
),
1195 mTryVariants(PR_TRUE
),
1196 mTryParts(PR_TRUE
) {}
1199 EnumCallback(const nsString
& aFamily
, PRBool aGeneric
, void *aData
);
1203 ResolverCallback (const nsAString
& aFamily
, void *aData
);
1205 PRBool
TryVariants(nsGlyphTable
* aGlyphTable
, const nsAString
& aFamily
);
1206 PRBool
TryParts(nsGlyphTable
* aGlyphTable
, const nsAString
& aFamily
);
1208 nsMathMLChar
* mChar
;
1209 nsPresContext
* mPresContext
;
1210 nsIRenderingContext
& mRenderingContext
;
1211 const nsStretchDirection mDirection
;
1212 const nscoord mTargetSize
;
1213 const PRUint32 mStretchHint
;
1214 nsBoundingMetrics
& mBoundingMetrics
;
1215 // Font families to search
1216 const nsAString
& mFamilies
;
1219 PRPackedBool mTryVariants
;
1220 PRPackedBool mTryParts
;
1223 nsAutoTArray
<nsGlyphTable
*,16> mTablesTried
;
1224 nsGlyphTable
* mGlyphTable
; // for this callback
1228 // 2. See if there are any glyphs of the appropriate size.
1229 // Returns PR_TRUE if the size is OK, PR_FALSE to keep searching.
1230 // Always updates the char if a better match is found.
1232 nsMathMLChar::StretchEnumContext::TryVariants(nsGlyphTable
* aGlyphTable
,
1233 const nsAString
& aFamily
)
1235 // Use our stretchy style context now that stretching is in progress
1236 nsFont font
= mChar
->mStyleContext
->GetStyleFont()->mFont
;
1237 // Ensure mRenderingContext.SetFont will be called:
1238 font
.name
.Truncate();
1240 PRBool isVertical
= (mDirection
== NS_STRETCH_DIRECTION_VERTICAL
);
1241 PRBool largeop
= (NS_STRETCH_LARGEOP
& mStretchHint
) != 0;
1242 PRBool largeopOnly
=
1243 largeop
&& (NS_STRETCH_VARIABLE_MASK
& mStretchHint
) == 0;
1244 PRBool maxWidth
= (NS_STRETCH_MAXWIDTH
& mStretchHint
) != 0;
1247 isVertical
? mBoundingMetrics
.ascent
+ mBoundingMetrics
.descent
1248 : mBoundingMetrics
.rightBearing
- mBoundingMetrics
.leftBearing
;
1249 PRBool haveBetter
= PR_FALSE
;
1251 // figure out the starting size : if this is a largeop, start at 2 else 1
1252 PRInt32 size
= 1; // size=0 is the char at its normal size
1253 if (largeop
&& aGlyphTable
->BigOf(mPresContext
, mChar
, 2).Exists()) {
1257 printf(" searching in %s ...\n",
1258 NS_LossyConvertUTF16toASCII(aFamily
).get());
1262 while ((ch
= aGlyphTable
->BigOf(mPresContext
, mChar
, size
)).Exists()) {
1264 SetFontFamily(mChar
->mStyleContext
->PresContext(), mRenderingContext
,
1265 font
, aGlyphTable
, ch
, aFamily
);
1267 NS_ASSERTION(maxWidth
|| ch
.code
!= mChar
->mGlyph
.code
||
1268 !font
.name
.Equals(mChar
->mFamily
),
1269 "glyph table incorrectly set -- duplicate found");
1271 nsBoundingMetrics bm
;
1272 nsresult rv
= mRenderingContext
.GetBoundingMetrics(&ch
.code
, 1, bm
);
1273 if (NS_SUCCEEDED(rv
)) {
1275 isVertical
? bm
.ascent
+ bm
.descent
1276 : bm
.rightBearing
- bm
.leftBearing
;
1279 IsSizeBetter(charSize
, bestSize
, mTargetSize
, mStretchHint
)) {
1281 // IsSizeBetter() checked that charSize < maxsize;
1282 // Leave ascent, descent, and bestsize as these contain maxsize.
1283 if (mBoundingMetrics
.width
< bm
.width
)
1284 mBoundingMetrics
.width
= bm
.width
;
1285 if (mBoundingMetrics
.leftBearing
> bm
.leftBearing
)
1286 mBoundingMetrics
.leftBearing
= bm
.leftBearing
;
1287 if (mBoundingMetrics
.rightBearing
< bm
.rightBearing
)
1288 mBoundingMetrics
.rightBearing
= bm
.rightBearing
;
1289 // Continue to check other sizes unless largeopOnly
1290 haveBetter
= largeopOnly
;
1293 mBoundingMetrics
= bm
;
1294 haveBetter
= PR_TRUE
;
1295 bestSize
= charSize
;
1296 mChar
->mGlyphTable
= aGlyphTable
;
1298 mChar
->mFamily
= font
.name
;
1301 printf(" size:%d Current best\n", size
);
1306 printf(" size:%d Rejected!\n", size
);
1309 break; // Not making an futher progress, stop searching
1313 // if largeopOnly is set, break now
1314 if (largeopOnly
) break;
1318 return haveBetter
&&
1319 (largeopOnly
|| IsSizeOK(mPresContext
, bestSize
, mTargetSize
, mStretchHint
));
1322 // 3. Build by parts.
1323 // Returns PR_TRUE if the size is OK, PR_FALSE to keep searching.
1324 // Always updates the char if a better match is found.
1326 nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable
* aGlyphTable
,
1327 const nsAString
& aFamily
)
1329 if (!aGlyphTable
->HasPartsOf(mPresContext
, mChar
))
1330 return PR_FALSE
; // to next table
1332 // See if this is a composite character /////////////////////////////////////
1333 if (aGlyphTable
->IsComposite(mPresContext
, mChar
)) {
1334 // let the child chars do the job
1335 nsBoundingMetrics compositeSize
;
1337 mChar
->ComposeChildren(mPresContext
, mRenderingContext
, aGlyphTable
,
1338 mTargetSize
, compositeSize
, mStretchHint
);
1340 printf(" Composing %d chars in font %s %s!\n",
1341 aGlyphTable
->ChildCountOf(mPresContext
, mChar
),
1342 NS_LossyConvertUTF16toASCII(fontName
).get(),
1343 NS_SUCCEEDED(rv
)? "OK" : "Rejected");
1346 return PR_FALSE
; // to next table
1348 // all went well, painting will be delegated from now on to children
1349 mChar
->mGlyph
= kNullGlyph
; // this will tell paint to build by parts
1350 mChar
->mGlyphTable
= aGlyphTable
;
1351 mBoundingMetrics
= compositeSize
;
1352 return PR_TRUE
; // no more searching
1355 // See if the parts of this table fit in the desired space //////////////////
1357 // Use our stretchy style context now that stretching is in progress
1358 nsFont font
= mChar
->mStyleContext
->GetStyleFont()->mFont
;
1359 // Ensure mRenderingContext.SetFont will be called:
1360 font
.name
.Truncate();
1362 // Compute the bounding metrics of all partial glyphs
1363 nsGlyphCode chdata
[4];
1364 nsBoundingMetrics bmdata
[4];
1365 nscoord sizedata
[4];
1366 nsGlyphCode glue
= aGlyphTable
->GlueOf(mPresContext
, mChar
);
1368 PRBool isVertical
= (mDirection
== NS_STRETCH_DIRECTION_VERTICAL
);
1369 PRBool maxWidth
= (NS_STRETCH_MAXWIDTH
& mStretchHint
) != 0;
1371 for (PRInt32 i
= 0; i
< 4; i
++) {
1374 case 0: ch
= aGlyphTable
->TopOf(mPresContext
, mChar
); break;
1375 case 1: ch
= aGlyphTable
->MiddleOf(mPresContext
, mChar
); break;
1376 case 2: ch
= aGlyphTable
->BottomOf(mPresContext
, mChar
); break;
1377 case 3: ch
= glue
; break;
1379 // empty slots are filled with the glue if it is not null
1380 if (!ch
.Exists()) ch
= glue
;
1381 nsBoundingMetrics bm
;
1384 // Null glue indicates that a rule will be drawn, which can stretch to
1385 // fill any space. Leave bounding metrics at 0.
1386 sizedata
[i
] = mTargetSize
;
1389 SetFontFamily(mChar
->mStyleContext
->PresContext(), mRenderingContext
,
1390 font
, aGlyphTable
, ch
, aFamily
);
1391 nsresult rv
= mRenderingContext
.GetBoundingMetrics(&ch
.code
, 1, bm
);
1392 if (NS_FAILED(rv
)) {
1393 // stop if we failed to compute the bounding metrics of a part.
1394 NS_WARNING("GetBoundingMetrics failed");
1395 return PR_FALSE
; // to next table
1398 // TODO: For the generic Unicode table, ideally we should check that the
1399 // glyphs are actually found and that they each come from the same
1402 sizedata
[i
] = isVertical
? bm
.ascent
+ bm
.descent
1403 : bm
.rightBearing
- bm
.leftBearing
;
1407 // Build by parts if we have successfully computed the
1408 // bounding metrics of all parts.
1409 nscoord computedSize
= ComputeSizeFromParts(mPresContext
, chdata
, sizedata
,
1412 nscoord currentSize
=
1413 isVertical
? mBoundingMetrics
.ascent
+ mBoundingMetrics
.descent
1414 : mBoundingMetrics
.rightBearing
- mBoundingMetrics
.leftBearing
;
1416 if (!IsSizeBetter(computedSize
, currentSize
, mTargetSize
, mStretchHint
)) {
1418 printf(" Font %s Rejected!\n",
1419 NS_LossyConvertUTF16toASCII(fontName
).get());
1421 return PR_FALSE
; // to next table
1425 printf(" Font %s Current best!\n",
1426 NS_LossyConvertUTF16toASCII(fontName
).get());
1429 // The computed size is the best we have found so far...
1430 // now is the time to compute and cache our bounding metrics
1437 lbearing
= mBoundingMetrics
.leftBearing
;
1438 rbearing
= mBoundingMetrics
.rightBearing
;
1439 width
= mBoundingMetrics
.width
;
1443 lbearing
= bmdata
[0].leftBearing
;
1444 rbearing
= bmdata
[0].rightBearing
;
1445 width
= bmdata
[0].width
;
1448 for (; i
< 4; i
++) {
1449 const nsBoundingMetrics
& bm
= bmdata
[i
];
1450 if (width
< bm
.width
) width
= bm
.width
;
1451 if (lbearing
> bm
.leftBearing
) lbearing
= bm
.leftBearing
;
1452 if (rbearing
< bm
.rightBearing
) rbearing
= bm
.rightBearing
;
1454 mBoundingMetrics
.width
= width
;
1455 // When maxWidth, updating ascent and descent indicates that no characters
1456 // larger than this character's minimum size need to be checked as they
1457 // will not be used.
1458 mBoundingMetrics
.ascent
= bmdata
[0].ascent
; // not used except with descent for height
1459 mBoundingMetrics
.descent
= computedSize
- mBoundingMetrics
.ascent
;
1460 mBoundingMetrics
.leftBearing
= lbearing
;
1461 mBoundingMetrics
.rightBearing
= rbearing
;
1464 nscoord ascent
= bmdata
[0].ascent
;
1465 nscoord descent
= bmdata
[0].descent
;
1466 for (PRInt32 i
= 1; i
< 4; i
++) {
1467 const nsBoundingMetrics
& bm
= bmdata
[i
];
1468 if (ascent
< bm
.ascent
) ascent
= bm
.ascent
;
1469 if (descent
< bm
.descent
) descent
= bm
.descent
;
1471 mBoundingMetrics
.width
= computedSize
;
1472 mBoundingMetrics
.ascent
= ascent
;
1473 mBoundingMetrics
.descent
= descent
;
1474 mBoundingMetrics
.leftBearing
= 0;
1475 mBoundingMetrics
.rightBearing
= computedSize
;
1478 return PR_FALSE
; // Continue to check other sizes
1481 mChar
->mGlyph
= kNullGlyph
; // this will tell paint to build by parts
1482 mChar
->mGlyphTable
= aGlyphTable
;
1483 mChar
->mFamily
= aFamily
;
1485 return IsSizeOK(mPresContext
, computedSize
, mTargetSize
, mStretchHint
);
1488 // This is only called for glyph table corresponding to a family that exists.
1489 // See if the table has a glyph that matches the container
1491 nsMathMLChar::StretchEnumContext::ResolverCallback (const nsAString
& aFamily
,
1494 StretchEnumContext
* context
= static_cast<StretchEnumContext
*>(aData
);
1495 nsGlyphTable
* glyphTable
= context
->mGlyphTable
;
1497 // Only try this table once.
1498 context
->mTablesTried
.AppendElement(glyphTable
);
1500 // If the unicode table is being used, then search all font families. If a
1501 // special table is being used then the font in this family should have the
1502 // specified glyphs.
1503 const nsAString
& family
= glyphTable
== &gGlyphTableList
->mUnicodeTable
?
1504 context
->mFamilies
: aFamily
;
1506 if(context
->mTryVariants
) {
1507 PRBool isOK
= context
->TryVariants(glyphTable
, family
);
1509 return PR_FALSE
; // no need to continue
1512 if(context
->mTryParts
) {
1513 PRBool isOK
= context
->TryParts(glyphTable
, family
);
1515 return PR_FALSE
; // no need to continue
1520 // This is called for each family, whether it exists or not
1522 nsMathMLChar::StretchEnumContext::EnumCallback(const nsString
& aFamily
,
1523 PRBool aGeneric
, void *aData
)
1525 StretchEnumContext
* context
= static_cast<StretchEnumContext
*>(aData
);
1527 // See if there is a special table for the family, but always use the
1528 // Unicode table for generic fonts.
1529 nsGlyphTable
* glyphTable
= aGeneric
?
1530 &gGlyphTableList
->mUnicodeTable
: gGlyphTableList
->GetGlyphTableFor(aFamily
);
1532 if (context
->mTablesTried
.IndexOf(glyphTable
) != context
->mTablesTried
.NoIndex
)
1533 return PR_TRUE
; // already tried this one
1535 context
->mGlyphTable
= glyphTable
;
1538 return ResolverCallback(aFamily
, aData
);
1541 gfxPlatform
*pf
= gfxPlatform::GetPlatform();
1543 pf
->ResolveFontName(aFamily
, ResolverCallback
, aData
, aborted
);
1544 return NS_SUCCEEDED(rv
) && !aborted
; // true means continue
1548 nsMathMLChar::StretchInternal(nsPresContext
* aPresContext
,
1549 nsIRenderingContext
& aRenderingContext
,
1550 nsStretchDirection
& aStretchDirection
,
1551 const nsBoundingMetrics
& aContainerSize
,
1552 nsBoundingMetrics
& aDesiredStretchSize
,
1553 PRUint32 aStretchHint
,
1554 // These are currently only used when
1555 // aStretchHint & NS_STRETCH_MAXWIDTH:
1557 PRBool aMaxSizeIsAbsolute
)
1559 // if we have been called before, and we didn't actually stretch, our
1560 // direction may have been set to NS_STRETCH_DIRECTION_UNSUPPORTED.
1561 // So first set our direction back to its instrinsic value
1562 nsStretchDirection direction
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
1563 if (mOperator
>= 0) {
1564 // mOperator is initialized in SetData() and remains unchanged
1565 direction
= nsMathMLOperators::GetStretchyDirectionAt(mOperator
);
1568 // Set default font and get the default bounding metrics
1569 // mStyleContext is a leaf context used only when stretching happens.
1570 // For the base size, the default font should come from the parent context
1571 nsAutoString fontName
;
1572 nsFont font
= mStyleContext
->GetParent()->GetStyleFont()->mFont
;
1574 // Override with specific fonts if applicable for this character
1575 nsCOMPtr
<nsIPrefBranch
> prefBranch
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
1576 nsAutoString families
;
1577 if (GetFontExtensionPref(prefBranch
, mData
[0], eExtension_base
, families
)) {
1578 font
.name
= families
;
1581 // Don't modify this nsMathMLChar when doing GetMaxWidth()
1582 PRBool maxWidth
= (NS_STRETCH_MAXWIDTH
& aStretchHint
) != 0;
1584 // Record the families in case there is no stretch. But don't bother
1585 // storing families when they are just those from the StyleContext.
1589 aRenderingContext
.SetFont(font
, nsnull
, aPresContext
->GetUserFontSet());
1591 aRenderingContext
.GetBoundingMetrics(mData
.get(), PRUint32(mData
.Length()),
1592 aDesiredStretchSize
);
1593 if (NS_FAILED(rv
)) {
1594 NS_WARNING("GetBoundingMetrics failed");
1598 ////////////////////////////////////////////////////////////////////////////////////
1599 // 1. Check the common situations where stretching is not actually needed
1600 ////////////////////////////////////////////////////////////////////////////////////
1602 // quick return if there is nothing special about this char
1604 (aStretchDirection
!= direction
&&
1605 aStretchDirection
!= NS_STRETCH_DIRECTION_DEFAULT
) ||
1606 (aStretchHint
& ~NS_STRETCH_MAXWIDTH
) == NS_STRETCH_NONE
) {
1610 // if no specified direction, attempt to stretch in our preferred direction
1611 if (aStretchDirection
== NS_STRETCH_DIRECTION_DEFAULT
) {
1612 aStretchDirection
= direction
;
1615 // see if this is a particular largeop or largeopOnly request
1616 PRBool largeop
= (NS_STRETCH_LARGEOP
& aStretchHint
) != 0;
1617 PRBool stretchy
= (NS_STRETCH_VARIABLE_MASK
& aStretchHint
) != 0;
1618 PRBool largeopOnly
= largeop
&& !stretchy
;
1620 PRBool isVertical
= (direction
== NS_STRETCH_DIRECTION_VERTICAL
);
1622 nscoord targetSize
=
1623 isVertical
? aContainerSize
.ascent
+ aContainerSize
.descent
1624 : aContainerSize
.rightBearing
- aContainerSize
.leftBearing
;
1627 // See if it is only necessary to consider glyphs up to some maximum size.
1628 // Set the current height to the maximum size, and set aStretchHint to
1629 // NS_STRETCH_SMALLER if the size is variable, so that only smaller sizes
1630 // are considered. targetSize from GetMaxWidth() is 0.
1632 // variable size stretch - consider all sizes < maxsize
1634 (aStretchHint
& ~NS_STRETCH_VARIABLE_MASK
) | NS_STRETCH_SMALLER
;
1637 // Use NS_MATHML_DELIMITER_FACTOR to allow some slightly larger glyphs as
1638 // maxsize is not enforced exactly.
1639 if (aMaxSize
== NS_MATHML_OPERATOR_SIZE_INFINITY
) {
1640 aDesiredStretchSize
.ascent
= nscoord_MAX
;
1641 aDesiredStretchSize
.descent
= 0;
1644 nscoord height
= aDesiredStretchSize
.ascent
+ aDesiredStretchSize
.descent
;
1646 if (aMaxSizeIsAbsolute
) {
1647 aDesiredStretchSize
.ascent
=
1648 NSToCoordRound(aMaxSize
/ NS_MATHML_DELIMITER_FACTOR
);
1649 aDesiredStretchSize
.descent
= 0;
1651 // else: leave height as 0
1654 float scale
= aMaxSizeIsAbsolute
? aMaxSize
/ height
: aMaxSize
;
1655 scale
/= NS_MATHML_DELIMITER_FACTOR
;
1656 aDesiredStretchSize
.ascent
=
1657 NSToCoordRound(scale
* aDesiredStretchSize
.ascent
);
1658 aDesiredStretchSize
.descent
=
1659 NSToCoordRound(scale
* aDesiredStretchSize
.descent
);
1664 if (!maxWidth
&& !largeop
) {
1665 // Doing Stretch() not GetMaxWidth(),
1666 // and not a largeop in display mode; return if size fits
1668 isVertical
? aDesiredStretchSize
.ascent
+ aDesiredStretchSize
.descent
1669 : aDesiredStretchSize
.rightBearing
- aDesiredStretchSize
.leftBearing
;
1671 if ((targetSize
<= 0) ||
1672 ((isVertical
&& charSize
>= targetSize
) ||
1673 IsSizeOK(aPresContext
, charSize
, targetSize
, aStretchHint
)))
1677 ////////////////////////////////////////////////////////////////////////////////////
1678 // 2/3. Search for a glyph or set of part glyphs of appropriate size
1679 ////////////////////////////////////////////////////////////////////////////////////
1681 font
= mStyleContext
->GetStyleFont()->mFont
;
1682 nsAutoString cssFamilies
;
1683 cssFamilies
= font
.name
;
1685 PRBool done
= PR_FALSE
;
1687 // See if there are preferred fonts for the variants of this char
1688 if (GetFontExtensionPref(prefBranch
, mData
[0], eExtension_variants
,
1690 font
.name
= families
;
1692 StretchEnumContext
enumData(this, aPresContext
, aRenderingContext
,
1693 aStretchDirection
, targetSize
, aStretchHint
,
1694 aDesiredStretchSize
, font
.name
);
1695 enumData
.mTryParts
= PR_FALSE
;
1697 done
= !font
.EnumerateFamilies(StretchEnumContext::EnumCallback
, &enumData
);
1700 // See if there are preferred fonts for the parts of this char
1701 if (!done
&& !largeopOnly
1702 && GetFontExtensionPref(prefBranch
, mData
[0], eExtension_parts
,
1704 font
.name
= families
;
1706 StretchEnumContext
enumData(this, aPresContext
, aRenderingContext
,
1707 aStretchDirection
, targetSize
, aStretchHint
,
1708 aDesiredStretchSize
, font
.name
);
1709 enumData
.mTryVariants
= PR_FALSE
;
1711 done
= !font
.EnumerateFamilies(StretchEnumContext::EnumCallback
, &enumData
);
1714 if (!done
) { // normal case
1715 // Use the css font-family but add preferred fallback fonts.
1716 font
.name
= cssFamilies
;
1717 NS_NAMED_LITERAL_CSTRING(defaultKey
, "font.mathfont-family");
1718 nsAutoString fallbackFonts
;
1719 if (GetPrefValue(prefBranch
, defaultKey
.get(), fallbackFonts
)) {
1720 AddFallbackFonts(font
.name
, fallbackFonts
);
1724 printf("Searching in "%s
" for a glyph of appropriate size for: 0x%04X:%c\n",
1725 font
.name
, mData
[0], mData
[0]&0x00FF);
1727 StretchEnumContext
enumData(this, aPresContext
, aRenderingContext
,
1728 aStretchDirection
, targetSize
, aStretchHint
,
1729 aDesiredStretchSize
, font
.name
);
1730 enumData
.mTryParts
= !largeopOnly
;
1732 font
.EnumerateFamilies(StretchEnumContext::EnumCallback
, &enumData
);
1739 nsMathMLChar::Stretch(nsPresContext
* aPresContext
,
1740 nsIRenderingContext
& aRenderingContext
,
1741 nsStretchDirection aStretchDirection
,
1742 const nsBoundingMetrics
& aContainerSize
,
1743 nsBoundingMetrics
& aDesiredStretchSize
,
1744 PRUint32 aStretchHint
)
1746 NS_ASSERTION(!(aStretchHint
&
1747 ~(NS_STRETCH_VARIABLE_MASK
| NS_STRETCH_LARGEOP
)),
1748 "Unexpected stretch flags");
1750 // This will be updated if a better match than the base character is found
1753 mDirection
= aStretchDirection
;
1755 StretchInternal(aPresContext
, aRenderingContext
, mDirection
,
1756 aContainerSize
, aDesiredStretchSize
, aStretchHint
);
1758 if (mGlyph
.font
== -1) { // no stretch happened
1759 // ensure that the char later behaves like a normal char
1760 // (will be reset back to its intrinsic value in case of dynamic updates)
1761 mDirection
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
1764 // Record the metrics
1765 mBoundingMetrics
= aDesiredStretchSize
;
1770 // What happens here is that the StretchInternal algorithm is used but
1771 // modified by passing the NS_STRETCH_MAXWIDTH stretch hint. That causes
1772 // StretchInternal to return horizontal bounding metrics that are the maximum
1773 // that might be returned from a Stretch.
1775 // In order to avoid considering widths of some characters in fonts that will
1776 // not be used for any stretch size, StretchInternal sets the initial height
1777 // to infinity and looks for any characters smaller than this height. When a
1778 // character built from parts is considered, (it will be used by Stretch for
1779 // any characters greater than its minimum size, so) the height is set to its
1780 // minimum size, so that only widths of smaller subsequent characters are
1783 nsMathMLChar::GetMaxWidth(nsPresContext
* aPresContext
,
1784 nsIRenderingContext
& aRenderingContext
,
1785 PRUint32 aStretchHint
,
1786 float aMaxSize
, PRBool aMaxSizeIsAbsolute
)
1788 nsBoundingMetrics bm
;
1789 nsStretchDirection direction
= NS_STRETCH_DIRECTION_VERTICAL
;
1790 const nsBoundingMetrics container
; // zero target size
1792 StretchInternal(aPresContext
, aRenderingContext
, direction
, container
,
1793 bm
, aStretchHint
| NS_STRETCH_MAXWIDTH
);
1795 return PR_MAX(bm
.width
, bm
.rightBearing
) - PR_MIN(0, bm
.leftBearing
);
1799 nsMathMLChar::ComposeChildren(nsPresContext
* aPresContext
,
1800 nsIRenderingContext
& aRenderingContext
,
1801 nsGlyphTable
* aGlyphTable
,
1802 nscoord aTargetSize
,
1803 nsBoundingMetrics
& aCompositeSize
,
1804 PRUint32 aStretchHint
)
1807 nsMathMLChar
* child
;
1808 PRInt32 count
= aGlyphTable
->ChildCountOf(aPresContext
, this);
1809 NS_ASSERTION(count
, "something is wrong somewhere");
1810 if (!count
) return NS_ERROR_FAILURE
;
1811 // if we haven't been here before, create the linked list of children now
1812 // otherwise, use what we have, adding more children as needed or deleting the extra
1813 nsMathMLChar
* last
= this;
1814 while ((i
< count
) && last
->mSibling
) {
1816 last
= last
->mSibling
;
1819 child
= new nsMathMLChar(this);
1821 if (mSibling
) delete mSibling
; // don't leave a dangling list ...
1823 return NS_ERROR_OUT_OF_MEMORY
;
1825 last
->mSibling
= child
;
1829 if (last
->mSibling
) {
1830 delete last
->mSibling
;
1831 last
->mSibling
= nsnull
;
1833 // let children stretch in an equal space
1834 nsBoundingMetrics splitSize
;
1835 if (NS_STRETCH_DIRECTION_HORIZONTAL
== mDirection
)
1836 splitSize
.width
= aTargetSize
/ count
;
1838 splitSize
.ascent
= aTargetSize
/ (count
* 2);
1839 splitSize
.descent
= splitSize
.ascent
;
1841 nscoord dx
= 0, dy
= 0;
1842 for (i
= 0, child
= mSibling
; child
; child
= child
->mSibling
, i
++) {
1843 // child chars should just inherit our values - which may change between calls...
1844 child
->mData
= mData
;
1845 child
->mOperator
= mOperator
;
1846 child
->mDirection
= mDirection
;
1847 child
->mStyleContext
= mStyleContext
;
1848 child
->mGlyphTable
= aGlyphTable
; // the child is associated to this table
1849 // there goes the Stretch() ...
1850 nsBoundingMetrics childSize
;
1851 nsresult rv
= child
->Stretch(aPresContext
, aRenderingContext
, mDirection
,
1852 splitSize
, childSize
, aStretchHint
);
1853 // check if something went wrong or the child couldn't fit in the alloted space
1854 if (NS_FAILED(rv
) || (NS_STRETCH_DIRECTION_UNSUPPORTED
== child
->mDirection
)) {
1855 delete mSibling
; // don't leave a dangling list behind ...
1857 return NS_ERROR_FAILURE
;
1859 child
->SetRect(nsRect(dx
, dy
, childSize
.width
, childSize
.ascent
+childSize
.descent
));
1861 aCompositeSize
= childSize
;
1863 if (NS_STRETCH_DIRECTION_HORIZONTAL
== mDirection
)
1864 aCompositeSize
+= childSize
;
1866 aCompositeSize
.descent
+= childSize
.ascent
+ childSize
.descent
;
1867 if (aCompositeSize
.leftBearing
> childSize
.leftBearing
)
1868 aCompositeSize
.leftBearing
= childSize
.leftBearing
;
1869 if (aCompositeSize
.rightBearing
< childSize
.rightBearing
)
1870 aCompositeSize
.rightBearing
= childSize
.rightBearing
;
1873 if (NS_STRETCH_DIRECTION_HORIZONTAL
== mDirection
)
1874 dx
+= childSize
.width
;
1876 dy
+= childSize
.ascent
+ childSize
.descent
;
1881 class nsDisplayMathMLSelectionRect
: public nsDisplayItem
{
1883 nsDisplayMathMLSelectionRect(nsIFrame
* aFrame
, const nsRect
& aRect
)
1884 : nsDisplayItem(aFrame
), mRect(aRect
) {
1885 MOZ_COUNT_CTOR(nsDisplayMathMLSelectionRect
);
1887 #ifdef NS_BUILD_REFCNT_LOGGING
1888 virtual ~nsDisplayMathMLSelectionRect() {
1889 MOZ_COUNT_DTOR(nsDisplayMathMLSelectionRect
);
1893 virtual void Paint(nsDisplayListBuilder
* aBuilder
, nsIRenderingContext
* aCtx
,
1894 const nsRect
& aDirtyRect
);
1895 NS_DISPLAY_DECL_NAME("MathMLSelectionRect")
1900 void nsDisplayMathMLSelectionRect::Paint(nsDisplayListBuilder
* aBuilder
,
1901 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
)
1903 // get color to use for selection from the look&feel object
1904 nscolor bgColor
= NS_RGB(0, 0, 0);
1905 mFrame
->PresContext()->LookAndFeel()->
1906 GetColor(nsILookAndFeel::eColor_TextSelectBackground
, bgColor
);
1907 aCtx
->SetColor(bgColor
);
1908 aCtx
->FillRect(mRect
+ aBuilder
->ToReferenceFrame(mFrame
));
1911 class nsDisplayMathMLCharBackground
: public nsDisplayItem
{
1913 nsDisplayMathMLCharBackground(nsIFrame
* aFrame
, const nsRect
& aRect
,
1914 nsStyleContext
* aStyleContext
)
1915 : nsDisplayItem(aFrame
), mStyleContext(aStyleContext
), mRect(aRect
) {
1916 MOZ_COUNT_CTOR(nsDisplayMathMLCharBackground
);
1918 #ifdef NS_BUILD_REFCNT_LOGGING
1919 virtual ~nsDisplayMathMLCharBackground() {
1920 MOZ_COUNT_DTOR(nsDisplayMathMLCharBackground
);
1924 virtual void Paint(nsDisplayListBuilder
* aBuilder
, nsIRenderingContext
* aCtx
,
1925 const nsRect
& aDirtyRect
);
1926 NS_DISPLAY_DECL_NAME("MathMLCharBackground")
1928 nsStyleContext
* mStyleContext
;
1932 void nsDisplayMathMLCharBackground::Paint(nsDisplayListBuilder
* aBuilder
,
1933 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
)
1935 const nsStyleBorder
* border
= mStyleContext
->GetStyleBorder();
1936 const nsStyleBackground
* backg
= mStyleContext
->GetStyleBackground();
1937 nsRect
rect(mRect
+ aBuilder
->ToReferenceFrame(mFrame
));
1938 nsCSSRendering::PaintBackgroundWithSC(mFrame
->PresContext(), *aCtx
, mFrame
,
1939 aDirtyRect
, rect
, *backg
, *border
,
1943 class nsDisplayMathMLCharForeground
: public nsDisplayItem
{
1945 nsDisplayMathMLCharForeground(nsIFrame
* aFrame
, nsMathMLChar
* aChar
,
1947 : nsDisplayItem(aFrame
), mChar(aChar
), mIsSelected(aIsSelected
) {
1948 MOZ_COUNT_CTOR(nsDisplayMathMLCharForeground
);
1950 #ifdef NS_BUILD_REFCNT_LOGGING
1951 virtual ~nsDisplayMathMLCharForeground() {
1952 MOZ_COUNT_DTOR(nsDisplayMathMLCharForeground
);
1956 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
) {
1958 mChar
->GetRect(rect
);
1960 aBuilder
->ToReferenceFrame(mFrame
) + rect
.TopLeft();
1961 nsBoundingMetrics bm
;
1962 mChar
->GetBoundingMetrics(bm
);
1963 return nsRect(offset
.x
+ bm
.leftBearing
, offset
.y
,
1964 bm
.rightBearing
- bm
.leftBearing
, bm
.ascent
+ bm
.descent
);
1967 virtual void Paint(nsDisplayListBuilder
* aBuilder
, nsIRenderingContext
* aCtx
,
1968 const nsRect
& aDirtyRect
)
1970 mChar
->PaintForeground(mFrame
->PresContext(), *aCtx
,
1971 aBuilder
->ToReferenceFrame(mFrame
), mIsSelected
);
1974 NS_DISPLAY_DECL_NAME("MathMLCharForeground")
1977 nsMathMLChar
* mChar
;
1978 PRPackedBool mIsSelected
;
1982 class nsDisplayMathMLCharDebug
: public nsDisplayItem
{
1984 nsDisplayMathMLCharDebug(nsIFrame
* aFrame
, const nsRect
& aRect
)
1985 : nsDisplayItem(aFrame
), mRect(aRect
) {
1986 MOZ_COUNT_CTOR(nsDisplayMathMLCharDebug
);
1988 #ifdef NS_BUILD_REFCNT_LOGGING
1989 virtual ~nsDisplayMathMLCharDebug() {
1990 MOZ_COUNT_DTOR(nsDisplayMathMLCharDebug
);
1994 virtual void Paint(nsDisplayListBuilder
* aBuilder
, nsIRenderingContext
* aCtx
,
1995 const nsRect
& aDirtyRect
);
1996 NS_DISPLAY_DECL_NAME("MathMLCharDebug")
2001 void nsDisplayMathMLCharDebug::Paint(nsDisplayListBuilder
* aBuilder
,
2002 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
)
2005 PRIntn skipSides
= 0;
2006 nsPresContext
* presContext
= mFrame
->PresContext();
2007 const nsStyleBorder
* border
= mFrame
->GetStyleBorder();
2008 nsStyleContext
* styleContext
= mFrame
->GetStyleContext();
2009 nsRect rect
= mRect
+ aBuilder
->ToReferenceFrame(mFrame
);
2010 nsCSSRendering::PaintBorder(presContext
, *aCtx
, mFrame
,
2011 aDirtyRect
, rect
, *border
, styleContext
,
2013 nsCSSRendering::PaintOutline(presContext
, *aCtx
, mFrame
,
2014 aDirtyRect
, rect
, *border
,
2015 *mFrame
->GetStyleOutline(), styleContext
);
2021 nsMathMLChar::Display(nsDisplayListBuilder
* aBuilder
,
2022 nsIFrame
* aForFrame
,
2023 const nsDisplayListSet
& aLists
,
2024 const nsRect
* aSelectedRect
)
2026 nsresult rv
= NS_OK
;
2027 nsStyleContext
* parentContext
= mStyleContext
->GetParent();
2028 nsStyleContext
* styleContext
= mStyleContext
;
2030 if (NS_STRETCH_DIRECTION_UNSUPPORTED
== mDirection
) {
2031 // normal drawing if there is nothing special about this char
2032 // Set default context to the parent context
2033 styleContext
= parentContext
;
2036 if (!styleContext
->GetStyleVisibility()->IsVisible())
2039 // if the leaf style context that we use for stretchy chars has a background
2040 // color we use it -- this feature is mostly used for testing and debugging
2041 // purposes. Normally, users will set the background on the container frame.
2042 // paint the selection background -- beware MathML frames overlap a lot
2043 if (aSelectedRect
&& !aSelectedRect
->IsEmpty()) {
2044 rv
= aLists
.BorderBackground()->AppendNewToTop(new (aBuilder
)
2045 nsDisplayMathMLSelectionRect(aForFrame
, *aSelectedRect
));
2046 NS_ENSURE_SUCCESS(rv
, rv
);
2048 else if (mRect
.width
&& mRect
.height
) {
2049 const nsStyleBackground
* backg
= styleContext
->GetStyleBackground();
2050 if (styleContext
!= parentContext
&&
2051 NS_GET_A(backg
->mBackgroundColor
) > 0) {
2052 rv
= aLists
.BorderBackground()->AppendNewToTop(new (aBuilder
)
2053 nsDisplayMathMLCharBackground(aForFrame
, mRect
, styleContext
));
2054 NS_ENSURE_SUCCESS(rv
, rv
);
2057 // our container frame will take care of painting its background
2059 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
2061 rv
= aLists
.BorderBackground()->AppendToTop(new (aBuilder
)
2062 nsDisplayMathMLCharDebug(aForFrame
, mRect
));
2063 NS_ENSURE_SUCCESS(rv
, rv
);
2066 return aLists
.Content()->AppendNewToTop(new (aBuilder
)
2067 nsDisplayMathMLCharForeground(aForFrame
, this,
2068 aSelectedRect
&& !aSelectedRect
->IsEmpty()));
2072 nsMathMLChar::PaintForeground(nsPresContext
* aPresContext
,
2073 nsIRenderingContext
& aRenderingContext
,
2077 nsStyleContext
* parentContext
= mStyleContext
->GetParent();
2078 nsStyleContext
* styleContext
= mStyleContext
;
2080 if (NS_STRETCH_DIRECTION_UNSUPPORTED
== mDirection
) {
2081 // normal drawing if there is nothing special about this char
2082 // Set default context to the parent context
2083 styleContext
= parentContext
;
2087 nscolor fgColor
= styleContext
->GetStyleColor()->mColor
;
2089 // get color to use for selection from the look&feel object
2090 aPresContext
->LookAndFeel()->
2091 GetColor(nsILookAndFeel::eColor_TextSelectForeground
, fgColor
);
2093 aRenderingContext
.SetColor(fgColor
);
2095 nsAutoString fontName
;
2096 nsFont
theFont(styleContext
->GetStyleFont()->mFont
);
2097 if (! mFamily
.IsEmpty()) {
2098 theFont
.name
= mFamily
;
2100 aRenderingContext
.SetFont(theFont
, nsnull
, aPresContext
->GetUserFontSet());
2102 if (NS_STRETCH_DIRECTION_UNSUPPORTED
== mDirection
) {
2103 // normal drawing if there is nothing special about this char ...
2104 // Grab some metrics to adjust the placements ...
2105 PRUint32 len
= PRUint32(mData
.Length());
2106 //printf("Painting %04X like a normal char\n", mData[0]);
2107 //aRenderingContext.SetColor(NS_RGB(255,0,0));
2108 aRenderingContext
.DrawString(mData
.get(), len
, mRect
.x
+ aPt
.x
,
2109 mRect
.y
+ aPt
.y
+ mBoundingMetrics
.ascent
);
2112 // Grab some metrics to adjust the placements ...
2113 // if there is a glyph of appropriate size, paint that glyph
2114 if (mGlyph
.Exists()) {
2115 //printf("Painting %04X with a glyph of appropriate size\n", mData[0]);
2116 //aRenderingContext.SetColor(NS_RGB(0,0,255));
2117 aRenderingContext
.DrawString(&mGlyph
.code
, 1, mRect
.x
+ aPt
.x
,
2118 mRect
.y
+ aPt
.y
+ mBoundingMetrics
.ascent
);
2120 else { // paint by parts
2121 // see if this is a composite char and let children paint themselves
2122 if (!mParent
&& mSibling
) { // only a "root" having child chars can enter here
2123 for (nsMathMLChar
* child
= mSibling
; child
; child
= child
->mSibling
) {
2124 //if (!mStyleContext->Equals(child->mStyleContext))
2125 // printf("char contexts are out of sync\n");
2126 child
->PaintForeground(aPresContext
, aRenderingContext
, aPt
,
2129 return; // that's all folks
2131 //aRenderingContext.SetColor(NS_RGB(0,255,0));
2132 nsRect r
= mRect
+ aPt
;
2133 if (NS_STRETCH_DIRECTION_VERTICAL
== mDirection
)
2134 PaintVertically(aPresContext
, aRenderingContext
, theFont
, styleContext
,
2136 else if (NS_STRETCH_DIRECTION_HORIZONTAL
== mDirection
)
2137 PaintHorizontally(aPresContext
, aRenderingContext
, theFont
, styleContext
,
2143 /* =================================================================================
2144 And now the helper routines that actually do the job of painting the char by parts
2147 class AutoPushClipRect
{
2148 nsIRenderingContext
& mCtx
;
2150 AutoPushClipRect(nsIRenderingContext
& aCtx
, const nsRect
& aRect
)
2153 mCtx
.SetClipRect(aRect
, nsClipCombine_kIntersect
);
2155 ~AutoPushClipRect() {
2161 SnapToDevPixels(const gfxContext
* aThebesContext
, PRInt32 aAppUnitsPerGfxUnit
,
2164 gfxPoint
pt(NSAppUnitsToFloatPixels(aPt
.x
, aAppUnitsPerGfxUnit
),
2165 NSAppUnitsToFloatPixels(aPt
.y
, aAppUnitsPerGfxUnit
));
2166 pt
= aThebesContext
->UserToDevice(pt
);
2168 pt
= aThebesContext
->DeviceToUser(pt
);
2169 return nsPoint(NSFloatPixelsToAppUnits(pt
.x
, aAppUnitsPerGfxUnit
),
2170 NSFloatPixelsToAppUnits(pt
.y
, aAppUnitsPerGfxUnit
));
2173 // paint a stretchy char by assembling glyphs vertically
2175 nsMathMLChar::PaintVertically(nsPresContext
* aPresContext
,
2176 nsIRenderingContext
& aRenderingContext
,
2178 nsStyleContext
* aStyleContext
,
2179 nsGlyphTable
* aGlyphTable
,
2182 nsresult rv
= NS_OK
;
2183 // Get the device pixel size in the vertical direction.
2184 // (This makes no effort to optimize for non-translation transformations.)
2185 nscoord oneDevPixel
= aPresContext
->AppUnitsPerDevPixel();
2187 // get metrics data to be re-used later
2189 nsGlyphCode ch
, chdata
[4];
2190 nsBoundingMetrics bmdata
[4];
2191 PRInt32 glue
, bottom
;
2192 nsGlyphCode chGlue
= aGlyphTable
->GlueOf(aPresContext
, this);
2193 for (PRInt32 j
= 0; j
< 4; ++j
) {
2196 ch
= aGlyphTable
->TopOf(aPresContext
, this);
2199 ch
= aGlyphTable
->MiddleOf(aPresContext
, this);
2201 continue; // no middle
2204 ch
= aGlyphTable
->BottomOf(aPresContext
, this);
2212 // empty slots are filled with the glue if it is not null
2213 if (!ch
.Exists()) ch
= chGlue
;
2214 // if (!ch.Exists()) glue is null, leave bounding metrics at 0
2216 SetFontFamily(aPresContext
, aRenderingContext
,
2217 aFont
, aGlyphTable
, ch
, mFamily
);
2218 rv
= aRenderingContext
.GetBoundingMetrics(&ch
.code
, 1, bmdata
[i
]);
2219 if (NS_FAILED(rv
)) {
2220 NS_WARNING("GetBoundingMetrics failed");
2227 nscoord dx
= aRect
.x
;
2228 nscoord offset
[3], start
[3], end
[3];
2229 nsRefPtr
<gfxContext
> ctx
= aRenderingContext
.ThebesContext();
2230 for (i
= 0; i
<= bottom
; ++i
) {
2232 const nsBoundingMetrics
& bm
= bmdata
[i
];
2234 if (0 == i
) { // top
2235 dy
= aRect
.y
+ bm
.ascent
;
2237 else if (bottom
== i
) { // bottom
2238 dy
= aRect
.y
+ aRect
.height
- bm
.descent
;
2241 dy
= aRect
.y
+ bm
.ascent
+ (aRect
.height
- (bm
.ascent
+ bm
.descent
))/2;
2243 // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
2244 // Do this now so that we can get the other dimensions right.
2245 // (This may not achieve much with non-rectangular transformations.)
2246 dy
= SnapToDevPixels(ctx
, oneDevPixel
, nsPoint(dx
, dy
)).y
;
2247 // abcissa passed to DrawString
2249 // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
2250 // pixel, so the bm values can include 1 row of faint pixels on each edge.
2251 // Don't rely on this pixel as it can look like a gap.
2252 start
[i
] = dy
- bm
.ascent
+ oneDevPixel
; // top join
2253 end
[i
] = dy
+ bm
.descent
- oneDevPixel
; // bottom join
2256 // If there are overlaps, then join at the mid point
2257 for (i
= 0; i
< bottom
; ++i
) {
2258 if (end
[i
] > start
[i
+1]) {
2259 end
[i
] = (end
[i
] + start
[i
+1]) / 2;
2260 start
[i
+1] = end
[i
];
2264 nsRect unionRect
= aRect
;
2265 unionRect
.x
+= mBoundingMetrics
.leftBearing
;
2267 mBoundingMetrics
.rightBearing
- mBoundingMetrics
.leftBearing
;
2268 unionRect
.Inflate(oneDevPixel
, oneDevPixel
);
2270 /////////////////////////////////////
2271 // draw top, middle, bottom
2272 for (i
= 0; i
<= bottom
; ++i
) {
2274 // glue can be null, and other parts could have been set to glue
2277 // bounding box of the part
2278 aRenderingContext
.SetColor(NS_RGB(0,0,0));
2279 aRenderingContext
.DrawRect(nsRect(dx
,start
[i
],aRect
.width
+30*(i
+1),end
[i
]-start
[i
]));
2281 nscoord dy
= offset
[i
];
2282 // Draw a glyph in a clipped area so that we don't have hairy chars
2284 nsRect clipRect
= unionRect
;
2285 // Clip at the join to get a solid edge (without overlap or gap), when
2286 // this won't change the glyph too much. If the glyph is too small to
2287 // clip then we'll overlap rather than have a gap.
2288 nscoord height
= bmdata
[i
].ascent
+ bmdata
[i
].descent
;
2290 height
* (1.0 - NS_MATHML_DELIMITER_FACTOR
) > oneDevPixel
) {
2291 if (0 == i
) { // top
2292 clipRect
.height
= end
[i
] - clipRect
.y
;
2294 else if (bottom
== i
) { // bottom
2295 clipRect
.height
-= start
[i
] - clipRect
.y
;
2296 clipRect
.y
= start
[i
];
2299 clipRect
.y
= start
[i
];
2300 clipRect
.height
= end
[i
] - start
[i
];
2303 if (!clipRect
.IsEmpty()) {
2304 AutoPushClipRect
clip(aRenderingContext
, clipRect
);
2305 SetFontFamily(aPresContext
, aRenderingContext
,
2306 aFont
, aGlyphTable
, ch
, mFamily
);
2307 aRenderingContext
.DrawString(&ch
.code
, 1, dx
, dy
);
2313 // fill the gap between top and middle, and between middle and bottom.
2314 if (!chGlue
.Exists()) { // null glue : draw a rule
2315 // figure out the dimensions of the rule to be drawn :
2316 // set lbearing to rightmost lbearing among the two current successive parts.
2317 // set rbearing to leftmost rbearing among the two current successive parts.
2318 // this not only satisfies the convention used for over/underbraces
2319 // in TeX, but also takes care of broken fonts like the stretchy integral
2320 // in Symbol for small font sizes in unix.
2321 nscoord lbearing
, rbearing
;
2322 PRInt32 first
= 0, last
= 1;
2323 while (last
<= bottom
) {
2324 if (chdata
[last
].Exists()) {
2325 lbearing
= bmdata
[last
].leftBearing
;
2326 rbearing
= bmdata
[last
].rightBearing
;
2327 if (chdata
[first
].Exists()) {
2328 if (lbearing
< bmdata
[first
].leftBearing
)
2329 lbearing
= bmdata
[first
].leftBearing
;
2330 if (rbearing
> bmdata
[first
].rightBearing
)
2331 rbearing
= bmdata
[first
].rightBearing
;
2334 else if (chdata
[first
].Exists()) {
2335 lbearing
= bmdata
[first
].leftBearing
;
2336 rbearing
= bmdata
[first
].rightBearing
;
2339 NS_ERROR("Cannot stretch - All parts missing");
2340 return NS_ERROR_UNEXPECTED
;
2342 // paint the rule between the parts
2343 nsRect
rule(aRect
.x
+ lbearing
, end
[first
],
2344 rbearing
- lbearing
, start
[last
] - end
[first
]);
2345 if (!rule
.IsEmpty())
2346 aRenderingContext
.FillRect(rule
);
2351 else if (bmdata
[glue
].ascent
+ bmdata
[glue
].descent
> 0) {
2353 nsBoundingMetrics
& bm
= bmdata
[glue
];
2354 // Ensure the stride for the glue is not reduced to less than one pixel
2355 if (bm
.ascent
+ bm
.descent
>= 3 * oneDevPixel
) {
2356 // To protect against gaps, pretend the glue is smaller than it is,
2357 // in order to trim off ends and thus get a solid edge for the join.
2358 bm
.ascent
-= oneDevPixel
;
2359 bm
.descent
-= oneDevPixel
;
2362 SetFontFamily(aPresContext
, aRenderingContext
,
2363 aFont
, aGlyphTable
, chGlue
, mFamily
);
2364 nsRect clipRect
= unionRect
;
2366 for (i
= 0; i
< bottom
; ++i
) {
2367 // Make sure not to draw outside the character
2368 nscoord dy
= PR_MAX(end
[i
], aRect
.y
);
2369 nscoord fillEnd
= PR_MIN(start
[i
+1], aRect
.YMost());
2371 // exact area to fill
2372 aRenderingContext
.SetColor(NS_RGB(255,0,0));
2374 clipRect
.height
= fillEnd
- dy
;
2375 aRenderingContext
.DrawRect(clipRect
);
2378 while (dy
< fillEnd
) {
2380 clipRect
.height
= PR_MIN(bm
.ascent
+ bm
.descent
, fillEnd
- dy
);
2381 AutoPushClipRect
clip(aRenderingContext
, clipRect
);
2383 aRenderingContext
.DrawString(&chGlue
.code
, 1, dx
, dy
);
2388 // last glyph that may cross past its boundary and collide with the next
2389 nscoord height
= bm
.ascent
+ bm
.descent
;
2390 aRenderingContext
.SetColor(NS_RGB(0,255,0));
2391 aRenderingContext
.DrawRect(nsRect(dx
, dy
-bm
.ascent
, aRect
.width
, height
));
2397 for (i
= 0; i
< bottom
; ++i
) {
2398 NS_ASSERTION(end
[i
] >= start
[i
+1],
2399 "gap between parts with missing glue glyph");
2406 // paint a stretchy char by assembling glyphs horizontally
2408 nsMathMLChar::PaintHorizontally(nsPresContext
* aPresContext
,
2409 nsIRenderingContext
& aRenderingContext
,
2411 nsStyleContext
* aStyleContext
,
2412 nsGlyphTable
* aGlyphTable
,
2415 nsresult rv
= NS_OK
;
2416 // Get the device pixel size in the horizontal direction.
2417 // (This makes no effort to optimize for non-translation transformations.)
2418 nscoord oneDevPixel
= aPresContext
->AppUnitsPerDevPixel();
2420 // get metrics data to be re-used later
2422 nsGlyphCode ch
, chdata
[4];
2423 nsBoundingMetrics bmdata
[4];
2424 PRInt32 glue
, right
;
2425 nsGlyphCode chGlue
= aGlyphTable
->GlueOf(aPresContext
, this);
2426 for (PRInt32 j
= 0; j
< 4; ++j
) {
2429 ch
= aGlyphTable
->LeftOf(aPresContext
, this);
2432 ch
= aGlyphTable
->MiddleOf(aPresContext
, this);
2434 continue; // no middle
2437 ch
= aGlyphTable
->RightOf(aPresContext
, this);
2445 // empty slots are filled with the glue if it is not null
2446 if (!ch
.Exists()) ch
= chGlue
;
2447 // if (!ch.Exists()) glue is null, leave bounding metrics at 0.
2449 SetFontFamily(aPresContext
, aRenderingContext
,
2450 aFont
, aGlyphTable
, ch
, mFamily
);
2451 rv
= aRenderingContext
.GetBoundingMetrics(&ch
.code
, 1, bmdata
[i
]);
2452 if (NS_FAILED(rv
)) {
2453 NS_WARNING("GetBoundingMetrics failed");
2460 nscoord dy
= aRect
.y
+ mBoundingMetrics
.ascent
;
2461 nscoord offset
[3], start
[3], end
[3];
2462 nsRefPtr
<gfxContext
> ctx
= aRenderingContext
.ThebesContext();
2463 for (i
= 0; i
<= right
; ++i
) {
2465 const nsBoundingMetrics
& bm
= bmdata
[i
];
2467 if (0 == i
) { // left
2468 dx
= aRect
.x
- bm
.leftBearing
;
2470 else if (right
== i
) { // right
2471 dx
= aRect
.x
+ aRect
.width
- bm
.rightBearing
;
2474 dx
= aRect
.x
+ (aRect
.width
- bm
.width
)/2;
2476 // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
2477 // Do this now so that we can get the other dimensions right.
2478 // (This may not achieve much with non-rectangular transformations.)
2479 dx
= SnapToDevPixels(ctx
, oneDevPixel
, nsPoint(dx
, dy
)).x
;
2480 // abcissa passed to DrawString
2482 // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
2483 // pixel, so the bm values can include 1 row of faint pixels on each edge.
2484 // Don't rely on this pixel as it can look like a gap.
2485 start
[i
] = dx
+ bm
.leftBearing
+ oneDevPixel
; // left join
2486 end
[i
] = dx
+ bm
.rightBearing
- oneDevPixel
; // right join
2489 // If there are overlaps, then join at the mid point
2490 for (i
= 0; i
< right
; ++i
) {
2491 if (end
[i
] > start
[i
+1]) {
2492 end
[i
] = (end
[i
] + start
[i
+1]) / 2;
2493 start
[i
+1] = end
[i
];
2497 nsRect unionRect
= aRect
;
2498 unionRect
.Inflate(oneDevPixel
, oneDevPixel
);
2500 ///////////////////////////
2501 // draw left, middle, right
2502 for (i
= 0; i
<= right
; ++i
) {
2504 // glue can be null, and other parts could have been set to glue
2507 aRenderingContext
.SetColor(NS_RGB(255,0,0));
2508 aRenderingContext
.DrawRect(nsRect(start
[i
], dy
- bmdata
[i
].ascent
,
2509 end
[i
] - start
[i
], bmdata
[i
].ascent
+ bmdata
[i
].descent
));
2511 nscoord dx
= offset
[i
];
2512 nsRect clipRect
= unionRect
;
2513 // Clip at the join to get a solid edge (without overlap or gap), when
2514 // this won't change the glyph too much. If the glyph is too small to
2515 // clip then we'll overlap rather than have a gap.
2516 nscoord width
= bmdata
[i
].rightBearing
- bmdata
[i
].leftBearing
;
2518 width
* (1.0 - NS_MATHML_DELIMITER_FACTOR
) > oneDevPixel
) {
2519 if (0 == i
) { // left
2520 clipRect
.width
= end
[i
] - clipRect
.x
;
2522 else if (right
== i
) { // right
2523 clipRect
.width
-= start
[i
] - clipRect
.x
;
2524 clipRect
.x
= start
[i
];
2527 clipRect
.x
= start
[i
];
2528 clipRect
.width
= end
[i
] - start
[i
];
2531 if (!clipRect
.IsEmpty()) {
2532 AutoPushClipRect
clip(aRenderingContext
, clipRect
);
2533 SetFontFamily(aPresContext
, aRenderingContext
,
2534 aFont
, aGlyphTable
, ch
, mFamily
);
2535 aRenderingContext
.DrawString(&ch
.code
, 1, dx
, dy
);
2541 // fill the gap between left and middle, and between middle and right.
2542 if (!chGlue
.Exists()) { // null glue : draw a rule
2543 // figure out the dimensions of the rule to be drawn :
2544 // set ascent to lowest ascent among the two current successive parts.
2545 // set descent to highest descent among the two current successive parts.
2546 // this satisfies the convention used for over/underbraces, and helps
2547 // fix broken fonts.
2548 nscoord ascent
, descent
;
2549 PRInt32 first
= 0, last
= 1;
2550 while (last
<= right
) {
2551 if (chdata
[last
].Exists()) {
2552 ascent
= bmdata
[last
].ascent
;
2553 descent
= bmdata
[last
].descent
;
2554 if (chdata
[first
].Exists()) {
2555 if (ascent
> bmdata
[first
].ascent
)
2556 ascent
= bmdata
[first
].ascent
;
2557 if (descent
> bmdata
[first
].descent
)
2558 descent
= bmdata
[first
].descent
;
2561 else if (chdata
[first
].Exists()) {
2562 ascent
= bmdata
[first
].ascent
;
2563 descent
= bmdata
[first
].descent
;
2566 NS_ERROR("Cannot stretch - All parts missing");
2567 return NS_ERROR_UNEXPECTED
;
2569 // paint the rule between the parts
2570 nsRect
rule(end
[first
], dy
- ascent
,
2571 start
[last
] - end
[first
], ascent
+ descent
);
2572 if (!rule
.IsEmpty())
2573 aRenderingContext
.FillRect(rule
);
2578 else if (bmdata
[glue
].rightBearing
- bmdata
[glue
].leftBearing
> 0) {
2580 nsBoundingMetrics
& bm
= bmdata
[glue
];
2581 // Ensure the stride for the glue is not reduced to less than one pixel
2582 if (bm
.rightBearing
- bm
.leftBearing
>= 3 * oneDevPixel
) {
2583 // To protect against gaps, pretend the glue is smaller than it is,
2584 // in order to trim off ends and thus get a solid edge for the join.
2585 bm
.leftBearing
+= oneDevPixel
;
2586 bm
.rightBearing
-= oneDevPixel
;
2589 SetFontFamily(aPresContext
, aRenderingContext
,
2590 aFont
, aGlyphTable
, chGlue
, mFamily
);
2591 nsRect clipRect
= unionRect
;
2593 for (i
= 0; i
< right
; ++i
) {
2594 // Make sure not to draw outside the character
2595 nscoord dx
= PR_MAX(end
[i
], aRect
.x
);
2596 nscoord fillEnd
= PR_MIN(start
[i
+1], aRect
.XMost());
2598 // rectangles in-between that are to be filled
2599 aRenderingContext
.SetColor(NS_RGB(255,0,0));
2601 clipRect
.width
= fillEnd
- dx
;
2602 aRenderingContext
.DrawRect(clipRect
);
2605 while (dx
< fillEnd
) {
2607 clipRect
.width
= PR_MIN(bm
.rightBearing
- bm
.leftBearing
, fillEnd
- dx
);
2608 AutoPushClipRect
clip(aRenderingContext
, clipRect
);
2609 dx
-= bm
.leftBearing
;
2610 aRenderingContext
.DrawString(&chGlue
.code
, 1, dx
, dy
);
2611 dx
+= bm
.rightBearing
;
2615 // last glyph that may cross past its boundary and collide with the next
2616 nscoord width
= bm
.rightBearing
- bm
.leftBearing
;
2617 aRenderingContext
.SetColor(NS_RGB(0,255,0));
2618 aRenderingContext
.DrawRect(nsRect(dx
+ bm
.leftBearing
, aRect
.y
, width
, aRect
.height
));
2624 for (i
= 0; i
< right
; ++i
) {
2625 NS_ASSERTION(end
[i
] >= start
[i
+1],
2626 "gap between parts with missing glue glyph");