Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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 static const float kLargeOpFactor = float(M_SQRT2);
38 static const float kIntegralFactor = 2.0;
40 // -----------------------------------------------------------------------------
41 static const nsGlyphCode kNullGlyph = {{{0, 0}}, 0};
43 // -----------------------------------------------------------------------------
44 // nsGlyphTable is a class that provides an interface for accessing glyphs
45 // of stretchy chars. It acts like a table that stores the variants of bigger
46 // sizes (if any) and the partial glyphs needed to build extensible symbols.
47 //
48 // Bigger sizes (if any) of the char can then be retrieved with BigOf(...).
49 // Partial glyphs can be retrieved with ElementAt(...).
50 //
51 // A table consists of "nsGlyphCode"s which are viewed either as Unicode
52 // points (for nsPropertiesTable) or as direct glyph indices (for
53 // nsOpenTypeTable)
54 // -----------------------------------------------------------------------------
56 class nsGlyphTable {
57 public:
58 virtual ~nsGlyphTable() {}
60 virtual const nsAString&
61 FontNameFor(const nsGlyphCode& aGlyphCode) const = 0;
63 // Getters for the parts
64 virtual nsGlyphCode ElementAt(gfxContext* aThebesContext,
65 int32_t aAppUnitsPerDevPixel,
66 gfxFontGroup* aFontGroup,
67 char16_t aChar,
68 bool aVertical,
69 uint32_t aPosition) = 0;
70 virtual nsGlyphCode BigOf(gfxContext* aThebesContext,
71 int32_t aAppUnitsPerDevPixel,
72 gfxFontGroup* aFontGroup,
73 char16_t aChar,
74 bool aVertical,
75 uint32_t aSize) = 0;
77 // True if this table contains parts to render this char
78 virtual bool HasPartsOf(gfxContext* aThebesContext,
79 int32_t aAppUnitsPerDevPixel,
80 gfxFontGroup* aFontGroup,
81 char16_t aChar,
82 bool aVertical) = 0;
84 virtual gfxTextRun* MakeTextRun(gfxContext* aThebesContext,
85 int32_t aAppUnitsPerDevPixel,
86 gfxFontGroup* aFontGroup,
87 const nsGlyphCode& aGlyph) = 0;
88 protected:
89 nsGlyphTable() : mCharCache(0) {}
90 // For speedy re-use, we always cache the last data used in the table.
91 // mCharCache is the Unicode point of the last char that was queried in this
92 // table.
93 char16_t mCharCache;
94 };
96 // An instance of nsPropertiesTable is associated with one primary font. Extra
97 // glyphs can be taken in other additional fonts when stretching certain
98 // characters.
99 // These supplementary fonts are referred to as "external" fonts to the table.
101 // General format of MathFont Property Files from which glyph data are
102 // retrieved:
103 // -----------------------------------------------------------------------------
104 // Each font should have its set of glyph data. For example, the glyph data for
105 // the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties"
106 // and "mathfontMTExtra.properties", respectively. The mathfont property file
107 // is a set of all the stretchy MathML characters that can be rendered with that
108 // font using larger and/or partial glyphs. The entry of each stretchy character
109 // in the mathfont property file gives, in that order, the 4 partial glyphs:
110 // Top (or Left), Middle, Bottom (or Right), Glue; and the variants of bigger
111 // sizes (if any).
112 // A position that is not relevant to a particular character is indicated there
113 // with the UNICODE REPLACEMENT CHARACTER 0xFFFD.
114 // -----------------------------------------------------------------------------
116 #define NS_TABLE_STATE_ERROR -1
117 #define NS_TABLE_STATE_EMPTY 0
118 #define NS_TABLE_STATE_READY 1
120 // helper to trim off comments from data in a MathFont Property File
121 static void
122 Clean(nsString& aValue)
123 {
124 // chop the trailing # comment portion if any ...
125 int32_t comment = aValue.RFindChar('#');
126 if (comment > 0) aValue.Truncate(comment);
127 aValue.CompressWhitespace();
128 }
130 // helper to load a MathFont Property File
131 static nsresult
132 LoadProperties(const nsString& aName,
133 nsCOMPtr<nsIPersistentProperties>& aProperties)
134 {
135 nsAutoString uriStr;
136 uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
137 uriStr.Append(aName);
138 uriStr.StripWhitespace(); // that may come from aName
139 uriStr.AppendLiteral(".properties");
140 return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties),
141 NS_ConvertUTF16toUTF8(uriStr));
142 }
144 class nsPropertiesTable MOZ_FINAL : public nsGlyphTable {
145 public:
146 explicit nsPropertiesTable(const nsString& aPrimaryFontName)
147 : mFontName(1) // ensure space for primary font name.
148 , mState(NS_TABLE_STATE_EMPTY)
149 {
150 MOZ_COUNT_CTOR(nsPropertiesTable);
151 mFontName.AppendElement(aPrimaryFontName);
152 }
154 ~nsPropertiesTable()
155 {
156 MOZ_COUNT_DTOR(nsPropertiesTable);
157 }
159 const nsAString& PrimaryFontName() const
160 {
161 return mFontName[0];
162 }
164 const nsAString&
165 FontNameFor(const nsGlyphCode& aGlyphCode) const MOZ_OVERRIDE
166 {
167 NS_ASSERTION(!aGlyphCode.IsGlyphID(),
168 "nsPropertiesTable can only access glyphs by code point");
169 return mFontName[aGlyphCode.font];
170 }
172 virtual nsGlyphCode ElementAt(gfxContext* aThebesContext,
173 int32_t aAppUnitsPerDevPixel,
174 gfxFontGroup* aFontGroup,
175 char16_t aChar,
176 bool aVertical,
177 uint32_t aPosition) MOZ_OVERRIDE;
179 virtual nsGlyphCode BigOf(gfxContext* aThebesContext,
180 int32_t aAppUnitsPerDevPixel,
181 gfxFontGroup* aFontGroup,
182 char16_t aChar,
183 bool aVertical,
184 uint32_t aSize) MOZ_OVERRIDE
185 {
186 return ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
187 aChar, aVertical, 4 + aSize);
188 }
190 virtual bool HasPartsOf(gfxContext* aThebesContext,
191 int32_t aAppUnitsPerDevPixel,
192 gfxFontGroup* aFontGroup,
193 char16_t aChar,
194 bool aVertical) MOZ_OVERRIDE
195 {
196 return (ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
197 aChar, aVertical, 0).Exists() ||
198 ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
199 aChar, aVertical, 1).Exists() ||
200 ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
201 aChar, aVertical, 2).Exists() ||
202 ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
203 aChar, aVertical, 3).Exists());
204 }
206 virtual gfxTextRun* MakeTextRun(gfxContext* aThebesContext,
207 int32_t aAppUnitsPerDevPixel,
208 gfxFontGroup* aFontGroup,
209 const nsGlyphCode& aGlyph) MOZ_OVERRIDE;
210 private:
212 // mFontName[0] is the primary font associated to this table. The others
213 // are possible "external" fonts for glyphs not in the primary font
214 // but which are needed to stretch certain characters in the table
215 nsTArray<nsString> mFontName;
217 // Tri-state variable for error/empty/ready
218 int32_t mState;
220 // The set of glyph data in this table, as provided by the MathFont Property
221 // File
222 nsCOMPtr<nsIPersistentProperties> mGlyphProperties;
224 // mGlyphCache is a buffer containing the glyph data associated with
225 // mCharCache.
226 // For a property line 'key = value' in the MathFont Property File,
227 // mCharCache will retain the 'key' -- which is a Unicode point, while
228 // mGlyphCache will retain the 'value', which is a consecutive list of
229 // nsGlyphCodes, i.e., the pairs of 'code@font' needed by the char -- in
230 // which 'code@0' can be specified
231 // without the optional '@0'. However, to ease subsequent processing,
232 // mGlyphCache excludes the '@' symbol and explicitly inserts all optional '0'
233 // that indicates the primary font identifier. Specifically therefore, the
234 // k-th glyph is characterized by :
235 // 1) mGlyphCache[3*k],mGlyphCache[3*k+1] : its Unicode point
236 // 2) mGlyphCache[3*k+2] : the numeric identifier of the font where it comes
237 // from.
238 // A font identifier of '0' means the default primary font associated to this
239 // table. Other digits map to the "external" fonts that may have been
240 // specified in the MathFont Property File.
241 nsString mGlyphCache;
242 };
244 /* virtual */
245 nsGlyphCode
246 nsPropertiesTable::ElementAt(gfxContext* /* aThebesContext */,
247 int32_t /* aAppUnitsPerDevPixel */,
248 gfxFontGroup* /* aFontGroup */,
249 char16_t aChar,
250 bool /* aVertical */,
251 uint32_t aPosition)
252 {
253 if (mState == NS_TABLE_STATE_ERROR) return kNullGlyph;
254 // Load glyph properties if this is the first time we have been here
255 if (mState == NS_TABLE_STATE_EMPTY) {
256 nsresult rv = LoadProperties(mFontName[0], mGlyphProperties);
257 #ifdef DEBUG
258 nsAutoCString uriStr;
259 uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
260 LossyAppendUTF16toASCII(mFontName[0], uriStr);
261 uriStr.StripWhitespace(); // that may come from mFontName
262 uriStr.AppendLiteral(".properties");
263 printf("Loading %s ... %s\n",
264 uriStr.get(),
265 (NS_FAILED(rv)) ? "Failed" : "Done");
266 #endif
267 if (NS_FAILED(rv)) {
268 mState = NS_TABLE_STATE_ERROR; // never waste time with this table again
269 return kNullGlyph;
270 }
271 mState = NS_TABLE_STATE_READY;
273 // see if there are external fonts needed for certain chars in this table
274 nsAutoCString key;
275 nsAutoString value;
276 for (int32_t i = 1; ; i++) {
277 key.AssignLiteral("external.");
278 key.AppendInt(i, 10);
279 rv = mGlyphProperties->GetStringProperty(key, value);
280 if (NS_FAILED(rv)) break;
281 Clean(value);
282 mFontName.AppendElement(value); // i.e., mFontName[i] holds this font name
283 }
284 }
286 // Update our cache if it is not associated to this character
287 if (mCharCache != aChar) {
288 // The key in the property file is interpreted as ASCII and kept
289 // as such ...
290 char key[10]; PR_snprintf(key, sizeof(key), "\\u%04X", aChar);
291 nsAutoString value;
292 nsresult rv = mGlyphProperties->GetStringProperty(nsDependentCString(key),
293 value);
294 if (NS_FAILED(rv)) return kNullGlyph;
295 Clean(value);
296 // See if this char uses external fonts; e.g., if the 2nd glyph is taken
297 // from the external font '1', the property line looks like
298 // \uNNNN = \uNNNN\uNNNN@1\uNNNN.
299 // This is where mGlyphCache is pre-processed to explicitly store all glyph
300 // codes as combined pairs of 'code@font', excluding the '@' separator. This
301 // means that mGlyphCache[3*k],mGlyphCache[3*k+1] will later be rendered
302 // with mFontName[mGlyphCache[3*k+2]]
303 // Note: font identifier is internally an ASCII digit to avoid the null
304 // char issue
305 nsAutoString buffer;
306 int32_t length = value.Length();
307 int32_t i = 0; // index in value
308 while (i < length) {
309 char16_t code = value[i];
310 ++i;
311 buffer.Append(code);
312 // Read the next word if we have a non-BMP character.
313 if (i < length && NS_IS_HIGH_SURROGATE(code)) {
314 code = value[i];
315 ++i;
316 } else {
317 code = char16_t('\0');
318 }
319 buffer.Append(code);
321 // See if an external font is needed for the code point.
322 // Limit of 9 external fonts
323 char16_t font = 0;
324 if (i+1 < length && value[i] == char16_t('@') &&
325 value[i+1] >= char16_t('0') && value[i+1] <= char16_t('9')) {
326 ++i;
327 font = value[i] - '0';
328 ++i;
329 if (font >= mFontName.Length()) {
330 NS_ERROR("Nonexistent font referenced in glyph table");
331 return kNullGlyph;
332 }
333 // The char cannot be handled if this font is not installed
334 if (!mFontName[font].Length()) {
335 return kNullGlyph;
336 }
337 }
338 buffer.Append(font);
339 }
340 // update our cache with the new settings
341 mGlyphCache.Assign(buffer);
342 mCharCache = aChar;
343 }
345 // 3* is to account for the code@font pairs
346 uint32_t index = 3*aPosition;
347 if (index+2 >= mGlyphCache.Length()) return kNullGlyph;
348 nsGlyphCode ch;
349 ch.code[0] = mGlyphCache.CharAt(index);
350 ch.code[1] = mGlyphCache.CharAt(index + 1);
351 ch.font = mGlyphCache.CharAt(index + 2);
352 return ch.code[0] == char16_t(0xFFFD) ? kNullGlyph : ch;
353 }
355 /* virtual */
356 gfxTextRun*
357 nsPropertiesTable::MakeTextRun(gfxContext* aThebesContext,
358 int32_t aAppUnitsPerDevPixel,
359 gfxFontGroup* aFontGroup,
360 const nsGlyphCode& aGlyph)
361 {
362 NS_ASSERTION(!aGlyph.IsGlyphID(),
363 "nsPropertiesTable can only access glyphs by code point");
364 return aFontGroup->
365 MakeTextRun(aGlyph.code, aGlyph.Length(), aThebesContext,
366 aAppUnitsPerDevPixel, 0);
367 }
369 // An instance of nsOpenTypeTable is associated with one gfxFontEntry that
370 // corresponds to an Open Type font with a MATH table. All the glyphs come from
371 // the same font and the calls to access size variants and parts are directly
372 // forwarded to the gfx code.
373 class nsOpenTypeTable MOZ_FINAL : public nsGlyphTable {
374 public:
375 ~nsOpenTypeTable()
376 {
377 MOZ_COUNT_DTOR(nsOpenTypeTable);
378 }
380 virtual nsGlyphCode ElementAt(gfxContext* aThebesContext,
381 int32_t aAppUnitsPerDevPixel,
382 gfxFontGroup* aFontGroup,
383 char16_t aChar,
384 bool aVertical,
385 uint32_t aPosition) MOZ_OVERRIDE;
386 virtual nsGlyphCode BigOf(gfxContext* aThebesContext,
387 int32_t aAppUnitsPerDevPixel,
388 gfxFontGroup* aFontGroup,
389 char16_t aChar,
390 bool aVertical,
391 uint32_t aSize) MOZ_OVERRIDE;
392 virtual bool HasPartsOf(gfxContext* aThebesContext,
393 int32_t aAppUnitsPerDevPixel,
394 gfxFontGroup* aFontGroup,
395 char16_t aChar,
396 bool aVertical) MOZ_OVERRIDE;
398 const nsAString&
399 FontNameFor(const nsGlyphCode& aGlyphCode) const MOZ_OVERRIDE {
400 NS_ASSERTION(aGlyphCode.IsGlyphID(),
401 "nsOpenTypeTable can only access glyphs by id");
402 return mFontEntry->FamilyName();
403 }
405 virtual gfxTextRun* MakeTextRun(gfxContext* aThebesContext,
406 int32_t aAppUnitsPerDevPixel,
407 gfxFontGroup* aFontGroup,
408 const nsGlyphCode& aGlyph) MOZ_OVERRIDE;
410 // This returns a new OpenTypeTable instance to give access to OpenType MATH
411 // table or nullptr if the font does not have such table. Ownership is passed
412 // to the caller.
413 static nsOpenTypeTable* Create(gfxFont* aFont)
414 {
415 if (!aFont->GetFontEntry()->TryGetMathTable(aFont)) {
416 return nullptr;
417 }
418 return new nsOpenTypeTable(aFont->GetFontEntry());
419 }
421 private:
422 nsRefPtr<gfxFontEntry> mFontEntry;
423 uint32_t mGlyphID;
425 explicit nsOpenTypeTable(gfxFontEntry* aFontEntry)
426 : mFontEntry(aFontEntry) {
427 MOZ_COUNT_CTOR(nsOpenTypeTable);
428 }
430 void UpdateCache(gfxContext* aThebesContext,
431 int32_t aAppUnitsPerDevPixel,
432 gfxFontGroup* aFontGroup,
433 char16_t aChar);
434 };
436 void
437 nsOpenTypeTable::UpdateCache(gfxContext* aThebesContext,
438 int32_t aAppUnitsPerDevPixel,
439 gfxFontGroup* aFontGroup,
440 char16_t aChar)
441 {
442 if (mCharCache != aChar) {
443 nsAutoPtr<gfxTextRun> textRun;
444 textRun = aFontGroup->
445 MakeTextRun(&aChar, 1, aThebesContext, aAppUnitsPerDevPixel, 0);
446 const gfxTextRun::CompressedGlyph& data = textRun->GetCharacterGlyphs()[0];
447 if (data.IsSimpleGlyph()) {
448 mGlyphID = data.GetSimpleGlyph();
449 } else if (data.GetGlyphCount() == 1) {
450 mGlyphID = textRun->GetDetailedGlyphs(0)->mGlyphID;
451 } else {
452 mGlyphID = 0;
453 }
454 mCharCache = aChar;
455 }
456 }
458 /* virtual */
459 nsGlyphCode
460 nsOpenTypeTable::ElementAt(gfxContext* aThebesContext,
461 int32_t aAppUnitsPerDevPixel,
462 gfxFontGroup* aFontGroup,
463 char16_t aChar,
464 bool aVertical,
465 uint32_t aPosition)
466 {
467 UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar);
469 uint32_t parts[4];
470 if (!mFontEntry->GetMathVariantsParts(mGlyphID, aVertical, parts)) {
471 return kNullGlyph;
472 }
474 uint32_t glyphID = parts[aPosition];
475 if (!glyphID) {
476 return kNullGlyph;
477 }
478 nsGlyphCode glyph;
479 glyph.glyphID = glyphID;
480 glyph.font = -1;
481 return glyph;
482 }
484 /* virtual */
485 nsGlyphCode
486 nsOpenTypeTable::BigOf(gfxContext* aThebesContext,
487 int32_t aAppUnitsPerDevPixel,
488 gfxFontGroup* aFontGroup,
489 char16_t aChar,
490 bool aVertical,
491 uint32_t aSize)
492 {
493 UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar);
495 uint32_t glyphID =
496 mFontEntry->GetMathVariantsSize(mGlyphID, aVertical, aSize);
497 if (!glyphID) {
498 return kNullGlyph;
499 }
501 nsGlyphCode glyph;
502 glyph.glyphID = glyphID;
503 glyph.font = -1;
504 return glyph;
505 }
507 /* virtual */
508 bool
509 nsOpenTypeTable::HasPartsOf(gfxContext* aThebesContext,
510 int32_t aAppUnitsPerDevPixel,
511 gfxFontGroup* aFontGroup,
512 char16_t aChar,
513 bool aVertical)
514 {
515 UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar);
517 uint32_t parts[4];
518 if (!mFontEntry->GetMathVariantsParts(mGlyphID, aVertical, parts)) {
519 return false;
520 }
522 return parts[0] || parts[1] || parts[2] || parts[3];
523 }
525 /* virtual */
526 gfxTextRun*
527 nsOpenTypeTable::MakeTextRun(gfxContext* aThebesContext,
528 int32_t aAppUnitsPerDevPixel,
529 gfxFontGroup* aFontGroup,
530 const nsGlyphCode& aGlyph)
531 {
532 NS_ASSERTION(aGlyph.IsGlyphID(),
533 "nsOpenTypeTable can only access glyphs by id");
535 gfxTextRunFactory::Parameters params = {
536 aThebesContext, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
537 };
538 gfxTextRun* textRun = gfxTextRun::Create(¶ms, 1, aFontGroup, 0);
539 textRun->AddGlyphRun(aFontGroup->GetFontAt(0), gfxTextRange::kFontGroup, 0,
540 false);
541 gfxTextRun::DetailedGlyph detailedGlyph;
542 detailedGlyph.mGlyphID = aGlyph.glyphID;
543 detailedGlyph.mAdvance =
544 NSToCoordRound(aAppUnitsPerDevPixel *
545 aFontGroup->GetFontAt(0)->
546 GetGlyphHAdvance(aThebesContext, aGlyph.glyphID));
547 detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
548 gfxShapedText::CompressedGlyph g;
549 g.SetComplex(true, true, 1);
550 textRun->SetGlyphs(0, g, &detailedGlyph);
552 return textRun;
553 }
555 // -----------------------------------------------------------------------------
556 // This is the list of all the applicable glyph tables.
557 // We will maintain a single global instance that will only reveal those
558 // glyph tables that are associated to fonts currently installed on the
559 // user' system. The class is an XPCOM shutdown observer to allow us to
560 // free its allocated data at shutdown
562 class nsGlyphTableList : public nsIObserver
563 {
564 public:
565 NS_DECL_ISUPPORTS
566 NS_DECL_NSIOBSERVER
568 nsPropertiesTable mUnicodeTable;
570 nsGlyphTableList()
571 : mUnicodeTable(NS_LITERAL_STRING("Unicode"))
572 {
573 MOZ_COUNT_CTOR(nsGlyphTableList);
574 }
576 virtual ~nsGlyphTableList()
577 {
578 MOZ_COUNT_DTOR(nsGlyphTableList);
579 }
581 nsresult Initialize();
582 nsresult Finalize();
584 // Add a glyph table in the list, return the new table that was added
585 nsGlyphTable*
586 AddGlyphTable(const nsString& aPrimaryFontName);
588 // Find the glyph table in the list corresponding to the given font family.
589 nsGlyphTable*
590 GetGlyphTableFor(const nsAString& aFamily);
592 private:
593 nsPropertiesTable* PropertiesTableAt(int32_t aIndex) {
594 return &mPropertiesTableList.ElementAt(aIndex);
595 }
596 int32_t PropertiesTableCount() {
597 return mPropertiesTableList.Length();
598 }
599 // List of glyph tables;
600 nsTArray<nsPropertiesTable> mPropertiesTableList;
601 };
603 NS_IMPL_ISUPPORTS(nsGlyphTableList, nsIObserver)
605 // -----------------------------------------------------------------------------
606 // Here is the global list of applicable glyph tables that we will be using
607 static nsGlyphTableList* gGlyphTableList = nullptr;
609 static bool gGlyphTableInitialized = false;
611 // XPCOM shutdown observer
612 NS_IMETHODIMP
613 nsGlyphTableList::Observe(nsISupports* aSubject,
614 const char* aTopic,
615 const char16_t* someData)
616 {
617 Finalize();
618 return NS_OK;
619 }
621 // Add an observer to XPCOM shutdown so that we can free our data at shutdown
622 nsresult
623 nsGlyphTableList::Initialize()
624 {
625 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
626 if (!obs)
627 return NS_ERROR_FAILURE;
629 nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
630 NS_ENSURE_SUCCESS(rv, rv);
632 return NS_OK;
633 }
635 // Remove our observer and free the memory that were allocated for us
636 nsresult
637 nsGlyphTableList::Finalize()
638 {
639 // Remove our observer from the observer service
640 nsresult rv = NS_OK;
641 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
642 if (obs)
643 rv = obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
644 else
645 rv = NS_ERROR_FAILURE;
647 gGlyphTableInitialized = false;
648 // our oneself will be destroyed when our |Release| is called by the observer
649 return rv;
650 }
652 nsGlyphTable*
653 nsGlyphTableList::AddGlyphTable(const nsString& aPrimaryFontName)
654 {
655 // See if there is already a special table for this family.
656 nsGlyphTable* glyphTable = GetGlyphTableFor(aPrimaryFontName);
657 if (glyphTable != &mUnicodeTable)
658 return glyphTable;
660 // allocate a table
661 glyphTable = mPropertiesTableList.AppendElement(aPrimaryFontName);
662 return glyphTable;
663 }
665 nsGlyphTable*
666 nsGlyphTableList::GetGlyphTableFor(const nsAString& aFamily)
667 {
668 for (int32_t i = 0; i < PropertiesTableCount(); i++) {
669 nsPropertiesTable* glyphTable = PropertiesTableAt(i);
670 const nsAString& fontName = glyphTable->PrimaryFontName();
671 // TODO: would be nice to consider StripWhitespace and other aliasing
672 if (fontName.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
673 return glyphTable;
674 }
675 }
676 // Fall back to default Unicode table
677 return &mUnicodeTable;
678 }
680 // -----------------------------------------------------------------------------
682 static nsresult
683 InitGlobals(nsPresContext* aPresContext)
684 {
685 NS_ASSERTION(!gGlyphTableInitialized, "Error -- already initialized");
686 gGlyphTableInitialized = true;
688 // Allocate the placeholders for the preferred parts and variants
689 nsresult rv = NS_ERROR_OUT_OF_MEMORY;
690 gGlyphTableList = new nsGlyphTableList();
691 if (gGlyphTableList) {
692 rv = gGlyphTableList->Initialize();
693 }
694 if (NS_FAILED(rv)) {
695 delete gGlyphTableList;
696 gGlyphTableList = nullptr;
697 return rv;
698 }
699 // The gGlyphTableList has been successfully registered as a shutdown
700 // observer and will be deleted at shutdown. We now add some private
701 // per font-family tables for stretchy operators, in order of preference.
702 // Do not include the Unicode table in this list.
703 if (!gGlyphTableList->AddGlyphTable(NS_LITERAL_STRING("MathJax_Main")) ||
704 !gGlyphTableList->AddGlyphTable(NS_LITERAL_STRING("STIXNonUnicode")) ||
705 !gGlyphTableList->AddGlyphTable(NS_LITERAL_STRING("STIXSizeOneSym")) ||
706 !gGlyphTableList->AddGlyphTable(NS_LITERAL_STRING("Standard Symbols L"))
707 #ifdef XP_WIN
708 || !gGlyphTableList->AddGlyphTable(NS_LITERAL_STRING("Symbol"))
709 #endif
710 ) {
711 rv = NS_ERROR_OUT_OF_MEMORY;
712 }
714 return rv;
715 }
717 // -----------------------------------------------------------------------------
718 // And now the implementation of nsMathMLChar
720 nsMathMLChar::~nsMathMLChar()
721 {
722 MOZ_COUNT_DTOR(nsMathMLChar);
723 mStyleContext->Release();
724 }
726 nsStyleContext*
727 nsMathMLChar::GetStyleContext() const
728 {
729 NS_ASSERTION(mStyleContext, "chars should always have style context");
730 return mStyleContext;
731 }
733 void
734 nsMathMLChar::SetStyleContext(nsStyleContext* aStyleContext)
735 {
736 NS_PRECONDITION(aStyleContext, "null ptr");
737 if (aStyleContext != mStyleContext) {
738 if (mStyleContext)
739 mStyleContext->Release();
740 if (aStyleContext) {
741 mStyleContext = aStyleContext;
742 aStyleContext->AddRef();
743 }
744 }
745 }
747 void
748 nsMathMLChar::SetData(nsPresContext* aPresContext,
749 nsString& aData)
750 {
751 if (!gGlyphTableInitialized) {
752 InitGlobals(aPresContext);
753 }
754 mData = aData;
755 // some assumptions until proven otherwise
756 // note that mGlyph is not initialized
757 mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
758 mBoundingMetrics = nsBoundingMetrics();
759 // check if stretching is applicable ...
760 if (gGlyphTableList && (1 == mData.Length())) {
761 mDirection = nsMathMLOperators::GetStretchyDirection(mData);
762 // default tentative table (not the one that is necessarily going
763 // to be used)
764 }
765 }
767 // -----------------------------------------------------------------------------
768 /*
769 The Stretch:
770 @param aContainerSize - suggested size for the stretched char
771 @param aDesiredStretchSize - OUT parameter. The desired size
772 after stretching. If no stretching is done, the output will
773 simply give the base size.
775 How it works?
776 Summary:-
777 The Stretch() method first looks for a glyph of appropriate
778 size; If a glyph is found, it is cached by this object and
779 its size is returned in aDesiredStretchSize. The cached
780 glyph will then be used at the painting stage.
781 If no glyph of appropriate size is found, a search is made
782 to see if the char can be built by parts.
784 Details:-
785 A character gets stretched through the following pipeline :
787 1) If the base size of the char is sufficient to cover the
788 container' size, we use that. If not, it will still be
789 used as a fallback if the other stages in the pipeline fail.
790 Issues :
791 a) The base size, the parts and the variants of a char can
792 be in different fonts. For eg., the base size for '(' should
793 come from a normal ascii font if CMEX10 is used, since CMEX10
794 only contains the stretched versions. Hence, there are two
795 style contexts in use throughout the process. The leaf style
796 context of the char holds fonts with which to try to stretch
797 the char. The parent style context of the char contains fonts
798 for normal rendering. So the parent context is the one used
799 to get the initial base size at the start of the pipeline.
800 b) For operators that can be largeop's in display mode,
801 we will skip the base size even if it fits, so that
802 the next stage in the pipeline is given a chance to find
803 a largeop variant. If the next stage fails, we fallback
804 to the base size.
806 2) We search for the first larger variant of the char that fits the
807 container' size. We first search for larger variants using the glyph
808 table corresponding to the first existing font specified in the list of
809 stretchy fonts held by the leaf style context (from -moz-math-stretchy in
810 mathml.css). Generic fonts are resolved by the preference
811 "font.mathfont-family".
812 Issues :
813 a) the largeop and display settings determine the starting
814 size when we do the above search, regardless of whether
815 smaller variants already fit the container' size.
816 b) if it is a largeopOnly request (i.e., a displaystyle operator
817 with largeop=true and stretchy=false), we break after finding
818 the first starting variant, regardless of whether that
819 variant fits the container's size.
821 3) If a variant of appropriate size wasn't found, we see if the char
822 can be built by parts using the same glyph table.
823 Issue:
824 There are chars that have no middle and glue glyphs. For
825 such chars, the parts need to be joined using the rule.
826 By convention (TeXbook p.225), the descent of the parts is
827 zero while their ascent gives the thickness of the rule that
828 should be used to join them.
830 4) If a match was not found in that glyph table, repeat from 2 to search the
831 ordered list of stretchy fonts for the first font with a glyph table that
832 provides a fit to the container size. If no fit is found, the closest fit
833 is used.
835 Of note:
836 When the pipeline completes successfully, the desired size of the
837 stretched char can actually be slightly larger or smaller than
838 aContainerSize. But it is the responsibility of the caller to
839 account for the spacing when setting aContainerSize, and to leave
840 any extra margin when placing the stretched char.
841 */
842 // -----------------------------------------------------------------------------
845 // plain TeX settings (TeXbook p.152)
846 #define NS_MATHML_DELIMITER_FACTOR 0.901f
847 #define NS_MATHML_DELIMITER_SHORTFALL_POINTS 5.0f
849 static bool
850 IsSizeOK(nsPresContext* aPresContext, nscoord a, nscoord b, uint32_t aHint)
851 {
852 // Normal: True if 'a' is around +/-10% of the target 'b' (10% is
853 // 1-DelimiterFactor). This often gives a chance to the base size to
854 // win, especially in the context of <mfenced> without tall elements
855 // or in sloppy markups without protective <mrow></mrow>
856 bool isNormal =
857 (aHint & NS_STRETCH_NORMAL) &&
858 Abs<float>(a - b) < (1.0f - NS_MATHML_DELIMITER_FACTOR) * float(b);
860 // Nearer: True if 'a' is around max{ +/-10% of 'b' , 'b' - 5pt },
861 // as documented in The TeXbook, Ch.17, p.152.
862 // i.e. within 10% and within 5pt
863 bool isNearer = false;
864 if (aHint & (NS_STRETCH_NEARER | NS_STRETCH_LARGEOP)) {
865 float c = std::max(float(b) * NS_MATHML_DELIMITER_FACTOR,
866 float(b) - nsPresContext::
867 CSSPointsToAppUnits(NS_MATHML_DELIMITER_SHORTFALL_POINTS));
868 isNearer = Abs<float>(b - a) <= float(b) - c;
869 }
871 // Smaller: Mainly for transitory use, to compare two candidate
872 // choices
873 bool isSmaller =
874 (aHint & NS_STRETCH_SMALLER) &&
875 float(a) >= NS_MATHML_DELIMITER_FACTOR * float(b) &&
876 a <= b;
878 // Larger: Critical to the sqrt code to ensure that the radical
879 // size is tall enough
880 bool isLarger =
881 (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP)) &&
882 a >= b;
884 return (isNormal || isSmaller || isNearer || isLarger);
885 }
887 static bool
888 IsSizeBetter(nscoord a, nscoord olda, nscoord b, uint32_t aHint)
889 {
890 if (0 == olda)
891 return true;
892 if (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
893 return (a >= olda) ? (olda < b) : (a >= b);
894 if (aHint & NS_STRETCH_SMALLER)
895 return (a <= olda) ? (olda > b) : (a <= b);
897 // XXXkt prob want log scale here i.e. 1.5 is closer to 1 than 0.5
898 return Abs(a - b) < Abs(olda - b);
899 }
901 // We want to place the glyphs even when they don't fit at their
902 // full extent, i.e., we may clip to tolerate a small amount of
903 // overlap between the parts. This is important to cater for fonts
904 // with long glues.
905 static nscoord
906 ComputeSizeFromParts(nsPresContext* aPresContext,
907 nsGlyphCode* aGlyphs,
908 nscoord* aSizes,
909 nscoord aTargetSize)
910 {
911 enum {first, middle, last, glue};
912 // Add the parts that cannot be left out.
913 nscoord sum = 0;
914 for (int32_t i = first; i <= last; i++) {
915 if (aGlyphs[i] != aGlyphs[glue]) {
916 sum += aSizes[i];
917 }
918 }
920 // Determine how much is used in joins
921 nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
922 int32_t joins = aGlyphs[middle] == aGlyphs[glue] ? 1 : 2;
924 // Pick a maximum size using a maximum number of glue glyphs that we are
925 // prepared to draw for one character.
926 const int32_t maxGlyphs = 1000;
928 // This also takes into account the fact that, if the glue has no size,
929 // then the character can't be lengthened.
930 nscoord maxSize = sum - 2 * joins * oneDevPixel + maxGlyphs * aSizes[glue];
931 if (maxSize < aTargetSize)
932 return maxSize; // settle with the maximum size
934 // Get the minimum allowable size using some flex.
935 nscoord minSize = NSToCoordRound(NS_MATHML_DELIMITER_FACTOR * sum);
937 if (minSize > aTargetSize)
938 return minSize; // settle with the minimum size
940 // Fill-up the target area
941 return aTargetSize;
942 }
944 // Insert aFallbackFamilies before the first generic family in or at the end
945 // of a CSS aFontName.
946 static void
947 AddFallbackFonts(nsAString& aFontName, const nsAString& aFallbackFamilies)
948 {
949 if (aFallbackFamilies.IsEmpty())
950 return;
952 if (aFontName.IsEmpty()) {
953 return;
954 }
956 static const char16_t kSingleQuote = char16_t('\'');
957 static const char16_t kDoubleQuote = char16_t('\"');
958 static const char16_t kComma = char16_t(',');
960 const char16_t *p_begin, *p_end;
961 aFontName.BeginReading(p_begin);
962 aFontName.EndReading(p_end);
964 const char16_t *p = p_begin;
965 const char16_t *p_name = nullptr;
966 while (p < p_end) {
967 while (nsCRT::IsAsciiSpace(*p))
968 if (++p == p_end)
969 goto insert;
971 p_name = p;
972 if (*p == kSingleQuote || *p == kDoubleQuote) {
973 // quoted font family
974 char16_t quoteMark = *p;
975 if (++p == p_end)
976 goto insert;
978 // XXX What about CSS character escapes?
979 while (*p != quoteMark)
980 if (++p == p_end)
981 goto insert;
983 while (++p != p_end && *p != kComma)
984 /* nothing */ ;
986 } else {
987 // unquoted font family
988 const char16_t *nameStart = p;
989 while (++p != p_end && *p != kComma)
990 /* nothing */ ;
992 nsAutoString family;
993 family = Substring(nameStart, p);
994 family.CompressWhitespace(false, true);
996 uint8_t generic;
997 nsFont::GetGenericID(family, &generic);
998 if (generic != kGenericFont_NONE)
999 goto insert;
1000 }
1002 ++p; // may advance past p_end
1003 }
1005 aFontName.Append(NS_LITERAL_STRING(",") + aFallbackFamilies);
1006 return;
1008 insert:
1009 if (p_name) {
1010 aFontName.Insert(aFallbackFamilies + NS_LITERAL_STRING(","),
1011 p_name - p_begin);
1012 }
1013 else { // whitespace or empty
1014 aFontName = aFallbackFamilies;
1015 }
1016 }
1018 // Update the font if there is a family change and returns the font group.
1019 bool
1020 nsMathMLChar::SetFontFamily(nsPresContext* aPresContext,
1021 const nsGlyphTable* aGlyphTable,
1022 const nsGlyphCode& aGlyphCode,
1023 const nsAString& aDefaultFamily,
1024 nsFont& aFont,
1025 nsRefPtr<gfxFontGroup>* aFontGroup)
1026 {
1027 const nsAString& family =
1028 aGlyphCode.font ? aGlyphTable->FontNameFor(aGlyphCode) : aDefaultFamily;
1029 if (!*aFontGroup || !family.Equals(aFont.name)) {
1030 nsFont font = aFont;
1031 font.name = family;
1032 nsRefPtr<nsFontMetrics> fm;
1033 aPresContext->DeviceContext()->
1034 GetMetricsFor(font,
1035 mStyleContext->StyleFont()->mLanguage,
1036 aPresContext->GetUserFontSet(),
1037 aPresContext->GetTextPerfMetrics(),
1038 *getter_AddRefs(fm));
1039 // Set the font if it is an unicode table
1040 // or if the same family name has been found
1041 if (aGlyphTable == &gGlyphTableList->mUnicodeTable ||
1042 fm->GetThebesFontGroup()->GetFontAt(0)->GetFontEntry()->
1043 FamilyName() == family) {
1044 aFont.name = family;
1045 *aFontGroup = fm->GetThebesFontGroup();
1046 } else {
1047 return false; // We did not set the font
1048 }
1049 }
1050 return true;
1051 }
1053 static nsBoundingMetrics
1054 MeasureTextRun(gfxContext* aThebesContext, gfxTextRun* aTextRun)
1055 {
1056 gfxTextRun::Metrics metrics =
1057 aTextRun->MeasureText(0, aTextRun->GetLength(),
1058 gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS,
1059 aThebesContext, nullptr);
1061 nsBoundingMetrics bm;
1062 bm.leftBearing = NSToCoordFloor(metrics.mBoundingBox.X());
1063 bm.rightBearing = NSToCoordCeil(metrics.mBoundingBox.XMost());
1064 bm.ascent = NSToCoordCeil(-metrics.mBoundingBox.Y());
1065 bm.descent = NSToCoordCeil(metrics.mBoundingBox.YMost());
1066 bm.width = NSToCoordRound(metrics.mAdvanceWidth);
1068 return bm;
1069 }
1071 class nsMathMLChar::StretchEnumContext {
1072 public:
1073 StretchEnumContext(nsMathMLChar* aChar,
1074 nsPresContext* aPresContext,
1075 gfxContext* aThebesContext,
1076 nsStretchDirection aStretchDirection,
1077 nscoord aTargetSize,
1078 uint32_t aStretchHint,
1079 nsBoundingMetrics& aStretchedMetrics,
1080 const nsAString& aFamilies,
1081 bool& aGlyphFound)
1082 : mChar(aChar),
1083 mPresContext(aPresContext),
1084 mThebesContext(aThebesContext),
1085 mDirection(aStretchDirection),
1086 mTargetSize(aTargetSize),
1087 mStretchHint(aStretchHint),
1088 mBoundingMetrics(aStretchedMetrics),
1089 mFamilies(aFamilies),
1090 mTryVariants(true),
1091 mTryParts(true),
1092 mGlyphFound(aGlyphFound) {}
1094 static bool
1095 EnumCallback(const nsString& aFamily, bool aGeneric, void *aData);
1097 private:
1098 bool TryVariants(nsGlyphTable* aGlyphTable,
1099 nsRefPtr<gfxFontGroup>* aFontGroup,
1100 const nsAString& aFamily);
1101 bool TryParts(nsGlyphTable* aGlyphTable,
1102 nsRefPtr<gfxFontGroup>* aFontGroup,
1103 const nsAString& aFamily);
1105 nsMathMLChar* mChar;
1106 nsPresContext* mPresContext;
1107 gfxContext* mThebesContext;
1108 const nsStretchDirection mDirection;
1109 const nscoord mTargetSize;
1110 const uint32_t mStretchHint;
1111 nsBoundingMetrics& mBoundingMetrics;
1112 // Font families to search
1113 const nsAString& mFamilies;
1115 public:
1116 bool mTryVariants;
1117 bool mTryParts;
1119 private:
1120 nsAutoTArray<nsGlyphTable*,16> mTablesTried;
1121 bool& mGlyphFound;
1122 };
1125 // 2. See if there are any glyphs of the appropriate size.
1126 // Returns true if the size is OK, false to keep searching.
1127 // Always updates the char if a better match is found.
1128 bool
1129 nsMathMLChar::
1130 StretchEnumContext::TryVariants(nsGlyphTable* aGlyphTable,
1131 nsRefPtr<gfxFontGroup>* aFontGroup,
1132 const nsAString& aFamily)
1133 {
1134 // Use our stretchy style context now that stretching is in progress
1135 nsStyleContext *sc = mChar->mStyleContext;
1136 nsFont font = sc->StyleFont()->mFont;
1138 bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
1139 nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
1140 char16_t uchar = mChar->mData[0];
1141 bool largeop = (NS_STRETCH_LARGEOP & mStretchHint) != 0;
1142 bool largeopOnly =
1143 largeop && (NS_STRETCH_VARIABLE_MASK & mStretchHint) == 0;
1144 bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
1146 nscoord bestSize =
1147 isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
1148 : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
1149 bool haveBetter = false;
1151 // start at size = 1 (size = 0 is the char at its normal size)
1152 int32_t size = 1;
1153 nsGlyphCode ch;
1154 nscoord displayOperatorMinHeight = 0;
1155 if (largeopOnly) {
1156 NS_ASSERTION(isVertical, "Stretching should be in the vertical direction");
1157 ch = aGlyphTable->BigOf(mThebesContext, oneDevPixel, *aFontGroup, uchar,
1158 isVertical, 0);
1159 if (ch.IsGlyphID()) {
1160 gfxFont* mathFont = aFontGroup->get()->GetFontAt(0);
1161 // For OpenType MATH fonts, we will rely on the DisplayOperatorMinHeight
1162 // to select the right size variant. Note that the value is sometimes too
1163 // small so we use kLargeOpFactor/kIntegralFactor as a minimum value.
1164 displayOperatorMinHeight =
1165 NSToCoordRound(mathFont->GetFontEntry()->
1166 GetMathConstant(gfxFontEntry::DisplayOperatorMinHeight) *
1167 mathFont->GetAdjustedSize() * oneDevPixel);
1168 nsAutoPtr<gfxTextRun> textRun;
1169 textRun = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel,
1170 *aFontGroup, ch);
1171 nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun);
1172 float largeopFactor = kLargeOpFactor;
1173 if (NS_STRETCH_INTEGRAL & mStretchHint) {
1174 // integrals are drawn taller
1175 largeopFactor = kIntegralFactor;
1176 }
1177 nscoord minHeight = largeopFactor * (bm.ascent + bm.descent);
1178 if (displayOperatorMinHeight < minHeight) {
1179 displayOperatorMinHeight = minHeight;
1180 }
1181 }
1182 }
1183 #ifdef NOISY_SEARCH
1184 printf(" searching in %s ...\n",
1185 NS_LossyConvertUTF16toASCII(aFamily).get());
1186 #endif
1187 while ((ch = aGlyphTable->BigOf(mThebesContext, oneDevPixel, *aFontGroup,
1188 uchar, isVertical, size)).Exists()) {
1190 if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamily, font,
1191 aFontGroup)) {
1192 // if largeopOnly is set, break now
1193 if (largeopOnly) break;
1194 ++size;
1195 continue;
1196 }
1198 nsAutoPtr<gfxTextRun> textRun;
1199 textRun = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel,
1200 *aFontGroup, ch);
1201 nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun);
1202 if (ch.IsGlyphID()) {
1203 gfxFont* mathFont = aFontGroup->get()->GetFontAt(0);
1204 if (mathFont->GetFontEntry()->TryGetMathTable(mathFont)) {
1205 // MeasureTextRun should have set the advance width to the right
1206 // bearing for OpenType MATH fonts. We now subtract the italic
1207 // correction, so that nsMathMLmmultiscripts will place the scripts
1208 // correctly.
1209 // Note that STIX-Word does not provide italic corrections but its
1210 // advance widths do not match right bearings.
1211 // (http://sourceforge.net/p/stixfonts/tracking/50/)
1212 gfxFloat italicCorrection;
1213 if (mathFont->GetFontEntry()->
1214 GetMathItalicsCorrection(ch.glyphID, &italicCorrection)) {
1215 bm.width -=
1216 NSToCoordRound(italicCorrection *
1217 mathFont->GetAdjustedSize() * oneDevPixel);
1218 if (bm.width < 0) {
1219 bm.width = 0;
1220 }
1221 }
1222 }
1223 }
1225 nscoord charSize =
1226 isVertical ? bm.ascent + bm.descent
1227 : bm.rightBearing - bm.leftBearing;
1229 if (largeopOnly ||
1230 IsSizeBetter(charSize, bestSize, mTargetSize, mStretchHint)) {
1231 mGlyphFound = true;
1232 if (maxWidth) {
1233 // IsSizeBetter() checked that charSize < maxsize;
1234 // Leave ascent, descent, and bestsize as these contain maxsize.
1235 if (mBoundingMetrics.width < bm.width)
1236 mBoundingMetrics.width = bm.width;
1237 if (mBoundingMetrics.leftBearing > bm.leftBearing)
1238 mBoundingMetrics.leftBearing = bm.leftBearing;
1239 if (mBoundingMetrics.rightBearing < bm.rightBearing)
1240 mBoundingMetrics.rightBearing = bm.rightBearing;
1241 // Continue to check other sizes unless largeopOnly
1242 haveBetter = largeopOnly;
1243 }
1244 else {
1245 mBoundingMetrics = bm;
1246 haveBetter = true;
1247 bestSize = charSize;
1248 mChar->mGlyphs[0] = textRun;
1249 mChar->mDraw = DRAW_VARIANT;
1250 }
1251 #ifdef NOISY_SEARCH
1252 printf(" size:%d Current best\n", size);
1253 #endif
1254 }
1255 else {
1256 #ifdef NOISY_SEARCH
1257 printf(" size:%d Rejected!\n", size);
1258 #endif
1259 if (haveBetter)
1260 break; // Not making an futher progress, stop searching
1261 }
1263 // If this a largeop only operator, we stop if the glyph is large enough.
1264 if (largeopOnly && (bm.ascent + bm.descent) >= displayOperatorMinHeight) {
1265 break;
1266 }
1267 ++size;
1268 }
1270 return haveBetter &&
1271 (largeopOnly ||
1272 IsSizeOK(mPresContext, bestSize, mTargetSize, mStretchHint));
1273 }
1275 // 3. Build by parts.
1276 // Returns true if the size is OK, false to keep searching.
1277 // Always updates the char if a better match is found.
1278 bool
1279 nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable* aGlyphTable,
1280 nsRefPtr<gfxFontGroup>* aFontGroup,
1281 const nsAString& aFamily)
1282 {
1283 // Use our stretchy style context now that stretching is in progress
1284 nsFont font = mChar->mStyleContext->StyleFont()->mFont;
1286 // Compute the bounding metrics of all partial glyphs
1287 nsAutoPtr<gfxTextRun> textRun[4];
1288 nsGlyphCode chdata[4];
1289 nsBoundingMetrics bmdata[4];
1290 nscoord sizedata[4];
1292 bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
1293 nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
1294 char16_t uchar = mChar->mData[0];
1295 bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
1296 if (!aGlyphTable->HasPartsOf(mThebesContext, oneDevPixel, *aFontGroup,
1297 uchar, isVertical))
1298 return false; // to next table
1300 for (int32_t i = 0; i < 4; i++) {
1301 nsGlyphCode ch = aGlyphTable->ElementAt(mThebesContext, oneDevPixel,
1302 *aFontGroup, uchar, isVertical, i);
1303 chdata[i] = ch;
1304 if (ch.Exists()) {
1305 if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamily, font,
1306 aFontGroup))
1307 return false;
1309 textRun[i] = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel,
1310 *aFontGroup, ch);
1311 nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun[i]);
1313 // TODO: For the generic Unicode table, ideally we should check that the
1314 // glyphs are actually found and that they each come from the same
1315 // font.
1316 bmdata[i] = bm;
1317 sizedata[i] = isVertical ? bm.ascent + bm.descent
1318 : bm.rightBearing - bm.leftBearing;
1319 } else {
1320 // Null glue indicates that a rule will be drawn, which can stretch to
1321 // fill any space.
1322 textRun[i] = nullptr;
1323 bmdata[i] = nsBoundingMetrics();
1324 sizedata[i] = i == 3 ? mTargetSize : 0;
1325 }
1326 }
1328 // Build by parts if we have successfully computed the
1329 // bounding metrics of all parts.
1330 nscoord computedSize = ComputeSizeFromParts(mPresContext, chdata, sizedata,
1331 mTargetSize);
1333 nscoord currentSize =
1334 isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
1335 : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
1337 if (!IsSizeBetter(computedSize, currentSize, mTargetSize, mStretchHint)) {
1338 #ifdef NOISY_SEARCH
1339 printf(" Font %s Rejected!\n",
1340 NS_LossyConvertUTF16toASCII(fontName).get());
1341 #endif
1342 return false; // to next table
1343 }
1345 #ifdef NOISY_SEARCH
1346 printf(" Font %s Current best!\n",
1347 NS_LossyConvertUTF16toASCII(fontName).get());
1348 #endif
1350 // The computed size is the best we have found so far...
1351 // now is the time to compute and cache our bounding metrics
1352 if (isVertical) {
1353 int32_t i;
1354 // Try and find the first existing part and then determine the extremal
1355 // horizontal metrics of the parts.
1356 for (i = 0; i <= 3 && !textRun[i]; i++);
1357 if (i == 4) {
1358 NS_ERROR("Cannot stretch - All parts missing");
1359 return false;
1360 }
1361 nscoord lbearing = bmdata[i].leftBearing;
1362 nscoord rbearing = bmdata[i].rightBearing;
1363 nscoord width = bmdata[i].width;
1364 i++;
1365 for (; i <= 3; i++) {
1366 if (!textRun[i]) continue;
1367 lbearing = std::min(lbearing, bmdata[i].leftBearing);
1368 rbearing = std::max(rbearing, bmdata[i].rightBearing);
1369 width = std::max(width, bmdata[i].width);
1370 }
1371 if (maxWidth) {
1372 lbearing = std::min(lbearing, mBoundingMetrics.leftBearing);
1373 rbearing = std::max(rbearing, mBoundingMetrics.rightBearing);
1374 width = std::max(width, mBoundingMetrics.width);
1375 }
1376 mBoundingMetrics.width = width;
1377 // When maxWidth, updating ascent and descent indicates that no characters
1378 // larger than this character's minimum size need to be checked as they
1379 // will not be used.
1380 mBoundingMetrics.ascent = bmdata[0].ascent; // not used except with descent
1381 // for height
1382 mBoundingMetrics.descent = computedSize - mBoundingMetrics.ascent;
1383 mBoundingMetrics.leftBearing = lbearing;
1384 mBoundingMetrics.rightBearing = rbearing;
1385 }
1386 else {
1387 int32_t i;
1388 // Try and find the first existing part and then determine the extremal
1389 // vertical metrics of the parts.
1390 for (i = 0; i <= 3 && !textRun[i]; i++);
1391 if (i == 4) {
1392 NS_ERROR("Cannot stretch - All parts missing");
1393 return false;
1394 }
1395 nscoord ascent = bmdata[i].ascent;
1396 nscoord descent = bmdata[i].descent;
1397 i++;
1398 for (; i <= 3; i++) {
1399 if (!textRun[i]) continue;
1400 ascent = std::max(ascent, bmdata[i].ascent);
1401 descent = std::max(descent, bmdata[i].descent);
1402 }
1403 mBoundingMetrics.width = computedSize;
1404 mBoundingMetrics.ascent = ascent;
1405 mBoundingMetrics.descent = descent;
1406 mBoundingMetrics.leftBearing = 0;
1407 mBoundingMetrics.rightBearing = computedSize;
1408 }
1409 mGlyphFound = true;
1410 if (maxWidth)
1411 return false; // Continue to check other sizes
1413 // reset
1414 mChar->mDraw = DRAW_PARTS;
1415 for (int32_t i = 0; i < 4; i++) {
1416 mChar->mGlyphs[i] = textRun[i];
1417 mChar->mBmData[i] = bmdata[i];
1418 }
1420 return IsSizeOK(mPresContext, computedSize, mTargetSize, mStretchHint);
1421 }
1423 // This is called for each family, whether it exists or not
1424 bool
1425 nsMathMLChar::StretchEnumContext::EnumCallback(const nsString& aFamily,
1426 bool aGeneric, void *aData)
1427 {
1428 StretchEnumContext* context = static_cast<StretchEnumContext*>(aData);
1430 // Check font family if it is not a generic one
1431 // We test with the kNullGlyph
1432 nsStyleContext *sc = context->mChar->mStyleContext;
1433 nsFont font = sc->StyleFont()->mFont;
1434 nsRefPtr<gfxFontGroup> fontGroup;
1435 if (!aGeneric && !context->mChar->SetFontFamily(context->mPresContext,
1436 nullptr, kNullGlyph, aFamily,
1437 font, &fontGroup))
1438 return true; // Could not set the family
1440 // Determine the glyph table to use for this font.
1441 nsAutoPtr<nsOpenTypeTable> openTypeTable;
1442 nsGlyphTable* glyphTable;
1443 if (aGeneric) {
1444 // This is a generic font, use the Unicode table.
1445 glyphTable = &gGlyphTableList->mUnicodeTable;
1446 } else {
1447 // If the font contains an Open Type MATH table, use it.
1448 openTypeTable = nsOpenTypeTable::Create(fontGroup->GetFontAt(0));
1449 if (openTypeTable) {
1450 glyphTable = openTypeTable;
1451 } else {
1452 // Otherwise try to find a .properties file corresponding to that font
1453 // family or fallback to the Unicode table.
1454 glyphTable = gGlyphTableList->GetGlyphTableFor(aFamily);
1455 }
1456 }
1458 if (!openTypeTable) {
1459 if (context->mTablesTried.Contains(glyphTable))
1460 return true; // already tried this one
1462 // Only try this table once.
1463 context->mTablesTried.AppendElement(glyphTable);
1464 }
1466 // If the unicode table is being used, then search all font families. If a
1467 // special table is being used then the font in this family should have the
1468 // specified glyphs.
1469 const nsAString& family = glyphTable == &gGlyphTableList->mUnicodeTable ?
1470 context->mFamilies : aFamily;
1472 if((context->mTryVariants &&
1473 context->TryVariants(glyphTable, &fontGroup, family)) ||
1474 (context->mTryParts && context->TryParts(glyphTable, &fontGroup, family)))
1475 return false; // no need to continue
1477 return true; // true means continue
1478 }
1480 nsresult
1481 nsMathMLChar::StretchInternal(nsPresContext* aPresContext,
1482 gfxContext* aThebesContext,
1483 nsStretchDirection& aStretchDirection,
1484 const nsBoundingMetrics& aContainerSize,
1485 nsBoundingMetrics& aDesiredStretchSize,
1486 uint32_t aStretchHint,
1487 // These are currently only used when
1488 // aStretchHint & NS_STRETCH_MAXWIDTH:
1489 float aMaxSize,
1490 bool aMaxSizeIsAbsolute)
1491 {
1492 // if we have been called before, and we didn't actually stretch, our
1493 // direction may have been set to NS_STRETCH_DIRECTION_UNSUPPORTED.
1494 // So first set our direction back to its instrinsic value
1495 nsStretchDirection direction = nsMathMLOperators::GetStretchyDirection(mData);
1497 // Set default font and get the default bounding metrics
1498 // mStyleContext is a leaf context used only when stretching happens.
1499 // For the base size, the default font should come from the parent context
1500 nsFont font = mStyleContext->GetParent()->StyleFont()->mFont;
1502 nsRefPtr<nsFontMetrics> fm;
1503 aPresContext->DeviceContext()->
1504 GetMetricsFor(font,
1505 mStyleContext->StyleFont()->mLanguage,
1506 aPresContext->GetUserFontSet(),
1507 aPresContext->GetTextPerfMetrics(),
1508 *getter_AddRefs(fm));
1509 uint32_t len = uint32_t(mData.Length());
1510 nsAutoPtr<gfxTextRun> textRun;
1511 textRun = fm->GetThebesFontGroup()->
1512 MakeTextRun(static_cast<const char16_t*>(mData.get()), len, aThebesContext,
1513 aPresContext->AppUnitsPerDevPixel(), 0);
1514 aDesiredStretchSize = MeasureTextRun(aThebesContext, textRun);
1515 mGlyphs[0] = textRun;
1517 bool maxWidth = (NS_STRETCH_MAXWIDTH & aStretchHint) != 0;
1518 if (!maxWidth) {
1519 mUnscaledAscent = aDesiredStretchSize.ascent;
1520 }
1522 //////////////////////////////////////////////////////////////////////////////
1523 // 1. Check the common situations where stretching is not actually needed
1524 //////////////////////////////////////////////////////////////////////////////
1526 // quick return if there is nothing special about this char
1527 if ((aStretchDirection != direction &&
1528 aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT) ||
1529 (aStretchHint & ~NS_STRETCH_MAXWIDTH) == NS_STRETCH_NONE) {
1530 mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
1531 return NS_OK;
1532 }
1534 // if no specified direction, attempt to stretch in our preferred direction
1535 if (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT) {
1536 aStretchDirection = direction;
1537 }
1539 // see if this is a particular largeop or largeopOnly request
1540 bool largeop = (NS_STRETCH_LARGEOP & aStretchHint) != 0;
1541 bool stretchy = (NS_STRETCH_VARIABLE_MASK & aStretchHint) != 0;
1542 bool largeopOnly = largeop && !stretchy;
1544 bool isVertical = (direction == NS_STRETCH_DIRECTION_VERTICAL);
1546 nscoord targetSize =
1547 isVertical ? aContainerSize.ascent + aContainerSize.descent
1548 : aContainerSize.rightBearing - aContainerSize.leftBearing;
1550 if (maxWidth) {
1551 // See if it is only necessary to consider glyphs up to some maximum size.
1552 // Set the current height to the maximum size, and set aStretchHint to
1553 // NS_STRETCH_SMALLER if the size is variable, so that only smaller sizes
1554 // are considered. targetSize from GetMaxWidth() is 0.
1555 if (stretchy) {
1556 // variable size stretch - consider all sizes < maxsize
1557 aStretchHint =
1558 (aStretchHint & ~NS_STRETCH_VARIABLE_MASK) | NS_STRETCH_SMALLER;
1559 }
1561 // Use NS_MATHML_DELIMITER_FACTOR to allow some slightly larger glyphs as
1562 // maxsize is not enforced exactly.
1563 if (aMaxSize == NS_MATHML_OPERATOR_SIZE_INFINITY) {
1564 aDesiredStretchSize.ascent = nscoord_MAX;
1565 aDesiredStretchSize.descent = 0;
1566 }
1567 else {
1568 nscoord height = aDesiredStretchSize.ascent + aDesiredStretchSize.descent;
1569 if (height == 0) {
1570 if (aMaxSizeIsAbsolute) {
1571 aDesiredStretchSize.ascent =
1572 NSToCoordRound(aMaxSize / NS_MATHML_DELIMITER_FACTOR);
1573 aDesiredStretchSize.descent = 0;
1574 }
1575 // else: leave height as 0
1576 }
1577 else {
1578 float scale = aMaxSizeIsAbsolute ? aMaxSize / height : aMaxSize;
1579 scale /= NS_MATHML_DELIMITER_FACTOR;
1580 aDesiredStretchSize.ascent =
1581 NSToCoordRound(scale * aDesiredStretchSize.ascent);
1582 aDesiredStretchSize.descent =
1583 NSToCoordRound(scale * aDesiredStretchSize.descent);
1584 }
1585 }
1586 }
1588 nsBoundingMetrics initialSize = aDesiredStretchSize;
1589 nscoord charSize =
1590 isVertical ? initialSize.ascent + initialSize.descent
1591 : initialSize.rightBearing - initialSize.leftBearing;
1593 bool done = false;
1595 if (!maxWidth && !largeop) {
1596 // Doing Stretch() not GetMaxWidth(),
1597 // and not a largeop in display mode; we're done if size fits
1598 if ((targetSize <= 0) ||
1599 ((isVertical && charSize >= targetSize) ||
1600 IsSizeOK(aPresContext, charSize, targetSize, aStretchHint)))
1601 done = true;
1602 }
1604 //////////////////////////////////////////////////////////////////////////////
1605 // 2/3. Search for a glyph or set of part glyphs of appropriate size
1606 //////////////////////////////////////////////////////////////////////////////
1608 bool glyphFound = false;
1610 if (!done) { // normal case
1611 // Use the css font-family but add preferred fallback fonts.
1612 font = mStyleContext->StyleFont()->mFont;
1613 NS_NAMED_LITERAL_CSTRING(defaultKey, "font.mathfont-family");
1614 nsAdoptingString fallbackFonts = Preferences::GetString(defaultKey.get());
1615 if (!fallbackFonts.IsEmpty()) {
1616 AddFallbackFonts(font.name, fallbackFonts);
1617 }
1619 #ifdef NOISY_SEARCH
1620 printf("Searching in "%s" for a glyph of appropriate size for: 0x%04X:%c\n",
1621 font.name, mData[0], mData[0]&0x00FF);
1622 #endif
1623 StretchEnumContext enumData(this, aPresContext, aThebesContext,
1624 aStretchDirection, targetSize, aStretchHint,
1625 aDesiredStretchSize, font.name, glyphFound);
1626 enumData.mTryParts = !largeopOnly;
1628 font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData);
1629 }
1631 if (!maxWidth) {
1632 // Now, we know how we are going to draw the char. Update the member
1633 // variables accordingly.
1634 mUnscaledAscent = aDesiredStretchSize.ascent;
1635 }
1637 if (glyphFound) {
1638 return NS_OK;
1639 }
1641 // stretchy character
1642 if (stretchy) {
1643 if (isVertical) {
1644 float scale =
1645 float(aContainerSize.ascent + aContainerSize.descent) /
1646 (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
1647 if (!largeop || scale > 1.0) {
1648 // make the character match the desired height.
1649 if (!maxWidth) {
1650 mScaleY *= scale;
1651 }
1652 aDesiredStretchSize.ascent *= scale;
1653 aDesiredStretchSize.descent *= scale;
1654 }
1655 } else {
1656 float scale =
1657 float(aContainerSize.rightBearing - aContainerSize.leftBearing) /
1658 (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
1659 if (!largeop || scale > 1.0) {
1660 // make the character match the desired width.
1661 if (!maxWidth) {
1662 mScaleX *= scale;
1663 }
1664 aDesiredStretchSize.leftBearing *= scale;
1665 aDesiredStretchSize.rightBearing *= scale;
1666 aDesiredStretchSize.width *= scale;
1667 }
1668 }
1669 }
1671 // We do not have a char variant for this largeop in display mode, so we
1672 // apply a scale transform to the base char.
1673 if (largeop) {
1674 float scale;
1675 float largeopFactor = kLargeOpFactor;
1677 // increase the width if it is not largeopFactor times larger
1678 // than the initial one.
1679 if ((aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing) <
1680 largeopFactor * (initialSize.rightBearing - initialSize.leftBearing)) {
1681 scale = (largeopFactor *
1682 (initialSize.rightBearing - initialSize.leftBearing)) /
1683 (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
1684 if (!maxWidth) {
1685 mScaleX *= scale;
1686 }
1687 aDesiredStretchSize.leftBearing *= scale;
1688 aDesiredStretchSize.rightBearing *= scale;
1689 aDesiredStretchSize.width *= scale;
1690 }
1692 // increase the height if it is not largeopFactor times larger
1693 // than the initial one.
1694 if (NS_STRETCH_INTEGRAL & aStretchHint) {
1695 // integrals are drawn taller
1696 largeopFactor = kIntegralFactor;
1697 }
1698 if ((aDesiredStretchSize.ascent + aDesiredStretchSize.descent) <
1699 largeopFactor * (initialSize.ascent + initialSize.descent)) {
1700 scale = (largeopFactor *
1701 (initialSize.ascent + initialSize.descent)) /
1702 (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
1703 if (!maxWidth) {
1704 mScaleY *= scale;
1705 }
1706 aDesiredStretchSize.ascent *= scale;
1707 aDesiredStretchSize.descent *= scale;
1708 }
1709 }
1711 return NS_OK;
1712 }
1714 nsresult
1715 nsMathMLChar::Stretch(nsPresContext* aPresContext,
1716 nsRenderingContext& aRenderingContext,
1717 nsStretchDirection aStretchDirection,
1718 const nsBoundingMetrics& aContainerSize,
1719 nsBoundingMetrics& aDesiredStretchSize,
1720 uint32_t aStretchHint,
1721 bool aRTL)
1722 {
1723 NS_ASSERTION(!(aStretchHint &
1724 ~(NS_STRETCH_VARIABLE_MASK | NS_STRETCH_LARGEOP |
1725 NS_STRETCH_INTEGRAL)),
1726 "Unexpected stretch flags");
1728 mDraw = DRAW_NORMAL;
1729 mMirrored = aRTL && nsMathMLOperators::IsMirrorableOperator(mData);
1730 mScaleY = mScaleX = 1.0;
1731 mDirection = aStretchDirection;
1732 nsresult rv =
1733 StretchInternal(aPresContext, aRenderingContext.ThebesContext(), mDirection,
1734 aContainerSize, aDesiredStretchSize, aStretchHint);
1736 // Record the metrics
1737 mBoundingMetrics = aDesiredStretchSize;
1739 return rv;
1740 }
1742 // What happens here is that the StretchInternal algorithm is used but
1743 // modified by passing the NS_STRETCH_MAXWIDTH stretch hint. That causes
1744 // StretchInternal to return horizontal bounding metrics that are the maximum
1745 // that might be returned from a Stretch.
1746 //
1747 // In order to avoid considering widths of some characters in fonts that will
1748 // not be used for any stretch size, StretchInternal sets the initial height
1749 // to infinity and looks for any characters smaller than this height. When a
1750 // character built from parts is considered, (it will be used by Stretch for
1751 // any characters greater than its minimum size, so) the height is set to its
1752 // minimum size, so that only widths of smaller subsequent characters are
1753 // considered.
1754 nscoord
1755 nsMathMLChar::GetMaxWidth(nsPresContext* aPresContext,
1756 nsRenderingContext& aRenderingContext,
1757 uint32_t aStretchHint,
1758 float aMaxSize, bool aMaxSizeIsAbsolute)
1759 {
1760 nsBoundingMetrics bm;
1761 nsStretchDirection direction = NS_STRETCH_DIRECTION_VERTICAL;
1762 const nsBoundingMetrics container; // zero target size
1764 StretchInternal(aPresContext, aRenderingContext.ThebesContext(), direction,
1765 container, bm, aStretchHint | NS_STRETCH_MAXWIDTH);
1767 return std::max(bm.width, bm.rightBearing) - std::min(0, bm.leftBearing);
1768 }
1770 class nsDisplayMathMLSelectionRect : public nsDisplayItem {
1771 public:
1772 nsDisplayMathMLSelectionRect(nsDisplayListBuilder* aBuilder,
1773 nsIFrame* aFrame, const nsRect& aRect)
1774 : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
1775 MOZ_COUNT_CTOR(nsDisplayMathMLSelectionRect);
1776 }
1777 #ifdef NS_BUILD_REFCNT_LOGGING
1778 virtual ~nsDisplayMathMLSelectionRect() {
1779 MOZ_COUNT_DTOR(nsDisplayMathMLSelectionRect);
1780 }
1781 #endif
1783 virtual void Paint(nsDisplayListBuilder* aBuilder,
1784 nsRenderingContext* aCtx);
1785 NS_DISPLAY_DECL_NAME("MathMLSelectionRect", TYPE_MATHML_SELECTION_RECT)
1786 private:
1787 nsRect mRect;
1788 };
1790 void nsDisplayMathMLSelectionRect::Paint(nsDisplayListBuilder* aBuilder,
1791 nsRenderingContext* aCtx)
1792 {
1793 // get color to use for selection from the look&feel object
1794 nscolor bgColor =
1795 LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground,
1796 NS_RGB(0, 0, 0));
1797 aCtx->SetColor(bgColor);
1798 aCtx->FillRect(mRect + ToReferenceFrame());
1799 }
1801 class nsDisplayMathMLCharBackground : public nsDisplayItem {
1802 public:
1803 nsDisplayMathMLCharBackground(nsDisplayListBuilder* aBuilder,
1804 nsIFrame* aFrame, const nsRect& aRect,
1805 nsStyleContext* aStyleContext)
1806 : nsDisplayItem(aBuilder, aFrame), mStyleContext(aStyleContext),
1807 mRect(aRect) {
1808 MOZ_COUNT_CTOR(nsDisplayMathMLCharBackground);
1809 }
1810 #ifdef NS_BUILD_REFCNT_LOGGING
1811 virtual ~nsDisplayMathMLCharBackground() {
1812 MOZ_COUNT_DTOR(nsDisplayMathMLCharBackground);
1813 }
1814 #endif
1816 virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
1817 const nsDisplayItemGeometry* aGeometry,
1818 nsRegion *aInvalidRegion) MOZ_OVERRIDE;
1819 virtual void Paint(nsDisplayListBuilder* aBuilder,
1820 nsRenderingContext* aCtx);
1821 NS_DISPLAY_DECL_NAME("MathMLCharBackground", TYPE_MATHML_CHAR_BACKGROUND)
1822 private:
1823 nsStyleContext* mStyleContext;
1824 nsRect mRect;
1825 };
1827 void
1828 nsDisplayMathMLCharBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
1829 const nsDisplayItemGeometry* aGeometry,
1830 nsRegion *aInvalidRegion)
1831 {
1832 AddInvalidRegionForSyncDecodeBackgroundImages(aBuilder, aGeometry, aInvalidRegion);
1834 nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
1835 }
1837 void nsDisplayMathMLCharBackground::Paint(nsDisplayListBuilder* aBuilder,
1838 nsRenderingContext* aCtx)
1839 {
1840 const nsStyleBorder* border = mStyleContext->StyleBorder();
1841 nsRect rect(mRect + ToReferenceFrame());
1842 nsCSSRendering::PaintBackgroundWithSC(mFrame->PresContext(), *aCtx, mFrame,
1843 mVisibleRect, rect,
1844 mStyleContext, *border,
1845 aBuilder->GetBackgroundPaintFlags());
1846 }
1848 class nsDisplayMathMLCharForeground : public nsDisplayItem {
1849 public:
1850 nsDisplayMathMLCharForeground(nsDisplayListBuilder* aBuilder,
1851 nsIFrame* aFrame, nsMathMLChar* aChar,
1852 uint32_t aIndex, bool aIsSelected)
1853 : nsDisplayItem(aBuilder, aFrame), mChar(aChar),
1854 mIndex(aIndex), mIsSelected(aIsSelected) {
1855 MOZ_COUNT_CTOR(nsDisplayMathMLCharForeground);
1856 }
1857 #ifdef NS_BUILD_REFCNT_LOGGING
1858 virtual ~nsDisplayMathMLCharForeground() {
1859 MOZ_COUNT_DTOR(nsDisplayMathMLCharForeground);
1860 }
1861 #endif
1863 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
1864 *aSnap = false;
1865 nsRect rect;
1866 mChar->GetRect(rect);
1867 nsPoint offset = ToReferenceFrame() + rect.TopLeft();
1868 nsBoundingMetrics bm;
1869 mChar->GetBoundingMetrics(bm);
1870 nsRect temp(offset.x + bm.leftBearing, offset.y,
1871 bm.rightBearing - bm.leftBearing, bm.ascent + bm.descent);
1872 // Bug 748220
1873 temp.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
1874 return temp;
1875 }
1877 virtual void Paint(nsDisplayListBuilder* aBuilder,
1878 nsRenderingContext* aCtx)
1879 {
1880 mChar->PaintForeground(mFrame->PresContext(), *aCtx,
1881 ToReferenceFrame(), mIsSelected);
1882 }
1884 NS_DISPLAY_DECL_NAME("MathMLCharForeground", TYPE_MATHML_CHAR_FOREGROUND)
1886 virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder)
1887 {
1888 bool snap;
1889 return GetBounds(aBuilder, &snap);
1890 }
1892 virtual uint32_t GetPerFrameKey() {
1893 return (mIndex << nsDisplayItem::TYPE_BITS)
1894 | nsDisplayItem::GetPerFrameKey();
1895 }
1897 private:
1898 nsMathMLChar* mChar;
1899 uint32_t mIndex;
1900 bool mIsSelected;
1901 };
1903 #ifdef DEBUG
1904 class nsDisplayMathMLCharDebug : public nsDisplayItem {
1905 public:
1906 nsDisplayMathMLCharDebug(nsDisplayListBuilder* aBuilder,
1907 nsIFrame* aFrame, const nsRect& aRect)
1908 : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
1909 MOZ_COUNT_CTOR(nsDisplayMathMLCharDebug);
1910 }
1911 #ifdef NS_BUILD_REFCNT_LOGGING
1912 virtual ~nsDisplayMathMLCharDebug() {
1913 MOZ_COUNT_DTOR(nsDisplayMathMLCharDebug);
1914 }
1915 #endif
1917 virtual void Paint(nsDisplayListBuilder* aBuilder,
1918 nsRenderingContext* aCtx);
1919 NS_DISPLAY_DECL_NAME("MathMLCharDebug", TYPE_MATHML_CHAR_DEBUG)
1921 private:
1922 nsRect mRect;
1923 };
1925 void nsDisplayMathMLCharDebug::Paint(nsDisplayListBuilder* aBuilder,
1926 nsRenderingContext* aCtx)
1927 {
1928 // for visual debug
1929 int skipSides = 0;
1930 nsPresContext* presContext = mFrame->PresContext();
1931 nsStyleContext* styleContext = mFrame->StyleContext();
1932 nsRect rect = mRect + ToReferenceFrame();
1933 nsCSSRendering::PaintBorder(presContext, *aCtx, mFrame,
1934 mVisibleRect, rect, styleContext, skipSides);
1935 nsCSSRendering::PaintOutline(presContext, *aCtx, mFrame,
1936 mVisibleRect, rect, styleContext);
1937 }
1938 #endif
1941 void
1942 nsMathMLChar::Display(nsDisplayListBuilder* aBuilder,
1943 nsIFrame* aForFrame,
1944 const nsDisplayListSet& aLists,
1945 uint32_t aIndex,
1946 const nsRect* aSelectedRect)
1947 {
1948 nsStyleContext* parentContext = mStyleContext->GetParent();
1949 nsStyleContext* styleContext = mStyleContext;
1951 if (mDraw == DRAW_NORMAL) {
1952 // normal drawing if there is nothing special about this char
1953 // Set default context to the parent context
1954 styleContext = parentContext;
1955 }
1957 if (!styleContext->StyleVisibility()->IsVisible())
1958 return;
1960 // if the leaf style context that we use for stretchy chars has a background
1961 // color we use it -- this feature is mostly used for testing and debugging
1962 // purposes. Normally, users will set the background on the container frame.
1963 // paint the selection background -- beware MathML frames overlap a lot
1964 if (aSelectedRect && !aSelectedRect->IsEmpty()) {
1965 aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
1966 nsDisplayMathMLSelectionRect(aBuilder, aForFrame, *aSelectedRect));
1967 }
1968 else if (mRect.width && mRect.height) {
1969 const nsStyleBackground* backg = styleContext->StyleBackground();
1970 if (styleContext != parentContext &&
1971 NS_GET_A(backg->mBackgroundColor) > 0) {
1972 aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
1973 nsDisplayMathMLCharBackground(aBuilder, aForFrame, mRect,
1974 styleContext));
1975 }
1976 //else
1977 // our container frame will take care of painting its background
1979 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
1980 // for visual debug
1981 aLists.BorderBackground()->AppendToTop(new (aBuilder)
1982 nsDisplayMathMLCharDebug(aBuilder, aForFrame, mRect));
1983 #endif
1984 }
1985 aLists.Content()->AppendNewToTop(new (aBuilder)
1986 nsDisplayMathMLCharForeground(aBuilder, aForFrame, this,
1987 aIndex,
1988 aSelectedRect &&
1989 !aSelectedRect->IsEmpty()));
1990 }
1992 void
1993 nsMathMLChar::ApplyTransforms(gfxContext* aThebesContext,
1994 int32_t aAppUnitsPerGfxUnit,
1995 nsRect &r)
1996 {
1997 // apply the transforms
1998 if (mMirrored) {
1999 nsPoint pt = r.TopRight();
2000 aThebesContext->
2001 Translate(gfxPoint(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit),
2002 NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit)));
2003 aThebesContext->Scale(-mScaleX, mScaleY);
2004 } else {
2005 nsPoint pt = r.TopLeft();
2006 aThebesContext->
2007 Translate(gfxPoint(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit),
2008 NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit)));
2009 aThebesContext->Scale(mScaleX, mScaleY);
2010 }
2012 // update the bounding rectangle.
2013 r.x = r.y = 0;
2014 r.width /= mScaleX;
2015 r.height /= mScaleY;
2016 }
2018 void
2019 nsMathMLChar::PaintForeground(nsPresContext* aPresContext,
2020 nsRenderingContext& aRenderingContext,
2021 nsPoint aPt,
2022 bool aIsSelected)
2023 {
2024 nsStyleContext* parentContext = mStyleContext->GetParent();
2025 nsStyleContext* styleContext = mStyleContext;
2027 if (mDraw == DRAW_NORMAL) {
2028 // normal drawing if there is nothing special about this char
2029 // Set default context to the parent context
2030 styleContext = parentContext;
2031 }
2033 nsRefPtr<gfxContext> thebesContext = aRenderingContext.ThebesContext();
2035 // Set color ...
2036 nscolor fgColor = styleContext->GetVisitedDependentColor(eCSSProperty_color);
2037 if (aIsSelected) {
2038 // get color to use for selection from the look&feel object
2039 fgColor = LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground,
2040 fgColor);
2041 }
2042 thebesContext->SetColor(fgColor);
2043 thebesContext->Save();
2044 nsRect r = mRect + aPt;
2045 ApplyTransforms(thebesContext, aPresContext->AppUnitsPerDevPixel(), r);
2047 switch(mDraw)
2048 {
2049 case DRAW_NORMAL:
2050 case DRAW_VARIANT:
2051 // draw a single glyph (base size or size variant)
2052 // XXXfredw verify if mGlyphs[0] is non-null to workaround bug 973322.
2053 if (mGlyphs[0]) {
2054 mGlyphs[0]->Draw(thebesContext, gfxPoint(0.0, mUnscaledAscent),
2055 DrawMode::GLYPH_FILL, 0, mGlyphs[0]->GetLength(),
2056 nullptr, nullptr, nullptr);
2057 }
2058 break;
2059 case DRAW_PARTS: {
2060 // paint by parts
2061 if (NS_STRETCH_DIRECTION_VERTICAL == mDirection)
2062 PaintVertically(aPresContext, thebesContext, r);
2063 else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
2064 PaintHorizontally(aPresContext, thebesContext, r);
2065 break;
2066 }
2067 default:
2068 NS_NOTREACHED("Unknown drawing method");
2069 break;
2070 }
2072 thebesContext->Restore();
2073 }
2075 /* =============================================================================
2076 Helper routines that actually do the job of painting the char by parts
2077 */
2079 class AutoPushClipRect {
2080 gfxContext* mThebesContext;
2081 public:
2082 AutoPushClipRect(gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit,
2083 const nsRect& aRect)
2084 : mThebesContext(aThebesContext) {
2085 mThebesContext->Save();
2086 mThebesContext->NewPath();
2087 gfxRect clip = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerGfxUnit);
2088 mThebesContext->SnappedRectangle(clip);
2089 mThebesContext->Clip();
2090 }
2091 ~AutoPushClipRect() {
2092 mThebesContext->Restore();
2093 }
2094 };
2096 static nsPoint
2097 SnapToDevPixels(const gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit,
2098 const nsPoint& aPt)
2099 {
2100 gfxPoint pt(NSAppUnitsToFloatPixels(aPt.x, aAppUnitsPerGfxUnit),
2101 NSAppUnitsToFloatPixels(aPt.y, aAppUnitsPerGfxUnit));
2102 pt = aThebesContext->UserToDevice(pt);
2103 pt.Round();
2104 pt = aThebesContext->DeviceToUser(pt);
2105 return nsPoint(NSFloatPixelsToAppUnits(pt.x, aAppUnitsPerGfxUnit),
2106 NSFloatPixelsToAppUnits(pt.y, aAppUnitsPerGfxUnit));
2107 }
2109 static void
2110 PaintRule(gfxContext* aThebesContext,
2111 int32_t aAppUnitsPerGfxUnit,
2112 nsRect& aRect)
2113 {
2114 aThebesContext->NewPath();
2115 gfxRect rect = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerGfxUnit);
2116 aThebesContext->SnappedRectangle(rect);
2117 aThebesContext->Fill();
2118 }
2120 // paint a stretchy char by assembling glyphs vertically
2121 nsresult
2122 nsMathMLChar::PaintVertically(nsPresContext* aPresContext,
2123 gfxContext* aThebesContext,
2124 nsRect& aRect)
2125 {
2126 // Get the device pixel size in the vertical direction.
2127 // (This makes no effort to optimize for non-translation transformations.)
2128 nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
2130 // get metrics data to be re-used later
2131 int32_t i = 0;
2132 nscoord dx = aRect.x;
2133 nscoord offset[3], start[3], end[3];
2134 for (i = 0; i <= 2; ++i) {
2135 const nsBoundingMetrics& bm = mBmData[i];
2136 nscoord dy;
2137 if (0 == i) { // top
2138 dy = aRect.y + bm.ascent;
2139 }
2140 else if (2 == i) { // bottom
2141 dy = aRect.y + aRect.height - bm.descent;
2142 }
2143 else { // middle
2144 dy = aRect.y + bm.ascent + (aRect.height - (bm.ascent + bm.descent))/2;
2145 }
2146 // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
2147 // Do this now so that we can get the other dimensions right.
2148 // (This may not achieve much with non-rectangular transformations.)
2149 dy = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).y;
2150 // abcissa passed to Draw
2151 offset[i] = dy;
2152 // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
2153 // pixel, so the bm values can include 1 row of faint pixels on each edge.
2154 // Don't rely on this pixel as it can look like a gap.
2155 if (bm.ascent + bm.descent >= 2 * oneDevPixel) {
2156 start[i] = dy - bm.ascent + oneDevPixel; // top join
2157 end[i] = dy + bm.descent - oneDevPixel; // bottom join
2158 } else {
2159 // To avoid overlaps, we don't add one pixel on each side when the part
2160 // is too small.
2161 start[i] = dy - bm.ascent; // top join
2162 end[i] = dy + bm.descent; // bottom join
2163 }
2164 }
2166 // If there are overlaps, then join at the mid point
2167 for (i = 0; i < 2; ++i) {
2168 if (end[i] > start[i+1]) {
2169 end[i] = (end[i] + start[i+1]) / 2;
2170 start[i+1] = end[i];
2171 }
2172 }
2174 nsRect unionRect = aRect;
2175 unionRect.x += mBoundingMetrics.leftBearing;
2176 unionRect.width =
2177 mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
2178 unionRect.Inflate(oneDevPixel, oneDevPixel);
2180 /////////////////////////////////////
2181 // draw top, middle, bottom
2182 for (i = 0; i <= 2; ++i) {
2183 // glue can be null
2184 if (mGlyphs[i]) {
2185 nscoord dy = offset[i];
2186 // Draw a glyph in a clipped area so that we don't have hairy chars
2187 // pending outside
2188 nsRect clipRect = unionRect;
2189 // Clip at the join to get a solid edge (without overlap or gap), when
2190 // this won't change the glyph too much. If the glyph is too small to
2191 // clip then we'll overlap rather than have a gap.
2192 nscoord height = mBmData[i].ascent + mBmData[i].descent;
2193 if (height * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
2194 if (0 == i) { // top
2195 clipRect.height = end[i] - clipRect.y;
2196 }
2197 else if (2 == i) { // bottom
2198 clipRect.height -= start[i] - clipRect.y;
2199 clipRect.y = start[i];
2200 }
2201 else { // middle
2202 clipRect.y = start[i];
2203 clipRect.height = end[i] - start[i];
2204 }
2205 }
2206 if (!clipRect.IsEmpty()) {
2207 AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
2208 mGlyphs[i]->Draw(aThebesContext, gfxPoint(dx, dy),
2209 DrawMode::GLYPH_FILL, 0, mGlyphs[i]->GetLength(),
2210 nullptr, nullptr, nullptr);
2211 }
2212 }
2213 }
2215 ///////////////
2216 // fill the gap between top and middle, and between middle and bottom.
2217 if (!mGlyphs[3]) { // null glue : draw a rule
2218 // figure out the dimensions of the rule to be drawn :
2219 // set lbearing to rightmost lbearing among the two current successive
2220 // parts.
2221 // set rbearing to leftmost rbearing among the two current successive parts.
2222 // this not only satisfies the convention used for over/underbraces
2223 // in TeX, but also takes care of broken fonts like the stretchy integral
2224 // in Symbol for small font sizes in unix.
2225 nscoord lbearing, rbearing;
2226 int32_t first = 0, last = 1;
2227 while (last <= 2) {
2228 if (mGlyphs[last]) {
2229 lbearing = mBmData[last].leftBearing;
2230 rbearing = mBmData[last].rightBearing;
2231 if (mGlyphs[first]) {
2232 if (lbearing < mBmData[first].leftBearing)
2233 lbearing = mBmData[first].leftBearing;
2234 if (rbearing > mBmData[first].rightBearing)
2235 rbearing = mBmData[first].rightBearing;
2236 }
2237 }
2238 else if (mGlyphs[first]) {
2239 lbearing = mBmData[first].leftBearing;
2240 rbearing = mBmData[first].rightBearing;
2241 }
2242 else {
2243 NS_ERROR("Cannot stretch - All parts missing");
2244 return NS_ERROR_UNEXPECTED;
2245 }
2246 // paint the rule between the parts
2247 nsRect rule(aRect.x + lbearing, end[first],
2248 rbearing - lbearing, start[last] - end[first]);
2249 PaintRule(aThebesContext, oneDevPixel, rule);
2250 first = last;
2251 last++;
2252 }
2253 }
2254 else if (mBmData[3].ascent + mBmData[3].descent > 0) {
2255 // glue is present
2256 nsBoundingMetrics& bm = mBmData[3];
2257 // Ensure the stride for the glue is not reduced to less than one pixel
2258 if (bm.ascent + bm.descent >= 3 * oneDevPixel) {
2259 // To protect against gaps, pretend the glue is smaller than it is,
2260 // in order to trim off ends and thus get a solid edge for the join.
2261 bm.ascent -= oneDevPixel;
2262 bm.descent -= oneDevPixel;
2263 }
2265 nsRect clipRect = unionRect;
2267 for (i = 0; i < 2; ++i) {
2268 // Make sure not to draw outside the character
2269 nscoord dy = std::max(end[i], aRect.y);
2270 nscoord fillEnd = std::min(start[i+1], aRect.YMost());
2271 while (dy < fillEnd) {
2272 clipRect.y = dy;
2273 clipRect.height = std::min(bm.ascent + bm.descent, fillEnd - dy);
2274 AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
2275 dy += bm.ascent;
2276 mGlyphs[3]->Draw(aThebesContext, gfxPoint(dx, dy),
2277 DrawMode::GLYPH_FILL, 0, mGlyphs[3]->GetLength(),
2278 nullptr, nullptr, nullptr);
2279 dy += bm.descent;
2280 }
2281 }
2282 }
2283 #ifdef DEBUG
2284 else {
2285 for (i = 0; i < 2; ++i) {
2286 NS_ASSERTION(end[i] >= start[i+1],
2287 "gap between parts with missing glue glyph");
2288 }
2289 }
2290 #endif
2291 return NS_OK;
2292 }
2294 // paint a stretchy char by assembling glyphs horizontally
2295 nsresult
2296 nsMathMLChar::PaintHorizontally(nsPresContext* aPresContext,
2297 gfxContext* aThebesContext,
2298 nsRect& aRect)
2299 {
2300 // Get the device pixel size in the horizontal direction.
2301 // (This makes no effort to optimize for non-translation transformations.)
2302 nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
2304 // get metrics data to be re-used later
2305 int32_t i = 0;
2306 nscoord dy = aRect.y + mBoundingMetrics.ascent;
2307 nscoord offset[3], start[3], end[3];
2308 for (i = 0; i <= 2; ++i) {
2309 const nsBoundingMetrics& bm = mBmData[i];
2310 nscoord dx;
2311 if (0 == i) { // left
2312 dx = aRect.x - bm.leftBearing;
2313 }
2314 else if (2 == i) { // right
2315 dx = aRect.x + aRect.width - bm.rightBearing;
2316 }
2317 else { // middle
2318 dx = aRect.x + (aRect.width - bm.width)/2;
2319 }
2320 // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
2321 // Do this now so that we can get the other dimensions right.
2322 // (This may not achieve much with non-rectangular transformations.)
2323 dx = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).x;
2324 // abcissa passed to Draw
2325 offset[i] = dx;
2326 // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
2327 // pixel, so the bm values can include 1 row of faint pixels on each edge.
2328 // Don't rely on this pixel as it can look like a gap.
2329 if (bm.rightBearing - bm.leftBearing >= 2 * oneDevPixel) {
2330 start[i] = dx + bm.leftBearing + oneDevPixel; // left join
2331 end[i] = dx + bm.rightBearing - oneDevPixel; // right join
2332 } else {
2333 // To avoid overlaps, we don't add one pixel on each side when the part
2334 // is too small.
2335 start[i] = dx + bm.leftBearing; // left join
2336 end[i] = dx + bm.rightBearing; // right join
2337 }
2338 }
2340 // If there are overlaps, then join at the mid point
2341 for (i = 0; i < 2; ++i) {
2342 if (end[i] > start[i+1]) {
2343 end[i] = (end[i] + start[i+1]) / 2;
2344 start[i+1] = end[i];
2345 }
2346 }
2348 nsRect unionRect = aRect;
2349 unionRect.Inflate(oneDevPixel, oneDevPixel);
2351 ///////////////////////////
2352 // draw left, middle, right
2353 for (i = 0; i <= 2; ++i) {
2354 // glue can be null
2355 if (mGlyphs[i]) {
2356 nscoord dx = offset[i];
2357 nsRect clipRect = unionRect;
2358 // Clip at the join to get a solid edge (without overlap or gap), when
2359 // this won't change the glyph too much. If the glyph is too small to
2360 // clip then we'll overlap rather than have a gap.
2361 nscoord width = mBmData[i].rightBearing - mBmData[i].leftBearing;
2362 if (width * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
2363 if (0 == i) { // left
2364 clipRect.width = end[i] - clipRect.x;
2365 }
2366 else if (2 == i) { // right
2367 clipRect.width -= start[i] - clipRect.x;
2368 clipRect.x = start[i];
2369 }
2370 else { // middle
2371 clipRect.x = start[i];
2372 clipRect.width = end[i] - start[i];
2373 }
2374 }
2375 if (!clipRect.IsEmpty()) {
2376 AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
2377 mGlyphs[i]->Draw(aThebesContext, gfxPoint(dx, dy),
2378 DrawMode::GLYPH_FILL, 0, mGlyphs[i]->GetLength(),
2379 nullptr, nullptr, nullptr);
2380 }
2381 }
2382 }
2384 ////////////////
2385 // fill the gap between left and middle, and between middle and right.
2386 if (!mGlyphs[3]) { // null glue : draw a rule
2387 // figure out the dimensions of the rule to be drawn :
2388 // set ascent to lowest ascent among the two current successive parts.
2389 // set descent to highest descent among the two current successive parts.
2390 // this satisfies the convention used for over/underbraces, and helps
2391 // fix broken fonts.
2392 nscoord ascent, descent;
2393 int32_t first = 0, last = 1;
2394 while (last <= 2) {
2395 if (mGlyphs[last]) {
2396 ascent = mBmData[last].ascent;
2397 descent = mBmData[last].descent;
2398 if (mGlyphs[first]) {
2399 if (ascent > mBmData[first].ascent)
2400 ascent = mBmData[first].ascent;
2401 if (descent > mBmData[first].descent)
2402 descent = mBmData[first].descent;
2403 }
2404 }
2405 else if (mGlyphs[first]) {
2406 ascent = mBmData[first].ascent;
2407 descent = mBmData[first].descent;
2408 }
2409 else {
2410 NS_ERROR("Cannot stretch - All parts missing");
2411 return NS_ERROR_UNEXPECTED;
2412 }
2413 // paint the rule between the parts
2414 nsRect rule(end[first], dy - ascent,
2415 start[last] - end[first], ascent + descent);
2416 PaintRule(aThebesContext, oneDevPixel, rule);
2417 first = last;
2418 last++;
2419 }
2420 }
2421 else if (mBmData[3].rightBearing - mBmData[3].leftBearing > 0) {
2422 // glue is present
2423 nsBoundingMetrics& bm = mBmData[3];
2424 // Ensure the stride for the glue is not reduced to less than one pixel
2425 if (bm.rightBearing - bm.leftBearing >= 3 * oneDevPixel) {
2426 // To protect against gaps, pretend the glue is smaller than it is,
2427 // in order to trim off ends and thus get a solid edge for the join.
2428 bm.leftBearing += oneDevPixel;
2429 bm.rightBearing -= oneDevPixel;
2430 }
2432 nsRect clipRect = unionRect;
2434 for (i = 0; i < 2; ++i) {
2435 // Make sure not to draw outside the character
2436 nscoord dx = std::max(end[i], aRect.x);
2437 nscoord fillEnd = std::min(start[i+1], aRect.XMost());
2438 while (dx < fillEnd) {
2439 clipRect.x = dx;
2440 clipRect.width = std::min(bm.rightBearing - bm.leftBearing, fillEnd - dx);
2441 AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
2442 dx -= bm.leftBearing;
2443 mGlyphs[3]->Draw(aThebesContext, gfxPoint(dx, dy),
2444 DrawMode::GLYPH_FILL, 0, mGlyphs[3]->GetLength(),
2445 nullptr, nullptr, nullptr);
2446 dx += bm.rightBearing;
2447 }
2448 }
2449 }
2450 #ifdef DEBUG
2451 else { // no glue
2452 for (i = 0; i < 2; ++i) {
2453 NS_ASSERTION(end[i] >= start[i+1],
2454 "gap between parts with missing glue glyph");
2455 }
2456 }
2457 #endif
2458 return NS_OK;
2459 }