Backed out changeset b88172246b66 due to Win32 debug failures.
[mozilla-central.git] / layout / mathml / nsMathMLChar.cpp
blob59a74b95238cc823c6b2acaa91649a345df4034e
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
42 #include "nsCOMPtr.h"
43 #include "nsFrame.h"
44 #include "nsPresContext.h"
45 #include "nsStyleContext.h"
46 #include "nsStyleConsts.h"
47 #include "nsString.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"
70 #endif
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
136 static PRBool
137 CheckFontExistence(nsPresContext* aPresContext, const nsString& aFontName)
139 PRBool aliased;
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)
146 return rv;
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)
152 static void
153 AlertMissingFonts(nsString& aMissingFonts)
155 nsCOMPtr<nsIStringBundleService> sbs =
156 mozilla::services::GetStringBundleService();
157 if (!sbs)
158 return;
160 nsCOMPtr<nsIStringBundle> sb;
161 sbs->CreateBundle("resource://gre/res/fonts/mathfont.properties", getter_AddRefs(sb));
162 if (!sb)
163 return;
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.
174 #endif
176 // helper to trim off comments from data in a MathFont Property File
177 static void
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
187 static nsresult
188 LoadProperties(const nsString& aName,
189 nsCOMPtr<nsIPersistentProperties>& aProperties)
191 nsAutoString uriStr;
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 // -----------------------------------------------------------------------------------
202 class nsGlyphTable {
203 public:
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),
208 mCharCache(0)
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
221 return mFontName[0];
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);
268 private:
269 nsGlyphCode ElementAt(nsPresContext* aPresContext, nsMathMLChar* aChar, PRUint32 aPosition);
271 // The type is either NS_TABLE_TYPE_UNICODE or NS_TABLE_TYPE_GLYPH_INDEX
272 PRInt32 mType;
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
280 PRInt32 mState;
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;
305 nsGlyphCode
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);
312 #ifdef NS_DEBUG
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",
319 uriStr.get(),
320 (NS_FAILED(rv)) ? "Failed" : "Done");
321 #endif
322 if (NS_FAILED(rv)) {
323 mState = NS_TABLE_STATE_ERROR; // never waste time with this table again
324 return kNullGlyph;
326 mState = NS_TABLE_STATE_READY;
328 // see if there are external fonts needed for certain chars in this table
329 nsCAutoString key;
330 nsAutoString value;
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;
336 Clean(value);
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
349 // as such ...
350 char key[10]; PR_snprintf(key, sizeof(key), "\\u%04X", uchar);
351 nsAutoString value;
352 nsresult rv = mGlyphProperties->GetStringProperty(nsDependentCString(key), value);
353 if (NS_FAILED(rv)) return kNullGlyph;
354 Clean(value);
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
361 nsAutoString buffer;
362 PRInt32 length = value.Length();
363 PRInt32 i = 0; // index in value
364 PRInt32 j = 0; // part/variant index
365 while (i < length) {
366 PRUnichar code = value[i];
367 ++i;
368 PRUnichar font = 0;
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
372 j = -1;
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('.'))) {
386 ++i;
387 // Need to implement this if we want it:
388 // Set (new) code from the value[i] position for (current) code.
389 if (1)
390 return kNullGlyph;
391 ++i;
393 #endif
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')) {
398 ++i;
399 font = value[i] - '0';
400 ++i;
401 if (font >= mFontName.Length()) {
402 NS_ERROR("Nonexistent font referenced in glyph table");
403 return kNullGlyph;
405 // The char cannot be handled if this font is not installed
406 if (!mFontName[font].Length() || !CheckFontExistence(aPresContext, mFontName[font])) {
407 return kNullGlyph;
410 buffer.Append(code);
411 buffer.Append(font);
412 ++j;
414 // update our cache with the new settings
415 mGlyphCache.Assign(buffer);
416 mCharCache = uchar;
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))) {
424 return kNullGlyph;
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.
429 PRUint32 offset = 0;
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;
442 nsGlyphCode ch;
443 ch.code = mGlyphCache.CharAt(index);
444 ch.font = mGlyphCache.CharAt(index + 1);
445 return (ch.code == PRUnichar(0xFFFD)) ? kNullGlyph : ch;
448 PRBool
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));
462 PRInt32
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);
471 PRBool
472 nsGlyphTable::Has(nsPresContext* aPresContext, nsMathMLChar* aChar)
474 return HasVariantsOf(aPresContext, aChar) || HasPartsOf(aPresContext, aChar);
477 PRBool
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();
484 PRBool
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
503 public:
504 NS_DECL_ISUPPORTS
505 NS_DECL_NSIOBSERVER
507 nsGlyphTable mUnicodeTable;
509 nsGlyphTableList()
510 : mUnicodeTable(NS_LITERAL_STRING("Unicode"))
512 MOZ_COUNT_CTOR(nsGlyphTableList);
515 virtual ~nsGlyphTableList()
517 MOZ_COUNT_DTOR(nsGlyphTableList);
520 nsresult Initialize();
521 nsresult Finalize();
523 // Add a glyph table in the list, return the new table that was added
524 nsGlyphTable*
525 AddGlyphTable(const nsString& aPrimaryFontName);
527 // Find a glyph table in the list that has a glyph for the given char
528 nsGlyphTable*
529 GetGlyphTableFor(nsPresContext* aPresContext,
530 nsMathMLChar* aChar);
532 // Find the glyph table in the list corresponding to the given font family.
533 nsGlyphTable*
534 GetGlyphTableFor(const nsAString& aFamily);
536 private:
537 nsGlyphTable* TableAt(PRInt32 aIndex) {
538 return &mTableList.ElementAt(aIndex);
540 PRInt32 Count() {
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
557 NS_IMETHODIMP
558 nsGlyphTableList::Observe(nsISupports* aSubject,
559 const char* aTopic,
560 const PRUnichar* someData)
562 Finalize();
563 return NS_OK;
566 // Add an observer to XPCOM shutdown so that we can free our data at shutdown
567 nsresult
568 nsGlyphTableList::Initialize()
570 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
571 if (!obs)
572 return NS_ERROR_FAILURE;
574 nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
575 NS_ENSURE_SUCCESS(rv, rv);
577 return NS_OK;
580 // Remove our observer and free the memory that were allocated for us
581 nsresult
582 nsGlyphTableList::Finalize()
584 // Remove our observer from the observer service
585 nsresult rv = NS_OK;
586 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
587 if (obs)
588 rv = obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
589 else
590 rv = NS_ERROR_FAILURE;
592 gInitialized = PR_FALSE;
593 // our oneself will be destroyed when our |Release| is called by the observer
594 return rv;
597 nsGlyphTable*
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)
603 return glyphTable;
605 // allocate a table
606 glyphTable = mTableList.AppendElement(aPrimaryFontName);
607 return glyphTable;
610 nsGlyphTable*
611 nsGlyphTableList::GetGlyphTableFor(nsPresContext* aPresContext,
612 nsMathMLChar* aChar)
614 if (mUnicodeTable.Has(aPresContext, aChar))
615 return &mUnicodeTable;
617 PRInt32 i;
618 for (i = 0; i < Count(); i++) {
619 nsGlyphTable* glyphTable = TableAt(i);
620 if (glyphTable->Has(aPresContext, aChar)) {
621 return glyphTable;
624 return nsnull;
627 nsGlyphTable*
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())) {
635 return glyphTable;
638 // Fall back to default Unicode table
639 return &mUnicodeTable;
642 // -----------------------------------------------------------------------------------
644 // retrieve a pref value set by the user
645 static PRBool
646 GetPrefValue(nsIPrefBranch* aPrefBranch, const char* aPrefKey, nsString& aPrefValue)
648 aPrefValue.Truncate();
649 if (aPrefBranch) {
650 nsCOMPtr<nsISupportsString> prefString;
651 aPrefBranch->GetComplexValue(aPrefKey,
652 NS_GET_IID(nsISupportsString),
653 getter_AddRefs(prefString));
654 if (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
666 // font families.
667 static PRBool
668 GetFontExtensionPref(nsIPrefBranch* aPrefBranch, PRUnichar aChar,
669 nsMathfontPrefExtension aExtension, nsString& aValue)
671 // initialize OUT param
672 aValue.Truncate();
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;
686 switch (aExtension)
688 case eExtension_base:
689 extension.AssignLiteral(".base");
690 break;
691 case eExtension_variants:
692 extension.AssignLiteral(".variants");
693 break;
694 case eExtension_parts:
695 extension.AssignLiteral(".parts");
696 break;
697 default:
698 return PR_FALSE;
701 // .\\uNNNN key
702 nsCAutoString key;
703 key.AssignASCII(kMathFontPrefix);
704 char ustr[10];
705 PR_snprintf(ustr, sizeof(ustr), "\\u%04X", aChar);
706 key.Append(ustr);
707 key.Append(extension);
708 // .\uNNNN key
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;
724 #endif
726 static PRBool
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)) {
735 //#ifndef _WIN32
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
741 //#endif
742 if (!missingFamilyList->IsEmpty()) {
743 missingFamilyList->AppendLiteral(", ");
745 missingFamilyList->Append(aFamily);
747 #endif
749 if (!gGlyphTableList->AddGlyphTable(aFamily))
750 return PR_FALSE; // stop in low-memory situations
751 return PR_TRUE; // don't stop
754 static nsresult
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();
766 if (NS_FAILED(rv)) {
767 delete gGlyphTableList;
768 gGlyphTableList = nsnull;
769 return rv;
772 else
773 The gGlyphTableList has been successfully registered as a shutdown observer.
774 It will be deleted at shutdown, even if a failure happens below.
777 nsCAutoString key;
778 nsAutoString value;
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
787 value.Truncate();
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);
815 #else
816 font.EnumerateFamilies(MathFontEnumCallback, nsnull);
817 #endif
818 return rv;
821 // -----------------------------------------------------------------------------------
822 // And now the implementation of nsMathMLChar
824 nsStyleContext*
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;
830 return NS_OK;
833 void
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) {
839 if (mStyleContext)
840 mStyleContext->Release();
841 if (aStyleContext) {
842 mStyleContext = aStyleContext;
843 aStyleContext->AddRef();
845 // Sync the pointers of child chars.
846 nsMathMLChar* child = mSibling;
847 while (child) {
848 child->mStyleContext = mStyleContext;
849 child = child->mSibling;
855 void
856 nsMathMLChar::SetData(nsPresContext* aPresContext,
857 nsString& aData)
859 NS_ASSERTION(!mParent, "invalid call - not allowed for child chars");
860 if (!gInitialized) {
861 InitGlobals(aPresContext);
863 mData = aData;
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
873 // to be used)
874 mGlyphTable = gGlyphTableList->GetGlyphTableFor(aPresContext, this);
878 // -----------------------------------------------------------------------------------
880 The Stretch:
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.
886 How it works?
887 Summary:-
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.
895 Details:-
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.
901 Issues :
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
915 to the base size.
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".
923 Issues :
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.
934 Issues:
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
942 from child chars.
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
952 is used.
954 Of note:
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
968 static PRBool
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>
975 PRBool isNormal =
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
989 // choices
990 PRBool isSmaller =
991 (aHint & NS_STRETCH_SMALLER)
992 && PRBool((float(a) >= (NS_MATHML_DELIMITER_FACTOR * float(b)))
993 && (a <= b));
994 // Larger: Critical to the sqrt code to ensure that the radical
995 // size is tall enough
996 PRBool isLarger =
997 (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
998 && PRBool(a >= b);
999 return (isNormal || isSmaller || isNearer || isLarger);
1002 static PRBool
1003 IsSizeBetter(nscoord a, nscoord olda, nscoord b, PRUint32 aHint)
1005 if (0 == olda)
1006 return PR_TRUE;
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
1019 // with long glues.
1020 static nscoord
1021 ComputeSizeFromParts(nsPresContext* aPresContext,
1022 nsGlyphCode* aGlyphs,
1023 nscoord* aSizes,
1024 nscoord aTargetSize)
1026 enum {first, middle, last, glue};
1027 // Add the parts that cannot be left out.
1028 nscoord sum = 0;
1029 for (PRInt32 i = first; i <= last; i++) {
1030 if (aGlyphs[i] != aGlyphs[glue]) {
1031 sum += aSizes[i];
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
1056 return aTargetSize;
1059 // Insert aFallbackFamilies before the first generic family in or at the end
1060 // of a CSS aFontName.
1061 static void
1062 AddFallbackFonts(nsAString& aFontName, const nsAString& aFallbackFamilies)
1064 if (aFallbackFamilies.IsEmpty())
1065 return;
1067 if (aFontName.IsEmpty()) {
1068 return;
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;
1081 while (p < p_end) {
1082 while (nsCRT::IsAsciiSpace(*p))
1083 if (++p == p_end)
1084 goto insert;
1086 p_name = p;
1087 if (*p == kSingleQuote || *p == kDoubleQuote) {
1088 // quoted font family
1089 PRUnichar quoteMark = *p;
1090 if (++p == p_end)
1091 goto insert;
1093 // XXX What about CSS character escapes?
1094 while (*p != quoteMark)
1095 if (++p == p_end)
1096 goto insert;
1098 while (++p != p_end && *p != kComma)
1099 /* nothing */ ;
1101 } else {
1102 // unquoted font family
1103 const PRUnichar *nameStart = p;
1104 while (++p != p_end && *p != kComma)
1105 /* nothing */ ;
1107 nsAutoString family;
1108 family = Substring(nameStart, p);
1109 family.CompressWhitespace(PR_FALSE, PR_TRUE);
1111 PRUint8 generic;
1112 nsFont::GetGenericID(family, &generic);
1113 if (generic != kGenericFont_NONE)
1114 goto insert;
1117 ++p; // may advance past p_end
1120 aFontName.Append(NS_LITERAL_STRING(",") + aFallbackFamilies);
1121 return;
1123 insert:
1124 if (p_name) {
1125 aFontName.Insert(aFallbackFamilies + NS_LITERAL_STRING(","),
1126 p_name - p_begin);
1128 else { // whitespace or empty
1129 aFontName = aFallbackFamilies;
1133 // Update the font and rendering context if there is a family change
1134 static void
1135 SetFontFamily(nsPresContext* aPresContext,
1136 nsIRenderingContext& aRenderingContext,
1137 nsFont& aFont,
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 {
1151 public:
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)
1160 : mChar(aChar),
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) {}
1171 static PRBool
1172 EnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData);
1174 private:
1175 static PRBool
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;
1191 public:
1192 PRPackedBool mTryVariants;
1193 PRPackedBool mTryParts;
1195 private:
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.
1204 PRBool
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;
1219 nscoord bestSize =
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()) {
1227 size = 2;
1229 #ifdef NOISY_SEARCH
1230 printf(" searching in %s ...\n",
1231 NS_LossyConvertUTF16toASCII(aFamily).get());
1232 #endif
1234 nsGlyphCode ch;
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)) {
1247 nscoord charSize =
1248 isVertical ? bm.ascent + bm.descent
1249 : bm.rightBearing - bm.leftBearing;
1251 if (largeopOnly ||
1252 IsSizeBetter(charSize, bestSize, mTargetSize, mStretchHint)) {
1253 if (maxWidth) {
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;
1265 else {
1266 mBoundingMetrics = bm;
1267 haveBetter = PR_TRUE;
1268 bestSize = charSize;
1269 mChar->mGlyphTable = aGlyphTable;
1270 mChar->mGlyph = ch;
1271 mChar->mFamily = font.name;
1273 #ifdef NOISY_SEARCH
1274 printf(" size:%d Current best\n", size);
1275 #endif
1277 else {
1278 #ifdef NOISY_SEARCH
1279 printf(" size:%d Rejected!\n", size);
1280 #endif
1281 if (haveBetter)
1282 break; // Not making an futher progress, stop searching
1286 // if largeopOnly is set, break now
1287 if (largeopOnly) break;
1288 ++size;
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.
1298 PRBool
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;
1309 nsresult rv =
1310 mChar->ComposeChildren(mPresContext, mRenderingContext, aGlyphTable,
1311 mTargetSize, compositeSize, mStretchHint);
1312 #ifdef NOISY_SEARCH
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");
1317 #endif
1318 if (NS_FAILED(rv))
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++) {
1345 nsGlyphCode ch;
1346 switch (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;
1355 chdata[i] = ch;
1356 if (!ch.Exists()) {
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;
1361 else {
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
1373 // font.
1374 bmdata[i] = bm;
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,
1383 mTargetSize);
1385 nscoord currentSize =
1386 isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
1387 : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
1389 if (!IsSizeBetter(computedSize, currentSize, mTargetSize, mStretchHint)) {
1390 #ifdef NOISY_SEARCH
1391 printf(" Font %s Rejected!\n",
1392 NS_LossyConvertUTF16toASCII(fontName).get());
1393 #endif
1394 return PR_FALSE; // to next table
1397 #ifdef NOISY_SEARCH
1398 printf(" Font %s Current best!\n",
1399 NS_LossyConvertUTF16toASCII(fontName).get());
1400 #endif
1402 // The computed size is the best we have found so far...
1403 // now is the time to compute and cache our bounding metrics
1404 if (isVertical) {
1405 PRInt32 i;
1406 nscoord lbearing;
1407 nscoord rbearing;
1408 nscoord width;
1409 if (maxWidth) {
1410 lbearing = mBoundingMetrics.leftBearing;
1411 rbearing = mBoundingMetrics.rightBearing;
1412 width = mBoundingMetrics.width;
1413 i = 0;
1415 else {
1416 lbearing = bmdata[0].leftBearing;
1417 rbearing = bmdata[0].rightBearing;
1418 width = bmdata[0].width;
1419 i = 1;
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;
1436 else {
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;
1450 if (maxWidth)
1451 return PR_FALSE; // Continue to check other sizes
1453 // reset
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
1463 PRBool
1464 nsMathMLChar::StretchEnumContext::ResolverCallback (const nsAString& aFamily,
1465 void *aData)
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);
1481 if (isOK)
1482 return PR_FALSE; // no need to continue
1485 if(context->mTryParts) {
1486 PRBool isOK = context->TryParts(glyphTable, family);
1487 if (isOK)
1488 return PR_FALSE; // no need to continue
1490 return PR_TRUE;
1493 // This is called for each family, whether it exists or not
1494 PRBool
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;
1510 if (aGeneric)
1511 return ResolverCallback(aFamily, aData);
1513 PRBool aborted;
1514 gfxPlatform *pf = gfxPlatform::GetPlatform();
1515 nsresult rv =
1516 pf->ResolveFontName(aFamily, ResolverCallback, aData, aborted);
1517 return NS_SUCCEEDED(rv) && !aborted; // true means continue
1520 nsresult
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:
1529 float aMaxSize,
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;
1551 if (!maxWidth) {
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.
1554 mFamily = families;
1557 aRenderingContext.SetFont(font, aPresContext->GetUserFontSet());
1558 nsresult rv =
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;
1564 return rv;
1567 if (!maxWidth) {
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;
1581 return NS_OK;
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;
1600 if (maxWidth) {
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.
1605 if (stretchy) {
1606 // variable size stretch - consider all sizes < maxsize
1607 aStretchHint =
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;
1617 else {
1618 nscoord height = aDesiredStretchSize.ascent + aDesiredStretchSize.descent;
1619 if (height == 0) {
1620 if (aMaxSizeIsAbsolute) {
1621 aDesiredStretchSize.ascent =
1622 NSToCoordRound(aMaxSize / NS_MATHML_DELIMITER_FACTOR);
1623 aDesiredStretchSize.descent = 0;
1625 // else: leave height as 0
1627 else {
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;
1639 nscoord charSize =
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)))
1651 done = PR_TRUE;
1654 ////////////////////////////////////////////////////////////////////////////////////
1655 // 2/3. Search for a glyph or set of part glyphs of appropriate size
1656 ////////////////////////////////////////////////////////////////////////////////////
1658 nsAutoString cssFamilies;
1660 if (!done) {
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,
1667 families)) {
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,
1681 families)) {
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);
1701 #ifdef NOISY_SEARCH
1702 printf("Searching in "%s" for a glyph of appropriate size for: 0x%04X:%c\n",
1703 font.name, mData[0], mData[0]&0x00FF);
1704 #endif
1705 StretchEnumContext enumData(this, aPresContext, aRenderingContext,
1706 aStretchDirection, targetSize, aStretchHint,
1707 aDesiredStretchSize, font.name);
1708 enumData.mTryParts = !largeopOnly;
1710 font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData);
1713 if (!maxWidth) {
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
1721 if (stretchy) {
1722 if (isVertical) {
1723 float scale =
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.
1728 mScaleY *= scale;
1729 aDesiredStretchSize.ascent *= scale;
1730 aDesiredStretchSize.descent *= scale;
1732 } else {
1733 float 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.
1738 mScaleX *= scale;
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) {
1749 float scale;
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);
1759 mScaleX *= scale;
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);
1776 mScaleY *= scale;
1777 aDesiredStretchSize.ascent *= scale;
1778 aDesiredStretchSize.descent *= scale;
1782 return NS_OK;
1785 nsresult
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
1799 mGlyph.font = -1;
1801 mDirection = aStretchDirection;
1802 nsresult rv =
1803 StretchInternal(aPresContext, aRenderingContext, mDirection,
1804 aContainerSize, aDesiredStretchSize, aStretchHint);
1806 // Record the metrics
1807 mBoundingMetrics = aDesiredStretchSize;
1809 return rv;
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
1823 // considered.
1824 nscoord
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);
1840 nsresult
1841 nsMathMLChar::ComposeChildren(nsPresContext* aPresContext,
1842 nsIRenderingContext& aRenderingContext,
1843 nsGlyphTable* aGlyphTable,
1844 nscoord aTargetSize,
1845 nsBoundingMetrics& aCompositeSize,
1846 PRUint32 aStretchHint)
1848 PRInt32 i = 0;
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) {
1857 i++;
1858 last = last->mSibling;
1860 while (i < count) {
1861 child = new nsMathMLChar(this);
1862 if (!child) {
1863 if (mSibling) delete mSibling; // don't leave a dangling list ...
1864 mSibling = nsnull;
1865 return NS_ERROR_OUT_OF_MEMORY;
1867 last->mSibling = child;
1868 last = child;
1869 i++;
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;
1879 else {
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 ...
1897 mSibling = nsnull;
1898 return NS_ERROR_FAILURE;
1900 child->SetRect(nsRect(dx, dy, childSize.width, childSize.ascent+childSize.descent));
1901 if (0 == i)
1902 aCompositeSize = childSize;
1903 else {
1904 if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
1905 aCompositeSize += childSize;
1906 else {
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;
1916 else
1917 dy += childSize.ascent + childSize.descent;
1919 return NS_OK;
1922 class nsDisplayMathMLSelectionRect : public nsDisplayItem {
1923 public:
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);
1933 #endif
1935 virtual void Paint(nsDisplayListBuilder* aBuilder,
1936 nsIRenderingContext* aCtx);
1937 NS_DISPLAY_DECL_NAME("MathMLSelectionRect", TYPE_MATHML_SELECTION_RECT)
1938 private:
1939 nsRect mRect;
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 {
1954 public:
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);
1965 #endif
1967 virtual void Paint(nsDisplayListBuilder* aBuilder,
1968 nsIRenderingContext* aCtx);
1969 NS_DISPLAY_DECL_NAME("MathMLCharBackground", TYPE_MATHML_CHAR_BACKGROUND)
1970 private:
1971 nsStyleContext* mStyleContext;
1972 nsRect mRect;
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,
1981 mVisibleRect, rect,
1982 mStyleContext, *border,
1983 aBuilder->GetBackgroundPaintFlags());
1986 class nsDisplayMathMLCharForeground : public nsDisplayItem {
1987 public:
1988 nsDisplayMathMLCharForeground(nsDisplayListBuilder* aBuilder,
1989 nsIFrame* aFrame, nsMathMLChar* aChar,
1990 PRBool aIsSelected)
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);
1998 #endif
2000 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
2001 nsRect rect;
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);
2024 private:
2025 nsMathMLChar* mChar;
2026 PRPackedBool mIsSelected;
2029 #ifdef NS_DEBUG
2030 class nsDisplayMathMLCharDebug : public nsDisplayItem {
2031 public:
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);
2041 #endif
2043 virtual void Paint(nsDisplayListBuilder* aBuilder,
2044 nsIRenderingContext* aCtx);
2045 NS_DISPLAY_DECL_NAME("MathMLCharDebug", TYPE_MATHML_CHAR_DEBUG)
2047 private:
2048 nsRect mRect;
2051 void nsDisplayMathMLCharDebug::Paint(nsDisplayListBuilder* aBuilder,
2052 nsIRenderingContext* aCtx)
2054 // for visual debug
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);
2064 #endif
2067 nsresult
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;
2077 if (mDrawNormal) {
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())
2084 return NS_OK;
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);
2103 //else
2104 // our container frame will take care of painting its background
2106 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
2107 // for visual debug
2108 rv = aLists.BorderBackground()->AppendToTop(new (aBuilder)
2109 nsDisplayMathMLCharDebug(aBuilder, aForFrame, mRect));
2110 NS_ENSURE_SUCCESS(rv, rv);
2111 #endif
2113 return aLists.Content()->AppendNewToTop(new (aBuilder)
2114 nsDisplayMathMLCharForeground(aBuilder, aForFrame, this,
2115 aSelectedRect && !aSelectedRect->IsEmpty()));
2118 void
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.
2126 r.x = r.y = 0;
2127 r.width /= mScaleX;
2128 r.height /= mScaleY;
2131 void
2132 nsMathMLChar::PaintForeground(nsPresContext* aPresContext,
2133 nsIRenderingContext& aRenderingContext,
2134 nsPoint aPt,
2135 PRBool aIsSelected)
2137 nsStyleContext* parentContext = mStyleContext->GetParent();
2138 nsStyleContext* styleContext = mStyleContext;
2140 if (mDrawNormal) {
2141 // normal drawing if there is nothing special about this char
2142 // Set default context to the parent context
2143 styleContext = parentContext;
2146 // Set color ...
2147 nscolor fgColor = styleContext->GetStyleColor()->mColor;
2148 if (aIsSelected) {
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);
2165 if (mDrawNormal) {
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);
2173 else {
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,
2185 mGlyphTable, r);
2186 else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
2187 PaintHorizontally(aPresContext, aRenderingContext, theFont, styleContext,
2188 mGlyphTable, r);
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;
2201 public:
2202 AutoPushClipRect(nsIRenderingContext& aCtx, const nsRect& aRect)
2203 : mCtx(aCtx) {
2204 mCtx.PushState();
2205 mCtx.SetClipRect(aRect, nsClipCombine_kIntersect);
2207 ~AutoPushClipRect() {
2208 mCtx.PopState();
2212 static nsPoint
2213 SnapToDevPixels(const gfxContext* aThebesContext, PRInt32 aAppUnitsPerGfxUnit,
2214 const nsPoint& aPt)
2216 gfxPoint pt(NSAppUnitsToFloatPixels(aPt.x, aAppUnitsPerGfxUnit),
2217 NSAppUnitsToFloatPixels(aPt.y, aAppUnitsPerGfxUnit));
2218 pt = aThebesContext->UserToDevice(pt);
2219 pt.Round();
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
2226 nsresult
2227 nsMathMLChar::PaintVertically(nsPresContext* aPresContext,
2228 nsIRenderingContext& aRenderingContext,
2229 nsFont& aFont,
2230 nsStyleContext* aStyleContext,
2231 nsGlyphTable* aGlyphTable,
2232 nsRect& aRect)
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
2240 PRInt32 i = 0;
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) {
2246 switch (j) {
2247 case 0:
2248 ch = aGlyphTable->TopOf(aPresContext, this);
2249 break;
2250 case 1:
2251 ch = aGlyphTable->MiddleOf(aPresContext, this);
2252 if (!ch.Exists())
2253 continue; // no middle
2254 break;
2255 case 2:
2256 ch = aGlyphTable->BottomOf(aPresContext, this);
2257 bottom = i;
2258 break;
2259 case 3:
2260 ch = chGlue;
2261 glue = i;
2262 break;
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
2267 if (ch.Exists()) {
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");
2273 return rv;
2276 chdata[i] = ch;
2277 ++i;
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) {
2283 ch = chdata[i];
2284 const nsBoundingMetrics& bm = bmdata[i];
2285 nscoord dy;
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;
2292 else { // middle
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
2300 offset[i] = dy;
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;
2318 unionRect.width =
2319 mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
2320 unionRect.Inflate(oneDevPixel, oneDevPixel);
2322 /////////////////////////////////////
2323 // draw top, middle, bottom
2324 for (i = 0; i <= bottom; ++i) {
2325 ch = chdata[i];
2326 // glue can be null, and other parts could have been set to glue
2327 if (ch.Exists()) {
2328 #ifdef SHOW_BORDERS
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]));
2332 #endif
2333 nscoord dy = offset[i];
2334 // Draw a glyph in a clipped area so that we don't have hairy chars
2335 // pending outside
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;
2341 if (ch == chGlue ||
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];
2350 else { // middle
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);
2364 ///////////////
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;
2390 else {
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);
2399 first = last;
2400 last++;
2403 else if (bmdata[glue].ascent + bmdata[glue].descent > 0) {
2404 // glue is present
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());
2422 #ifdef SHOW_BORDERS
2423 // exact area to fill
2424 aRenderingContext.SetColor(NS_RGB(255,0,0));
2425 clipRect.y = dy;
2426 clipRect.height = fillEnd - dy;
2427 aRenderingContext.DrawRect(clipRect);
2429 #endif
2430 while (dy < fillEnd) {
2431 clipRect.y = dy;
2432 clipRect.height = NS_MIN(bm.ascent + bm.descent, fillEnd - dy);
2433 AutoPushClipRect clip(aRenderingContext, clipRect);
2434 dy += bm.ascent;
2435 aRenderingContext.DrawString(&chGlue.code, 1, dx, dy);
2436 dy += bm.descent;
2438 #ifdef SHOW_BORDERS
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));
2444 #endif
2447 #ifdef DEBUG
2448 else {
2449 for (i = 0; i < bottom; ++i) {
2450 NS_ASSERTION(end[i] >= start[i+1],
2451 "gap between parts with missing glue glyph");
2454 #endif
2455 return NS_OK;
2458 // paint a stretchy char by assembling glyphs horizontally
2459 nsresult
2460 nsMathMLChar::PaintHorizontally(nsPresContext* aPresContext,
2461 nsIRenderingContext& aRenderingContext,
2462 nsFont& aFont,
2463 nsStyleContext* aStyleContext,
2464 nsGlyphTable* aGlyphTable,
2465 nsRect& aRect)
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
2473 PRInt32 i = 0;
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) {
2479 switch (j) {
2480 case 0:
2481 ch = aGlyphTable->LeftOf(aPresContext, this);
2482 break;
2483 case 1:
2484 ch = aGlyphTable->MiddleOf(aPresContext, this);
2485 if (!ch.Exists())
2486 continue; // no middle
2487 break;
2488 case 2:
2489 ch = aGlyphTable->RightOf(aPresContext, this);
2490 right = i;
2491 break;
2492 case 3:
2493 ch = chGlue;
2494 glue = i;
2495 break;
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.
2500 if (ch.Exists()) {
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");
2506 return rv;
2509 chdata[i] = ch;
2510 ++i;
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) {
2516 ch = chdata[i];
2517 const nsBoundingMetrics& bm = bmdata[i];
2518 nscoord dx;
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;
2525 else { // middle
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
2533 offset[i] = dx;
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) {
2555 ch = chdata[i];
2556 // glue can be null, and other parts could have been set to glue
2557 if (ch.Exists()) {
2558 #ifdef SHOW_BORDERS
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));
2562 #endif
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;
2569 if (ch == chGlue ||
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];
2578 else { // middle
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);
2592 ////////////////
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;
2617 else {
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);
2626 first = last;
2627 last++;
2630 else if (bmdata[glue].rightBearing - bmdata[glue].leftBearing > 0) {
2631 // glue is present
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());
2649 #ifdef SHOW_BORDERS
2650 // rectangles in-between that are to be filled
2651 aRenderingContext.SetColor(NS_RGB(255,0,0));
2652 clipRect.x = dx;
2653 clipRect.width = fillEnd - dx;
2654 aRenderingContext.DrawRect(clipRect);
2656 #endif
2657 while (dx < fillEnd) {
2658 clipRect.x = dx;
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;
2665 #ifdef SHOW_BORDERS
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));
2671 #endif
2674 #ifdef DEBUG
2675 else { // no glue
2676 for (i = 0; i < right; ++i) {
2677 NS_ASSERTION(end[i] >= start[i+1],
2678 "gap between parts with missing glue glyph");
2681 #endif
2682 return NS_OK;