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
26 * Frederic Wang <fred.wang@free.fr>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
44 #include "nsPresContext.h"
45 #include "nsStyleContext.h"
46 #include "nsStyleConsts.h"
48 #include "nsUnicharUtils.h"
49 #include "nsIRenderingContext.h"
50 #include "gfxPlatform.h"
51 #include "nsIFontMetrics.h"
53 #include "nsIPrefBranch.h"
54 #include "nsIPrefService.h"
55 #include "nsISupportsPrimitives.h"
56 #include "nsIComponentManager.h"
57 #include "nsIPersistentProperties2.h"
58 #include "nsIServiceManager.h"
59 #include "nsIObserverService.h"
60 #include "nsIObserver.h"
61 #include "nsNetUtil.h"
63 #include "nsILookAndFeel.h"
64 #include "nsIDeviceContext.h"
65 #include "nsCSSRendering.h"
66 #include "prprf.h" // For PR_snprintf()
68 #if ALERT_MISSING_FONTS
69 #include "nsIStringBundle.h"
71 #include "nsDisplayList.h"
73 #include "nsMathMLOperators.h"
74 #include "nsMathMLChar.h"
76 //#define SHOW_BORDERS 1
77 //#define NOISY_SEARCH 1
79 // -----------------------------------------------------------------------------------
80 static const PRUnichar kSpaceCh
= PRUnichar(' ');
81 static const nsGlyphCode kNullGlyph
= {0, 0};
82 typedef enum {eExtension_base
, eExtension_variants
, eExtension_parts
}
83 nsMathfontPrefExtension
;
85 // -----------------------------------------------------------------------------------
86 // nsGlyphTable is a class that provides an interface for accessing glyphs
87 // of stretchy chars. It acts like a table that stores the variants of bigger
88 // sizes (if any) and the partial glyphs needed to build extensible symbols.
89 // An instance of nsGlyphTable is associated to one primary font. Extra glyphs
90 // can be taken in other additional fonts when stretching certain characters.
91 // These supplementary fonts are referred to as "external" fonts to the table.
93 // A char for which nsGlyphTable::Has(aChar) is true means that the table
94 // contains some glyphs (bigger and/or partial) that can be used to render
95 // the char. Bigger sizes (if any) of the char can then be retrieved with
96 // BigOf(aSize). Partial glyphs can be retrieved with TopOf(), GlueOf(), etc.
98 // A table consists of "nsGlyphCode"s which are viewed either as Unicode
99 // points or as direct glyph indices, depending on the type of the table.
100 // XXX The latter is not yet supported.
102 // General format of MathFont Property Files from which glyph data are retrieved:
103 // -----------------------------------------------------------------------------------
104 // Each font should have its set of glyph data. For example, the glyph data for
105 // the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties"
106 // and "mathfontMTExtra.properties", respectively. The mathfont property file is a
107 // set of all the stretchy MathML characters that can be rendered with that font
108 // using larger and/or partial glyphs. The entry of each stretchy character in the
109 // mathfont property file gives, in that order, the 4 partial glyphs: Top (or Left),
110 // Middle, Bottom (or Right), Glue; and the variants of bigger sizes (if any).
111 // A position that is not relevant to a particular character is indicated there
112 // with the UNICODE REPLACEMENT CHARACTER 0xFFFD.
113 // Characters that need to be built recursively from other characters are said
114 // to be composite. For example, chars like over/underbrace in CMEX10 have to
115 // be built from two half stretchy chars and joined in the middle (TeXbook, p.225).
116 // Such chars are handled in a special manner by the nsMathMLChar class, which allows
117 // several (2 or more) child chars to be composed in order to render another char.
118 // To specify such chars, their list of glyphs in the property file should be given
119 // as space-separated segments of glyphs. Each segment gives the 4 partial
120 // glyphs with which to build the child char that will be joined with its other
121 // siblings. In this code, when this situation happens (see the detailed description
122 // of Stretch() below), the original char (referred to as "parent") creates a
123 // singly-linked list of child chars, asking them to stretch in an equally divided
124 // space. The nsGlyphTable embeds the necessary logic to guarantee correctness in a
125 // recursive stretch (and in the use of TopOf(), GlueOf(), etc) on these child chars.
126 // -----------------------------------------------------------------------------------
128 #define NS_TABLE_TYPE_UNICODE 0
129 #define NS_TABLE_TYPE_GLYPH_INDEX 1
131 #define NS_TABLE_STATE_ERROR -1
132 #define NS_TABLE_STATE_EMPTY 0
133 #define NS_TABLE_STATE_READY 1
135 // helper to check if a font is installed
137 CheckFontExistence(nsPresContext
* aPresContext
, const nsString
& aFontName
)
140 nsAutoString localName
;
141 nsIDeviceContext
*deviceContext
= aPresContext
->DeviceContext();
142 deviceContext
->GetLocalFontName(aFontName
, localName
, aliased
);
143 // XXXkt CheckFontExistence always returns NS_OK.
144 PRBool rv
= (aliased
|| (NS_OK
== deviceContext
->CheckFontExistence(localName
)));
145 // (see bug 35824 for comments about the aliased localName)
149 #if ALERT_MISSING_FONTS
150 // alert the user if some of the needed MathML fonts are not installed.
151 // it is non-modal (i.e., it doesn't wait for input from the user)
153 AlertMissingFonts(nsString
& aMissingFonts
)
155 nsCOMPtr
<nsIStringBundleService
> sbs
=
156 mozilla::services::GetStringBundleService();
160 nsCOMPtr
<nsIStringBundle
> sb
;
161 sbs
->CreateBundle("resource://gre/res/fonts/mathfont.properties", getter_AddRefs(sb
));
165 nsXPIDLString title
, message
;
166 const PRUnichar
* strings
[] = { aMissingFonts
.get() };
167 sb
->GetStringFromName(NS_LITERAL_STRING("mathfont_missing_dialog_title").get(), getter_Copies(title
));
168 sb
->FormatStringFromName(NS_LITERAL_STRING("mathfont_missing_dialog_message").get(),
169 strings
, 1, getter_Copies(message
));
171 // XXX Bug 309090 - could show a notification bar here. Bug 563114 removed
172 // the nsINonBlockingAlertService interface that was previously used here.
176 // helper to trim off comments from data in a MathFont Property File
178 Clean(nsString
& aValue
)
180 // chop the trailing # comment portion if any ...
181 PRInt32 comment
= aValue
.RFindChar('#');
182 if (comment
> 0) aValue
.Truncate(comment
);
183 aValue
.CompressWhitespace();
186 // helper to load a MathFont Property File
188 LoadProperties(const nsString
& aName
,
189 nsCOMPtr
<nsIPersistentProperties
>& aProperties
)
192 uriStr
.AssignLiteral("resource://gre/res/fonts/mathfont");
193 uriStr
.Append(aName
);
194 uriStr
.StripWhitespace(); // that may come from aName
195 uriStr
.AppendLiteral(".properties");
196 return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties
),
197 NS_ConvertUTF16toUTF8(uriStr
));
200 // -----------------------------------------------------------------------------------
204 explicit nsGlyphTable(const nsString
& aPrimaryFontName
)
205 : mType(NS_TABLE_TYPE_UNICODE
),
206 mFontName(1), // ensure space for primary font name.
207 mState(NS_TABLE_STATE_EMPTY
),
210 MOZ_COUNT_CTOR(nsGlyphTable
);
211 mFontName
.AppendElement(aPrimaryFontName
);
214 ~nsGlyphTable() // not a virtual destructor: this class is not intended to be subclassed
216 MOZ_COUNT_DTOR(nsGlyphTable
);
219 const nsAString
& PrimaryFontName() const
224 const nsAString
& FontNameFor(const nsGlyphCode
& aGlyphCode
) const
226 return mFontName
[aGlyphCode
.font
];
229 // True if this table contains some glyphs (variants and/or parts)
230 // or contains child chars that can be used to render this char
231 PRBool
Has(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
);
233 // True if this table contains variants of larger sizes to render this char
234 PRBool
HasVariantsOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
);
236 // True if this table contains parts (or composite parts) to render this char
237 PRBool
HasPartsOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
);
239 // True if aChar is to be assembled from other child chars in this table
240 PRBool
IsComposite(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
);
242 // The number of child chars to assemble in order to render aChar
243 PRInt32
ChildCountOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
);
245 // Getters for the parts
246 nsGlyphCode
TopOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
) {
247 return ElementAt(aPresContext
, aChar
, 0);
249 nsGlyphCode
MiddleOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
) {
250 return ElementAt(aPresContext
, aChar
, 1);
252 nsGlyphCode
BottomOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
) {
253 return ElementAt(aPresContext
, aChar
, 2);
255 nsGlyphCode
GlueOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
) {
256 return ElementAt(aPresContext
, aChar
, 3);
258 nsGlyphCode
BigOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
, PRInt32 aSize
) {
259 return ElementAt(aPresContext
, aChar
, 4 + aSize
);
261 nsGlyphCode
LeftOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
) {
262 return ElementAt(aPresContext
, aChar
, 0);
264 nsGlyphCode
RightOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
) {
265 return ElementAt(aPresContext
, aChar
, 2);
269 nsGlyphCode
ElementAt(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
, PRUint32 aPosition
);
271 // The type is either NS_TABLE_TYPE_UNICODE or NS_TABLE_TYPE_GLYPH_INDEX
274 // mFontName[0] is the primary font associated to this table. The others
275 // are possible "external" fonts for glyphs not in the primary font
276 // but which are needed to stretch certain characters in the table
277 nsTArray
<nsString
> mFontName
;
279 // Tri-state variable for error/empty/ready
282 // The set of glyph data in this table, as provided by the MathFont Property File
283 nsCOMPtr
<nsIPersistentProperties
> mGlyphProperties
;
285 // For speedy re-use, we always cache the last data used in the table.
286 // mCharCache is the Unicode point of the last char that was queried in this
287 // table. mGlyphCache is a buffer containing the glyph data associated to
288 // that char. For a property line 'key = value' in the MathFont Property File,
289 // mCharCache will retain the 'key' -- which is a Unicode point, while mGlyphCache
290 // will retain the 'value', which is a consecutive list of nsGlyphCodes, i.e.,
291 // the pairs of 'code@font' needed by the char -- in which 'code@0' can be specified
292 // without the optional '@0'. However, to ease subsequent processing, mGlyphCache
293 // excludes the '@' symbol and explicitly inserts all optional '0' that indicates
294 // the primary font identifier. Specifically therefore, the k-th glyph is
295 // characterized by :
296 // 1) mGlyphCache[2*k] : its Unicode point (or glyph index -- depending on mType),
297 // 2) mGlyphCache[2*k+1] : the numeric identifier of the font where it comes from.
298 // A font identifier of '0' means the default primary font associated to this
299 // table. Other digits map to the "external" fonts that may have been specified
300 // in the MathFont Property File.
301 nsString mGlyphCache
;
302 PRUnichar mCharCache
;
306 nsGlyphTable::ElementAt(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
, PRUint32 aPosition
)
308 if (mState
== NS_TABLE_STATE_ERROR
) return kNullGlyph
;
309 // Load glyph properties if this is the first time we have been here
310 if (mState
== NS_TABLE_STATE_EMPTY
) {
311 nsresult rv
= LoadProperties(mFontName
[0], mGlyphProperties
);
313 nsCAutoString uriStr
;
314 uriStr
.AssignLiteral("resource://gre/res/fonts/mathfont");
315 LossyAppendUTF16toASCII(mFontName
[0], uriStr
);
316 uriStr
.StripWhitespace(); // that may come from mFontName
317 uriStr
.AppendLiteral(".properties");
318 printf("Loading %s ... %s\n",
320 (NS_FAILED(rv
)) ? "Failed" : "Done");
323 mState
= NS_TABLE_STATE_ERROR
; // never waste time with this table again
326 mState
= NS_TABLE_STATE_READY
;
328 // see if there are external fonts needed for certain chars in this table
331 for (PRInt32 i
= 1; ; i
++) {
332 key
.AssignLiteral("external.");
333 key
.AppendInt(i
, 10);
334 rv
= mGlyphProperties
->GetStringProperty(key
, value
);
335 if (NS_FAILED(rv
)) break;
337 mFontName
.AppendElement(value
); // i.e., mFontName[i] holds this font name
341 // If aChar is a child char to be used by a parent composite char, make
342 // sure that it is really attached to this table
343 if (aChar
->mParent
&& (aChar
->mGlyphTable
!= this)) return kNullGlyph
;
345 // Update our cache if it is not associated to this character
346 PRUnichar uchar
= aChar
->mData
[0];
347 if (mCharCache
!= uchar
) {
348 // The key in the property file is interpreted as ASCII and kept
350 char key
[10]; PR_snprintf(key
, sizeof(key
), "\\u%04X", uchar
);
352 nsresult rv
= mGlyphProperties
->GetStringProperty(nsDependentCString(key
), value
);
353 if (NS_FAILED(rv
)) return kNullGlyph
;
355 // See if this char uses external fonts; e.g., if the 2nd glyph is taken from the
356 // external font '1', the property line looks like \uNNNN = \uNNNN\uNNNN@1\uNNNN.
357 // This is where mGlyphCache is pre-processed to explicitly store all glyph codes
358 // as combined pairs of 'code@font', excluding the '@' separator. This means that
359 // mGlyphCache[2*k] will later be rendered with mFontName[mGlyphCache[2*k+1]]
360 // Note: font identifier is internally an ASCII digit to avoid the null char issue
362 PRInt32 length
= value
.Length();
363 PRInt32 i
= 0; // index in value
364 PRInt32 j
= 0; // part/variant index
366 PRUnichar code
= value
[i
];
369 // see if we are at the beginning of a child char
370 if (code
== kSpaceCh
) {
371 // reset the annotation indicator to be 0 for the next code point
374 #if 0 // If we want this then the nsGlyphTableList must be declared
375 // or the UnicodeTable could be made a global.
376 // See if this code point is an *indirect reference* to the Unicode
377 // table and lookup the code there.
378 else if (code
== PRUnichar(0xF8FF) && gGlyphTableList
&&
379 this != &gGlyphTableList
->mUnicodeTable
) {
380 code
= gGlyphTableList
->mUnicodeTable
.
381 ElementAt(aPresContext
, aChar
, aPosition
).code
;
383 // see if this code point is a *direct reference* to
384 // the Unicode table, and lookup the [TLMBRG1-9] position for code.
385 else if ((i
+1 < length
) && (value
[i
] == PRUnichar('.'))) {
387 // Need to implement this if we want it:
388 // Set (new) code from the value[i] position for (current) code.
394 // See if an external font is needed for the code point.
395 // Limit of 9 external fonts
396 if (i
+1 < length
&& value
[i
] == PRUnichar('@') &&
397 value
[i
+1] >= PRUnichar('0') && value
[i
+1] <= PRUnichar('9')) {
399 font
= value
[i
] - '0';
401 if (font
>= mFontName
.Length()) {
402 NS_ERROR("Nonexistent font referenced in glyph table");
405 // The char cannot be handled if this font is not installed
406 if (!mFontName
[font
].Length() || !CheckFontExistence(aPresContext
, mFontName
[font
])) {
414 // update our cache with the new settings
415 mGlyphCache
.Assign(buffer
);
419 // If aChar is a composite char, only its children are allowed
420 // to use its glyphs in this table, i.e., the parent char itself
421 // is disabled and cannot be stretched directly with these glyphs.
422 // This guarantees a coherent behavior in Stretch().
423 if (!aChar
->mParent
&& (kNotFound
!= mGlyphCache
.FindChar(kSpaceCh
))) {
427 // If aChar is a child char, the index of the glyph is relative to
428 // the offset of the list of glyphs corresponding to the child char.
430 PRUint32 length
= mGlyphCache
.Length();
431 if (aChar
->mParent
) {
432 nsMathMLChar
* child
= aChar
->mParent
->mSibling
;
433 // XXXkt composite chars can't have size variants
434 while (child
&& (child
!= aChar
)) {
435 offset
+= 5; // skip the 4 partial glyphs + the whitespace separator
436 child
= child
->mSibling
;
438 length
= 2*(offset
+ 4); // stay confined in the 4 partial glyphs of this child
440 PRUint32 index
= 2*(offset
+ aPosition
); // 2* is to account for the code@font pairs
441 if (index
+1 >= length
) return kNullGlyph
;
443 ch
.code
= mGlyphCache
.CharAt(index
);
444 ch
.font
= mGlyphCache
.CharAt(index
+ 1);
445 return (ch
.code
== PRUnichar(0xFFFD)) ? kNullGlyph
: ch
;
449 nsGlyphTable::IsComposite(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
)
451 // there is only one level of recursion in our model. a child
452 // cannot be composite because it cannot have its own children
453 if (aChar
->mParent
) return PR_FALSE
;
454 // shortcut to sync the cache with this char...
455 mCharCache
= 0; mGlyphCache
.Truncate(); ElementAt(aPresContext
, aChar
, 0);
456 // the cache remained empty if the char wasn't found in this table
457 if (8 >= mGlyphCache
.Length()) return PR_FALSE
;
458 // the lists of glyphs of a composite char are space-separated
459 return (kSpaceCh
== mGlyphCache
.CharAt(8));
463 nsGlyphTable::ChildCountOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
)
465 // this will sync the cache as well ...
466 if (!IsComposite(aPresContext
, aChar
)) return 0;
467 // the lists of glyphs of a composite char are space-separated
468 return 1 + mGlyphCache
.CountChar(kSpaceCh
);
472 nsGlyphTable::Has(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
)
474 return HasVariantsOf(aPresContext
, aChar
) || HasPartsOf(aPresContext
, aChar
);
478 nsGlyphTable::HasVariantsOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
)
480 //XXXkt all variants must be in the same file as size 1
481 return BigOf(aPresContext
, aChar
, 1).Exists();
485 nsGlyphTable::HasPartsOf(nsPresContext
* aPresContext
, nsMathMLChar
* aChar
)
487 return GlueOf(aPresContext
, aChar
).Exists() ||
488 TopOf(aPresContext
, aChar
).Exists() ||
489 BottomOf(aPresContext
, aChar
).Exists() ||
490 MiddleOf(aPresContext
, aChar
).Exists() ||
491 IsComposite(aPresContext
, aChar
);
494 // -----------------------------------------------------------------------------------
495 // This is the list of all the applicable glyph tables.
496 // We will maintain a single global instance that will only reveal those
497 // glyph tables that are associated to fonts currently installed on the
498 // user' system. The class is an XPCOM shutdown observer to allow us to
499 // free its allocated data at shutdown
501 class nsGlyphTableList
: public nsIObserver
507 nsGlyphTable mUnicodeTable
;
510 : mUnicodeTable(NS_LITERAL_STRING("Unicode"))
512 MOZ_COUNT_CTOR(nsGlyphTableList
);
515 virtual ~nsGlyphTableList()
517 MOZ_COUNT_DTOR(nsGlyphTableList
);
520 nsresult
Initialize();
523 // Add a glyph table in the list, return the new table that was added
525 AddGlyphTable(const nsString
& aPrimaryFontName
);
527 // Find a glyph table in the list that has a glyph for the given char
529 GetGlyphTableFor(nsPresContext
* aPresContext
,
530 nsMathMLChar
* aChar
);
532 // Find the glyph table in the list corresponding to the given font family.
534 GetGlyphTableFor(const nsAString
& aFamily
);
537 nsGlyphTable
* TableAt(PRInt32 aIndex
) {
538 return &mTableList
.ElementAt(aIndex
);
541 return mTableList
.Length();
544 // List of glyph tables;
545 nsTArray
<nsGlyphTable
> mTableList
;
548 NS_IMPL_ISUPPORTS1(nsGlyphTableList
, nsIObserver
)
550 // -----------------------------------------------------------------------------------
551 // Here is the global list of applicable glyph tables that we will be using
552 static nsGlyphTableList
* gGlyphTableList
= nsnull
;
554 static PRBool gInitialized
= PR_FALSE
;
556 // XPCOM shutdown observer
558 nsGlyphTableList::Observe(nsISupports
* aSubject
,
560 const PRUnichar
* someData
)
566 // Add an observer to XPCOM shutdown so that we can free our data at shutdown
568 nsGlyphTableList::Initialize()
570 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
572 return NS_ERROR_FAILURE
;
574 nsresult rv
= obs
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, PR_FALSE
);
575 NS_ENSURE_SUCCESS(rv
, rv
);
580 // Remove our observer and free the memory that were allocated for us
582 nsGlyphTableList::Finalize()
584 // Remove our observer from the observer service
586 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
588 rv
= obs
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
590 rv
= NS_ERROR_FAILURE
;
592 gInitialized
= PR_FALSE
;
593 // our oneself will be destroyed when our |Release| is called by the observer
598 nsGlyphTableList::AddGlyphTable(const nsString
& aPrimaryFontName
)
600 // See if there is already a special table for this family.
601 nsGlyphTable
* glyphTable
= GetGlyphTableFor(aPrimaryFontName
);
602 if (glyphTable
!= &mUnicodeTable
)
606 glyphTable
= mTableList
.AppendElement(aPrimaryFontName
);
611 nsGlyphTableList::GetGlyphTableFor(nsPresContext
* aPresContext
,
614 if (mUnicodeTable
.Has(aPresContext
, aChar
))
615 return &mUnicodeTable
;
618 for (i
= 0; i
< Count(); i
++) {
619 nsGlyphTable
* glyphTable
= TableAt(i
);
620 if (glyphTable
->Has(aPresContext
, aChar
)) {
628 nsGlyphTableList::GetGlyphTableFor(const nsAString
& aFamily
)
630 for (PRInt32 i
= 0; i
< Count(); i
++) {
631 nsGlyphTable
* glyphTable
= TableAt(i
);
632 const nsAString
& fontName
= glyphTable
->PrimaryFontName();
633 // TODO: would be nice to consider StripWhitespace and other aliasing
634 if (fontName
.Equals(aFamily
, nsCaseInsensitiveStringComparator())) {
638 // Fall back to default Unicode table
639 return &mUnicodeTable
;
642 // -----------------------------------------------------------------------------------
644 // retrieve a pref value set by the user
646 GetPrefValue(nsIPrefBranch
* aPrefBranch
, const char* aPrefKey
, nsString
& aPrefValue
)
648 aPrefValue
.Truncate();
650 nsCOMPtr
<nsISupportsString
> prefString
;
651 aPrefBranch
->GetComplexValue(aPrefKey
,
652 NS_GET_IID(nsISupportsString
),
653 getter_AddRefs(prefString
));
655 prefString
->GetData(aPrefValue
);
658 return !aPrefValue
.IsEmpty();
661 // Lookup the preferences:
662 // "font.mathfont-family.\uNNNN.base" -- fonts for the base size
663 // "font.mathfont-family.\uNNNN.variants" -- fonts for larger glyphs
664 // "font.mathfont-family.\uNNNN.parts" -- fonts for partial glyphs
665 // Given the char code and mode of stretch, retrieve the preferred extension
668 GetFontExtensionPref(nsIPrefBranch
* aPrefBranch
, PRUnichar aChar
,
669 nsMathfontPrefExtension aExtension
, nsString
& aValue
)
671 // initialize OUT param
674 // We are going to try two keys because some users specify their pref as
675 // user_pref("font.mathfont-family.\uNNNN.base", "...") rather than
676 // user_pref("font.mathfont-family.\\uNNNN.base", "...").
677 // The \uNNNN in the former is interpreted as an UTF16 escape sequence by
678 // JavaScript and is converted to the internal UTF8 string that JavaScript uses.
679 // But clueless users who are not savvy of JavaScript have no idea as to what
680 // is going on and are baffled as to why their pref setting is not working.
681 // So to save countless explanations, we are going to support both keys.
683 static const char* kMathFontPrefix
= "font.mathfont-family.";
685 nsCAutoString extension
;
688 case eExtension_base
:
689 extension
.AssignLiteral(".base");
691 case eExtension_variants
:
692 extension
.AssignLiteral(".variants");
694 case eExtension_parts
:
695 extension
.AssignLiteral(".parts");
703 key
.AssignASCII(kMathFontPrefix
);
705 PR_snprintf(ustr
, sizeof(ustr
), "\\u%04X", aChar
);
707 key
.Append(extension
);
709 nsCAutoString alternateKey
;
710 alternateKey
.AssignASCII(kMathFontPrefix
);
711 NS_ConvertUTF16toUTF8
tmp(&aChar
, 1);
712 alternateKey
.Append(tmp
);
713 alternateKey
.Append(extension
);
715 return GetPrefValue(aPrefBranch
, key
.get(), aValue
) ||
716 GetPrefValue(aPrefBranch
, alternateKey
.get(), aValue
);
719 #if ALERT_MISSING_FONTS
720 struct MathFontEnumContext
{
721 nsPresContext
* mPresContext
;
722 nsString
* mMissingFamilyList
;
727 MathFontEnumCallback(const nsString
& aFamily
, PRBool aGeneric
, void *aData
)
729 #if ALERT_MISSING_FONTS
730 // check if the font is missing
731 MathFontEnumContext
* context
= (MathFontEnumContext
*)aData
;
732 nsPresContext
* presContext
= context
->mPresContext
;
733 nsString
* missingFamilyList
= context
->mMissingFamilyList
;
734 if (!CheckFontExistence(presContext
, aFamily
)) {
736 // XXX In principle, the mathfont-family list in the mathfont.properties file
737 // is customizable depending on the platform. For now, this is here since there
738 // is no need to alert Linux users about TrueType fonts specific to Windows.
739 if (aFamily
.LowerCaseEqualsLiteral("mt extra"))
740 return PR_TRUE
; // continue to try other fonts
742 if (!missingFamilyList
->IsEmpty()) {
743 missingFamilyList
->AppendLiteral(", ");
745 missingFamilyList
->Append(aFamily
);
749 if (!gGlyphTableList
->AddGlyphTable(aFamily
))
750 return PR_FALSE
; // stop in low-memory situations
751 return PR_TRUE
; // don't stop
755 InitGlobals(nsPresContext
* aPresContext
)
757 NS_ASSERTION(!gInitialized
, "Error -- already initialized");
758 gInitialized
= PR_TRUE
;
760 // Allocate the placeholders for the preferred parts and variants
761 nsresult rv
= NS_ERROR_OUT_OF_MEMORY
;
762 gGlyphTableList
= new nsGlyphTableList();
763 if (gGlyphTableList
) {
764 rv
= gGlyphTableList
->Initialize();
767 delete gGlyphTableList
;
768 gGlyphTableList
= nsnull
;
773 The gGlyphTableList has been successfully registered as a shutdown observer.
774 It will be deleted at shutdown, even if a failure happens below.
779 nsCOMPtr
<nsIPersistentProperties
> mathfontProp
;
780 nsCOMPtr
<nsIPrefBranch
> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID
));
782 // Add the math fonts in the gGlyphTableList in order of preference ...
783 // Note: we only load font-names at this stage. The actual glyph tables will
784 // be loaded lazily (see nsGlyphTable::ElementAt()).
786 // Load the "mathfont.properties" file
788 rv
= LoadProperties(value
, mathfontProp
);
789 if (NS_FAILED(rv
)) return rv
;
791 // Get the list of mathfonts having special glyph tables to be used for
792 // stretchy characters.
793 // We just want to iterate over the font-family list using the
794 // callback mechanism that nsFont has...
795 nsFont
font("", 0, 0, 0, 0, 0, 0);
796 NS_NAMED_LITERAL_CSTRING(defaultKey
, "font.mathfont-glyph-tables");
797 rv
= mathfontProp
->GetStringProperty(defaultKey
, font
.name
);
798 if (NS_FAILED(rv
)) return rv
;
800 // Parse the font list and append an entry for each family to gGlyphTableList
801 nsAutoString missingFamilyList
;
803 #if ALERT_MISSING_FONTS
804 // We don't really need all these fonts, so alerting on some missing is not
805 // right. The best place to alert would be Stretch when we notice that we
806 // can't get the char we want. In this way the user would not be alerted
807 // unnecessarily when the document contains only simple math. The alert
808 // also needs a "don't tell me again box".
809 MathFontEnumContext context
= {aPresContext
, &missingFamilyList
};
810 font
.EnumerateFamilies(MathFontEnumCallback
, &context
);
811 // alert the user if some of the expected fonts are missing
812 if (!missingFamilyList
.IsEmpty()) {
813 AlertMissingFonts(missingFamilyList
);
816 font
.EnumerateFamilies(MathFontEnumCallback
, nsnull
);
821 // -----------------------------------------------------------------------------------
822 // And now the implementation of nsMathMLChar
825 nsMathMLChar::GetStyleContext() const
827 NS_ASSERTION(!mParent
, "invalid call - not allowed for child chars");
828 NS_ASSERTION(mStyleContext
, "chars should always have style context");
829 return mStyleContext
;
834 nsMathMLChar::SetStyleContext(nsStyleContext
* aStyleContext
)
836 NS_ASSERTION(!mParent
, "invalid call - not allowed for child chars");
837 NS_PRECONDITION(aStyleContext
, "null ptr");
838 if (aStyleContext
!= mStyleContext
) {
840 mStyleContext
->Release();
842 mStyleContext
= aStyleContext
;
843 aStyleContext
->AddRef();
845 // Sync the pointers of child chars.
846 nsMathMLChar
* child
= mSibling
;
848 child
->mStyleContext
= mStyleContext
;
849 child
= child
->mSibling
;
856 nsMathMLChar::SetData(nsPresContext
* aPresContext
,
859 NS_ASSERTION(!mParent
, "invalid call - not allowed for child chars");
861 InitGlobals(aPresContext
);
864 // some assumptions until proven otherwise
865 // note that mGlyph is not initialized
866 mDirection
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
867 mBoundingMetrics
.Clear();
868 mGlyphTable
= nsnull
;
869 // check if stretching is applicable ...
870 if (gGlyphTableList
&& (1 == mData
.Length())) {
871 mDirection
= nsMathMLOperators::GetStretchyDirection(mData
);
872 // default tentative table (not the one that is necessarily going
874 mGlyphTable
= gGlyphTableList
->GetGlyphTableFor(aPresContext
, this);
878 // -----------------------------------------------------------------------------------
881 @param aContainerSize - suggested size for the stretched char
882 @param aDesiredStretchSize - OUT parameter. The desired size
883 after stretching. If no stretching is done, the output will
884 simply give the base size.
888 The Stretch() method first looks for a glyph of appropriate
889 size; If a glyph is found, it is cached by this object and
890 its size is returned in aDesiredStretchSize. The cached
891 glyph will then be used at the painting stage.
892 If no glyph of appropriate size is found, a search is made
893 to see if the char can be built by parts.
896 A character gets stretched through the following pipeline :
898 1) If the base size of the char is sufficient to cover the
899 container' size, we use that. If not, it will still be
900 used as a fallback if the other stages in the pipeline fail.
902 a) The base size, the parts and the variants of a char can
903 be in different fonts. For eg., the base size for '(' should
904 come from a normal ascii font if CMEX10 is used, since CMEX10
905 only contains the stretched versions. Hence, there are two
906 style contexts in use throughout the process. The leaf style
907 context of the char holds fonts with which to try to stretch
908 the char. The parent style context of the char contains fonts
909 for normal rendering. So the parent context is the one used
910 to get the initial base size at the start of the pipeline.
911 b) For operators that can be largeop's in display mode,
912 we will skip the base size even if it fits, so that
913 the next stage in the pipeline is given a chance to find
914 a largeop variant. If the next stage fails, we fallback
917 2) We search for the first larger variant of the char that fits the
918 container' size. We first search for larger variants using the glyph
919 table corresponding to the first existing font specified in the list of
920 stretchy fonts held by the leaf style context (from -moz-math-stretchy in
921 mathml.css). Generic fonts are resolved by the preference
922 "font.mathfont-family".
924 a) the largeop and display settings determine the starting
925 size when we do the above search, regardless of whether
926 smaller variants already fit the container' size.
927 b) if it is a largeopOnly request (i.e., a displaystyle operator
928 with largeop=true and stretchy=false), we break after finding
929 the first starting variant, regardless of whether that
930 variant fits the container's size.
932 3) If a variant of appropriate size wasn't found, we see if the char
933 can be built by parts using the same glyph table.
935 a) Certain chars like over/underbrace in CMEX10 have to be built
936 from two half stretchy chars and joined in the middle. Such
937 chars are handled in a special manner. When this situation is
938 detected, the initial char (referred to as "parent") creates a
939 singly-linked list of child chars, asking them to stretch in
940 a divided space. A convention is used in the setup of
941 nsGlyphTable to express that a composite parent char can be built
943 b) There are some chars that have no middle and glue glyphs. For
944 such chars, the parts need to be joined using the rule.
945 By convention (TeXbook p.225), the descent of the parts is
946 zero while their ascent gives the thickness of the rule that
947 should be used to join them.
949 4) If a match was not found in that glyph table, repeat from 2 to search the
950 ordered list of stretchy fonts for the first font with a glyph table that
951 provides a fit to the container size. If no fit is found, the closest fit
955 When the pipeline completes successfully, the desired size of the
956 stretched char can actually be slightly larger or smaller than
957 aContainerSize. But it is the responsibility of the caller to
958 account for the spacing when setting aContainerSize, and to leave
959 any extra margin when placing the stretched char.
961 // -----------------------------------------------------------------------------------
964 // plain TeX settings (TeXbook p.152)
965 #define NS_MATHML_DELIMITER_FACTOR 0.901f
966 #define NS_MATHML_DELIMITER_SHORTFALL_POINTS 5.0f
969 IsSizeOK(nsPresContext
* aPresContext
, nscoord a
, nscoord b
, PRUint32 aHint
)
971 // Normal: True if 'a' is around +/-10% of the target 'b' (10% is
972 // 1-DelimiterFactor). This often gives a chance to the base size to
973 // win, especially in the context of <mfenced> without tall elements
974 // or in sloppy markups without protective <mrow></mrow>
976 (aHint
& NS_STRETCH_NORMAL
)
977 && PRBool(float(PR_ABS(a
- b
))
978 < (1.0f
- NS_MATHML_DELIMITER_FACTOR
) * float(b
));
979 // Nearer: True if 'a' is around max{ +/-10% of 'b' , 'b' - 5pt },
980 // as documented in The TeXbook, Ch.17, p.152.
981 // i.e. within 10% and within 5pt
982 PRBool isNearer
= PR_FALSE
;
983 if (aHint
& (NS_STRETCH_NEARER
| NS_STRETCH_LARGEOP
)) {
984 float c
= NS_MAX(float(b
) * NS_MATHML_DELIMITER_FACTOR
,
985 float(b
) - nsPresContext::CSSPointsToAppUnits(NS_MATHML_DELIMITER_SHORTFALL_POINTS
));
986 isNearer
= PRBool(float(PR_ABS(b
- a
)) <= (float(b
) - c
));
988 // Smaller: Mainly for transitory use, to compare two candidate
991 (aHint
& NS_STRETCH_SMALLER
)
992 && PRBool((float(a
) >= (NS_MATHML_DELIMITER_FACTOR
* float(b
)))
994 // Larger: Critical to the sqrt code to ensure that the radical
995 // size is tall enough
997 (aHint
& (NS_STRETCH_LARGER
| NS_STRETCH_LARGEOP
))
999 return (isNormal
|| isSmaller
|| isNearer
|| isLarger
);
1003 IsSizeBetter(nscoord a
, nscoord olda
, nscoord b
, PRUint32 aHint
)
1007 if (aHint
& (NS_STRETCH_LARGER
| NS_STRETCH_LARGEOP
))
1008 return (a
>= olda
) ? (olda
< b
) : (a
>= b
);
1009 if (aHint
& NS_STRETCH_SMALLER
)
1010 return (a
<= olda
) ? (olda
> b
) : (a
<= b
);
1012 // XXXkt prob want log scale here i.e. 1.5 is closer to 1 than 0.5
1013 return PR_ABS(a
- b
) < PR_ABS(olda
- b
);
1016 // We want to place the glyphs even when they don't fit at their
1017 // full extent, i.e., we may clip to tolerate a small amount of
1018 // overlap between the parts. This is important to cater for fonts
1021 ComputeSizeFromParts(nsPresContext
* aPresContext
,
1022 nsGlyphCode
* aGlyphs
,
1024 nscoord aTargetSize
)
1026 enum {first
, middle
, last
, glue
};
1027 // Add the parts that cannot be left out.
1029 for (PRInt32 i
= first
; i
<= last
; i
++) {
1030 if (aGlyphs
[i
] != aGlyphs
[glue
]) {
1035 // Determine how much is used in joins
1036 nscoord oneDevPixel
= aPresContext
->AppUnitsPerDevPixel();
1037 PRInt32 joins
= aGlyphs
[middle
] == aGlyphs
[glue
] ? 1 : 2;
1039 // Pick a maximum size using a maximum number of glue glyphs that we are
1040 // prepared to draw for one character.
1041 const PRInt32 maxGlyphs
= 1000;
1043 // This also takes into account the fact that, if the glue has no size,
1044 // then the character can't be lengthened.
1045 nscoord maxSize
= sum
- 2 * joins
* oneDevPixel
+ maxGlyphs
* aSizes
[glue
];
1046 if (maxSize
< aTargetSize
)
1047 return maxSize
; // settle with the maximum size
1049 // Get the minimum allowable size using some flex.
1050 nscoord minSize
= NSToCoordRound(NS_MATHML_DELIMITER_FACTOR
* sum
);
1052 if (minSize
> aTargetSize
)
1053 return minSize
; // settle with the minimum size
1055 // Fill-up the target area
1059 // Insert aFallbackFamilies before the first generic family in or at the end
1060 // of a CSS aFontName.
1062 AddFallbackFonts(nsAString
& aFontName
, const nsAString
& aFallbackFamilies
)
1064 if (aFallbackFamilies
.IsEmpty())
1067 if (aFontName
.IsEmpty()) {
1071 static const PRUnichar kSingleQuote
= PRUnichar('\'');
1072 static const PRUnichar kDoubleQuote
= PRUnichar('\"');
1073 static const PRUnichar kComma
= PRUnichar(',');
1075 const PRUnichar
*p_begin
, *p_end
;
1076 aFontName
.BeginReading(p_begin
);
1077 aFontName
.EndReading(p_end
);
1079 const PRUnichar
*p
= p_begin
;
1080 const PRUnichar
*p_name
= nsnull
;
1082 while (nsCRT::IsAsciiSpace(*p
))
1087 if (*p
== kSingleQuote
|| *p
== kDoubleQuote
) {
1088 // quoted font family
1089 PRUnichar quoteMark
= *p
;
1093 // XXX What about CSS character escapes?
1094 while (*p
!= quoteMark
)
1098 while (++p
!= p_end
&& *p
!= kComma
)
1102 // unquoted font family
1103 const PRUnichar
*nameStart
= p
;
1104 while (++p
!= p_end
&& *p
!= kComma
)
1107 nsAutoString family
;
1108 family
= Substring(nameStart
, p
);
1109 family
.CompressWhitespace(PR_FALSE
, PR_TRUE
);
1112 nsFont::GetGenericID(family
, &generic
);
1113 if (generic
!= kGenericFont_NONE
)
1117 ++p
; // may advance past p_end
1120 aFontName
.Append(NS_LITERAL_STRING(",") + aFallbackFamilies
);
1125 aFontName
.Insert(aFallbackFamilies
+ NS_LITERAL_STRING(","),
1128 else { // whitespace or empty
1129 aFontName
= aFallbackFamilies
;
1133 // Update the font and rendering context if there is a family change
1135 SetFontFamily(nsPresContext
* aPresContext
,
1136 nsIRenderingContext
& aRenderingContext
,
1138 const nsGlyphTable
* aGlyphTable
,
1139 const nsGlyphCode
& aGlyphCode
,
1140 const nsAString
& aDefaultFamily
)
1142 const nsAString
& family
=
1143 aGlyphCode
.font
? aGlyphTable
->FontNameFor(aGlyphCode
) : aDefaultFamily
;
1144 if (! family
.Equals(aFont
.name
)) {
1145 aFont
.name
= family
;
1146 aRenderingContext
.SetFont(aFont
, aPresContext
->GetUserFontSet());
1150 class nsMathMLChar::StretchEnumContext
{
1152 StretchEnumContext(nsMathMLChar
* aChar
,
1153 nsPresContext
* aPresContext
,
1154 nsIRenderingContext
& aRenderingContext
,
1155 nsStretchDirection aStretchDirection
,
1156 nscoord aTargetSize
,
1157 PRUint32 aStretchHint
,
1158 nsBoundingMetrics
& aStretchedMetrics
,
1159 const nsAString
& aFamilies
)
1161 mPresContext(aPresContext
),
1162 mRenderingContext(aRenderingContext
),
1163 mDirection(aStretchDirection
),
1164 mTargetSize(aTargetSize
),
1165 mStretchHint(aStretchHint
),
1166 mBoundingMetrics(aStretchedMetrics
),
1167 mFamilies(aFamilies
),
1168 mTryVariants(PR_TRUE
),
1169 mTryParts(PR_TRUE
) {}
1172 EnumCallback(const nsString
& aFamily
, PRBool aGeneric
, void *aData
);
1176 ResolverCallback (const nsAString
& aFamily
, void *aData
);
1178 PRBool
TryVariants(nsGlyphTable
* aGlyphTable
, const nsAString
& aFamily
);
1179 PRBool
TryParts(nsGlyphTable
* aGlyphTable
, const nsAString
& aFamily
);
1181 nsMathMLChar
* mChar
;
1182 nsPresContext
* mPresContext
;
1183 nsIRenderingContext
& mRenderingContext
;
1184 const nsStretchDirection mDirection
;
1185 const nscoord mTargetSize
;
1186 const PRUint32 mStretchHint
;
1187 nsBoundingMetrics
& mBoundingMetrics
;
1188 // Font families to search
1189 const nsAString
& mFamilies
;
1192 PRPackedBool mTryVariants
;
1193 PRPackedBool mTryParts
;
1196 nsAutoTArray
<nsGlyphTable
*,16> mTablesTried
;
1197 nsGlyphTable
* mGlyphTable
; // for this callback
1201 // 2. See if there are any glyphs of the appropriate size.
1202 // Returns PR_TRUE if the size is OK, PR_FALSE to keep searching.
1203 // Always updates the char if a better match is found.
1205 nsMathMLChar::StretchEnumContext::TryVariants(nsGlyphTable
* aGlyphTable
,
1206 const nsAString
& aFamily
)
1208 // Use our stretchy style context now that stretching is in progress
1209 nsFont font
= mChar
->mStyleContext
->GetStyleFont()->mFont
;
1210 // Ensure mRenderingContext.SetFont will be called:
1211 font
.name
.Truncate();
1213 PRBool isVertical
= (mDirection
== NS_STRETCH_DIRECTION_VERTICAL
);
1214 PRBool largeop
= (NS_STRETCH_LARGEOP
& mStretchHint
) != 0;
1215 PRBool largeopOnly
=
1216 largeop
&& (NS_STRETCH_VARIABLE_MASK
& mStretchHint
) == 0;
1217 PRBool maxWidth
= (NS_STRETCH_MAXWIDTH
& mStretchHint
) != 0;
1220 isVertical
? mBoundingMetrics
.ascent
+ mBoundingMetrics
.descent
1221 : mBoundingMetrics
.rightBearing
- mBoundingMetrics
.leftBearing
;
1222 PRBool haveBetter
= PR_FALSE
;
1224 // figure out the starting size : if this is a largeop, start at 2 else 1
1225 PRInt32 size
= 1; // size=0 is the char at its normal size
1226 if (largeop
&& aGlyphTable
->BigOf(mPresContext
, mChar
, 2).Exists()) {
1230 printf(" searching in %s ...\n",
1231 NS_LossyConvertUTF16toASCII(aFamily
).get());
1235 while ((ch
= aGlyphTable
->BigOf(mPresContext
, mChar
, size
)).Exists()) {
1237 SetFontFamily(mChar
->mStyleContext
->PresContext(), mRenderingContext
,
1238 font
, aGlyphTable
, ch
, aFamily
);
1240 NS_ASSERTION(maxWidth
|| ch
.code
!= mChar
->mGlyph
.code
||
1241 !font
.name
.Equals(mChar
->mFamily
),
1242 "glyph table incorrectly set -- duplicate found");
1244 nsBoundingMetrics bm
;
1245 nsresult rv
= mRenderingContext
.GetBoundingMetrics(&ch
.code
, 1, bm
);
1246 if (NS_SUCCEEDED(rv
)) {
1248 isVertical
? bm
.ascent
+ bm
.descent
1249 : bm
.rightBearing
- bm
.leftBearing
;
1252 IsSizeBetter(charSize
, bestSize
, mTargetSize
, mStretchHint
)) {
1254 // IsSizeBetter() checked that charSize < maxsize;
1255 // Leave ascent, descent, and bestsize as these contain maxsize.
1256 if (mBoundingMetrics
.width
< bm
.width
)
1257 mBoundingMetrics
.width
= bm
.width
;
1258 if (mBoundingMetrics
.leftBearing
> bm
.leftBearing
)
1259 mBoundingMetrics
.leftBearing
= bm
.leftBearing
;
1260 if (mBoundingMetrics
.rightBearing
< bm
.rightBearing
)
1261 mBoundingMetrics
.rightBearing
= bm
.rightBearing
;
1262 // Continue to check other sizes unless largeopOnly
1263 haveBetter
= largeopOnly
;
1266 mBoundingMetrics
= bm
;
1267 haveBetter
= PR_TRUE
;
1268 bestSize
= charSize
;
1269 mChar
->mGlyphTable
= aGlyphTable
;
1271 mChar
->mFamily
= font
.name
;
1274 printf(" size:%d Current best\n", size
);
1279 printf(" size:%d Rejected!\n", size
);
1282 break; // Not making an futher progress, stop searching
1286 // if largeopOnly is set, break now
1287 if (largeopOnly
) break;
1291 return haveBetter
&&
1292 (largeopOnly
|| IsSizeOK(mPresContext
, bestSize
, mTargetSize
, mStretchHint
));
1295 // 3. Build by parts.
1296 // Returns PR_TRUE if the size is OK, PR_FALSE to keep searching.
1297 // Always updates the char if a better match is found.
1299 nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable
* aGlyphTable
,
1300 const nsAString
& aFamily
)
1302 if (!aGlyphTable
->HasPartsOf(mPresContext
, mChar
))
1303 return PR_FALSE
; // to next table
1305 // See if this is a composite character /////////////////////////////////////
1306 if (aGlyphTable
->IsComposite(mPresContext
, mChar
)) {
1307 // let the child chars do the job
1308 nsBoundingMetrics compositeSize
;
1310 mChar
->ComposeChildren(mPresContext
, mRenderingContext
, aGlyphTable
,
1311 mTargetSize
, compositeSize
, mStretchHint
);
1313 printf(" Composing %d chars in font %s %s!\n",
1314 aGlyphTable
->ChildCountOf(mPresContext
, mChar
),
1315 NS_LossyConvertUTF16toASCII(fontName
).get(),
1316 NS_SUCCEEDED(rv
)? "OK" : "Rejected");
1319 return PR_FALSE
; // to next table
1321 // all went well, painting will be delegated from now on to children
1322 mChar
->mGlyph
= kNullGlyph
; // this will tell paint to build by parts
1323 mChar
->mGlyphTable
= aGlyphTable
;
1324 mBoundingMetrics
= compositeSize
;
1325 return PR_TRUE
; // no more searching
1328 // See if the parts of this table fit in the desired space //////////////////
1330 // Use our stretchy style context now that stretching is in progress
1331 nsFont font
= mChar
->mStyleContext
->GetStyleFont()->mFont
;
1332 // Ensure mRenderingContext.SetFont will be called:
1333 font
.name
.Truncate();
1335 // Compute the bounding metrics of all partial glyphs
1336 nsGlyphCode chdata
[4];
1337 nsBoundingMetrics bmdata
[4];
1338 nscoord sizedata
[4];
1339 nsGlyphCode glue
= aGlyphTable
->GlueOf(mPresContext
, mChar
);
1341 PRBool isVertical
= (mDirection
== NS_STRETCH_DIRECTION_VERTICAL
);
1342 PRBool maxWidth
= (NS_STRETCH_MAXWIDTH
& mStretchHint
) != 0;
1344 for (PRInt32 i
= 0; i
< 4; i
++) {
1347 case 0: ch
= aGlyphTable
->TopOf(mPresContext
, mChar
); break;
1348 case 1: ch
= aGlyphTable
->MiddleOf(mPresContext
, mChar
); break;
1349 case 2: ch
= aGlyphTable
->BottomOf(mPresContext
, mChar
); break;
1350 case 3: ch
= glue
; break;
1352 // empty slots are filled with the glue if it is not null
1353 if (!ch
.Exists()) ch
= glue
;
1354 nsBoundingMetrics bm
;
1357 // Null glue indicates that a rule will be drawn, which can stretch to
1358 // fill any space. Leave bounding metrics at 0.
1359 sizedata
[i
] = mTargetSize
;
1362 SetFontFamily(mChar
->mStyleContext
->PresContext(), mRenderingContext
,
1363 font
, aGlyphTable
, ch
, aFamily
);
1364 nsresult rv
= mRenderingContext
.GetBoundingMetrics(&ch
.code
, 1, bm
);
1365 if (NS_FAILED(rv
)) {
1366 // stop if we failed to compute the bounding metrics of a part.
1367 NS_WARNING("GetBoundingMetrics failed");
1368 return PR_FALSE
; // to next table
1371 // TODO: For the generic Unicode table, ideally we should check that the
1372 // glyphs are actually found and that they each come from the same
1375 sizedata
[i
] = isVertical
? bm
.ascent
+ bm
.descent
1376 : bm
.rightBearing
- bm
.leftBearing
;
1380 // Build by parts if we have successfully computed the
1381 // bounding metrics of all parts.
1382 nscoord computedSize
= ComputeSizeFromParts(mPresContext
, chdata
, sizedata
,
1385 nscoord currentSize
=
1386 isVertical
? mBoundingMetrics
.ascent
+ mBoundingMetrics
.descent
1387 : mBoundingMetrics
.rightBearing
- mBoundingMetrics
.leftBearing
;
1389 if (!IsSizeBetter(computedSize
, currentSize
, mTargetSize
, mStretchHint
)) {
1391 printf(" Font %s Rejected!\n",
1392 NS_LossyConvertUTF16toASCII(fontName
).get());
1394 return PR_FALSE
; // to next table
1398 printf(" Font %s Current best!\n",
1399 NS_LossyConvertUTF16toASCII(fontName
).get());
1402 // The computed size is the best we have found so far...
1403 // now is the time to compute and cache our bounding metrics
1410 lbearing
= mBoundingMetrics
.leftBearing
;
1411 rbearing
= mBoundingMetrics
.rightBearing
;
1412 width
= mBoundingMetrics
.width
;
1416 lbearing
= bmdata
[0].leftBearing
;
1417 rbearing
= bmdata
[0].rightBearing
;
1418 width
= bmdata
[0].width
;
1421 for (; i
< 4; i
++) {
1422 const nsBoundingMetrics
& bm
= bmdata
[i
];
1423 if (width
< bm
.width
) width
= bm
.width
;
1424 if (lbearing
> bm
.leftBearing
) lbearing
= bm
.leftBearing
;
1425 if (rbearing
< bm
.rightBearing
) rbearing
= bm
.rightBearing
;
1427 mBoundingMetrics
.width
= width
;
1428 // When maxWidth, updating ascent and descent indicates that no characters
1429 // larger than this character's minimum size need to be checked as they
1430 // will not be used.
1431 mBoundingMetrics
.ascent
= bmdata
[0].ascent
; // not used except with descent for height
1432 mBoundingMetrics
.descent
= computedSize
- mBoundingMetrics
.ascent
;
1433 mBoundingMetrics
.leftBearing
= lbearing
;
1434 mBoundingMetrics
.rightBearing
= rbearing
;
1437 nscoord ascent
= bmdata
[0].ascent
;
1438 nscoord descent
= bmdata
[0].descent
;
1439 for (PRInt32 i
= 1; i
< 4; i
++) {
1440 const nsBoundingMetrics
& bm
= bmdata
[i
];
1441 if (ascent
< bm
.ascent
) ascent
= bm
.ascent
;
1442 if (descent
< bm
.descent
) descent
= bm
.descent
;
1444 mBoundingMetrics
.width
= computedSize
;
1445 mBoundingMetrics
.ascent
= ascent
;
1446 mBoundingMetrics
.descent
= descent
;
1447 mBoundingMetrics
.leftBearing
= 0;
1448 mBoundingMetrics
.rightBearing
= computedSize
;
1451 return PR_FALSE
; // Continue to check other sizes
1454 mChar
->mGlyph
= kNullGlyph
; // this will tell paint to build by parts
1455 mChar
->mGlyphTable
= aGlyphTable
;
1456 mChar
->mFamily
= aFamily
;
1458 return IsSizeOK(mPresContext
, computedSize
, mTargetSize
, mStretchHint
);
1461 // This is only called for glyph table corresponding to a family that exists.
1462 // See if the table has a glyph that matches the container
1464 nsMathMLChar::StretchEnumContext::ResolverCallback (const nsAString
& aFamily
,
1467 StretchEnumContext
* context
= static_cast<StretchEnumContext
*>(aData
);
1468 nsGlyphTable
* glyphTable
= context
->mGlyphTable
;
1470 // Only try this table once.
1471 context
->mTablesTried
.AppendElement(glyphTable
);
1473 // If the unicode table is being used, then search all font families. If a
1474 // special table is being used then the font in this family should have the
1475 // specified glyphs.
1476 const nsAString
& family
= glyphTable
== &gGlyphTableList
->mUnicodeTable
?
1477 context
->mFamilies
: aFamily
;
1479 if(context
->mTryVariants
) {
1480 PRBool isOK
= context
->TryVariants(glyphTable
, family
);
1482 return PR_FALSE
; // no need to continue
1485 if(context
->mTryParts
) {
1486 PRBool isOK
= context
->TryParts(glyphTable
, family
);
1488 return PR_FALSE
; // no need to continue
1493 // This is called for each family, whether it exists or not
1495 nsMathMLChar::StretchEnumContext::EnumCallback(const nsString
& aFamily
,
1496 PRBool aGeneric
, void *aData
)
1498 StretchEnumContext
* context
= static_cast<StretchEnumContext
*>(aData
);
1500 // See if there is a special table for the family, but always use the
1501 // Unicode table for generic fonts.
1502 nsGlyphTable
* glyphTable
= aGeneric
?
1503 &gGlyphTableList
->mUnicodeTable
: gGlyphTableList
->GetGlyphTableFor(aFamily
);
1505 if (context
->mTablesTried
.Contains(glyphTable
))
1506 return PR_TRUE
; // already tried this one
1508 context
->mGlyphTable
= glyphTable
;
1511 return ResolverCallback(aFamily
, aData
);
1514 gfxPlatform
*pf
= gfxPlatform::GetPlatform();
1516 pf
->ResolveFontName(aFamily
, ResolverCallback
, aData
, aborted
);
1517 return NS_SUCCEEDED(rv
) && !aborted
; // true means continue
1521 nsMathMLChar::StretchInternal(nsPresContext
* aPresContext
,
1522 nsIRenderingContext
& aRenderingContext
,
1523 nsStretchDirection
& aStretchDirection
,
1524 const nsBoundingMetrics
& aContainerSize
,
1525 nsBoundingMetrics
& aDesiredStretchSize
,
1526 PRUint32 aStretchHint
,
1527 // These are currently only used when
1528 // aStretchHint & NS_STRETCH_MAXWIDTH:
1530 PRBool aMaxSizeIsAbsolute
)
1532 // if we have been called before, and we didn't actually stretch, our
1533 // direction may have been set to NS_STRETCH_DIRECTION_UNSUPPORTED.
1534 // So first set our direction back to its instrinsic value
1535 nsStretchDirection direction
= nsMathMLOperators::GetStretchyDirection(mData
);
1537 // Set default font and get the default bounding metrics
1538 // mStyleContext is a leaf context used only when stretching happens.
1539 // For the base size, the default font should come from the parent context
1540 nsFont font
= mStyleContext
->GetParent()->GetStyleFont()->mFont
;
1542 // Override with specific fonts if applicable for this character
1543 nsCOMPtr
<nsIPrefBranch
> prefBranch
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
1544 nsAutoString families
;
1545 if (GetFontExtensionPref(prefBranch
, mData
[0], eExtension_base
, families
)) {
1546 font
.name
= families
;
1549 // Don't modify this nsMathMLChar when doing GetMaxWidth()
1550 PRBool maxWidth
= (NS_STRETCH_MAXWIDTH
& aStretchHint
) != 0;
1552 // Record the families in case there is no stretch. But don't bother
1553 // storing families when they are just those from the StyleContext.
1557 aRenderingContext
.SetFont(font
, aPresContext
->GetUserFontSet());
1559 aRenderingContext
.GetBoundingMetrics(mData
.get(), PRUint32(mData
.Length()),
1560 aDesiredStretchSize
);
1561 if (NS_FAILED(rv
)) {
1562 NS_WARNING("GetBoundingMetrics failed");
1563 mDirection
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
1568 mScaleY
= mScaleX
= 1.0;
1569 mUnscaledAscent
= aDesiredStretchSize
.ascent
;
1572 ////////////////////////////////////////////////////////////////////////////////////
1573 // 1. Check the common situations where stretching is not actually needed
1574 ////////////////////////////////////////////////////////////////////////////////////
1576 // quick return if there is nothing special about this char
1577 if ((aStretchDirection
!= direction
&&
1578 aStretchDirection
!= NS_STRETCH_DIRECTION_DEFAULT
) ||
1579 (aStretchHint
& ~NS_STRETCH_MAXWIDTH
) == NS_STRETCH_NONE
) {
1580 mDirection
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
1584 // if no specified direction, attempt to stretch in our preferred direction
1585 if (aStretchDirection
== NS_STRETCH_DIRECTION_DEFAULT
) {
1586 aStretchDirection
= direction
;
1589 // see if this is a particular largeop or largeopOnly request
1590 PRBool largeop
= (NS_STRETCH_LARGEOP
& aStretchHint
) != 0;
1591 PRBool stretchy
= (NS_STRETCH_VARIABLE_MASK
& aStretchHint
) != 0;
1592 PRBool largeopOnly
= largeop
&& !stretchy
;
1594 PRBool isVertical
= (direction
== NS_STRETCH_DIRECTION_VERTICAL
);
1596 nscoord targetSize
=
1597 isVertical
? aContainerSize
.ascent
+ aContainerSize
.descent
1598 : aContainerSize
.rightBearing
- aContainerSize
.leftBearing
;
1601 // See if it is only necessary to consider glyphs up to some maximum size.
1602 // Set the current height to the maximum size, and set aStretchHint to
1603 // NS_STRETCH_SMALLER if the size is variable, so that only smaller sizes
1604 // are considered. targetSize from GetMaxWidth() is 0.
1606 // variable size stretch - consider all sizes < maxsize
1608 (aStretchHint
& ~NS_STRETCH_VARIABLE_MASK
) | NS_STRETCH_SMALLER
;
1611 // Use NS_MATHML_DELIMITER_FACTOR to allow some slightly larger glyphs as
1612 // maxsize is not enforced exactly.
1613 if (aMaxSize
== NS_MATHML_OPERATOR_SIZE_INFINITY
) {
1614 aDesiredStretchSize
.ascent
= nscoord_MAX
;
1615 aDesiredStretchSize
.descent
= 0;
1618 nscoord height
= aDesiredStretchSize
.ascent
+ aDesiredStretchSize
.descent
;
1620 if (aMaxSizeIsAbsolute
) {
1621 aDesiredStretchSize
.ascent
=
1622 NSToCoordRound(aMaxSize
/ NS_MATHML_DELIMITER_FACTOR
);
1623 aDesiredStretchSize
.descent
= 0;
1625 // else: leave height as 0
1628 float scale
= aMaxSizeIsAbsolute
? aMaxSize
/ height
: aMaxSize
;
1629 scale
/= NS_MATHML_DELIMITER_FACTOR
;
1630 aDesiredStretchSize
.ascent
=
1631 NSToCoordRound(scale
* aDesiredStretchSize
.ascent
);
1632 aDesiredStretchSize
.descent
=
1633 NSToCoordRound(scale
* aDesiredStretchSize
.descent
);
1638 nsBoundingMetrics initialSize
= aDesiredStretchSize
;
1640 isVertical
? initialSize
.ascent
+ initialSize
.descent
1641 : initialSize
.rightBearing
- initialSize
.leftBearing
;
1643 PRBool done
= (mGlyphTable
? PR_FALSE
: PR_TRUE
);
1645 if (!done
&& !maxWidth
&& !largeop
) {
1646 // Doing Stretch() not GetMaxWidth(),
1647 // and not a largeop in display mode; we're done if size fits
1648 if ((targetSize
<= 0) ||
1649 ((isVertical
&& charSize
>= targetSize
) ||
1650 IsSizeOK(aPresContext
, charSize
, targetSize
, aStretchHint
)))
1654 ////////////////////////////////////////////////////////////////////////////////////
1655 // 2/3. Search for a glyph or set of part glyphs of appropriate size
1656 ////////////////////////////////////////////////////////////////////////////////////
1658 nsAutoString cssFamilies
;
1661 font
= mStyleContext
->GetStyleFont()->mFont
;
1662 cssFamilies
= font
.name
;
1665 // See if there are preferred fonts for the variants of this char
1666 if (!done
&& GetFontExtensionPref(prefBranch
, mData
[0], eExtension_variants
,
1668 font
.name
= families
;
1670 StretchEnumContext
enumData(this, aPresContext
, aRenderingContext
,
1671 aStretchDirection
, targetSize
, aStretchHint
,
1672 aDesiredStretchSize
, font
.name
);
1673 enumData
.mTryParts
= PR_FALSE
;
1675 done
= !font
.EnumerateFamilies(StretchEnumContext::EnumCallback
, &enumData
);
1678 // See if there are preferred fonts for the parts of this char
1679 if (!done
&& !largeopOnly
1680 && GetFontExtensionPref(prefBranch
, mData
[0], eExtension_parts
,
1682 font
.name
= families
;
1684 StretchEnumContext
enumData(this, aPresContext
, aRenderingContext
,
1685 aStretchDirection
, targetSize
, aStretchHint
,
1686 aDesiredStretchSize
, font
.name
);
1687 enumData
.mTryVariants
= PR_FALSE
;
1689 done
= !font
.EnumerateFamilies(StretchEnumContext::EnumCallback
, &enumData
);
1692 if (!done
) { // normal case
1693 // Use the css font-family but add preferred fallback fonts.
1694 font
.name
= cssFamilies
;
1695 NS_NAMED_LITERAL_CSTRING(defaultKey
, "font.mathfont-family");
1696 nsAutoString fallbackFonts
;
1697 if (GetPrefValue(prefBranch
, defaultKey
.get(), fallbackFonts
)) {
1698 AddFallbackFonts(font
.name
, fallbackFonts
);
1702 printf("Searching in "%s
" for a glyph of appropriate size for: 0x%04X:%c\n",
1703 font
.name
, mData
[0], mData
[0]&0x00FF);
1705 StretchEnumContext
enumData(this, aPresContext
, aRenderingContext
,
1706 aStretchDirection
, targetSize
, aStretchHint
,
1707 aDesiredStretchSize
, font
.name
);
1708 enumData
.mTryParts
= !largeopOnly
;
1710 font
.EnumerateFamilies(StretchEnumContext::EnumCallback
, &enumData
);
1714 // Now, we know how we are going to draw the char. Update the member
1715 // variables accordingly.
1716 mDrawNormal
= (mGlyph
.font
== -1);
1717 mUnscaledAscent
= aDesiredStretchSize
.ascent
;
1720 // stretchy character
1724 float(aContainerSize
.ascent
+ aContainerSize
.descent
) /
1725 (aDesiredStretchSize
.ascent
+ aDesiredStretchSize
.descent
);
1726 if (!largeop
|| scale
> 1.0) {
1727 // make the character match the desired height.
1729 aDesiredStretchSize
.ascent
*= scale
;
1730 aDesiredStretchSize
.descent
*= scale
;
1734 float(aContainerSize
.rightBearing
- aContainerSize
.leftBearing
) /
1735 (aDesiredStretchSize
.rightBearing
- aDesiredStretchSize
.leftBearing
);
1736 if (!largeop
|| scale
> 1.0) {
1737 // make the character match the desired width.
1739 aDesiredStretchSize
.leftBearing
*= scale
;
1740 aDesiredStretchSize
.rightBearing
*= scale
;
1741 aDesiredStretchSize
.width
*= scale
;
1746 // We do not have a char variant for this largeop in display mode, so we
1747 // apply a scale transform to the base char.
1748 if (mGlyph
.font
== -1 && largeop
) {
1750 float largeopFactor
= M_SQRT2
;
1752 // increase the width if it is not largeopFactor times larger
1753 // than the initial one.
1754 if ((aDesiredStretchSize
.rightBearing
- aDesiredStretchSize
.leftBearing
) <
1755 largeopFactor
* (initialSize
.rightBearing
- initialSize
.leftBearing
)) {
1756 scale
= (largeopFactor
*
1757 (initialSize
.rightBearing
- initialSize
.leftBearing
)) /
1758 (aDesiredStretchSize
.rightBearing
- aDesiredStretchSize
.leftBearing
);
1760 aDesiredStretchSize
.leftBearing
*= scale
;
1761 aDesiredStretchSize
.rightBearing
*= scale
;
1762 aDesiredStretchSize
.width
*= scale
;
1765 // increase the height if it is not largeopFactor times larger
1766 // than the initial one.
1767 if (NS_STRETCH_INTEGRAL
& aStretchHint
) {
1768 // integrals are drawn taller
1769 largeopFactor
= 2.0;
1771 if ((aDesiredStretchSize
.ascent
+ aDesiredStretchSize
.descent
) <
1772 largeopFactor
* (initialSize
.ascent
+ initialSize
.descent
)) {
1773 scale
= (largeopFactor
*
1774 (initialSize
.ascent
+ initialSize
.descent
)) /
1775 (aDesiredStretchSize
.ascent
+ aDesiredStretchSize
.descent
);
1777 aDesiredStretchSize
.ascent
*= scale
;
1778 aDesiredStretchSize
.descent
*= scale
;
1786 nsMathMLChar::Stretch(nsPresContext
* aPresContext
,
1787 nsIRenderingContext
& aRenderingContext
,
1788 nsStretchDirection aStretchDirection
,
1789 const nsBoundingMetrics
& aContainerSize
,
1790 nsBoundingMetrics
& aDesiredStretchSize
,
1791 PRUint32 aStretchHint
)
1793 NS_ASSERTION(!(aStretchHint
&
1794 ~(NS_STRETCH_VARIABLE_MASK
| NS_STRETCH_LARGEOP
|
1795 NS_STRETCH_INTEGRAL
)),
1796 "Unexpected stretch flags");
1798 // This will be updated if a better match than the base character is found
1801 mDirection
= aStretchDirection
;
1803 StretchInternal(aPresContext
, aRenderingContext
, mDirection
,
1804 aContainerSize
, aDesiredStretchSize
, aStretchHint
);
1806 // Record the metrics
1807 mBoundingMetrics
= aDesiredStretchSize
;
1812 // What happens here is that the StretchInternal algorithm is used but
1813 // modified by passing the NS_STRETCH_MAXWIDTH stretch hint. That causes
1814 // StretchInternal to return horizontal bounding metrics that are the maximum
1815 // that might be returned from a Stretch.
1817 // In order to avoid considering widths of some characters in fonts that will
1818 // not be used for any stretch size, StretchInternal sets the initial height
1819 // to infinity and looks for any characters smaller than this height. When a
1820 // character built from parts is considered, (it will be used by Stretch for
1821 // any characters greater than its minimum size, so) the height is set to its
1822 // minimum size, so that only widths of smaller subsequent characters are
1825 nsMathMLChar::GetMaxWidth(nsPresContext
* aPresContext
,
1826 nsIRenderingContext
& aRenderingContext
,
1827 PRUint32 aStretchHint
,
1828 float aMaxSize
, PRBool aMaxSizeIsAbsolute
)
1830 nsBoundingMetrics bm
;
1831 nsStretchDirection direction
= NS_STRETCH_DIRECTION_VERTICAL
;
1832 const nsBoundingMetrics container
; // zero target size
1834 StretchInternal(aPresContext
, aRenderingContext
, direction
, container
,
1835 bm
, aStretchHint
| NS_STRETCH_MAXWIDTH
);
1837 return NS_MAX(bm
.width
, bm
.rightBearing
) - NS_MIN(0, bm
.leftBearing
);
1841 nsMathMLChar::ComposeChildren(nsPresContext
* aPresContext
,
1842 nsIRenderingContext
& aRenderingContext
,
1843 nsGlyphTable
* aGlyphTable
,
1844 nscoord aTargetSize
,
1845 nsBoundingMetrics
& aCompositeSize
,
1846 PRUint32 aStretchHint
)
1849 nsMathMLChar
* child
;
1850 PRInt32 count
= aGlyphTable
->ChildCountOf(aPresContext
, this);
1851 NS_ASSERTION(count
, "something is wrong somewhere");
1852 if (!count
) return NS_ERROR_FAILURE
;
1853 // if we haven't been here before, create the linked list of children now
1854 // otherwise, use what we have, adding more children as needed or deleting the extra
1855 nsMathMLChar
* last
= this;
1856 while ((i
< count
) && last
->mSibling
) {
1858 last
= last
->mSibling
;
1861 child
= new nsMathMLChar(this);
1863 if (mSibling
) delete mSibling
; // don't leave a dangling list ...
1865 return NS_ERROR_OUT_OF_MEMORY
;
1867 last
->mSibling
= child
;
1871 if (last
->mSibling
) {
1872 delete last
->mSibling
;
1873 last
->mSibling
= nsnull
;
1875 // let children stretch in an equal space
1876 nsBoundingMetrics splitSize
;
1877 if (NS_STRETCH_DIRECTION_HORIZONTAL
== mDirection
)
1878 splitSize
.width
= aTargetSize
/ count
;
1880 splitSize
.ascent
= aTargetSize
/ (count
* 2);
1881 splitSize
.descent
= splitSize
.ascent
;
1883 nscoord dx
= 0, dy
= 0;
1884 for (i
= 0, child
= mSibling
; child
; child
= child
->mSibling
, i
++) {
1885 // child chars should just inherit our values - which may change between calls...
1886 child
->mData
= mData
;
1887 child
->mDirection
= mDirection
;
1888 child
->mStyleContext
= mStyleContext
;
1889 child
->mGlyphTable
= aGlyphTable
; // the child is associated to this table
1890 // there goes the Stretch() ...
1891 nsBoundingMetrics childSize
;
1892 nsresult rv
= child
->Stretch(aPresContext
, aRenderingContext
, mDirection
,
1893 splitSize
, childSize
, aStretchHint
);
1894 // check if something went wrong or the child couldn't fit in the alloted space
1895 if (NS_FAILED(rv
) || (NS_STRETCH_DIRECTION_UNSUPPORTED
== child
->mDirection
)) {
1896 delete mSibling
; // don't leave a dangling list behind ...
1898 return NS_ERROR_FAILURE
;
1900 child
->SetRect(nsRect(dx
, dy
, childSize
.width
, childSize
.ascent
+childSize
.descent
));
1902 aCompositeSize
= childSize
;
1904 if (NS_STRETCH_DIRECTION_HORIZONTAL
== mDirection
)
1905 aCompositeSize
+= childSize
;
1907 aCompositeSize
.descent
+= childSize
.ascent
+ childSize
.descent
;
1908 if (aCompositeSize
.leftBearing
> childSize
.leftBearing
)
1909 aCompositeSize
.leftBearing
= childSize
.leftBearing
;
1910 if (aCompositeSize
.rightBearing
< childSize
.rightBearing
)
1911 aCompositeSize
.rightBearing
= childSize
.rightBearing
;
1914 if (NS_STRETCH_DIRECTION_HORIZONTAL
== mDirection
)
1915 dx
+= childSize
.width
;
1917 dy
+= childSize
.ascent
+ childSize
.descent
;
1922 class nsDisplayMathMLSelectionRect
: public nsDisplayItem
{
1924 nsDisplayMathMLSelectionRect(nsDisplayListBuilder
* aBuilder
,
1925 nsIFrame
* aFrame
, const nsRect
& aRect
)
1926 : nsDisplayItem(aBuilder
, aFrame
), mRect(aRect
) {
1927 MOZ_COUNT_CTOR(nsDisplayMathMLSelectionRect
);
1929 #ifdef NS_BUILD_REFCNT_LOGGING
1930 virtual ~nsDisplayMathMLSelectionRect() {
1931 MOZ_COUNT_DTOR(nsDisplayMathMLSelectionRect
);
1935 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
1936 nsIRenderingContext
* aCtx
);
1937 NS_DISPLAY_DECL_NAME("MathMLSelectionRect", TYPE_MATHML_SELECTION_RECT
)
1942 void nsDisplayMathMLSelectionRect::Paint(nsDisplayListBuilder
* aBuilder
,
1943 nsIRenderingContext
* aCtx
)
1945 // get color to use for selection from the look&feel object
1946 nscolor bgColor
= NS_RGB(0, 0, 0);
1947 mFrame
->PresContext()->LookAndFeel()->
1948 GetColor(nsILookAndFeel::eColor_TextSelectBackground
, bgColor
);
1949 aCtx
->SetColor(bgColor
);
1950 aCtx
->FillRect(mRect
+ ToReferenceFrame());
1953 class nsDisplayMathMLCharBackground
: public nsDisplayItem
{
1955 nsDisplayMathMLCharBackground(nsDisplayListBuilder
* aBuilder
,
1956 nsIFrame
* aFrame
, const nsRect
& aRect
,
1957 nsStyleContext
* aStyleContext
)
1958 : nsDisplayItem(aBuilder
, aFrame
), mStyleContext(aStyleContext
), mRect(aRect
) {
1959 MOZ_COUNT_CTOR(nsDisplayMathMLCharBackground
);
1961 #ifdef NS_BUILD_REFCNT_LOGGING
1962 virtual ~nsDisplayMathMLCharBackground() {
1963 MOZ_COUNT_DTOR(nsDisplayMathMLCharBackground
);
1967 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
1968 nsIRenderingContext
* aCtx
);
1969 NS_DISPLAY_DECL_NAME("MathMLCharBackground", TYPE_MATHML_CHAR_BACKGROUND
)
1971 nsStyleContext
* mStyleContext
;
1975 void nsDisplayMathMLCharBackground::Paint(nsDisplayListBuilder
* aBuilder
,
1976 nsIRenderingContext
* aCtx
)
1978 const nsStyleBorder
* border
= mStyleContext
->GetStyleBorder();
1979 nsRect
rect(mRect
+ ToReferenceFrame());
1980 nsCSSRendering::PaintBackgroundWithSC(mFrame
->PresContext(), *aCtx
, mFrame
,
1982 mStyleContext
, *border
,
1983 aBuilder
->GetBackgroundPaintFlags());
1986 class nsDisplayMathMLCharForeground
: public nsDisplayItem
{
1988 nsDisplayMathMLCharForeground(nsDisplayListBuilder
* aBuilder
,
1989 nsIFrame
* aFrame
, nsMathMLChar
* aChar
,
1991 : nsDisplayItem(aBuilder
, aFrame
), mChar(aChar
), mIsSelected(aIsSelected
) {
1992 MOZ_COUNT_CTOR(nsDisplayMathMLCharForeground
);
1994 #ifdef NS_BUILD_REFCNT_LOGGING
1995 virtual ~nsDisplayMathMLCharForeground() {
1996 MOZ_COUNT_DTOR(nsDisplayMathMLCharForeground
);
2000 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
) {
2002 mChar
->GetRect(rect
);
2003 nsPoint offset
= ToReferenceFrame() + rect
.TopLeft();
2004 nsBoundingMetrics bm
;
2005 mChar
->GetBoundingMetrics(bm
);
2006 return nsRect(offset
.x
+ bm
.leftBearing
, offset
.y
,
2007 bm
.rightBearing
- bm
.leftBearing
, bm
.ascent
+ bm
.descent
);
2010 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
2011 nsIRenderingContext
* aCtx
)
2013 mChar
->PaintForeground(mFrame
->PresContext(), *aCtx
,
2014 ToReferenceFrame(), mIsSelected
);
2017 NS_DISPLAY_DECL_NAME("MathMLCharForeground", TYPE_MATHML_CHAR_FOREGROUND
)
2019 virtual nsRect
GetComponentAlphaBounds(nsDisplayListBuilder
* aBuilder
)
2021 return GetBounds(aBuilder
);
2025 nsMathMLChar
* mChar
;
2026 PRPackedBool mIsSelected
;
2030 class nsDisplayMathMLCharDebug
: public nsDisplayItem
{
2032 nsDisplayMathMLCharDebug(nsDisplayListBuilder
* aBuilder
,
2033 nsIFrame
* aFrame
, const nsRect
& aRect
)
2034 : nsDisplayItem(aBuilder
, aFrame
), mRect(aRect
) {
2035 MOZ_COUNT_CTOR(nsDisplayMathMLCharDebug
);
2037 #ifdef NS_BUILD_REFCNT_LOGGING
2038 virtual ~nsDisplayMathMLCharDebug() {
2039 MOZ_COUNT_DTOR(nsDisplayMathMLCharDebug
);
2043 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
2044 nsIRenderingContext
* aCtx
);
2045 NS_DISPLAY_DECL_NAME("MathMLCharDebug", TYPE_MATHML_CHAR_DEBUG
)
2051 void nsDisplayMathMLCharDebug::Paint(nsDisplayListBuilder
* aBuilder
,
2052 nsIRenderingContext
* aCtx
)
2055 PRIntn skipSides
= 0;
2056 nsPresContext
* presContext
= mFrame
->PresContext();
2057 nsStyleContext
* styleContext
= mFrame
->GetStyleContext();
2058 nsRect rect
= mRect
+ ToReferenceFrame();
2059 nsCSSRendering::PaintBorder(presContext
, *aCtx
, mFrame
,
2060 mVisibleRect
, rect
, styleContext
, skipSides
);
2061 nsCSSRendering::PaintOutline(presContext
, *aCtx
, mFrame
,
2062 mVisibleRect
, rect
, styleContext
);
2068 nsMathMLChar::Display(nsDisplayListBuilder
* aBuilder
,
2069 nsIFrame
* aForFrame
,
2070 const nsDisplayListSet
& aLists
,
2071 const nsRect
* aSelectedRect
)
2073 nsresult rv
= NS_OK
;
2074 nsStyleContext
* parentContext
= mStyleContext
->GetParent();
2075 nsStyleContext
* styleContext
= mStyleContext
;
2078 // normal drawing if there is nothing special about this char
2079 // Set default context to the parent context
2080 styleContext
= parentContext
;
2083 if (!styleContext
->GetStyleVisibility()->IsVisible())
2086 // if the leaf style context that we use for stretchy chars has a background
2087 // color we use it -- this feature is mostly used for testing and debugging
2088 // purposes. Normally, users will set the background on the container frame.
2089 // paint the selection background -- beware MathML frames overlap a lot
2090 if (aSelectedRect
&& !aSelectedRect
->IsEmpty()) {
2091 rv
= aLists
.BorderBackground()->AppendNewToTop(new (aBuilder
)
2092 nsDisplayMathMLSelectionRect(aBuilder
, aForFrame
, *aSelectedRect
));
2093 NS_ENSURE_SUCCESS(rv
, rv
);
2095 else if (mRect
.width
&& mRect
.height
) {
2096 const nsStyleBackground
* backg
= styleContext
->GetStyleBackground();
2097 if (styleContext
!= parentContext
&&
2098 NS_GET_A(backg
->mBackgroundColor
) > 0) {
2099 rv
= aLists
.BorderBackground()->AppendNewToTop(new (aBuilder
)
2100 nsDisplayMathMLCharBackground(aBuilder
, aForFrame
, mRect
, styleContext
));
2101 NS_ENSURE_SUCCESS(rv
, rv
);
2104 // our container frame will take care of painting its background
2106 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
2108 rv
= aLists
.BorderBackground()->AppendToTop(new (aBuilder
)
2109 nsDisplayMathMLCharDebug(aBuilder
, aForFrame
, mRect
));
2110 NS_ENSURE_SUCCESS(rv
, rv
);
2113 return aLists
.Content()->AppendNewToTop(new (aBuilder
)
2114 nsDisplayMathMLCharForeground(aBuilder
, aForFrame
, this,
2115 aSelectedRect
&& !aSelectedRect
->IsEmpty()));
2119 nsMathMLChar::ApplyTransforms(nsIRenderingContext
& aRenderingContext
, nsRect
&r
)
2121 // apply the transforms
2122 aRenderingContext
.Translate(r
.x
, r
.y
);
2123 aRenderingContext
.Scale(mScaleX
, mScaleY
);
2125 // update the bounding rectangle.
2128 r
.height
/= mScaleY
;
2132 nsMathMLChar::PaintForeground(nsPresContext
* aPresContext
,
2133 nsIRenderingContext
& aRenderingContext
,
2137 nsStyleContext
* parentContext
= mStyleContext
->GetParent();
2138 nsStyleContext
* styleContext
= mStyleContext
;
2141 // normal drawing if there is nothing special about this char
2142 // Set default context to the parent context
2143 styleContext
= parentContext
;
2147 nscolor fgColor
= styleContext
->GetStyleColor()->mColor
;
2149 // get color to use for selection from the look&feel object
2150 aPresContext
->LookAndFeel()->
2151 GetColor(nsILookAndFeel::eColor_TextSelectForeground
, fgColor
);
2153 aRenderingContext
.SetColor(fgColor
);
2155 nsFont
theFont(styleContext
->GetStyleFont()->mFont
);
2156 if (! mFamily
.IsEmpty()) {
2157 theFont
.name
= mFamily
;
2159 aRenderingContext
.SetFont(theFont
, aPresContext
->GetUserFontSet());
2161 aRenderingContext
.PushState();
2162 nsRect r
= mRect
+ aPt
;
2163 ApplyTransforms(aRenderingContext
, r
);
2166 // normal drawing if there is nothing special about this char ...
2167 // Grab some metrics to adjust the placements ...
2168 PRUint32 len
= PRUint32(mData
.Length());
2169 //printf("Painting %04X like a normal char\n", mData[0]);
2170 //aRenderingContext.SetColor(NS_RGB(255,0,0));
2171 aRenderingContext
.DrawString(mData
.get(), len
, 0, mUnscaledAscent
);
2174 // Grab some metrics to adjust the placements ...
2175 // if there is a glyph of appropriate size, paint that glyph
2176 if (mGlyph
.Exists()) {
2177 //printf("Painting %04X with a glyph of appropriate size\n", mData[0]);
2178 //aRenderingContext.SetColor(NS_RGB(0,0,255));
2179 aRenderingContext
.DrawString(&mGlyph
.code
, 1, 0, mUnscaledAscent
);
2181 else { // paint by parts
2182 //aRenderingContext.SetColor(NS_RGB(0,255,0));
2183 if (NS_STRETCH_DIRECTION_VERTICAL
== mDirection
)
2184 PaintVertically(aPresContext
, aRenderingContext
, theFont
, styleContext
,
2186 else if (NS_STRETCH_DIRECTION_HORIZONTAL
== mDirection
)
2187 PaintHorizontally(aPresContext
, aRenderingContext
, theFont
, styleContext
,
2192 aRenderingContext
.PopState();
2195 /* =================================================================================
2196 And now the helper routines that actually do the job of painting the char by parts
2199 class AutoPushClipRect
{
2200 nsIRenderingContext
& mCtx
;
2202 AutoPushClipRect(nsIRenderingContext
& aCtx
, const nsRect
& aRect
)
2205 mCtx
.SetClipRect(aRect
, nsClipCombine_kIntersect
);
2207 ~AutoPushClipRect() {
2213 SnapToDevPixels(const gfxContext
* aThebesContext
, PRInt32 aAppUnitsPerGfxUnit
,
2216 gfxPoint
pt(NSAppUnitsToFloatPixels(aPt
.x
, aAppUnitsPerGfxUnit
),
2217 NSAppUnitsToFloatPixels(aPt
.y
, aAppUnitsPerGfxUnit
));
2218 pt
= aThebesContext
->UserToDevice(pt
);
2220 pt
= aThebesContext
->DeviceToUser(pt
);
2221 return nsPoint(NSFloatPixelsToAppUnits(pt
.x
, aAppUnitsPerGfxUnit
),
2222 NSFloatPixelsToAppUnits(pt
.y
, aAppUnitsPerGfxUnit
));
2225 // paint a stretchy char by assembling glyphs vertically
2227 nsMathMLChar::PaintVertically(nsPresContext
* aPresContext
,
2228 nsIRenderingContext
& aRenderingContext
,
2230 nsStyleContext
* aStyleContext
,
2231 nsGlyphTable
* aGlyphTable
,
2234 nsresult rv
= NS_OK
;
2235 // Get the device pixel size in the vertical direction.
2236 // (This makes no effort to optimize for non-translation transformations.)
2237 nscoord oneDevPixel
= aPresContext
->AppUnitsPerDevPixel();
2239 // get metrics data to be re-used later
2241 nsGlyphCode ch
, chdata
[4];
2242 nsBoundingMetrics bmdata
[4];
2243 PRInt32 glue
, bottom
;
2244 nsGlyphCode chGlue
= aGlyphTable
->GlueOf(aPresContext
, this);
2245 for (PRInt32 j
= 0; j
< 4; ++j
) {
2248 ch
= aGlyphTable
->TopOf(aPresContext
, this);
2251 ch
= aGlyphTable
->MiddleOf(aPresContext
, this);
2253 continue; // no middle
2256 ch
= aGlyphTable
->BottomOf(aPresContext
, this);
2264 // empty slots are filled with the glue if it is not null
2265 if (!ch
.Exists()) ch
= chGlue
;
2266 // if (!ch.Exists()) glue is null, leave bounding metrics at 0
2268 SetFontFamily(aPresContext
, aRenderingContext
,
2269 aFont
, aGlyphTable
, ch
, mFamily
);
2270 rv
= aRenderingContext
.GetBoundingMetrics(&ch
.code
, 1, bmdata
[i
]);
2271 if (NS_FAILED(rv
)) {
2272 NS_WARNING("GetBoundingMetrics failed");
2279 nscoord dx
= aRect
.x
;
2280 nscoord offset
[3], start
[3], end
[3];
2281 nsRefPtr
<gfxContext
> ctx
= aRenderingContext
.ThebesContext();
2282 for (i
= 0; i
<= bottom
; ++i
) {
2284 const nsBoundingMetrics
& bm
= bmdata
[i
];
2286 if (0 == i
) { // top
2287 dy
= aRect
.y
+ bm
.ascent
;
2289 else if (bottom
== i
) { // bottom
2290 dy
= aRect
.y
+ aRect
.height
- bm
.descent
;
2293 dy
= aRect
.y
+ bm
.ascent
+ (aRect
.height
- (bm
.ascent
+ bm
.descent
))/2;
2295 // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
2296 // Do this now so that we can get the other dimensions right.
2297 // (This may not achieve much with non-rectangular transformations.)
2298 dy
= SnapToDevPixels(ctx
, oneDevPixel
, nsPoint(dx
, dy
)).y
;
2299 // abcissa passed to DrawString
2301 // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
2302 // pixel, so the bm values can include 1 row of faint pixels on each edge.
2303 // Don't rely on this pixel as it can look like a gap.
2304 start
[i
] = dy
- bm
.ascent
+ oneDevPixel
; // top join
2305 end
[i
] = dy
+ bm
.descent
- oneDevPixel
; // bottom join
2308 // If there are overlaps, then join at the mid point
2309 for (i
= 0; i
< bottom
; ++i
) {
2310 if (end
[i
] > start
[i
+1]) {
2311 end
[i
] = (end
[i
] + start
[i
+1]) / 2;
2312 start
[i
+1] = end
[i
];
2316 nsRect unionRect
= aRect
;
2317 unionRect
.x
+= mBoundingMetrics
.leftBearing
;
2319 mBoundingMetrics
.rightBearing
- mBoundingMetrics
.leftBearing
;
2320 unionRect
.Inflate(oneDevPixel
, oneDevPixel
);
2322 /////////////////////////////////////
2323 // draw top, middle, bottom
2324 for (i
= 0; i
<= bottom
; ++i
) {
2326 // glue can be null, and other parts could have been set to glue
2329 // bounding box of the part
2330 aRenderingContext
.SetColor(NS_RGB(0,0,0));
2331 aRenderingContext
.DrawRect(nsRect(dx
,start
[i
],aRect
.width
+30*(i
+1),end
[i
]-start
[i
]));
2333 nscoord dy
= offset
[i
];
2334 // Draw a glyph in a clipped area so that we don't have hairy chars
2336 nsRect clipRect
= unionRect
;
2337 // Clip at the join to get a solid edge (without overlap or gap), when
2338 // this won't change the glyph too much. If the glyph is too small to
2339 // clip then we'll overlap rather than have a gap.
2340 nscoord height
= bmdata
[i
].ascent
+ bmdata
[i
].descent
;
2342 height
* (1.0 - NS_MATHML_DELIMITER_FACTOR
) > oneDevPixel
) {
2343 if (0 == i
) { // top
2344 clipRect
.height
= end
[i
] - clipRect
.y
;
2346 else if (bottom
== i
) { // bottom
2347 clipRect
.height
-= start
[i
] - clipRect
.y
;
2348 clipRect
.y
= start
[i
];
2351 clipRect
.y
= start
[i
];
2352 clipRect
.height
= end
[i
] - start
[i
];
2355 if (!clipRect
.IsEmpty()) {
2356 AutoPushClipRect
clip(aRenderingContext
, clipRect
);
2357 SetFontFamily(aPresContext
, aRenderingContext
,
2358 aFont
, aGlyphTable
, ch
, mFamily
);
2359 aRenderingContext
.DrawString(&ch
.code
, 1, dx
, dy
);
2365 // fill the gap between top and middle, and between middle and bottom.
2366 if (!chGlue
.Exists()) { // null glue : draw a rule
2367 // figure out the dimensions of the rule to be drawn :
2368 // set lbearing to rightmost lbearing among the two current successive parts.
2369 // set rbearing to leftmost rbearing among the two current successive parts.
2370 // this not only satisfies the convention used for over/underbraces
2371 // in TeX, but also takes care of broken fonts like the stretchy integral
2372 // in Symbol for small font sizes in unix.
2373 nscoord lbearing
, rbearing
;
2374 PRInt32 first
= 0, last
= 1;
2375 while (last
<= bottom
) {
2376 if (chdata
[last
].Exists()) {
2377 lbearing
= bmdata
[last
].leftBearing
;
2378 rbearing
= bmdata
[last
].rightBearing
;
2379 if (chdata
[first
].Exists()) {
2380 if (lbearing
< bmdata
[first
].leftBearing
)
2381 lbearing
= bmdata
[first
].leftBearing
;
2382 if (rbearing
> bmdata
[first
].rightBearing
)
2383 rbearing
= bmdata
[first
].rightBearing
;
2386 else if (chdata
[first
].Exists()) {
2387 lbearing
= bmdata
[first
].leftBearing
;
2388 rbearing
= bmdata
[first
].rightBearing
;
2391 NS_ERROR("Cannot stretch - All parts missing");
2392 return NS_ERROR_UNEXPECTED
;
2394 // paint the rule between the parts
2395 nsRect
rule(aRect
.x
+ lbearing
, end
[first
],
2396 rbearing
- lbearing
, start
[last
] - end
[first
]);
2397 if (!rule
.IsEmpty())
2398 aRenderingContext
.FillRect(rule
);
2403 else if (bmdata
[glue
].ascent
+ bmdata
[glue
].descent
> 0) {
2405 nsBoundingMetrics
& bm
= bmdata
[glue
];
2406 // Ensure the stride for the glue is not reduced to less than one pixel
2407 if (bm
.ascent
+ bm
.descent
>= 3 * oneDevPixel
) {
2408 // To protect against gaps, pretend the glue is smaller than it is,
2409 // in order to trim off ends and thus get a solid edge for the join.
2410 bm
.ascent
-= oneDevPixel
;
2411 bm
.descent
-= oneDevPixel
;
2414 SetFontFamily(aPresContext
, aRenderingContext
,
2415 aFont
, aGlyphTable
, chGlue
, mFamily
);
2416 nsRect clipRect
= unionRect
;
2418 for (i
= 0; i
< bottom
; ++i
) {
2419 // Make sure not to draw outside the character
2420 nscoord dy
= NS_MAX(end
[i
], aRect
.y
);
2421 nscoord fillEnd
= NS_MIN(start
[i
+1], aRect
.YMost());
2423 // exact area to fill
2424 aRenderingContext
.SetColor(NS_RGB(255,0,0));
2426 clipRect
.height
= fillEnd
- dy
;
2427 aRenderingContext
.DrawRect(clipRect
);
2430 while (dy
< fillEnd
) {
2432 clipRect
.height
= NS_MIN(bm
.ascent
+ bm
.descent
, fillEnd
- dy
);
2433 AutoPushClipRect
clip(aRenderingContext
, clipRect
);
2435 aRenderingContext
.DrawString(&chGlue
.code
, 1, dx
, dy
);
2440 // last glyph that may cross past its boundary and collide with the next
2441 nscoord height
= bm
.ascent
+ bm
.descent
;
2442 aRenderingContext
.SetColor(NS_RGB(0,255,0));
2443 aRenderingContext
.DrawRect(nsRect(dx
, dy
-bm
.ascent
, aRect
.width
, height
));
2449 for (i
= 0; i
< bottom
; ++i
) {
2450 NS_ASSERTION(end
[i
] >= start
[i
+1],
2451 "gap between parts with missing glue glyph");
2458 // paint a stretchy char by assembling glyphs horizontally
2460 nsMathMLChar::PaintHorizontally(nsPresContext
* aPresContext
,
2461 nsIRenderingContext
& aRenderingContext
,
2463 nsStyleContext
* aStyleContext
,
2464 nsGlyphTable
* aGlyphTable
,
2467 nsresult rv
= NS_OK
;
2468 // Get the device pixel size in the horizontal direction.
2469 // (This makes no effort to optimize for non-translation transformations.)
2470 nscoord oneDevPixel
= aPresContext
->AppUnitsPerDevPixel();
2472 // get metrics data to be re-used later
2474 nsGlyphCode ch
, chdata
[4];
2475 nsBoundingMetrics bmdata
[4];
2476 PRInt32 glue
, right
;
2477 nsGlyphCode chGlue
= aGlyphTable
->GlueOf(aPresContext
, this);
2478 for (PRInt32 j
= 0; j
< 4; ++j
) {
2481 ch
= aGlyphTable
->LeftOf(aPresContext
, this);
2484 ch
= aGlyphTable
->MiddleOf(aPresContext
, this);
2486 continue; // no middle
2489 ch
= aGlyphTable
->RightOf(aPresContext
, this);
2497 // empty slots are filled with the glue if it is not null
2498 if (!ch
.Exists()) ch
= chGlue
;
2499 // if (!ch.Exists()) glue is null, leave bounding metrics at 0.
2501 SetFontFamily(aPresContext
, aRenderingContext
,
2502 aFont
, aGlyphTable
, ch
, mFamily
);
2503 rv
= aRenderingContext
.GetBoundingMetrics(&ch
.code
, 1, bmdata
[i
]);
2504 if (NS_FAILED(rv
)) {
2505 NS_WARNING("GetBoundingMetrics failed");
2512 nscoord dy
= aRect
.y
+ mBoundingMetrics
.ascent
;
2513 nscoord offset
[3], start
[3], end
[3];
2514 nsRefPtr
<gfxContext
> ctx
= aRenderingContext
.ThebesContext();
2515 for (i
= 0; i
<= right
; ++i
) {
2517 const nsBoundingMetrics
& bm
= bmdata
[i
];
2519 if (0 == i
) { // left
2520 dx
= aRect
.x
- bm
.leftBearing
;
2522 else if (right
== i
) { // right
2523 dx
= aRect
.x
+ aRect
.width
- bm
.rightBearing
;
2526 dx
= aRect
.x
+ (aRect
.width
- bm
.width
)/2;
2528 // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
2529 // Do this now so that we can get the other dimensions right.
2530 // (This may not achieve much with non-rectangular transformations.)
2531 dx
= SnapToDevPixels(ctx
, oneDevPixel
, nsPoint(dx
, dy
)).x
;
2532 // abcissa passed to DrawString
2534 // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
2535 // pixel, so the bm values can include 1 row of faint pixels on each edge.
2536 // Don't rely on this pixel as it can look like a gap.
2537 start
[i
] = dx
+ bm
.leftBearing
+ oneDevPixel
; // left join
2538 end
[i
] = dx
+ bm
.rightBearing
- oneDevPixel
; // right join
2541 // If there are overlaps, then join at the mid point
2542 for (i
= 0; i
< right
; ++i
) {
2543 if (end
[i
] > start
[i
+1]) {
2544 end
[i
] = (end
[i
] + start
[i
+1]) / 2;
2545 start
[i
+1] = end
[i
];
2549 nsRect unionRect
= aRect
;
2550 unionRect
.Inflate(oneDevPixel
, oneDevPixel
);
2552 ///////////////////////////
2553 // draw left, middle, right
2554 for (i
= 0; i
<= right
; ++i
) {
2556 // glue can be null, and other parts could have been set to glue
2559 aRenderingContext
.SetColor(NS_RGB(255,0,0));
2560 aRenderingContext
.DrawRect(nsRect(start
[i
], dy
- bmdata
[i
].ascent
,
2561 end
[i
] - start
[i
], bmdata
[i
].ascent
+ bmdata
[i
].descent
));
2563 nscoord dx
= offset
[i
];
2564 nsRect clipRect
= unionRect
;
2565 // Clip at the join to get a solid edge (without overlap or gap), when
2566 // this won't change the glyph too much. If the glyph is too small to
2567 // clip then we'll overlap rather than have a gap.
2568 nscoord width
= bmdata
[i
].rightBearing
- bmdata
[i
].leftBearing
;
2570 width
* (1.0 - NS_MATHML_DELIMITER_FACTOR
) > oneDevPixel
) {
2571 if (0 == i
) { // left
2572 clipRect
.width
= end
[i
] - clipRect
.x
;
2574 else if (right
== i
) { // right
2575 clipRect
.width
-= start
[i
] - clipRect
.x
;
2576 clipRect
.x
= start
[i
];
2579 clipRect
.x
= start
[i
];
2580 clipRect
.width
= end
[i
] - start
[i
];
2583 if (!clipRect
.IsEmpty()) {
2584 AutoPushClipRect
clip(aRenderingContext
, clipRect
);
2585 SetFontFamily(aPresContext
, aRenderingContext
,
2586 aFont
, aGlyphTable
, ch
, mFamily
);
2587 aRenderingContext
.DrawString(&ch
.code
, 1, dx
, dy
);
2593 // fill the gap between left and middle, and between middle and right.
2594 if (!chGlue
.Exists()) { // null glue : draw a rule
2595 // figure out the dimensions of the rule to be drawn :
2596 // set ascent to lowest ascent among the two current successive parts.
2597 // set descent to highest descent among the two current successive parts.
2598 // this satisfies the convention used for over/underbraces, and helps
2599 // fix broken fonts.
2600 nscoord ascent
, descent
;
2601 PRInt32 first
= 0, last
= 1;
2602 while (last
<= right
) {
2603 if (chdata
[last
].Exists()) {
2604 ascent
= bmdata
[last
].ascent
;
2605 descent
= bmdata
[last
].descent
;
2606 if (chdata
[first
].Exists()) {
2607 if (ascent
> bmdata
[first
].ascent
)
2608 ascent
= bmdata
[first
].ascent
;
2609 if (descent
> bmdata
[first
].descent
)
2610 descent
= bmdata
[first
].descent
;
2613 else if (chdata
[first
].Exists()) {
2614 ascent
= bmdata
[first
].ascent
;
2615 descent
= bmdata
[first
].descent
;
2618 NS_ERROR("Cannot stretch - All parts missing");
2619 return NS_ERROR_UNEXPECTED
;
2621 // paint the rule between the parts
2622 nsRect
rule(end
[first
], dy
- ascent
,
2623 start
[last
] - end
[first
], ascent
+ descent
);
2624 if (!rule
.IsEmpty())
2625 aRenderingContext
.FillRect(rule
);
2630 else if (bmdata
[glue
].rightBearing
- bmdata
[glue
].leftBearing
> 0) {
2632 nsBoundingMetrics
& bm
= bmdata
[glue
];
2633 // Ensure the stride for the glue is not reduced to less than one pixel
2634 if (bm
.rightBearing
- bm
.leftBearing
>= 3 * oneDevPixel
) {
2635 // To protect against gaps, pretend the glue is smaller than it is,
2636 // in order to trim off ends and thus get a solid edge for the join.
2637 bm
.leftBearing
+= oneDevPixel
;
2638 bm
.rightBearing
-= oneDevPixel
;
2641 SetFontFamily(aPresContext
, aRenderingContext
,
2642 aFont
, aGlyphTable
, chGlue
, mFamily
);
2643 nsRect clipRect
= unionRect
;
2645 for (i
= 0; i
< right
; ++i
) {
2646 // Make sure not to draw outside the character
2647 nscoord dx
= NS_MAX(end
[i
], aRect
.x
);
2648 nscoord fillEnd
= NS_MIN(start
[i
+1], aRect
.XMost());
2650 // rectangles in-between that are to be filled
2651 aRenderingContext
.SetColor(NS_RGB(255,0,0));
2653 clipRect
.width
= fillEnd
- dx
;
2654 aRenderingContext
.DrawRect(clipRect
);
2657 while (dx
< fillEnd
) {
2659 clipRect
.width
= NS_MIN(bm
.rightBearing
- bm
.leftBearing
, fillEnd
- dx
);
2660 AutoPushClipRect
clip(aRenderingContext
, clipRect
);
2661 dx
-= bm
.leftBearing
;
2662 aRenderingContext
.DrawString(&chGlue
.code
, 1, dx
, dy
);
2663 dx
+= bm
.rightBearing
;
2667 // last glyph that may cross past its boundary and collide with the next
2668 nscoord width
= bm
.rightBearing
- bm
.leftBearing
;
2669 aRenderingContext
.SetColor(NS_RGB(0,255,0));
2670 aRenderingContext
.DrawRect(nsRect(dx
+ bm
.leftBearing
, aRect
.y
, width
, aRect
.height
));
2676 for (i
= 0; i
< right
; ++i
) {
2677 NS_ASSERTION(end
[i
] >= start
[i
+1],
2678 "gap between parts with missing glue glyph");