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