Bumping manifests a=b2g-bump
[gecko.git] / layout / mathml / nsMathMLChar.cpp
blobe03f42db46d7351f85427995016f7e9397db9059
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsMathMLChar.h"
7 #include "mozilla/MathAlgorithms.h"
9 #include "nsCOMPtr.h"
10 #include "nsIFrame.h"
11 #include "nsPresContext.h"
12 #include "nsStyleContext.h"
13 #include "nsUnicharUtils.h"
14 #include "nsRenderingContext.h"
16 #include "mozilla/Preferences.h"
17 #include "nsIPersistentProperties2.h"
18 #include "nsIObserverService.h"
19 #include "nsIObserver.h"
20 #include "nsNetUtil.h"
22 #include "mozilla/LookAndFeel.h"
23 #include "nsCSSRendering.h"
24 #include "prprf.h" // For PR_snprintf()
26 #include "nsDisplayList.h"
28 #include "nsMathMLOperators.h"
29 #include <algorithm>
31 #include "gfxMathTable.h"
33 using namespace mozilla;
35 //#define NOISY_SEARCH 1
37 // BUG 848725 Drawing failure with stretchy horizontal parenthesis when no fonts
38 // are installed. "kMaxScaleFactor" is required to limit the scale for the
39 // vertical and horizontal stretchy operators.
40 static const float kMaxScaleFactor = 20.0;
41 static const float kLargeOpFactor = float(M_SQRT2);
42 static const float kIntegralFactor = 2.0;
44 static void
45 NormalizeDefaultFont(nsFont& aFont)
47 if (aFont.fontlist.GetDefaultFontType() != eFamily_none) {
48 aFont.fontlist.Append(FontFamilyName(aFont.fontlist.GetDefaultFontType()));
49 aFont.fontlist.SetDefaultFontType(eFamily_none);
53 // -----------------------------------------------------------------------------
54 static const nsGlyphCode kNullGlyph = {{{0, 0}}, 0};
56 // -----------------------------------------------------------------------------
57 // nsGlyphTable is a class that provides an interface for accessing glyphs
58 // of stretchy chars. It acts like a table that stores the variants of bigger
59 // sizes (if any) and the partial glyphs needed to build extensible symbols.
61 // Bigger sizes (if any) of the char can then be retrieved with BigOf(...).
62 // Partial glyphs can be retrieved with ElementAt(...).
64 // A table consists of "nsGlyphCode"s which are viewed either as Unicode
65 // points (for nsPropertiesTable) or as direct glyph indices (for
66 // nsOpenTypeTable)
67 // -----------------------------------------------------------------------------
69 class nsGlyphTable {
70 public:
71 virtual ~nsGlyphTable() {}
73 virtual const FontFamilyName&
74 FontNameFor(const nsGlyphCode& aGlyphCode) const = 0;
76 // Getters for the parts
77 virtual nsGlyphCode ElementAt(gfxContext* aThebesContext,
78 int32_t aAppUnitsPerDevPixel,
79 gfxFontGroup* aFontGroup,
80 char16_t aChar,
81 bool aVertical,
82 uint32_t aPosition) = 0;
83 virtual nsGlyphCode BigOf(gfxContext* aThebesContext,
84 int32_t aAppUnitsPerDevPixel,
85 gfxFontGroup* aFontGroup,
86 char16_t aChar,
87 bool aVertical,
88 uint32_t aSize) = 0;
90 // True if this table contains parts to render this char
91 virtual bool HasPartsOf(gfxContext* aThebesContext,
92 int32_t aAppUnitsPerDevPixel,
93 gfxFontGroup* aFontGroup,
94 char16_t aChar,
95 bool aVertical) = 0;
97 virtual gfxTextRun* MakeTextRun(gfxContext* aThebesContext,
98 int32_t aAppUnitsPerDevPixel,
99 gfxFontGroup* aFontGroup,
100 const nsGlyphCode& aGlyph) = 0;
101 protected:
102 nsGlyphTable() : mCharCache(0) {}
103 // For speedy re-use, we always cache the last data used in the table.
104 // mCharCache is the Unicode point of the last char that was queried in this
105 // table.
106 char16_t mCharCache;
109 // An instance of nsPropertiesTable is associated with one primary font. Extra
110 // glyphs can be taken in other additional fonts when stretching certain
111 // characters.
112 // These supplementary fonts are referred to as "external" fonts to the table.
114 // General format of MathFont Property Files from which glyph data are
115 // retrieved:
116 // -----------------------------------------------------------------------------
117 // Each font should have its set of glyph data. For example, the glyph data for
118 // the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties"
119 // and "mathfontMTExtra.properties", respectively. The mathfont property file
120 // is a set of all the stretchy MathML characters that can be rendered with that
121 // font using larger and/or partial glyphs. The entry of each stretchy character
122 // in the mathfont property file gives, in that order, the 4 partial glyphs:
123 // Top (or Left), Middle, Bottom (or Right), Glue; and the variants of bigger
124 // sizes (if any).
125 // A position that is not relevant to a particular character is indicated there
126 // with the UNICODE REPLACEMENT CHARACTER 0xFFFD.
127 // -----------------------------------------------------------------------------
129 #define NS_TABLE_STATE_ERROR -1
130 #define NS_TABLE_STATE_EMPTY 0
131 #define NS_TABLE_STATE_READY 1
133 // helper to trim off comments from data in a MathFont Property File
134 static void
135 Clean(nsString& aValue)
137 // chop the trailing # comment portion if any ...
138 int32_t comment = aValue.RFindChar('#');
139 if (comment > 0) aValue.Truncate(comment);
140 aValue.CompressWhitespace();
143 // helper to load a MathFont Property File
144 static nsresult
145 LoadProperties(const nsString& aName,
146 nsCOMPtr<nsIPersistentProperties>& aProperties)
148 nsAutoString uriStr;
149 uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
150 uriStr.Append(aName);
151 uriStr.StripWhitespace(); // that may come from aName
152 uriStr.AppendLiteral(".properties");
153 return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties),
154 NS_ConvertUTF16toUTF8(uriStr));
157 class nsPropertiesTable MOZ_FINAL : public nsGlyphTable {
158 public:
159 explicit nsPropertiesTable(const nsString& aPrimaryFontName)
160 : mState(NS_TABLE_STATE_EMPTY)
162 MOZ_COUNT_CTOR(nsPropertiesTable);
163 mGlyphCodeFonts.AppendElement(FontFamilyName(aPrimaryFontName, eUnquotedName));
166 ~nsPropertiesTable()
168 MOZ_COUNT_DTOR(nsPropertiesTable);
171 const FontFamilyName& PrimaryFontName() const
173 return mGlyphCodeFonts[0];
176 const FontFamilyName&
177 FontNameFor(const nsGlyphCode& aGlyphCode) const MOZ_OVERRIDE
179 NS_ASSERTION(!aGlyphCode.IsGlyphID(),
180 "nsPropertiesTable can only access glyphs by code point");
181 return mGlyphCodeFonts[aGlyphCode.font];
184 virtual nsGlyphCode ElementAt(gfxContext* aThebesContext,
185 int32_t aAppUnitsPerDevPixel,
186 gfxFontGroup* aFontGroup,
187 char16_t aChar,
188 bool aVertical,
189 uint32_t aPosition) MOZ_OVERRIDE;
191 virtual nsGlyphCode BigOf(gfxContext* aThebesContext,
192 int32_t aAppUnitsPerDevPixel,
193 gfxFontGroup* aFontGroup,
194 char16_t aChar,
195 bool aVertical,
196 uint32_t aSize) MOZ_OVERRIDE
198 return ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
199 aChar, aVertical, 4 + aSize);
202 virtual bool HasPartsOf(gfxContext* aThebesContext,
203 int32_t aAppUnitsPerDevPixel,
204 gfxFontGroup* aFontGroup,
205 char16_t aChar,
206 bool aVertical) MOZ_OVERRIDE
208 return (ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
209 aChar, aVertical, 0).Exists() ||
210 ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
211 aChar, aVertical, 1).Exists() ||
212 ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
213 aChar, aVertical, 2).Exists() ||
214 ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
215 aChar, aVertical, 3).Exists());
218 virtual gfxTextRun* MakeTextRun(gfxContext* aThebesContext,
219 int32_t aAppUnitsPerDevPixel,
220 gfxFontGroup* aFontGroup,
221 const nsGlyphCode& aGlyph) MOZ_OVERRIDE;
222 private:
224 // mGlyphCodeFonts[0] is the primary font associated to this table. The
225 // others are possible "external" fonts for glyphs not in the primary font
226 // but which are needed to stretch certain characters in the table
227 nsTArray<FontFamilyName> mGlyphCodeFonts;
229 // Tri-state variable for error/empty/ready
230 int32_t mState;
232 // The set of glyph data in this table, as provided by the MathFont Property
233 // File
234 nsCOMPtr<nsIPersistentProperties> mGlyphProperties;
236 // mGlyphCache is a buffer containing the glyph data associated with
237 // mCharCache.
238 // For a property line 'key = value' in the MathFont Property File,
239 // mCharCache will retain the 'key' -- which is a Unicode point, while
240 // mGlyphCache will retain the 'value', which is a consecutive list of
241 // nsGlyphCodes, i.e., the pairs of 'code@font' needed by the char -- in
242 // which 'code@0' can be specified
243 // without the optional '@0'. However, to ease subsequent processing,
244 // mGlyphCache excludes the '@' symbol and explicitly inserts all optional '0'
245 // that indicates the primary font identifier. Specifically therefore, the
246 // k-th glyph is characterized by :
247 // 1) mGlyphCache[3*k],mGlyphCache[3*k+1] : its Unicode point
248 // 2) mGlyphCache[3*k+2] : the numeric identifier of the font where it comes
249 // from.
250 // A font identifier of '0' means the default primary font associated to this
251 // table. Other digits map to the "external" fonts that may have been
252 // specified in the MathFont Property File.
253 nsString mGlyphCache;
256 /* virtual */
257 nsGlyphCode
258 nsPropertiesTable::ElementAt(gfxContext* /* aThebesContext */,
259 int32_t /* aAppUnitsPerDevPixel */,
260 gfxFontGroup* /* aFontGroup */,
261 char16_t aChar,
262 bool /* aVertical */,
263 uint32_t aPosition)
265 if (mState == NS_TABLE_STATE_ERROR) return kNullGlyph;
266 // Load glyph properties if this is the first time we have been here
267 if (mState == NS_TABLE_STATE_EMPTY) {
268 nsAutoString primaryFontName;
269 mGlyphCodeFonts[0].AppendToString(primaryFontName);
270 nsresult rv = LoadProperties(primaryFontName, mGlyphProperties);
271 #ifdef DEBUG
272 nsAutoCString uriStr;
273 uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
274 LossyAppendUTF16toASCII(primaryFontName, uriStr);
275 uriStr.StripWhitespace(); // that may come from mGlyphCodeFonts
276 uriStr.AppendLiteral(".properties");
277 printf("Loading %s ... %s\n",
278 uriStr.get(),
279 (NS_FAILED(rv)) ? "Failed" : "Done");
280 #endif
281 if (NS_FAILED(rv)) {
282 mState = NS_TABLE_STATE_ERROR; // never waste time with this table again
283 return kNullGlyph;
285 mState = NS_TABLE_STATE_READY;
287 // see if there are external fonts needed for certain chars in this table
288 nsAutoCString key;
289 nsAutoString value;
290 for (int32_t i = 1; ; i++) {
291 key.AssignLiteral("external.");
292 key.AppendInt(i, 10);
293 rv = mGlyphProperties->GetStringProperty(key, value);
294 if (NS_FAILED(rv)) break;
295 Clean(value);
296 mGlyphCodeFonts.AppendElement(FontFamilyName(value, eUnquotedName)); // i.e., mGlyphCodeFonts[i] holds this font name
300 // Update our cache if it is not associated to this character
301 if (mCharCache != aChar) {
302 // The key in the property file is interpreted as ASCII and kept
303 // as such ...
304 char key[10]; PR_snprintf(key, sizeof(key), "\\u%04X", aChar);
305 nsAutoString value;
306 nsresult rv = mGlyphProperties->GetStringProperty(nsDependentCString(key),
307 value);
308 if (NS_FAILED(rv)) return kNullGlyph;
309 Clean(value);
310 // See if this char uses external fonts; e.g., if the 2nd glyph is taken
311 // from the external font '1', the property line looks like
312 // \uNNNN = \uNNNN\uNNNN@1\uNNNN.
313 // This is where mGlyphCache is pre-processed to explicitly store all glyph
314 // codes as combined pairs of 'code@font', excluding the '@' separator. This
315 // means that mGlyphCache[3*k],mGlyphCache[3*k+1] will later be rendered
316 // with mGlyphCodeFonts[mGlyphCache[3*k+2]]
317 // Note: font identifier is internally an ASCII digit to avoid the null
318 // char issue
319 nsAutoString buffer;
320 int32_t length = value.Length();
321 int32_t i = 0; // index in value
322 while (i < length) {
323 char16_t code = value[i];
324 ++i;
325 buffer.Append(code);
326 // Read the next word if we have a non-BMP character.
327 if (i < length && NS_IS_HIGH_SURROGATE(code)) {
328 code = value[i];
329 ++i;
330 } else {
331 code = char16_t('\0');
333 buffer.Append(code);
335 // See if an external font is needed for the code point.
336 // Limit of 9 external fonts
337 char16_t font = 0;
338 if (i+1 < length && value[i] == char16_t('@') &&
339 value[i+1] >= char16_t('0') && value[i+1] <= char16_t('9')) {
340 ++i;
341 font = value[i] - '0';
342 ++i;
343 if (font >= mGlyphCodeFonts.Length()) {
344 NS_ERROR("Nonexistent font referenced in glyph table");
345 return kNullGlyph;
347 // The char cannot be handled if this font is not installed
348 if (!mGlyphCodeFonts[font].mName.Length()) {
349 return kNullGlyph;
352 buffer.Append(font);
354 // update our cache with the new settings
355 mGlyphCache.Assign(buffer);
356 mCharCache = aChar;
359 // 3* is to account for the code@font pairs
360 uint32_t index = 3*aPosition;
361 if (index+2 >= mGlyphCache.Length()) return kNullGlyph;
362 nsGlyphCode ch;
363 ch.code[0] = mGlyphCache.CharAt(index);
364 ch.code[1] = mGlyphCache.CharAt(index + 1);
365 ch.font = mGlyphCache.CharAt(index + 2);
366 return ch.code[0] == char16_t(0xFFFD) ? kNullGlyph : ch;
369 /* virtual */
370 gfxTextRun*
371 nsPropertiesTable::MakeTextRun(gfxContext* aThebesContext,
372 int32_t aAppUnitsPerDevPixel,
373 gfxFontGroup* aFontGroup,
374 const nsGlyphCode& aGlyph)
376 NS_ASSERTION(!aGlyph.IsGlyphID(),
377 "nsPropertiesTable can only access glyphs by code point");
378 return aFontGroup->
379 MakeTextRun(aGlyph.code, aGlyph.Length(), aThebesContext,
380 aAppUnitsPerDevPixel, 0);
383 // An instance of nsOpenTypeTable is associated with one gfxFontEntry that
384 // corresponds to an Open Type font with a MATH table. All the glyphs come from
385 // the same font and the calls to access size variants and parts are directly
386 // forwarded to the gfx code.
387 class nsOpenTypeTable MOZ_FINAL : public nsGlyphTable {
388 public:
389 ~nsOpenTypeTable()
391 MOZ_COUNT_DTOR(nsOpenTypeTable);
394 virtual nsGlyphCode ElementAt(gfxContext* aThebesContext,
395 int32_t aAppUnitsPerDevPixel,
396 gfxFontGroup* aFontGroup,
397 char16_t aChar,
398 bool aVertical,
399 uint32_t aPosition) MOZ_OVERRIDE;
400 virtual nsGlyphCode BigOf(gfxContext* aThebesContext,
401 int32_t aAppUnitsPerDevPixel,
402 gfxFontGroup* aFontGroup,
403 char16_t aChar,
404 bool aVertical,
405 uint32_t aSize) MOZ_OVERRIDE;
406 virtual bool HasPartsOf(gfxContext* aThebesContext,
407 int32_t aAppUnitsPerDevPixel,
408 gfxFontGroup* aFontGroup,
409 char16_t aChar,
410 bool aVertical) MOZ_OVERRIDE;
412 const FontFamilyName&
413 FontNameFor(const nsGlyphCode& aGlyphCode) const MOZ_OVERRIDE {
414 NS_ASSERTION(aGlyphCode.IsGlyphID(),
415 "nsOpenTypeTable can only access glyphs by id");
416 return mFontFamilyName;
419 virtual gfxTextRun* MakeTextRun(gfxContext* aThebesContext,
420 int32_t aAppUnitsPerDevPixel,
421 gfxFontGroup* aFontGroup,
422 const nsGlyphCode& aGlyph) MOZ_OVERRIDE;
424 // This returns a new OpenTypeTable instance to give access to OpenType MATH
425 // table or nullptr if the font does not have such table. Ownership is passed
426 // to the caller.
427 static nsOpenTypeTable* Create(gfxFont* aFont)
429 if (!aFont->GetFontEntry()->TryGetMathTable()) {
430 return nullptr;
432 return new nsOpenTypeTable(aFont->GetFontEntry());
435 private:
436 nsRefPtr<gfxFontEntry> mFontEntry;
437 FontFamilyName mFontFamilyName;
438 uint32_t mGlyphID;
440 explicit nsOpenTypeTable(gfxFontEntry* aFontEntry)
441 : mFontEntry(aFontEntry),
442 mFontFamilyName(aFontEntry->FamilyName(), eUnquotedName) {
443 MOZ_COUNT_CTOR(nsOpenTypeTable);
446 void UpdateCache(gfxContext* aThebesContext,
447 int32_t aAppUnitsPerDevPixel,
448 gfxFontGroup* aFontGroup,
449 char16_t aChar);
452 void
453 nsOpenTypeTable::UpdateCache(gfxContext* aThebesContext,
454 int32_t aAppUnitsPerDevPixel,
455 gfxFontGroup* aFontGroup,
456 char16_t aChar)
458 if (mCharCache != aChar) {
459 nsAutoPtr<gfxTextRun> textRun;
460 textRun = aFontGroup->
461 MakeTextRun(&aChar, 1, aThebesContext, aAppUnitsPerDevPixel, 0);
462 const gfxTextRun::CompressedGlyph& data = textRun->GetCharacterGlyphs()[0];
463 if (data.IsSimpleGlyph()) {
464 mGlyphID = data.GetSimpleGlyph();
465 } else if (data.GetGlyphCount() == 1) {
466 mGlyphID = textRun->GetDetailedGlyphs(0)->mGlyphID;
467 } else {
468 mGlyphID = 0;
470 mCharCache = aChar;
474 /* virtual */
475 nsGlyphCode
476 nsOpenTypeTable::ElementAt(gfxContext* aThebesContext,
477 int32_t aAppUnitsPerDevPixel,
478 gfxFontGroup* aFontGroup,
479 char16_t aChar,
480 bool aVertical,
481 uint32_t aPosition)
483 UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar);
485 uint32_t parts[4];
486 if (!mFontEntry->GetMathVariantsParts(mGlyphID, aVertical, parts)) {
487 return kNullGlyph;
490 uint32_t glyphID = parts[aPosition];
491 if (!glyphID) {
492 return kNullGlyph;
494 nsGlyphCode glyph;
495 glyph.glyphID = glyphID;
496 glyph.font = -1;
497 return glyph;
500 /* virtual */
501 nsGlyphCode
502 nsOpenTypeTable::BigOf(gfxContext* aThebesContext,
503 int32_t aAppUnitsPerDevPixel,
504 gfxFontGroup* aFontGroup,
505 char16_t aChar,
506 bool aVertical,
507 uint32_t aSize)
509 UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar);
511 uint32_t glyphID =
512 mFontEntry->GetMathVariantsSize(mGlyphID, aVertical, aSize);
513 if (!glyphID) {
514 return kNullGlyph;
517 nsGlyphCode glyph;
518 glyph.glyphID = glyphID;
519 glyph.font = -1;
520 return glyph;
523 /* virtual */
524 bool
525 nsOpenTypeTable::HasPartsOf(gfxContext* aThebesContext,
526 int32_t aAppUnitsPerDevPixel,
527 gfxFontGroup* aFontGroup,
528 char16_t aChar,
529 bool aVertical)
531 UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar);
533 uint32_t parts[4];
534 if (!mFontEntry->GetMathVariantsParts(mGlyphID, aVertical, parts)) {
535 return false;
538 return parts[0] || parts[1] || parts[2] || parts[3];
541 /* virtual */
542 gfxTextRun*
543 nsOpenTypeTable::MakeTextRun(gfxContext* aThebesContext,
544 int32_t aAppUnitsPerDevPixel,
545 gfxFontGroup* aFontGroup,
546 const nsGlyphCode& aGlyph)
548 NS_ASSERTION(aGlyph.IsGlyphID(),
549 "nsOpenTypeTable can only access glyphs by id");
551 gfxTextRunFactory::Parameters params = {
552 aThebesContext, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
554 gfxTextRun* textRun = gfxTextRun::Create(&params, 1, aFontGroup, 0);
555 textRun->AddGlyphRun(aFontGroup->GetFontAt(0), gfxTextRange::kFontGroup, 0,
556 false);
557 gfxTextRun::DetailedGlyph detailedGlyph;
558 detailedGlyph.mGlyphID = aGlyph.glyphID;
559 detailedGlyph.mAdvance =
560 NSToCoordRound(aAppUnitsPerDevPixel *
561 aFontGroup->GetFontAt(0)->
562 GetGlyphHAdvance(aThebesContext, aGlyph.glyphID));
563 detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
564 gfxShapedText::CompressedGlyph g;
565 g.SetComplex(true, true, 1);
566 textRun->SetGlyphs(0, g, &detailedGlyph);
568 return textRun;
571 // -----------------------------------------------------------------------------
572 // This is the list of all the applicable glyph tables.
573 // We will maintain a single global instance that will only reveal those
574 // glyph tables that are associated to fonts currently installed on the
575 // user' system. The class is an XPCOM shutdown observer to allow us to
576 // free its allocated data at shutdown
578 class nsGlyphTableList MOZ_FINAL : public nsIObserver
580 public:
581 NS_DECL_ISUPPORTS
582 NS_DECL_NSIOBSERVER
584 nsPropertiesTable mUnicodeTable;
586 nsGlyphTableList()
587 : mUnicodeTable(NS_LITERAL_STRING("Unicode"))
589 MOZ_COUNT_CTOR(nsGlyphTableList);
592 nsresult Initialize();
593 nsresult Finalize();
595 // Add a glyph table in the list, return the new table that was added
596 nsGlyphTable*
597 AddGlyphTable(const nsString& aPrimaryFontName);
599 // Find the glyph table in the list corresponding to the given font family.
600 nsGlyphTable*
601 GetGlyphTableFor(const nsAString& aFamily);
603 private:
604 ~nsGlyphTableList()
606 MOZ_COUNT_DTOR(nsGlyphTableList);
609 nsPropertiesTable* PropertiesTableAt(int32_t aIndex) {
610 return &mPropertiesTableList.ElementAt(aIndex);
612 int32_t PropertiesTableCount() {
613 return mPropertiesTableList.Length();
615 // List of glyph tables;
616 nsTArray<nsPropertiesTable> mPropertiesTableList;
619 NS_IMPL_ISUPPORTS(nsGlyphTableList, nsIObserver)
621 // -----------------------------------------------------------------------------
622 // Here is the global list of applicable glyph tables that we will be using
623 static nsGlyphTableList* gGlyphTableList = nullptr;
625 static bool gGlyphTableInitialized = false;
627 // XPCOM shutdown observer
628 NS_IMETHODIMP
629 nsGlyphTableList::Observe(nsISupports* aSubject,
630 const char* aTopic,
631 const char16_t* someData)
633 Finalize();
634 return NS_OK;
637 // Add an observer to XPCOM shutdown so that we can free our data at shutdown
638 nsresult
639 nsGlyphTableList::Initialize()
641 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
642 if (!obs)
643 return NS_ERROR_FAILURE;
645 nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
646 NS_ENSURE_SUCCESS(rv, rv);
648 return NS_OK;
651 // Remove our observer and free the memory that were allocated for us
652 nsresult
653 nsGlyphTableList::Finalize()
655 // Remove our observer from the observer service
656 nsresult rv = NS_OK;
657 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
658 if (obs)
659 rv = obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
660 else
661 rv = NS_ERROR_FAILURE;
663 gGlyphTableInitialized = false;
664 // our oneself will be destroyed when our |Release| is called by the observer
665 NS_IF_RELEASE(gGlyphTableList);
666 return rv;
669 nsGlyphTable*
670 nsGlyphTableList::AddGlyphTable(const nsString& aPrimaryFontName)
672 // See if there is already a special table for this family.
673 nsGlyphTable* glyphTable = GetGlyphTableFor(aPrimaryFontName);
674 if (glyphTable != &mUnicodeTable)
675 return glyphTable;
677 // allocate a table
678 glyphTable = mPropertiesTableList.AppendElement(aPrimaryFontName);
679 return glyphTable;
682 nsGlyphTable*
683 nsGlyphTableList::GetGlyphTableFor(const nsAString& aFamily)
685 for (int32_t i = 0; i < PropertiesTableCount(); i++) {
686 nsPropertiesTable* glyphTable = PropertiesTableAt(i);
687 const FontFamilyName& primaryFontName = glyphTable->PrimaryFontName();
688 nsAutoString primaryFontNameStr;
689 primaryFontName.AppendToString(primaryFontNameStr);
690 // TODO: would be nice to consider StripWhitespace and other aliasing
691 if (primaryFontNameStr.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
692 return glyphTable;
695 // Fall back to default Unicode table
696 return &mUnicodeTable;
699 // -----------------------------------------------------------------------------
701 static nsresult
702 InitGlobals(nsPresContext* aPresContext)
704 NS_ASSERTION(!gGlyphTableInitialized, "Error -- already initialized");
705 gGlyphTableInitialized = true;
707 // Allocate the placeholders for the preferred parts and variants
708 nsresult rv = NS_ERROR_OUT_OF_MEMORY;
709 nsRefPtr<nsGlyphTableList> glyphTableList = new nsGlyphTableList();
710 if (glyphTableList) {
711 rv = glyphTableList->Initialize();
713 if (NS_FAILED(rv)) {
714 return rv;
716 // The gGlyphTableList has been successfully registered as a shutdown
717 // observer and will be deleted at shutdown. We now add some private
718 // per font-family tables for stretchy operators, in order of preference.
719 // Do not include the Unicode table in this list.
720 if (!glyphTableList->AddGlyphTable(NS_LITERAL_STRING("MathJax_Main")) ||
721 !glyphTableList->AddGlyphTable(NS_LITERAL_STRING("STIXGeneral")) ||
722 !glyphTableList->AddGlyphTable(NS_LITERAL_STRING("Standard Symbols L"))
723 #ifdef XP_WIN
724 || !glyphTableList->AddGlyphTable(NS_LITERAL_STRING("Symbol"))
725 #endif
727 rv = NS_ERROR_OUT_OF_MEMORY;
730 glyphTableList.forget(&gGlyphTableList);
731 return rv;
734 // -----------------------------------------------------------------------------
735 // And now the implementation of nsMathMLChar
737 nsMathMLChar::~nsMathMLChar()
739 MOZ_COUNT_DTOR(nsMathMLChar);
740 mStyleContext->Release();
743 nsStyleContext*
744 nsMathMLChar::GetStyleContext() const
746 NS_ASSERTION(mStyleContext, "chars should always have style context");
747 return mStyleContext;
750 void
751 nsMathMLChar::SetStyleContext(nsStyleContext* aStyleContext)
753 NS_PRECONDITION(aStyleContext, "null ptr");
754 if (aStyleContext != mStyleContext) {
755 if (mStyleContext)
756 mStyleContext->Release();
757 if (aStyleContext) {
758 mStyleContext = aStyleContext;
759 aStyleContext->AddRef();
764 void
765 nsMathMLChar::SetData(nsPresContext* aPresContext,
766 nsString& aData)
768 if (!gGlyphTableInitialized) {
769 InitGlobals(aPresContext);
771 mData = aData;
772 // some assumptions until proven otherwise
773 // note that mGlyph is not initialized
774 mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
775 mBoundingMetrics = nsBoundingMetrics();
776 // check if stretching is applicable ...
777 if (gGlyphTableList && (1 == mData.Length())) {
778 mDirection = nsMathMLOperators::GetStretchyDirection(mData);
779 // default tentative table (not the one that is necessarily going
780 // to be used)
784 // -----------------------------------------------------------------------------
786 The Stretch:
787 @param aContainerSize - suggested size for the stretched char
788 @param aDesiredStretchSize - OUT parameter. The desired size
789 after stretching. If no stretching is done, the output will
790 simply give the base size.
792 How it works?
793 Summary:-
794 The Stretch() method first looks for a glyph of appropriate
795 size; If a glyph is found, it is cached by this object and
796 its size is returned in aDesiredStretchSize. The cached
797 glyph will then be used at the painting stage.
798 If no glyph of appropriate size is found, a search is made
799 to see if the char can be built by parts.
801 Details:-
802 A character gets stretched through the following pipeline :
804 1) If the base size of the char is sufficient to cover the
805 container' size, we use that. If not, it will still be
806 used as a fallback if the other stages in the pipeline fail.
807 Issues :
808 a) The base size, the parts and the variants of a char can
809 be in different fonts. For eg., the base size for '(' should
810 come from a normal ascii font if CMEX10 is used, since CMEX10
811 only contains the stretched versions. Hence, there are two
812 style contexts in use throughout the process. The leaf style
813 context of the char holds fonts with which to try to stretch
814 the char. The parent style context of the char contains fonts
815 for normal rendering. So the parent context is the one used
816 to get the initial base size at the start of the pipeline.
817 b) For operators that can be largeop's in display mode,
818 we will skip the base size even if it fits, so that
819 the next stage in the pipeline is given a chance to find
820 a largeop variant. If the next stage fails, we fallback
821 to the base size.
823 2) We search for the first larger variant of the char that fits the
824 container' size. We first search for larger variants using the glyph
825 table corresponding to the first existing font specified in the list of
826 stretchy fonts held by the leaf style context (from -moz-math-stretchy in
827 mathml.css). Generic fonts are resolved by the preference
828 "font.mathfont-family".
829 Issues :
830 a) the largeop and display settings determine the starting
831 size when we do the above search, regardless of whether
832 smaller variants already fit the container' size.
833 b) if it is a largeopOnly request (i.e., a displaystyle operator
834 with largeop=true and stretchy=false), we break after finding
835 the first starting variant, regardless of whether that
836 variant fits the container's size.
838 3) If a variant of appropriate size wasn't found, we see if the char
839 can be built by parts using the same glyph table.
840 Issue:
841 There are chars that have no middle and glue glyphs. For
842 such chars, the parts need to be joined using the rule.
843 By convention (TeXbook p.225), the descent of the parts is
844 zero while their ascent gives the thickness of the rule that
845 should be used to join them.
847 4) If a match was not found in that glyph table, repeat from 2 to search the
848 ordered list of stretchy fonts for the first font with a glyph table that
849 provides a fit to the container size. If no fit is found, the closest fit
850 is used.
852 Of note:
853 When the pipeline completes successfully, the desired size of the
854 stretched char can actually be slightly larger or smaller than
855 aContainerSize. But it is the responsibility of the caller to
856 account for the spacing when setting aContainerSize, and to leave
857 any extra margin when placing the stretched char.
859 // -----------------------------------------------------------------------------
862 // plain TeX settings (TeXbook p.152)
863 #define NS_MATHML_DELIMITER_FACTOR 0.901f
864 #define NS_MATHML_DELIMITER_SHORTFALL_POINTS 5.0f
866 static bool
867 IsSizeOK(nsPresContext* aPresContext, nscoord a, nscoord b, uint32_t aHint)
869 // Normal: True if 'a' is around +/-10% of the target 'b' (10% is
870 // 1-DelimiterFactor). This often gives a chance to the base size to
871 // win, especially in the context of <mfenced> without tall elements
872 // or in sloppy markups without protective <mrow></mrow>
873 bool isNormal =
874 (aHint & NS_STRETCH_NORMAL) &&
875 Abs<float>(a - b) < (1.0f - NS_MATHML_DELIMITER_FACTOR) * float(b);
877 // Nearer: True if 'a' is around max{ +/-10% of 'b' , 'b' - 5pt },
878 // as documented in The TeXbook, Ch.17, p.152.
879 // i.e. within 10% and within 5pt
880 bool isNearer = false;
881 if (aHint & (NS_STRETCH_NEARER | NS_STRETCH_LARGEOP)) {
882 float c = std::max(float(b) * NS_MATHML_DELIMITER_FACTOR,
883 float(b) - nsPresContext::
884 CSSPointsToAppUnits(NS_MATHML_DELIMITER_SHORTFALL_POINTS));
885 isNearer = Abs<float>(b - a) <= float(b) - c;
888 // Smaller: Mainly for transitory use, to compare two candidate
889 // choices
890 bool isSmaller =
891 (aHint & NS_STRETCH_SMALLER) &&
892 float(a) >= NS_MATHML_DELIMITER_FACTOR * float(b) &&
893 a <= b;
895 // Larger: Critical to the sqrt code to ensure that the radical
896 // size is tall enough
897 bool isLarger =
898 (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP)) &&
899 a >= b;
901 return (isNormal || isSmaller || isNearer || isLarger);
904 static bool
905 IsSizeBetter(nscoord a, nscoord olda, nscoord b, uint32_t aHint)
907 if (0 == olda)
908 return true;
909 if (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
910 return (a >= olda) ? (olda < b) : (a >= b);
911 if (aHint & NS_STRETCH_SMALLER)
912 return (a <= olda) ? (olda > b) : (a <= b);
914 // XXXkt prob want log scale here i.e. 1.5 is closer to 1 than 0.5
915 return Abs(a - b) < Abs(olda - b);
918 // We want to place the glyphs even when they don't fit at their
919 // full extent, i.e., we may clip to tolerate a small amount of
920 // overlap between the parts. This is important to cater for fonts
921 // with long glues.
922 static nscoord
923 ComputeSizeFromParts(nsPresContext* aPresContext,
924 nsGlyphCode* aGlyphs,
925 nscoord* aSizes,
926 nscoord aTargetSize)
928 enum {first, middle, last, glue};
929 // Add the parts that cannot be left out.
930 nscoord sum = 0;
931 for (int32_t i = first; i <= last; i++) {
932 if (aGlyphs[i] != aGlyphs[glue]) {
933 sum += aSizes[i];
937 // Determine how much is used in joins
938 nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
939 int32_t joins = aGlyphs[middle] == aGlyphs[glue] ? 1 : 2;
941 // Pick a maximum size using a maximum number of glue glyphs that we are
942 // prepared to draw for one character.
943 const int32_t maxGlyphs = 1000;
945 // This also takes into account the fact that, if the glue has no size,
946 // then the character can't be lengthened.
947 nscoord maxSize = sum - 2 * joins * oneDevPixel + maxGlyphs * aSizes[glue];
948 if (maxSize < aTargetSize)
949 return maxSize; // settle with the maximum size
951 // Get the minimum allowable size using some flex.
952 nscoord minSize = NSToCoordRound(NS_MATHML_DELIMITER_FACTOR * sum);
954 if (minSize > aTargetSize)
955 return minSize; // settle with the minimum size
957 // Fill-up the target area
958 return aTargetSize;
961 // Update the font if there is a family change and returns the font group.
962 bool
963 nsMathMLChar::SetFontFamily(nsPresContext* aPresContext,
964 const nsGlyphTable* aGlyphTable,
965 const nsGlyphCode& aGlyphCode,
966 const FontFamilyList& aDefaultFamilyList,
967 nsFont& aFont,
968 nsRefPtr<gfxFontGroup>* aFontGroup)
970 FontFamilyList glyphCodeFont;
972 if (aGlyphCode.font) {
973 glyphCodeFont.Append(aGlyphTable->FontNameFor(aGlyphCode));
976 const FontFamilyList& familyList =
977 aGlyphCode.font ? glyphCodeFont : aDefaultFamilyList;
979 if (!*aFontGroup || !(aFont.fontlist == familyList)) {
980 nsFont font = aFont;
981 font.fontlist = familyList;
982 nsRefPtr<nsFontMetrics> fm;
983 aPresContext->DeviceContext()->
984 GetMetricsFor(font,
985 mStyleContext->StyleFont()->mLanguage,
986 aPresContext->GetUserFontSet(),
987 aPresContext->GetTextPerfMetrics(),
988 *getter_AddRefs(fm));
989 // Set the font if it is an unicode table
990 // or if the same family name has been found
991 gfxFont *firstFont = fm->GetThebesFontGroup()->GetFontAt(0);
992 FontFamilyList firstFontList;
993 if (firstFont) {
994 firstFontList.Append(
995 FontFamilyName(firstFont->GetFontEntry()->FamilyName(), eUnquotedName));
997 if (aGlyphTable == &gGlyphTableList->mUnicodeTable ||
998 firstFontList == familyList) {
999 aFont.fontlist = familyList;
1000 *aFontGroup = fm->GetThebesFontGroup();
1001 } else {
1002 return false; // We did not set the font
1005 return true;
1008 static nsBoundingMetrics
1009 MeasureTextRun(gfxContext* aThebesContext, gfxTextRun* aTextRun)
1011 gfxTextRun::Metrics metrics =
1012 aTextRun->MeasureText(0, aTextRun->GetLength(),
1013 gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS,
1014 aThebesContext, nullptr);
1016 nsBoundingMetrics bm;
1017 bm.leftBearing = NSToCoordFloor(metrics.mBoundingBox.X());
1018 bm.rightBearing = NSToCoordCeil(metrics.mBoundingBox.XMost());
1019 bm.ascent = NSToCoordCeil(-metrics.mBoundingBox.Y());
1020 bm.descent = NSToCoordCeil(metrics.mBoundingBox.YMost());
1021 bm.width = NSToCoordRound(metrics.mAdvanceWidth);
1023 return bm;
1026 class nsMathMLChar::StretchEnumContext {
1027 public:
1028 StretchEnumContext(nsMathMLChar* aChar,
1029 nsPresContext* aPresContext,
1030 gfxContext* aThebesContext,
1031 nsStretchDirection aStretchDirection,
1032 nscoord aTargetSize,
1033 uint32_t aStretchHint,
1034 nsBoundingMetrics& aStretchedMetrics,
1035 const FontFamilyList& aFamilyList,
1036 bool& aGlyphFound)
1037 : mChar(aChar),
1038 mPresContext(aPresContext),
1039 mThebesContext(aThebesContext),
1040 mDirection(aStretchDirection),
1041 mTargetSize(aTargetSize),
1042 mStretchHint(aStretchHint),
1043 mBoundingMetrics(aStretchedMetrics),
1044 mFamilyList(aFamilyList),
1045 mTryVariants(true),
1046 mTryParts(true),
1047 mGlyphFound(aGlyphFound) {}
1049 static bool
1050 EnumCallback(const FontFamilyName& aFamily, bool aGeneric, void *aData);
1052 private:
1053 bool TryVariants(nsGlyphTable* aGlyphTable,
1054 nsRefPtr<gfxFontGroup>* aFontGroup,
1055 const FontFamilyList& aFamilyList);
1056 bool TryParts(nsGlyphTable* aGlyphTable,
1057 nsRefPtr<gfxFontGroup>* aFontGroup,
1058 const FontFamilyList& aFamilyList);
1060 nsMathMLChar* mChar;
1061 nsPresContext* mPresContext;
1062 gfxContext* mThebesContext;
1063 const nsStretchDirection mDirection;
1064 const nscoord mTargetSize;
1065 const uint32_t mStretchHint;
1066 nsBoundingMetrics& mBoundingMetrics;
1067 // Font families to search
1068 const FontFamilyList& mFamilyList;
1070 public:
1071 bool mTryVariants;
1072 bool mTryParts;
1074 private:
1075 nsAutoTArray<nsGlyphTable*,16> mTablesTried;
1076 bool& mGlyphFound;
1080 // 2. See if there are any glyphs of the appropriate size.
1081 // Returns true if the size is OK, false to keep searching.
1082 // Always updates the char if a better match is found.
1083 bool
1084 nsMathMLChar::
1085 StretchEnumContext::TryVariants(nsGlyphTable* aGlyphTable,
1086 nsRefPtr<gfxFontGroup>* aFontGroup,
1087 const FontFamilyList& aFamilyList)
1089 // Use our stretchy style context now that stretching is in progress
1090 nsStyleContext *sc = mChar->mStyleContext;
1091 nsFont font = sc->StyleFont()->mFont;
1092 NormalizeDefaultFont(font);
1094 bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
1095 nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
1096 char16_t uchar = mChar->mData[0];
1097 bool largeop = (NS_STRETCH_LARGEOP & mStretchHint) != 0;
1098 bool largeopOnly =
1099 largeop && (NS_STRETCH_VARIABLE_MASK & mStretchHint) == 0;
1100 bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
1102 nscoord bestSize =
1103 isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
1104 : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
1105 bool haveBetter = false;
1107 // start at size = 1 (size = 0 is the char at its normal size)
1108 int32_t size = 1;
1109 nsGlyphCode ch;
1110 nscoord displayOperatorMinHeight = 0;
1111 if (largeopOnly) {
1112 NS_ASSERTION(isVertical, "Stretching should be in the vertical direction");
1113 ch = aGlyphTable->BigOf(mThebesContext, oneDevPixel, *aFontGroup, uchar,
1114 isVertical, 0);
1115 if (ch.IsGlyphID()) {
1116 gfxFont* mathFont = aFontGroup->get()->GetFirstMathFont();
1117 // For OpenType MATH fonts, we will rely on the DisplayOperatorMinHeight
1118 // to select the right size variant. Note that the value is sometimes too
1119 // small so we use kLargeOpFactor/kIntegralFactor as a minimum value.
1120 if (mathFont) {
1121 displayOperatorMinHeight =
1122 mathFont->GetMathConstant(gfxFontEntry::DisplayOperatorMinHeight,
1123 oneDevPixel);
1124 nsAutoPtr<gfxTextRun> textRun;
1125 textRun = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel,
1126 *aFontGroup, ch);
1127 nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun);
1128 float largeopFactor = kLargeOpFactor;
1129 if (NS_STRETCH_INTEGRAL & mStretchHint) {
1130 // integrals are drawn taller
1131 largeopFactor = kIntegralFactor;
1133 nscoord minHeight = largeopFactor * (bm.ascent + bm.descent);
1134 if (displayOperatorMinHeight < minHeight) {
1135 displayOperatorMinHeight = minHeight;
1140 #ifdef NOISY_SEARCH
1141 printf(" searching in %s ...\n",
1142 NS_LossyConvertUTF16toASCII(aFamily).get());
1143 #endif
1144 while ((ch = aGlyphTable->BigOf(mThebesContext, oneDevPixel, *aFontGroup,
1145 uchar, isVertical, size)).Exists()) {
1147 if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamilyList, font,
1148 aFontGroup)) {
1149 // if largeopOnly is set, break now
1150 if (largeopOnly) break;
1151 ++size;
1152 continue;
1155 nsAutoPtr<gfxTextRun> textRun;
1156 textRun = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel,
1157 *aFontGroup, ch);
1158 nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun);
1159 if (ch.IsGlyphID()) {
1160 gfxFont* mathFont = aFontGroup->get()->GetFirstMathFont();
1161 if (mathFont) {
1162 // MeasureTextRun should have set the advance width to the right
1163 // bearing for OpenType MATH fonts. We now subtract the italic
1164 // correction, so that nsMathMLmmultiscripts will place the scripts
1165 // correctly.
1166 // Note that STIX-Word does not provide italic corrections but its
1167 // advance widths do not match right bearings.
1168 // (http://sourceforge.net/p/stixfonts/tracking/50/)
1169 gfxFloat italicCorrection;
1170 if (mathFont->GetFontEntry()->
1171 GetMathItalicsCorrection(ch.glyphID, &italicCorrection)) {
1172 bm.width -=
1173 NSToCoordRound(italicCorrection *
1174 mathFont->GetAdjustedSize() * oneDevPixel);
1175 if (bm.width < 0) {
1176 bm.width = 0;
1182 nscoord charSize =
1183 isVertical ? bm.ascent + bm.descent
1184 : bm.rightBearing - bm.leftBearing;
1186 if (largeopOnly ||
1187 IsSizeBetter(charSize, bestSize, mTargetSize, mStretchHint)) {
1188 mGlyphFound = true;
1189 if (maxWidth) {
1190 // IsSizeBetter() checked that charSize < maxsize;
1191 // Leave ascent, descent, and bestsize as these contain maxsize.
1192 if (mBoundingMetrics.width < bm.width)
1193 mBoundingMetrics.width = bm.width;
1194 if (mBoundingMetrics.leftBearing > bm.leftBearing)
1195 mBoundingMetrics.leftBearing = bm.leftBearing;
1196 if (mBoundingMetrics.rightBearing < bm.rightBearing)
1197 mBoundingMetrics.rightBearing = bm.rightBearing;
1198 // Continue to check other sizes unless largeopOnly
1199 haveBetter = largeopOnly;
1201 else {
1202 mBoundingMetrics = bm;
1203 haveBetter = true;
1204 bestSize = charSize;
1205 mChar->mGlyphs[0] = textRun;
1206 mChar->mDraw = DRAW_VARIANT;
1208 #ifdef NOISY_SEARCH
1209 printf(" size:%d Current best\n", size);
1210 #endif
1212 else {
1213 #ifdef NOISY_SEARCH
1214 printf(" size:%d Rejected!\n", size);
1215 #endif
1216 if (haveBetter)
1217 break; // Not making an futher progress, stop searching
1220 // If this a largeop only operator, we stop if the glyph is large enough.
1221 if (largeopOnly && (bm.ascent + bm.descent) >= displayOperatorMinHeight) {
1222 break;
1224 ++size;
1227 return haveBetter &&
1228 (largeopOnly ||
1229 IsSizeOK(mPresContext, bestSize, mTargetSize, mStretchHint));
1232 // 3. Build by parts.
1233 // Returns true if the size is OK, false to keep searching.
1234 // Always updates the char if a better match is found.
1235 bool
1236 nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable* aGlyphTable,
1237 nsRefPtr<gfxFontGroup>* aFontGroup,
1238 const FontFamilyList& aFamilyList)
1240 // Use our stretchy style context now that stretching is in progress
1241 nsFont font = mChar->mStyleContext->StyleFont()->mFont;
1242 NormalizeDefaultFont(font);
1244 // Compute the bounding metrics of all partial glyphs
1245 nsAutoPtr<gfxTextRun> textRun[4];
1246 nsGlyphCode chdata[4];
1247 nsBoundingMetrics bmdata[4];
1248 nscoord sizedata[4];
1250 bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
1251 nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
1252 char16_t uchar = mChar->mData[0];
1253 bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
1254 if (!aGlyphTable->HasPartsOf(mThebesContext, oneDevPixel, *aFontGroup,
1255 uchar, isVertical))
1256 return false; // to next table
1258 for (int32_t i = 0; i < 4; i++) {
1259 nsGlyphCode ch = aGlyphTable->ElementAt(mThebesContext, oneDevPixel,
1260 *aFontGroup, uchar, isVertical, i);
1261 chdata[i] = ch;
1262 if (ch.Exists()) {
1263 if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamilyList, font,
1264 aFontGroup))
1265 return false;
1267 textRun[i] = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel,
1268 *aFontGroup, ch);
1269 nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun[i]);
1270 bmdata[i] = bm;
1271 sizedata[i] = isVertical ? bm.ascent + bm.descent
1272 : bm.rightBearing - bm.leftBearing;
1273 } else {
1274 // Null glue indicates that a rule will be drawn, which can stretch to
1275 // fill any space.
1276 textRun[i] = nullptr;
1277 bmdata[i] = nsBoundingMetrics();
1278 sizedata[i] = i == 3 ? mTargetSize : 0;
1282 // For the Unicode table, we check that all the glyphs are actually found and
1283 // come from the same font.
1284 if (aGlyphTable == &gGlyphTableList->mUnicodeTable) {
1285 gfxFont* unicodeFont = nullptr;
1286 for (int32_t i = 0; i < 4; i++) {
1287 if (!textRun[i]) {
1288 continue;
1290 if (textRun[i]->GetLength() != 1 ||
1291 textRun[i]->GetCharacterGlyphs()[0].IsMissing()) {
1292 return false;
1294 uint32_t numGlyphRuns;
1295 const gfxTextRun::GlyphRun* glyphRuns =
1296 textRun[i]->GetGlyphRuns(&numGlyphRuns);
1297 if (numGlyphRuns != 1) {
1298 return false;
1300 if (!unicodeFont) {
1301 unicodeFont = glyphRuns[0].mFont;
1302 } else if (unicodeFont != glyphRuns[0].mFont) {
1303 return false;
1308 // Build by parts if we have successfully computed the
1309 // bounding metrics of all parts.
1310 nscoord computedSize = ComputeSizeFromParts(mPresContext, chdata, sizedata,
1311 mTargetSize);
1313 nscoord currentSize =
1314 isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
1315 : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
1317 if (!IsSizeBetter(computedSize, currentSize, mTargetSize, mStretchHint)) {
1318 #ifdef NOISY_SEARCH
1319 printf(" Font %s Rejected!\n",
1320 NS_LossyConvertUTF16toASCII(fontName).get());
1321 #endif
1322 return false; // to next table
1325 #ifdef NOISY_SEARCH
1326 printf(" Font %s Current best!\n",
1327 NS_LossyConvertUTF16toASCII(fontName).get());
1328 #endif
1330 // The computed size is the best we have found so far...
1331 // now is the time to compute and cache our bounding metrics
1332 if (isVertical) {
1333 int32_t i;
1334 // Try and find the first existing part and then determine the extremal
1335 // horizontal metrics of the parts.
1336 for (i = 0; i <= 3 && !textRun[i]; i++);
1337 if (i == 4) {
1338 NS_ERROR("Cannot stretch - All parts missing");
1339 return false;
1341 nscoord lbearing = bmdata[i].leftBearing;
1342 nscoord rbearing = bmdata[i].rightBearing;
1343 nscoord width = bmdata[i].width;
1344 i++;
1345 for (; i <= 3; i++) {
1346 if (!textRun[i]) continue;
1347 lbearing = std::min(lbearing, bmdata[i].leftBearing);
1348 rbearing = std::max(rbearing, bmdata[i].rightBearing);
1349 width = std::max(width, bmdata[i].width);
1351 if (maxWidth) {
1352 lbearing = std::min(lbearing, mBoundingMetrics.leftBearing);
1353 rbearing = std::max(rbearing, mBoundingMetrics.rightBearing);
1354 width = std::max(width, mBoundingMetrics.width);
1356 mBoundingMetrics.width = width;
1357 // When maxWidth, updating ascent and descent indicates that no characters
1358 // larger than this character's minimum size need to be checked as they
1359 // will not be used.
1360 mBoundingMetrics.ascent = bmdata[0].ascent; // not used except with descent
1361 // for height
1362 mBoundingMetrics.descent = computedSize - mBoundingMetrics.ascent;
1363 mBoundingMetrics.leftBearing = lbearing;
1364 mBoundingMetrics.rightBearing = rbearing;
1366 else {
1367 int32_t i;
1368 // Try and find the first existing part and then determine the extremal
1369 // vertical metrics of the parts.
1370 for (i = 0; i <= 3 && !textRun[i]; i++);
1371 if (i == 4) {
1372 NS_ERROR("Cannot stretch - All parts missing");
1373 return false;
1375 nscoord ascent = bmdata[i].ascent;
1376 nscoord descent = bmdata[i].descent;
1377 i++;
1378 for (; i <= 3; i++) {
1379 if (!textRun[i]) continue;
1380 ascent = std::max(ascent, bmdata[i].ascent);
1381 descent = std::max(descent, bmdata[i].descent);
1383 mBoundingMetrics.width = computedSize;
1384 mBoundingMetrics.ascent = ascent;
1385 mBoundingMetrics.descent = descent;
1386 mBoundingMetrics.leftBearing = 0;
1387 mBoundingMetrics.rightBearing = computedSize;
1389 mGlyphFound = true;
1390 if (maxWidth)
1391 return false; // Continue to check other sizes
1393 // reset
1394 mChar->mDraw = DRAW_PARTS;
1395 for (int32_t i = 0; i < 4; i++) {
1396 mChar->mGlyphs[i] = textRun[i];
1397 mChar->mBmData[i] = bmdata[i];
1400 return IsSizeOK(mPresContext, computedSize, mTargetSize, mStretchHint);
1403 // This is called for each family, whether it exists or not
1404 bool
1405 nsMathMLChar::StretchEnumContext::EnumCallback(const FontFamilyName& aFamily,
1406 bool aGeneric, void *aData)
1408 StretchEnumContext* context = static_cast<StretchEnumContext*>(aData);
1410 // for comparisons, force use of unquoted names
1411 FontFamilyName unquotedFamilyName(aFamily);
1412 if (unquotedFamilyName.mType == eFamily_named_quoted) {
1413 unquotedFamilyName.mType = eFamily_named;
1416 // Check font family if it is not a generic one
1417 // We test with the kNullGlyph
1418 nsStyleContext *sc = context->mChar->mStyleContext;
1419 nsFont font = sc->StyleFont()->mFont;
1420 NormalizeDefaultFont(font);
1421 nsRefPtr<gfxFontGroup> fontGroup;
1422 FontFamilyList family;
1423 family.Append(unquotedFamilyName);
1424 if (!aGeneric && !context->mChar->SetFontFamily(context->mPresContext,
1425 nullptr, kNullGlyph, family,
1426 font, &fontGroup))
1427 return true; // Could not set the family
1429 // Determine the glyph table to use for this font.
1430 nsAutoPtr<nsOpenTypeTable> openTypeTable;
1431 nsGlyphTable* glyphTable;
1432 if (aGeneric) {
1433 // This is a generic font, use the Unicode table.
1434 glyphTable = &gGlyphTableList->mUnicodeTable;
1435 } else {
1436 // If the font contains an Open Type MATH table, use it.
1437 openTypeTable = nsOpenTypeTable::Create(fontGroup->GetFontAt(0));
1438 if (openTypeTable) {
1439 glyphTable = openTypeTable;
1440 } else {
1441 // Otherwise try to find a .properties file corresponding to that font
1442 // family or fallback to the Unicode table.
1443 nsAutoString familyName;
1444 unquotedFamilyName.AppendToString(familyName);
1445 glyphTable = gGlyphTableList->GetGlyphTableFor(familyName);
1449 if (!openTypeTable) {
1450 if (context->mTablesTried.Contains(glyphTable))
1451 return true; // already tried this one
1453 // Only try this table once.
1454 context->mTablesTried.AppendElement(glyphTable);
1457 // If the unicode table is being used, then search all font families. If a
1458 // special table is being used then the font in this family should have the
1459 // specified glyphs.
1460 const FontFamilyList& familyList = glyphTable == &gGlyphTableList->mUnicodeTable ?
1461 context->mFamilyList : family;
1463 if((context->mTryVariants &&
1464 context->TryVariants(glyphTable, &fontGroup, familyList)) ||
1465 (context->mTryParts && context->TryParts(glyphTable,
1466 &fontGroup,
1467 familyList)))
1468 return false; // no need to continue
1470 return true; // true means continue
1473 // insert math fallback families just before the first generic or at the end
1474 // when no generic present
1475 static void
1476 InsertMathFallbacks(FontFamilyList& aFamilyList,
1477 nsTArray<nsString>& aFallbacks)
1479 FontFamilyList aMergedList;
1481 bool inserted = false;
1482 const nsTArray<FontFamilyName>& fontlist = aFamilyList.GetFontlist();
1483 uint32_t i, num = fontlist.Length();
1484 for (i = 0; i < num; i++) {
1485 const FontFamilyName& name = fontlist[i];
1486 if (!inserted && name.IsGeneric()) {
1487 inserted = true;
1488 aMergedList.Append(aFallbacks);
1490 aMergedList.Append(name);
1493 if (!inserted) {
1494 aMergedList.Append(aFallbacks);
1496 aFamilyList = aMergedList;
1499 nsresult
1500 nsMathMLChar::StretchInternal(nsPresContext* aPresContext,
1501 gfxContext* aThebesContext,
1502 nsStretchDirection& aStretchDirection,
1503 const nsBoundingMetrics& aContainerSize,
1504 nsBoundingMetrics& aDesiredStretchSize,
1505 uint32_t aStretchHint,
1506 // These are currently only used when
1507 // aStretchHint & NS_STRETCH_MAXWIDTH:
1508 float aMaxSize,
1509 bool aMaxSizeIsAbsolute)
1511 // if we have been called before, and we didn't actually stretch, our
1512 // direction may have been set to NS_STRETCH_DIRECTION_UNSUPPORTED.
1513 // So first set our direction back to its instrinsic value
1514 nsStretchDirection direction = nsMathMLOperators::GetStretchyDirection(mData);
1516 // Set default font and get the default bounding metrics
1517 // mStyleContext is a leaf context used only when stretching happens.
1518 // For the base size, the default font should come from the parent context
1519 nsFont font = mStyleContext->GetParent()->StyleFont()->mFont;
1520 NormalizeDefaultFont(font);
1522 nsRefPtr<nsFontMetrics> fm;
1523 aPresContext->DeviceContext()->
1524 GetMetricsFor(font,
1525 mStyleContext->StyleFont()->mLanguage,
1526 aPresContext->GetUserFontSet(),
1527 aPresContext->GetTextPerfMetrics(),
1528 *getter_AddRefs(fm));
1529 uint32_t len = uint32_t(mData.Length());
1530 nsAutoPtr<gfxTextRun> textRun;
1531 textRun = fm->GetThebesFontGroup()->
1532 MakeTextRun(static_cast<const char16_t*>(mData.get()), len, aThebesContext,
1533 aPresContext->AppUnitsPerDevPixel(), 0);
1534 aDesiredStretchSize = MeasureTextRun(aThebesContext, textRun);
1535 mGlyphs[0] = textRun;
1537 bool maxWidth = (NS_STRETCH_MAXWIDTH & aStretchHint) != 0;
1538 if (!maxWidth) {
1539 mUnscaledAscent = aDesiredStretchSize.ascent;
1542 //////////////////////////////////////////////////////////////////////////////
1543 // 1. Check the common situations where stretching is not actually needed
1544 //////////////////////////////////////////////////////////////////////////////
1546 // quick return if there is nothing special about this char
1547 if ((aStretchDirection != direction &&
1548 aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT) ||
1549 (aStretchHint & ~NS_STRETCH_MAXWIDTH) == NS_STRETCH_NONE) {
1550 mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
1551 return NS_OK;
1554 // if no specified direction, attempt to stretch in our preferred direction
1555 if (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT) {
1556 aStretchDirection = direction;
1559 // see if this is a particular largeop or largeopOnly request
1560 bool largeop = (NS_STRETCH_LARGEOP & aStretchHint) != 0;
1561 bool stretchy = (NS_STRETCH_VARIABLE_MASK & aStretchHint) != 0;
1562 bool largeopOnly = largeop && !stretchy;
1564 bool isVertical = (direction == NS_STRETCH_DIRECTION_VERTICAL);
1566 nscoord targetSize =
1567 isVertical ? aContainerSize.ascent + aContainerSize.descent
1568 : aContainerSize.rightBearing - aContainerSize.leftBearing;
1570 if (maxWidth) {
1571 // See if it is only necessary to consider glyphs up to some maximum size.
1572 // Set the current height to the maximum size, and set aStretchHint to
1573 // NS_STRETCH_SMALLER if the size is variable, so that only smaller sizes
1574 // are considered. targetSize from GetMaxWidth() is 0.
1575 if (stretchy) {
1576 // variable size stretch - consider all sizes < maxsize
1577 aStretchHint =
1578 (aStretchHint & ~NS_STRETCH_VARIABLE_MASK) | NS_STRETCH_SMALLER;
1581 // Use NS_MATHML_DELIMITER_FACTOR to allow some slightly larger glyphs as
1582 // maxsize is not enforced exactly.
1583 if (aMaxSize == NS_MATHML_OPERATOR_SIZE_INFINITY) {
1584 aDesiredStretchSize.ascent = nscoord_MAX;
1585 aDesiredStretchSize.descent = 0;
1587 else {
1588 nscoord height = aDesiredStretchSize.ascent + aDesiredStretchSize.descent;
1589 if (height == 0) {
1590 if (aMaxSizeIsAbsolute) {
1591 aDesiredStretchSize.ascent =
1592 NSToCoordRound(aMaxSize / NS_MATHML_DELIMITER_FACTOR);
1593 aDesiredStretchSize.descent = 0;
1595 // else: leave height as 0
1597 else {
1598 float scale = aMaxSizeIsAbsolute ? aMaxSize / height : aMaxSize;
1599 scale /= NS_MATHML_DELIMITER_FACTOR;
1600 aDesiredStretchSize.ascent =
1601 NSToCoordRound(scale * aDesiredStretchSize.ascent);
1602 aDesiredStretchSize.descent =
1603 NSToCoordRound(scale * aDesiredStretchSize.descent);
1608 nsBoundingMetrics initialSize = aDesiredStretchSize;
1609 nscoord charSize =
1610 isVertical ? initialSize.ascent + initialSize.descent
1611 : initialSize.rightBearing - initialSize.leftBearing;
1613 bool done = false;
1615 if (!maxWidth && !largeop) {
1616 // Doing Stretch() not GetMaxWidth(),
1617 // and not a largeop in display mode; we're done if size fits
1618 if ((targetSize <= 0) ||
1619 ((isVertical && charSize >= targetSize) ||
1620 IsSizeOK(aPresContext, charSize, targetSize, aStretchHint)))
1621 done = true;
1624 //////////////////////////////////////////////////////////////////////////////
1625 // 2/3. Search for a glyph or set of part glyphs of appropriate size
1626 //////////////////////////////////////////////////////////////////////////////
1628 bool glyphFound = false;
1630 if (!done) { // normal case
1631 // Use the css font-family but add preferred fallback fonts.
1632 font = mStyleContext->StyleFont()->mFont;
1633 NormalizeDefaultFont(font);
1635 // really shouldn't be doing things this way but for now
1636 // insert fallbacks into the list
1637 nsAutoTArray<nsString, 10> mathFallbacks;
1638 gfxFontUtils::GetPrefsFontList("font.mathfont-family", mathFallbacks);
1639 InsertMathFallbacks(font.fontlist, mathFallbacks);
1642 #ifdef NOISY_SEARCH
1643 nsAutoString fontlistStr;
1644 font.fontlist.ToString(fontlistStr, false, true);
1645 printf("Searching in "%s" for a glyph of appropriate size for: 0x%04X:%c\n",
1646 NS_ConvertUTF16toUTF8(fontlistStr).get(), mData[0], mData[0]&0x00FF);
1647 #endif
1648 StretchEnumContext enumData(this, aPresContext, aThebesContext,
1649 aStretchDirection, targetSize, aStretchHint,
1650 aDesiredStretchSize, font.fontlist, glyphFound);
1651 enumData.mTryParts = !largeopOnly;
1653 const nsTArray<FontFamilyName>& fontlist = font.fontlist.GetFontlist();
1654 uint32_t i, num = fontlist.Length();
1655 bool next = true;
1656 for (i = 0; i < num && next; i++) {
1657 const FontFamilyName& name = fontlist[i];
1658 next = StretchEnumContext::EnumCallback(name, name.IsGeneric(), &enumData);
1662 if (!maxWidth) {
1663 // Now, we know how we are going to draw the char. Update the member
1664 // variables accordingly.
1665 mUnscaledAscent = aDesiredStretchSize.ascent;
1668 if (glyphFound) {
1669 return NS_OK;
1672 // stretchy character
1673 if (stretchy) {
1674 if (isVertical) {
1675 float scale =
1676 std::min(kMaxScaleFactor, float(aContainerSize.ascent + aContainerSize.descent) /
1677 (aDesiredStretchSize.ascent + aDesiredStretchSize.descent));
1678 if (!largeop || scale > 1.0) {
1679 // make the character match the desired height.
1680 if (!maxWidth) {
1681 mScaleY *= scale;
1683 aDesiredStretchSize.ascent *= scale;
1684 aDesiredStretchSize.descent *= scale;
1686 } else {
1687 float scale =
1688 std::min(kMaxScaleFactor, float(aContainerSize.rightBearing - aContainerSize.leftBearing) /
1689 (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing));
1690 if (!largeop || scale > 1.0) {
1691 // make the character match the desired width.
1692 if (!maxWidth) {
1693 mScaleX *= scale;
1695 aDesiredStretchSize.leftBearing *= scale;
1696 aDesiredStretchSize.rightBearing *= scale;
1697 aDesiredStretchSize.width *= scale;
1702 // We do not have a char variant for this largeop in display mode, so we
1703 // apply a scale transform to the base char.
1704 if (largeop) {
1705 float scale;
1706 float largeopFactor = kLargeOpFactor;
1708 // increase the width if it is not largeopFactor times larger
1709 // than the initial one.
1710 if ((aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing) <
1711 largeopFactor * (initialSize.rightBearing - initialSize.leftBearing)) {
1712 scale = (largeopFactor *
1713 (initialSize.rightBearing - initialSize.leftBearing)) /
1714 (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
1715 if (!maxWidth) {
1716 mScaleX *= scale;
1718 aDesiredStretchSize.leftBearing *= scale;
1719 aDesiredStretchSize.rightBearing *= scale;
1720 aDesiredStretchSize.width *= scale;
1723 // increase the height if it is not largeopFactor times larger
1724 // than the initial one.
1725 if (NS_STRETCH_INTEGRAL & aStretchHint) {
1726 // integrals are drawn taller
1727 largeopFactor = kIntegralFactor;
1729 if ((aDesiredStretchSize.ascent + aDesiredStretchSize.descent) <
1730 largeopFactor * (initialSize.ascent + initialSize.descent)) {
1731 scale = (largeopFactor *
1732 (initialSize.ascent + initialSize.descent)) /
1733 (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
1734 if (!maxWidth) {
1735 mScaleY *= scale;
1737 aDesiredStretchSize.ascent *= scale;
1738 aDesiredStretchSize.descent *= scale;
1742 return NS_OK;
1745 nsresult
1746 nsMathMLChar::Stretch(nsPresContext* aPresContext,
1747 nsRenderingContext& aRenderingContext,
1748 nsStretchDirection aStretchDirection,
1749 const nsBoundingMetrics& aContainerSize,
1750 nsBoundingMetrics& aDesiredStretchSize,
1751 uint32_t aStretchHint,
1752 bool aRTL)
1754 NS_ASSERTION(!(aStretchHint &
1755 ~(NS_STRETCH_VARIABLE_MASK | NS_STRETCH_LARGEOP |
1756 NS_STRETCH_INTEGRAL)),
1757 "Unexpected stretch flags");
1759 mDraw = DRAW_NORMAL;
1760 mMirrored = aRTL && nsMathMLOperators::IsMirrorableOperator(mData);
1761 mScaleY = mScaleX = 1.0;
1762 mDirection = aStretchDirection;
1763 nsresult rv =
1764 StretchInternal(aPresContext, aRenderingContext.ThebesContext(), mDirection,
1765 aContainerSize, aDesiredStretchSize, aStretchHint);
1767 // Record the metrics
1768 mBoundingMetrics = aDesiredStretchSize;
1770 return rv;
1773 // What happens here is that the StretchInternal algorithm is used but
1774 // modified by passing the NS_STRETCH_MAXWIDTH stretch hint. That causes
1775 // StretchInternal to return horizontal bounding metrics that are the maximum
1776 // that might be returned from a Stretch.
1778 // In order to avoid considering widths of some characters in fonts that will
1779 // not be used for any stretch size, StretchInternal sets the initial height
1780 // to infinity and looks for any characters smaller than this height. When a
1781 // character built from parts is considered, (it will be used by Stretch for
1782 // any characters greater than its minimum size, so) the height is set to its
1783 // minimum size, so that only widths of smaller subsequent characters are
1784 // considered.
1785 nscoord
1786 nsMathMLChar::GetMaxWidth(nsPresContext* aPresContext,
1787 nsRenderingContext& aRenderingContext,
1788 uint32_t aStretchHint,
1789 float aMaxSize, bool aMaxSizeIsAbsolute)
1791 nsBoundingMetrics bm;
1792 nsStretchDirection direction = NS_STRETCH_DIRECTION_VERTICAL;
1793 const nsBoundingMetrics container; // zero target size
1795 StretchInternal(aPresContext, aRenderingContext.ThebesContext(), direction,
1796 container, bm, aStretchHint | NS_STRETCH_MAXWIDTH);
1798 return std::max(bm.width, bm.rightBearing) - std::min(0, bm.leftBearing);
1801 class nsDisplayMathMLSelectionRect : public nsDisplayItem {
1802 public:
1803 nsDisplayMathMLSelectionRect(nsDisplayListBuilder* aBuilder,
1804 nsIFrame* aFrame, const nsRect& aRect)
1805 : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
1806 MOZ_COUNT_CTOR(nsDisplayMathMLSelectionRect);
1808 #ifdef NS_BUILD_REFCNT_LOGGING
1809 virtual ~nsDisplayMathMLSelectionRect() {
1810 MOZ_COUNT_DTOR(nsDisplayMathMLSelectionRect);
1812 #endif
1814 virtual void Paint(nsDisplayListBuilder* aBuilder,
1815 nsRenderingContext* aCtx);
1816 NS_DISPLAY_DECL_NAME("MathMLSelectionRect", TYPE_MATHML_SELECTION_RECT)
1817 private:
1818 nsRect mRect;
1821 void nsDisplayMathMLSelectionRect::Paint(nsDisplayListBuilder* aBuilder,
1822 nsRenderingContext* aCtx)
1824 // get color to use for selection from the look&feel object
1825 nscolor bgColor =
1826 LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground,
1827 NS_RGB(0, 0, 0));
1828 aCtx->SetColor(bgColor);
1829 aCtx->FillRect(mRect + ToReferenceFrame());
1832 class nsDisplayMathMLCharBackground : public nsDisplayItem {
1833 public:
1834 nsDisplayMathMLCharBackground(nsDisplayListBuilder* aBuilder,
1835 nsIFrame* aFrame, const nsRect& aRect,
1836 nsStyleContext* aStyleContext)
1837 : nsDisplayItem(aBuilder, aFrame), mStyleContext(aStyleContext),
1838 mRect(aRect) {
1839 MOZ_COUNT_CTOR(nsDisplayMathMLCharBackground);
1841 #ifdef NS_BUILD_REFCNT_LOGGING
1842 virtual ~nsDisplayMathMLCharBackground() {
1843 MOZ_COUNT_DTOR(nsDisplayMathMLCharBackground);
1845 #endif
1847 virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
1848 const nsDisplayItemGeometry* aGeometry,
1849 nsRegion *aInvalidRegion) MOZ_OVERRIDE;
1850 virtual void Paint(nsDisplayListBuilder* aBuilder,
1851 nsRenderingContext* aCtx);
1852 NS_DISPLAY_DECL_NAME("MathMLCharBackground", TYPE_MATHML_CHAR_BACKGROUND)
1853 private:
1854 nsStyleContext* mStyleContext;
1855 nsRect mRect;
1858 void
1859 nsDisplayMathMLCharBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
1860 const nsDisplayItemGeometry* aGeometry,
1861 nsRegion *aInvalidRegion)
1863 AddInvalidRegionForSyncDecodeBackgroundImages(aBuilder, aGeometry, aInvalidRegion);
1865 nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
1868 void nsDisplayMathMLCharBackground::Paint(nsDisplayListBuilder* aBuilder,
1869 nsRenderingContext* aCtx)
1871 const nsStyleBorder* border = mStyleContext->StyleBorder();
1872 nsRect rect(mRect + ToReferenceFrame());
1873 nsCSSRendering::PaintBackgroundWithSC(mFrame->PresContext(), *aCtx, mFrame,
1874 mVisibleRect, rect,
1875 mStyleContext, *border,
1876 aBuilder->GetBackgroundPaintFlags());
1879 class nsDisplayMathMLCharForeground : public nsDisplayItem {
1880 public:
1881 nsDisplayMathMLCharForeground(nsDisplayListBuilder* aBuilder,
1882 nsIFrame* aFrame, nsMathMLChar* aChar,
1883 uint32_t aIndex, bool aIsSelected)
1884 : nsDisplayItem(aBuilder, aFrame), mChar(aChar),
1885 mIndex(aIndex), mIsSelected(aIsSelected) {
1886 MOZ_COUNT_CTOR(nsDisplayMathMLCharForeground);
1888 #ifdef NS_BUILD_REFCNT_LOGGING
1889 virtual ~nsDisplayMathMLCharForeground() {
1890 MOZ_COUNT_DTOR(nsDisplayMathMLCharForeground);
1892 #endif
1894 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
1895 *aSnap = false;
1896 nsRect rect;
1897 mChar->GetRect(rect);
1898 nsPoint offset = ToReferenceFrame() + rect.TopLeft();
1899 nsBoundingMetrics bm;
1900 mChar->GetBoundingMetrics(bm);
1901 nsRect temp(offset.x + bm.leftBearing, offset.y,
1902 bm.rightBearing - bm.leftBearing, bm.ascent + bm.descent);
1903 // Bug 748220
1904 temp.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
1905 return temp;
1908 virtual void Paint(nsDisplayListBuilder* aBuilder,
1909 nsRenderingContext* aCtx)
1911 mChar->PaintForeground(mFrame->PresContext(), *aCtx,
1912 ToReferenceFrame(), mIsSelected);
1915 NS_DISPLAY_DECL_NAME("MathMLCharForeground", TYPE_MATHML_CHAR_FOREGROUND)
1917 virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder)
1919 bool snap;
1920 return GetBounds(aBuilder, &snap);
1923 virtual uint32_t GetPerFrameKey() {
1924 return (mIndex << nsDisplayItem::TYPE_BITS)
1925 | nsDisplayItem::GetPerFrameKey();
1928 private:
1929 nsMathMLChar* mChar;
1930 uint32_t mIndex;
1931 bool mIsSelected;
1934 #ifdef DEBUG
1935 class nsDisplayMathMLCharDebug : public nsDisplayItem {
1936 public:
1937 nsDisplayMathMLCharDebug(nsDisplayListBuilder* aBuilder,
1938 nsIFrame* aFrame, const nsRect& aRect)
1939 : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
1940 MOZ_COUNT_CTOR(nsDisplayMathMLCharDebug);
1942 #ifdef NS_BUILD_REFCNT_LOGGING
1943 virtual ~nsDisplayMathMLCharDebug() {
1944 MOZ_COUNT_DTOR(nsDisplayMathMLCharDebug);
1946 #endif
1948 virtual void Paint(nsDisplayListBuilder* aBuilder,
1949 nsRenderingContext* aCtx);
1950 NS_DISPLAY_DECL_NAME("MathMLCharDebug", TYPE_MATHML_CHAR_DEBUG)
1952 private:
1953 nsRect mRect;
1956 void nsDisplayMathMLCharDebug::Paint(nsDisplayListBuilder* aBuilder,
1957 nsRenderingContext* aCtx)
1959 // for visual debug
1960 Sides skipSides;
1961 nsPresContext* presContext = mFrame->PresContext();
1962 nsStyleContext* styleContext = mFrame->StyleContext();
1963 nsRect rect = mRect + ToReferenceFrame();
1964 nsCSSRendering::PaintBorder(presContext, *aCtx, mFrame,
1965 mVisibleRect, rect, styleContext, skipSides);
1966 nsCSSRendering::PaintOutline(presContext, *aCtx, mFrame,
1967 mVisibleRect, rect, styleContext);
1969 #endif
1972 void
1973 nsMathMLChar::Display(nsDisplayListBuilder* aBuilder,
1974 nsIFrame* aForFrame,
1975 const nsDisplayListSet& aLists,
1976 uint32_t aIndex,
1977 const nsRect* aSelectedRect)
1979 nsStyleContext* parentContext = mStyleContext->GetParent();
1980 nsStyleContext* styleContext = mStyleContext;
1982 if (mDraw == DRAW_NORMAL) {
1983 // normal drawing if there is nothing special about this char
1984 // Set default context to the parent context
1985 styleContext = parentContext;
1988 if (!styleContext->StyleVisibility()->IsVisible())
1989 return;
1991 // if the leaf style context that we use for stretchy chars has a background
1992 // color we use it -- this feature is mostly used for testing and debugging
1993 // purposes. Normally, users will set the background on the container frame.
1994 // paint the selection background -- beware MathML frames overlap a lot
1995 if (aSelectedRect && !aSelectedRect->IsEmpty()) {
1996 aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
1997 nsDisplayMathMLSelectionRect(aBuilder, aForFrame, *aSelectedRect));
1999 else if (mRect.width && mRect.height) {
2000 const nsStyleBackground* backg = styleContext->StyleBackground();
2001 if (styleContext != parentContext &&
2002 NS_GET_A(backg->mBackgroundColor) > 0) {
2003 aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
2004 nsDisplayMathMLCharBackground(aBuilder, aForFrame, mRect,
2005 styleContext));
2007 //else
2008 // our container frame will take care of painting its background
2010 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
2011 // for visual debug
2012 aLists.BorderBackground()->AppendToTop(new (aBuilder)
2013 nsDisplayMathMLCharDebug(aBuilder, aForFrame, mRect));
2014 #endif
2016 aLists.Content()->AppendNewToTop(new (aBuilder)
2017 nsDisplayMathMLCharForeground(aBuilder, aForFrame, this,
2018 aIndex,
2019 aSelectedRect &&
2020 !aSelectedRect->IsEmpty()));
2023 void
2024 nsMathMLChar::ApplyTransforms(gfxContext* aThebesContext,
2025 int32_t aAppUnitsPerGfxUnit,
2026 nsRect &r)
2028 // apply the transforms
2029 if (mMirrored) {
2030 nsPoint pt = r.TopRight();
2031 aThebesContext->
2032 Translate(gfxPoint(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit),
2033 NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit)));
2034 aThebesContext->Scale(-mScaleX, mScaleY);
2035 } else {
2036 nsPoint pt = r.TopLeft();
2037 aThebesContext->
2038 Translate(gfxPoint(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit),
2039 NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit)));
2040 aThebesContext->Scale(mScaleX, mScaleY);
2043 // update the bounding rectangle.
2044 r.x = r.y = 0;
2045 r.width /= mScaleX;
2046 r.height /= mScaleY;
2049 void
2050 nsMathMLChar::PaintForeground(nsPresContext* aPresContext,
2051 nsRenderingContext& aRenderingContext,
2052 nsPoint aPt,
2053 bool aIsSelected)
2055 nsStyleContext* parentContext = mStyleContext->GetParent();
2056 nsStyleContext* styleContext = mStyleContext;
2058 if (mDraw == DRAW_NORMAL) {
2059 // normal drawing if there is nothing special about this char
2060 // Set default context to the parent context
2061 styleContext = parentContext;
2064 nsRefPtr<gfxContext> thebesContext = aRenderingContext.ThebesContext();
2066 // Set color ...
2067 nscolor fgColor = styleContext->GetVisitedDependentColor(eCSSProperty_color);
2068 if (aIsSelected) {
2069 // get color to use for selection from the look&feel object
2070 fgColor = LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground,
2071 fgColor);
2073 thebesContext->SetColor(fgColor);
2074 thebesContext->Save();
2075 nsRect r = mRect + aPt;
2076 ApplyTransforms(thebesContext, aPresContext->AppUnitsPerDevPixel(), r);
2078 switch(mDraw)
2080 case DRAW_NORMAL:
2081 case DRAW_VARIANT:
2082 // draw a single glyph (base size or size variant)
2083 // XXXfredw verify if mGlyphs[0] is non-null to workaround bug 973322.
2084 if (mGlyphs[0]) {
2085 mGlyphs[0]->Draw(thebesContext, gfxPoint(0.0, mUnscaledAscent),
2086 DrawMode::GLYPH_FILL, 0, mGlyphs[0]->GetLength(),
2087 nullptr, nullptr, nullptr);
2089 break;
2090 case DRAW_PARTS: {
2091 // paint by parts
2092 if (NS_STRETCH_DIRECTION_VERTICAL == mDirection)
2093 PaintVertically(aPresContext, thebesContext, r);
2094 else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
2095 PaintHorizontally(aPresContext, thebesContext, r);
2096 break;
2098 default:
2099 NS_NOTREACHED("Unknown drawing method");
2100 break;
2103 thebesContext->Restore();
2106 /* =============================================================================
2107 Helper routines that actually do the job of painting the char by parts
2110 class AutoPushClipRect {
2111 gfxContext* mThebesContext;
2112 public:
2113 AutoPushClipRect(gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit,
2114 const nsRect& aRect)
2115 : mThebesContext(aThebesContext) {
2116 mThebesContext->Save();
2117 mThebesContext->NewPath();
2118 gfxRect clip = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerGfxUnit);
2119 mThebesContext->SnappedRectangle(clip);
2120 mThebesContext->Clip();
2122 ~AutoPushClipRect() {
2123 mThebesContext->Restore();
2127 static nsPoint
2128 SnapToDevPixels(const gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit,
2129 const nsPoint& aPt)
2131 gfxPoint pt(NSAppUnitsToFloatPixels(aPt.x, aAppUnitsPerGfxUnit),
2132 NSAppUnitsToFloatPixels(aPt.y, aAppUnitsPerGfxUnit));
2133 pt = aThebesContext->UserToDevice(pt);
2134 pt.Round();
2135 pt = aThebesContext->DeviceToUser(pt);
2136 return nsPoint(NSFloatPixelsToAppUnits(pt.x, aAppUnitsPerGfxUnit),
2137 NSFloatPixelsToAppUnits(pt.y, aAppUnitsPerGfxUnit));
2140 static void
2141 PaintRule(gfxContext* aThebesContext,
2142 int32_t aAppUnitsPerGfxUnit,
2143 nsRect& aRect)
2145 aThebesContext->NewPath();
2146 gfxRect rect = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerGfxUnit);
2147 aThebesContext->SnappedRectangle(rect);
2148 aThebesContext->Fill();
2151 // paint a stretchy char by assembling glyphs vertically
2152 nsresult
2153 nsMathMLChar::PaintVertically(nsPresContext* aPresContext,
2154 gfxContext* aThebesContext,
2155 nsRect& aRect)
2157 // Get the device pixel size in the vertical direction.
2158 // (This makes no effort to optimize for non-translation transformations.)
2159 nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
2161 // get metrics data to be re-used later
2162 int32_t i = 0;
2163 nscoord dx = aRect.x;
2164 nscoord offset[3], start[3], end[3];
2165 for (i = 0; i <= 2; ++i) {
2166 const nsBoundingMetrics& bm = mBmData[i];
2167 nscoord dy;
2168 if (0 == i) { // top
2169 dy = aRect.y + bm.ascent;
2171 else if (2 == i) { // bottom
2172 dy = aRect.y + aRect.height - bm.descent;
2174 else { // middle
2175 dy = aRect.y + bm.ascent + (aRect.height - (bm.ascent + bm.descent))/2;
2177 // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
2178 // Do this now so that we can get the other dimensions right.
2179 // (This may not achieve much with non-rectangular transformations.)
2180 dy = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).y;
2181 // abcissa passed to Draw
2182 offset[i] = dy;
2183 // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
2184 // pixel, so the bm values can include 1 row of faint pixels on each edge.
2185 // Don't rely on this pixel as it can look like a gap.
2186 if (bm.ascent + bm.descent >= 2 * oneDevPixel) {
2187 start[i] = dy - bm.ascent + oneDevPixel; // top join
2188 end[i] = dy + bm.descent - oneDevPixel; // bottom join
2189 } else {
2190 // To avoid overlaps, we don't add one pixel on each side when the part
2191 // is too small.
2192 start[i] = dy - bm.ascent; // top join
2193 end[i] = dy + bm.descent; // bottom join
2197 // If there are overlaps, then join at the mid point
2198 for (i = 0; i < 2; ++i) {
2199 if (end[i] > start[i+1]) {
2200 end[i] = (end[i] + start[i+1]) / 2;
2201 start[i+1] = end[i];
2205 nsRect unionRect = aRect;
2206 unionRect.x += mBoundingMetrics.leftBearing;
2207 unionRect.width =
2208 mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
2209 unionRect.Inflate(oneDevPixel, oneDevPixel);
2211 /////////////////////////////////////
2212 // draw top, middle, bottom
2213 for (i = 0; i <= 2; ++i) {
2214 // glue can be null
2215 if (mGlyphs[i]) {
2216 nscoord dy = offset[i];
2217 // Draw a glyph in a clipped area so that we don't have hairy chars
2218 // pending outside
2219 nsRect clipRect = unionRect;
2220 // Clip at the join to get a solid edge (without overlap or gap), when
2221 // this won't change the glyph too much. If the glyph is too small to
2222 // clip then we'll overlap rather than have a gap.
2223 nscoord height = mBmData[i].ascent + mBmData[i].descent;
2224 if (height * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
2225 if (0 == i) { // top
2226 clipRect.height = end[i] - clipRect.y;
2228 else if (2 == i) { // bottom
2229 clipRect.height -= start[i] - clipRect.y;
2230 clipRect.y = start[i];
2232 else { // middle
2233 clipRect.y = start[i];
2234 clipRect.height = end[i] - start[i];
2237 if (!clipRect.IsEmpty()) {
2238 AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
2239 mGlyphs[i]->Draw(aThebesContext, gfxPoint(dx, dy),
2240 DrawMode::GLYPH_FILL, 0, mGlyphs[i]->GetLength(),
2241 nullptr, nullptr, nullptr);
2246 ///////////////
2247 // fill the gap between top and middle, and between middle and bottom.
2248 if (!mGlyphs[3]) { // null glue : draw a rule
2249 // figure out the dimensions of the rule to be drawn :
2250 // set lbearing to rightmost lbearing among the two current successive
2251 // parts.
2252 // set rbearing to leftmost rbearing among the two current successive parts.
2253 // this not only satisfies the convention used for over/underbraces
2254 // in TeX, but also takes care of broken fonts like the stretchy integral
2255 // in Symbol for small font sizes in unix.
2256 nscoord lbearing, rbearing;
2257 int32_t first = 0, last = 1;
2258 while (last <= 2) {
2259 if (mGlyphs[last]) {
2260 lbearing = mBmData[last].leftBearing;
2261 rbearing = mBmData[last].rightBearing;
2262 if (mGlyphs[first]) {
2263 if (lbearing < mBmData[first].leftBearing)
2264 lbearing = mBmData[first].leftBearing;
2265 if (rbearing > mBmData[first].rightBearing)
2266 rbearing = mBmData[first].rightBearing;
2269 else if (mGlyphs[first]) {
2270 lbearing = mBmData[first].leftBearing;
2271 rbearing = mBmData[first].rightBearing;
2273 else {
2274 NS_ERROR("Cannot stretch - All parts missing");
2275 return NS_ERROR_UNEXPECTED;
2277 // paint the rule between the parts
2278 nsRect rule(aRect.x + lbearing, end[first],
2279 rbearing - lbearing, start[last] - end[first]);
2280 PaintRule(aThebesContext, oneDevPixel, rule);
2281 first = last;
2282 last++;
2285 else if (mBmData[3].ascent + mBmData[3].descent > 0) {
2286 // glue is present
2287 nsBoundingMetrics& bm = mBmData[3];
2288 // Ensure the stride for the glue is not reduced to less than one pixel
2289 if (bm.ascent + bm.descent >= 3 * oneDevPixel) {
2290 // To protect against gaps, pretend the glue is smaller than it is,
2291 // in order to trim off ends and thus get a solid edge for the join.
2292 bm.ascent -= oneDevPixel;
2293 bm.descent -= oneDevPixel;
2296 nsRect clipRect = unionRect;
2298 for (i = 0; i < 2; ++i) {
2299 // Make sure not to draw outside the character
2300 nscoord dy = std::max(end[i], aRect.y);
2301 nscoord fillEnd = std::min(start[i+1], aRect.YMost());
2302 while (dy < fillEnd) {
2303 clipRect.y = dy;
2304 clipRect.height = std::min(bm.ascent + bm.descent, fillEnd - dy);
2305 AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
2306 dy += bm.ascent;
2307 mGlyphs[3]->Draw(aThebesContext, gfxPoint(dx, dy),
2308 DrawMode::GLYPH_FILL, 0, mGlyphs[3]->GetLength(),
2309 nullptr, nullptr, nullptr);
2310 dy += bm.descent;
2314 #ifdef DEBUG
2315 else {
2316 for (i = 0; i < 2; ++i) {
2317 NS_ASSERTION(end[i] >= start[i+1],
2318 "gap between parts with missing glue glyph");
2321 #endif
2322 return NS_OK;
2325 // paint a stretchy char by assembling glyphs horizontally
2326 nsresult
2327 nsMathMLChar::PaintHorizontally(nsPresContext* aPresContext,
2328 gfxContext* aThebesContext,
2329 nsRect& aRect)
2331 // Get the device pixel size in the horizontal direction.
2332 // (This makes no effort to optimize for non-translation transformations.)
2333 nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
2335 // get metrics data to be re-used later
2336 int32_t i = 0;
2337 nscoord dy = aRect.y + mBoundingMetrics.ascent;
2338 nscoord offset[3], start[3], end[3];
2339 for (i = 0; i <= 2; ++i) {
2340 const nsBoundingMetrics& bm = mBmData[i];
2341 nscoord dx;
2342 if (0 == i) { // left
2343 dx = aRect.x - bm.leftBearing;
2345 else if (2 == i) { // right
2346 dx = aRect.x + aRect.width - bm.rightBearing;
2348 else { // middle
2349 dx = aRect.x + (aRect.width - bm.width)/2;
2351 // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
2352 // Do this now so that we can get the other dimensions right.
2353 // (This may not achieve much with non-rectangular transformations.)
2354 dx = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).x;
2355 // abcissa passed to Draw
2356 offset[i] = dx;
2357 // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
2358 // pixel, so the bm values can include 1 row of faint pixels on each edge.
2359 // Don't rely on this pixel as it can look like a gap.
2360 if (bm.rightBearing - bm.leftBearing >= 2 * oneDevPixel) {
2361 start[i] = dx + bm.leftBearing + oneDevPixel; // left join
2362 end[i] = dx + bm.rightBearing - oneDevPixel; // right join
2363 } else {
2364 // To avoid overlaps, we don't add one pixel on each side when the part
2365 // is too small.
2366 start[i] = dx + bm.leftBearing; // left join
2367 end[i] = dx + bm.rightBearing; // right join
2371 // If there are overlaps, then join at the mid point
2372 for (i = 0; i < 2; ++i) {
2373 if (end[i] > start[i+1]) {
2374 end[i] = (end[i] + start[i+1]) / 2;
2375 start[i+1] = end[i];
2379 nsRect unionRect = aRect;
2380 unionRect.Inflate(oneDevPixel, oneDevPixel);
2382 ///////////////////////////
2383 // draw left, middle, right
2384 for (i = 0; i <= 2; ++i) {
2385 // glue can be null
2386 if (mGlyphs[i]) {
2387 nscoord dx = offset[i];
2388 nsRect clipRect = unionRect;
2389 // Clip at the join to get a solid edge (without overlap or gap), when
2390 // this won't change the glyph too much. If the glyph is too small to
2391 // clip then we'll overlap rather than have a gap.
2392 nscoord width = mBmData[i].rightBearing - mBmData[i].leftBearing;
2393 if (width * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
2394 if (0 == i) { // left
2395 clipRect.width = end[i] - clipRect.x;
2397 else if (2 == i) { // right
2398 clipRect.width -= start[i] - clipRect.x;
2399 clipRect.x = start[i];
2401 else { // middle
2402 clipRect.x = start[i];
2403 clipRect.width = end[i] - start[i];
2406 if (!clipRect.IsEmpty()) {
2407 AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
2408 mGlyphs[i]->Draw(aThebesContext, gfxPoint(dx, dy),
2409 DrawMode::GLYPH_FILL, 0, mGlyphs[i]->GetLength(),
2410 nullptr, nullptr, nullptr);
2415 ////////////////
2416 // fill the gap between left and middle, and between middle and right.
2417 if (!mGlyphs[3]) { // null glue : draw a rule
2418 // figure out the dimensions of the rule to be drawn :
2419 // set ascent to lowest ascent among the two current successive parts.
2420 // set descent to highest descent among the two current successive parts.
2421 // this satisfies the convention used for over/underbraces, and helps
2422 // fix broken fonts.
2423 nscoord ascent, descent;
2424 int32_t first = 0, last = 1;
2425 while (last <= 2) {
2426 if (mGlyphs[last]) {
2427 ascent = mBmData[last].ascent;
2428 descent = mBmData[last].descent;
2429 if (mGlyphs[first]) {
2430 if (ascent > mBmData[first].ascent)
2431 ascent = mBmData[first].ascent;
2432 if (descent > mBmData[first].descent)
2433 descent = mBmData[first].descent;
2436 else if (mGlyphs[first]) {
2437 ascent = mBmData[first].ascent;
2438 descent = mBmData[first].descent;
2440 else {
2441 NS_ERROR("Cannot stretch - All parts missing");
2442 return NS_ERROR_UNEXPECTED;
2444 // paint the rule between the parts
2445 nsRect rule(end[first], dy - ascent,
2446 start[last] - end[first], ascent + descent);
2447 PaintRule(aThebesContext, oneDevPixel, rule);
2448 first = last;
2449 last++;
2452 else if (mBmData[3].rightBearing - mBmData[3].leftBearing > 0) {
2453 // glue is present
2454 nsBoundingMetrics& bm = mBmData[3];
2455 // Ensure the stride for the glue is not reduced to less than one pixel
2456 if (bm.rightBearing - bm.leftBearing >= 3 * oneDevPixel) {
2457 // To protect against gaps, pretend the glue is smaller than it is,
2458 // in order to trim off ends and thus get a solid edge for the join.
2459 bm.leftBearing += oneDevPixel;
2460 bm.rightBearing -= oneDevPixel;
2463 nsRect clipRect = unionRect;
2465 for (i = 0; i < 2; ++i) {
2466 // Make sure not to draw outside the character
2467 nscoord dx = std::max(end[i], aRect.x);
2468 nscoord fillEnd = std::min(start[i+1], aRect.XMost());
2469 while (dx < fillEnd) {
2470 clipRect.x = dx;
2471 clipRect.width = std::min(bm.rightBearing - bm.leftBearing, fillEnd - dx);
2472 AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
2473 dx -= bm.leftBearing;
2474 mGlyphs[3]->Draw(aThebesContext, gfxPoint(dx, dy),
2475 DrawMode::GLYPH_FILL, 0, mGlyphs[3]->GetLength(),
2476 nullptr, nullptr, nullptr);
2477 dx += bm.rightBearing;
2481 #ifdef DEBUG
2482 else { // no glue
2483 for (i = 0; i < 2; ++i) {
2484 NS_ASSERTION(end[i] >= start[i+1],
2485 "gap between parts with missing glue glyph");
2488 #endif
2489 return NS_OK;