1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/mathml/nsMathMLChar.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2459 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsMathMLChar.h" 1.10 +#include "mozilla/MathAlgorithms.h" 1.11 + 1.12 +#include "nsCOMPtr.h" 1.13 +#include "nsIFrame.h" 1.14 +#include "nsPresContext.h" 1.15 +#include "nsStyleContext.h" 1.16 +#include "nsUnicharUtils.h" 1.17 +#include "nsRenderingContext.h" 1.18 + 1.19 +#include "mozilla/Preferences.h" 1.20 +#include "nsIPersistentProperties2.h" 1.21 +#include "nsIObserverService.h" 1.22 +#include "nsIObserver.h" 1.23 +#include "nsNetUtil.h" 1.24 + 1.25 +#include "mozilla/LookAndFeel.h" 1.26 +#include "nsCSSRendering.h" 1.27 +#include "prprf.h" // For PR_snprintf() 1.28 + 1.29 +#include "nsDisplayList.h" 1.30 + 1.31 +#include "nsMathMLOperators.h" 1.32 +#include <algorithm> 1.33 + 1.34 +#include "gfxMathTable.h" 1.35 + 1.36 +using namespace mozilla; 1.37 + 1.38 +//#define NOISY_SEARCH 1 1.39 + 1.40 +static const float kLargeOpFactor = float(M_SQRT2); 1.41 +static const float kIntegralFactor = 2.0; 1.42 + 1.43 +// ----------------------------------------------------------------------------- 1.44 +static const nsGlyphCode kNullGlyph = {{{0, 0}}, 0}; 1.45 + 1.46 +// ----------------------------------------------------------------------------- 1.47 +// nsGlyphTable is a class that provides an interface for accessing glyphs 1.48 +// of stretchy chars. It acts like a table that stores the variants of bigger 1.49 +// sizes (if any) and the partial glyphs needed to build extensible symbols. 1.50 +// 1.51 +// Bigger sizes (if any) of the char can then be retrieved with BigOf(...). 1.52 +// Partial glyphs can be retrieved with ElementAt(...). 1.53 +// 1.54 +// A table consists of "nsGlyphCode"s which are viewed either as Unicode 1.55 +// points (for nsPropertiesTable) or as direct glyph indices (for 1.56 +// nsOpenTypeTable) 1.57 +// ----------------------------------------------------------------------------- 1.58 + 1.59 +class nsGlyphTable { 1.60 +public: 1.61 + virtual ~nsGlyphTable() {} 1.62 + 1.63 + virtual const nsAString& 1.64 + FontNameFor(const nsGlyphCode& aGlyphCode) const = 0; 1.65 + 1.66 + // Getters for the parts 1.67 + virtual nsGlyphCode ElementAt(gfxContext* aThebesContext, 1.68 + int32_t aAppUnitsPerDevPixel, 1.69 + gfxFontGroup* aFontGroup, 1.70 + char16_t aChar, 1.71 + bool aVertical, 1.72 + uint32_t aPosition) = 0; 1.73 + virtual nsGlyphCode BigOf(gfxContext* aThebesContext, 1.74 + int32_t aAppUnitsPerDevPixel, 1.75 + gfxFontGroup* aFontGroup, 1.76 + char16_t aChar, 1.77 + bool aVertical, 1.78 + uint32_t aSize) = 0; 1.79 + 1.80 + // True if this table contains parts to render this char 1.81 + virtual bool HasPartsOf(gfxContext* aThebesContext, 1.82 + int32_t aAppUnitsPerDevPixel, 1.83 + gfxFontGroup* aFontGroup, 1.84 + char16_t aChar, 1.85 + bool aVertical) = 0; 1.86 + 1.87 + virtual gfxTextRun* MakeTextRun(gfxContext* aThebesContext, 1.88 + int32_t aAppUnitsPerDevPixel, 1.89 + gfxFontGroup* aFontGroup, 1.90 + const nsGlyphCode& aGlyph) = 0; 1.91 +protected: 1.92 + nsGlyphTable() : mCharCache(0) {} 1.93 + // For speedy re-use, we always cache the last data used in the table. 1.94 + // mCharCache is the Unicode point of the last char that was queried in this 1.95 + // table. 1.96 + char16_t mCharCache; 1.97 +}; 1.98 + 1.99 +// An instance of nsPropertiesTable is associated with one primary font. Extra 1.100 +// glyphs can be taken in other additional fonts when stretching certain 1.101 +// characters. 1.102 +// These supplementary fonts are referred to as "external" fonts to the table. 1.103 + 1.104 +// General format of MathFont Property Files from which glyph data are 1.105 +// retrieved: 1.106 +// ----------------------------------------------------------------------------- 1.107 +// Each font should have its set of glyph data. For example, the glyph data for 1.108 +// the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties" 1.109 +// and "mathfontMTExtra.properties", respectively. The mathfont property file 1.110 +// is a set of all the stretchy MathML characters that can be rendered with that 1.111 +// font using larger and/or partial glyphs. The entry of each stretchy character 1.112 +// in the mathfont property file gives, in that order, the 4 partial glyphs: 1.113 +// Top (or Left), Middle, Bottom (or Right), Glue; and the variants of bigger 1.114 +// sizes (if any). 1.115 +// A position that is not relevant to a particular character is indicated there 1.116 +// with the UNICODE REPLACEMENT CHARACTER 0xFFFD. 1.117 +// ----------------------------------------------------------------------------- 1.118 + 1.119 +#define NS_TABLE_STATE_ERROR -1 1.120 +#define NS_TABLE_STATE_EMPTY 0 1.121 +#define NS_TABLE_STATE_READY 1 1.122 + 1.123 +// helper to trim off comments from data in a MathFont Property File 1.124 +static void 1.125 +Clean(nsString& aValue) 1.126 +{ 1.127 + // chop the trailing # comment portion if any ... 1.128 + int32_t comment = aValue.RFindChar('#'); 1.129 + if (comment > 0) aValue.Truncate(comment); 1.130 + aValue.CompressWhitespace(); 1.131 +} 1.132 + 1.133 +// helper to load a MathFont Property File 1.134 +static nsresult 1.135 +LoadProperties(const nsString& aName, 1.136 + nsCOMPtr<nsIPersistentProperties>& aProperties) 1.137 +{ 1.138 + nsAutoString uriStr; 1.139 + uriStr.AssignLiteral("resource://gre/res/fonts/mathfont"); 1.140 + uriStr.Append(aName); 1.141 + uriStr.StripWhitespace(); // that may come from aName 1.142 + uriStr.AppendLiteral(".properties"); 1.143 + return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties), 1.144 + NS_ConvertUTF16toUTF8(uriStr)); 1.145 +} 1.146 + 1.147 +class nsPropertiesTable MOZ_FINAL : public nsGlyphTable { 1.148 +public: 1.149 + explicit nsPropertiesTable(const nsString& aPrimaryFontName) 1.150 + : mFontName(1) // ensure space for primary font name. 1.151 + , mState(NS_TABLE_STATE_EMPTY) 1.152 + { 1.153 + MOZ_COUNT_CTOR(nsPropertiesTable); 1.154 + mFontName.AppendElement(aPrimaryFontName); 1.155 + } 1.156 + 1.157 + ~nsPropertiesTable() 1.158 + { 1.159 + MOZ_COUNT_DTOR(nsPropertiesTable); 1.160 + } 1.161 + 1.162 + const nsAString& PrimaryFontName() const 1.163 + { 1.164 + return mFontName[0]; 1.165 + } 1.166 + 1.167 + const nsAString& 1.168 + FontNameFor(const nsGlyphCode& aGlyphCode) const MOZ_OVERRIDE 1.169 + { 1.170 + NS_ASSERTION(!aGlyphCode.IsGlyphID(), 1.171 + "nsPropertiesTable can only access glyphs by code point"); 1.172 + return mFontName[aGlyphCode.font]; 1.173 + } 1.174 + 1.175 + virtual nsGlyphCode ElementAt(gfxContext* aThebesContext, 1.176 + int32_t aAppUnitsPerDevPixel, 1.177 + gfxFontGroup* aFontGroup, 1.178 + char16_t aChar, 1.179 + bool aVertical, 1.180 + uint32_t aPosition) MOZ_OVERRIDE; 1.181 + 1.182 + virtual nsGlyphCode BigOf(gfxContext* aThebesContext, 1.183 + int32_t aAppUnitsPerDevPixel, 1.184 + gfxFontGroup* aFontGroup, 1.185 + char16_t aChar, 1.186 + bool aVertical, 1.187 + uint32_t aSize) MOZ_OVERRIDE 1.188 + { 1.189 + return ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, 1.190 + aChar, aVertical, 4 + aSize); 1.191 + } 1.192 + 1.193 + virtual bool HasPartsOf(gfxContext* aThebesContext, 1.194 + int32_t aAppUnitsPerDevPixel, 1.195 + gfxFontGroup* aFontGroup, 1.196 + char16_t aChar, 1.197 + bool aVertical) MOZ_OVERRIDE 1.198 + { 1.199 + return (ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, 1.200 + aChar, aVertical, 0).Exists() || 1.201 + ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, 1.202 + aChar, aVertical, 1).Exists() || 1.203 + ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, 1.204 + aChar, aVertical, 2).Exists() || 1.205 + ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, 1.206 + aChar, aVertical, 3).Exists()); 1.207 + } 1.208 + 1.209 + virtual gfxTextRun* MakeTextRun(gfxContext* aThebesContext, 1.210 + int32_t aAppUnitsPerDevPixel, 1.211 + gfxFontGroup* aFontGroup, 1.212 + const nsGlyphCode& aGlyph) MOZ_OVERRIDE; 1.213 +private: 1.214 + 1.215 + // mFontName[0] is the primary font associated to this table. The others 1.216 + // are possible "external" fonts for glyphs not in the primary font 1.217 + // but which are needed to stretch certain characters in the table 1.218 + nsTArray<nsString> mFontName; 1.219 + 1.220 + // Tri-state variable for error/empty/ready 1.221 + int32_t mState; 1.222 + 1.223 + // The set of glyph data in this table, as provided by the MathFont Property 1.224 + // File 1.225 + nsCOMPtr<nsIPersistentProperties> mGlyphProperties; 1.226 + 1.227 + // mGlyphCache is a buffer containing the glyph data associated with 1.228 + // mCharCache. 1.229 + // For a property line 'key = value' in the MathFont Property File, 1.230 + // mCharCache will retain the 'key' -- which is a Unicode point, while 1.231 + // mGlyphCache will retain the 'value', which is a consecutive list of 1.232 + // nsGlyphCodes, i.e., the pairs of 'code@font' needed by the char -- in 1.233 + // which 'code@0' can be specified 1.234 + // without the optional '@0'. However, to ease subsequent processing, 1.235 + // mGlyphCache excludes the '@' symbol and explicitly inserts all optional '0' 1.236 + // that indicates the primary font identifier. Specifically therefore, the 1.237 + // k-th glyph is characterized by : 1.238 + // 1) mGlyphCache[3*k],mGlyphCache[3*k+1] : its Unicode point 1.239 + // 2) mGlyphCache[3*k+2] : the numeric identifier of the font where it comes 1.240 + // from. 1.241 + // A font identifier of '0' means the default primary font associated to this 1.242 + // table. Other digits map to the "external" fonts that may have been 1.243 + // specified in the MathFont Property File. 1.244 + nsString mGlyphCache; 1.245 +}; 1.246 + 1.247 +/* virtual */ 1.248 +nsGlyphCode 1.249 +nsPropertiesTable::ElementAt(gfxContext* /* aThebesContext */, 1.250 + int32_t /* aAppUnitsPerDevPixel */, 1.251 + gfxFontGroup* /* aFontGroup */, 1.252 + char16_t aChar, 1.253 + bool /* aVertical */, 1.254 + uint32_t aPosition) 1.255 +{ 1.256 + if (mState == NS_TABLE_STATE_ERROR) return kNullGlyph; 1.257 + // Load glyph properties if this is the first time we have been here 1.258 + if (mState == NS_TABLE_STATE_EMPTY) { 1.259 + nsresult rv = LoadProperties(mFontName[0], mGlyphProperties); 1.260 +#ifdef DEBUG 1.261 + nsAutoCString uriStr; 1.262 + uriStr.AssignLiteral("resource://gre/res/fonts/mathfont"); 1.263 + LossyAppendUTF16toASCII(mFontName[0], uriStr); 1.264 + uriStr.StripWhitespace(); // that may come from mFontName 1.265 + uriStr.AppendLiteral(".properties"); 1.266 + printf("Loading %s ... %s\n", 1.267 + uriStr.get(), 1.268 + (NS_FAILED(rv)) ? "Failed" : "Done"); 1.269 +#endif 1.270 + if (NS_FAILED(rv)) { 1.271 + mState = NS_TABLE_STATE_ERROR; // never waste time with this table again 1.272 + return kNullGlyph; 1.273 + } 1.274 + mState = NS_TABLE_STATE_READY; 1.275 + 1.276 + // see if there are external fonts needed for certain chars in this table 1.277 + nsAutoCString key; 1.278 + nsAutoString value; 1.279 + for (int32_t i = 1; ; i++) { 1.280 + key.AssignLiteral("external."); 1.281 + key.AppendInt(i, 10); 1.282 + rv = mGlyphProperties->GetStringProperty(key, value); 1.283 + if (NS_FAILED(rv)) break; 1.284 + Clean(value); 1.285 + mFontName.AppendElement(value); // i.e., mFontName[i] holds this font name 1.286 + } 1.287 + } 1.288 + 1.289 + // Update our cache if it is not associated to this character 1.290 + if (mCharCache != aChar) { 1.291 + // The key in the property file is interpreted as ASCII and kept 1.292 + // as such ... 1.293 + char key[10]; PR_snprintf(key, sizeof(key), "\\u%04X", aChar); 1.294 + nsAutoString value; 1.295 + nsresult rv = mGlyphProperties->GetStringProperty(nsDependentCString(key), 1.296 + value); 1.297 + if (NS_FAILED(rv)) return kNullGlyph; 1.298 + Clean(value); 1.299 + // See if this char uses external fonts; e.g., if the 2nd glyph is taken 1.300 + // from the external font '1', the property line looks like 1.301 + // \uNNNN = \uNNNN\uNNNN@1\uNNNN. 1.302 + // This is where mGlyphCache is pre-processed to explicitly store all glyph 1.303 + // codes as combined pairs of 'code@font', excluding the '@' separator. This 1.304 + // means that mGlyphCache[3*k],mGlyphCache[3*k+1] will later be rendered 1.305 + // with mFontName[mGlyphCache[3*k+2]] 1.306 + // Note: font identifier is internally an ASCII digit to avoid the null 1.307 + // char issue 1.308 + nsAutoString buffer; 1.309 + int32_t length = value.Length(); 1.310 + int32_t i = 0; // index in value 1.311 + while (i < length) { 1.312 + char16_t code = value[i]; 1.313 + ++i; 1.314 + buffer.Append(code); 1.315 + // Read the next word if we have a non-BMP character. 1.316 + if (i < length && NS_IS_HIGH_SURROGATE(code)) { 1.317 + code = value[i]; 1.318 + ++i; 1.319 + } else { 1.320 + code = char16_t('\0'); 1.321 + } 1.322 + buffer.Append(code); 1.323 + 1.324 + // See if an external font is needed for the code point. 1.325 + // Limit of 9 external fonts 1.326 + char16_t font = 0; 1.327 + if (i+1 < length && value[i] == char16_t('@') && 1.328 + value[i+1] >= char16_t('0') && value[i+1] <= char16_t('9')) { 1.329 + ++i; 1.330 + font = value[i] - '0'; 1.331 + ++i; 1.332 + if (font >= mFontName.Length()) { 1.333 + NS_ERROR("Nonexistent font referenced in glyph table"); 1.334 + return kNullGlyph; 1.335 + } 1.336 + // The char cannot be handled if this font is not installed 1.337 + if (!mFontName[font].Length()) { 1.338 + return kNullGlyph; 1.339 + } 1.340 + } 1.341 + buffer.Append(font); 1.342 + } 1.343 + // update our cache with the new settings 1.344 + mGlyphCache.Assign(buffer); 1.345 + mCharCache = aChar; 1.346 + } 1.347 + 1.348 + // 3* is to account for the code@font pairs 1.349 + uint32_t index = 3*aPosition; 1.350 + if (index+2 >= mGlyphCache.Length()) return kNullGlyph; 1.351 + nsGlyphCode ch; 1.352 + ch.code[0] = mGlyphCache.CharAt(index); 1.353 + ch.code[1] = mGlyphCache.CharAt(index + 1); 1.354 + ch.font = mGlyphCache.CharAt(index + 2); 1.355 + return ch.code[0] == char16_t(0xFFFD) ? kNullGlyph : ch; 1.356 +} 1.357 + 1.358 +/* virtual */ 1.359 +gfxTextRun* 1.360 +nsPropertiesTable::MakeTextRun(gfxContext* aThebesContext, 1.361 + int32_t aAppUnitsPerDevPixel, 1.362 + gfxFontGroup* aFontGroup, 1.363 + const nsGlyphCode& aGlyph) 1.364 +{ 1.365 + NS_ASSERTION(!aGlyph.IsGlyphID(), 1.366 + "nsPropertiesTable can only access glyphs by code point"); 1.367 + return aFontGroup-> 1.368 + MakeTextRun(aGlyph.code, aGlyph.Length(), aThebesContext, 1.369 + aAppUnitsPerDevPixel, 0); 1.370 +} 1.371 + 1.372 +// An instance of nsOpenTypeTable is associated with one gfxFontEntry that 1.373 +// corresponds to an Open Type font with a MATH table. All the glyphs come from 1.374 +// the same font and the calls to access size variants and parts are directly 1.375 +// forwarded to the gfx code. 1.376 +class nsOpenTypeTable MOZ_FINAL : public nsGlyphTable { 1.377 +public: 1.378 + ~nsOpenTypeTable() 1.379 + { 1.380 + MOZ_COUNT_DTOR(nsOpenTypeTable); 1.381 + } 1.382 + 1.383 + virtual nsGlyphCode ElementAt(gfxContext* aThebesContext, 1.384 + int32_t aAppUnitsPerDevPixel, 1.385 + gfxFontGroup* aFontGroup, 1.386 + char16_t aChar, 1.387 + bool aVertical, 1.388 + uint32_t aPosition) MOZ_OVERRIDE; 1.389 + virtual nsGlyphCode BigOf(gfxContext* aThebesContext, 1.390 + int32_t aAppUnitsPerDevPixel, 1.391 + gfxFontGroup* aFontGroup, 1.392 + char16_t aChar, 1.393 + bool aVertical, 1.394 + uint32_t aSize) MOZ_OVERRIDE; 1.395 + virtual bool HasPartsOf(gfxContext* aThebesContext, 1.396 + int32_t aAppUnitsPerDevPixel, 1.397 + gfxFontGroup* aFontGroup, 1.398 + char16_t aChar, 1.399 + bool aVertical) MOZ_OVERRIDE; 1.400 + 1.401 + const nsAString& 1.402 + FontNameFor(const nsGlyphCode& aGlyphCode) const MOZ_OVERRIDE { 1.403 + NS_ASSERTION(aGlyphCode.IsGlyphID(), 1.404 + "nsOpenTypeTable can only access glyphs by id"); 1.405 + return mFontEntry->FamilyName(); 1.406 + } 1.407 + 1.408 + virtual gfxTextRun* MakeTextRun(gfxContext* aThebesContext, 1.409 + int32_t aAppUnitsPerDevPixel, 1.410 + gfxFontGroup* aFontGroup, 1.411 + const nsGlyphCode& aGlyph) MOZ_OVERRIDE; 1.412 + 1.413 + // This returns a new OpenTypeTable instance to give access to OpenType MATH 1.414 + // table or nullptr if the font does not have such table. Ownership is passed 1.415 + // to the caller. 1.416 + static nsOpenTypeTable* Create(gfxFont* aFont) 1.417 + { 1.418 + if (!aFont->GetFontEntry()->TryGetMathTable(aFont)) { 1.419 + return nullptr; 1.420 + } 1.421 + return new nsOpenTypeTable(aFont->GetFontEntry()); 1.422 + } 1.423 + 1.424 +private: 1.425 + nsRefPtr<gfxFontEntry> mFontEntry; 1.426 + uint32_t mGlyphID; 1.427 + 1.428 + explicit nsOpenTypeTable(gfxFontEntry* aFontEntry) 1.429 + : mFontEntry(aFontEntry) { 1.430 + MOZ_COUNT_CTOR(nsOpenTypeTable); 1.431 + } 1.432 + 1.433 + void UpdateCache(gfxContext* aThebesContext, 1.434 + int32_t aAppUnitsPerDevPixel, 1.435 + gfxFontGroup* aFontGroup, 1.436 + char16_t aChar); 1.437 +}; 1.438 + 1.439 +void 1.440 +nsOpenTypeTable::UpdateCache(gfxContext* aThebesContext, 1.441 + int32_t aAppUnitsPerDevPixel, 1.442 + gfxFontGroup* aFontGroup, 1.443 + char16_t aChar) 1.444 +{ 1.445 + if (mCharCache != aChar) { 1.446 + nsAutoPtr<gfxTextRun> textRun; 1.447 + textRun = aFontGroup-> 1.448 + MakeTextRun(&aChar, 1, aThebesContext, aAppUnitsPerDevPixel, 0); 1.449 + const gfxTextRun::CompressedGlyph& data = textRun->GetCharacterGlyphs()[0]; 1.450 + if (data.IsSimpleGlyph()) { 1.451 + mGlyphID = data.GetSimpleGlyph(); 1.452 + } else if (data.GetGlyphCount() == 1) { 1.453 + mGlyphID = textRun->GetDetailedGlyphs(0)->mGlyphID; 1.454 + } else { 1.455 + mGlyphID = 0; 1.456 + } 1.457 + mCharCache = aChar; 1.458 + } 1.459 +} 1.460 + 1.461 +/* virtual */ 1.462 +nsGlyphCode 1.463 +nsOpenTypeTable::ElementAt(gfxContext* aThebesContext, 1.464 + int32_t aAppUnitsPerDevPixel, 1.465 + gfxFontGroup* aFontGroup, 1.466 + char16_t aChar, 1.467 + bool aVertical, 1.468 + uint32_t aPosition) 1.469 +{ 1.470 + UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar); 1.471 + 1.472 + uint32_t parts[4]; 1.473 + if (!mFontEntry->GetMathVariantsParts(mGlyphID, aVertical, parts)) { 1.474 + return kNullGlyph; 1.475 + } 1.476 + 1.477 + uint32_t glyphID = parts[aPosition]; 1.478 + if (!glyphID) { 1.479 + return kNullGlyph; 1.480 + } 1.481 + nsGlyphCode glyph; 1.482 + glyph.glyphID = glyphID; 1.483 + glyph.font = -1; 1.484 + return glyph; 1.485 +} 1.486 + 1.487 +/* virtual */ 1.488 +nsGlyphCode 1.489 +nsOpenTypeTable::BigOf(gfxContext* aThebesContext, 1.490 + int32_t aAppUnitsPerDevPixel, 1.491 + gfxFontGroup* aFontGroup, 1.492 + char16_t aChar, 1.493 + bool aVertical, 1.494 + uint32_t aSize) 1.495 +{ 1.496 + UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar); 1.497 + 1.498 + uint32_t glyphID = 1.499 + mFontEntry->GetMathVariantsSize(mGlyphID, aVertical, aSize); 1.500 + if (!glyphID) { 1.501 + return kNullGlyph; 1.502 + } 1.503 + 1.504 + nsGlyphCode glyph; 1.505 + glyph.glyphID = glyphID; 1.506 + glyph.font = -1; 1.507 + return glyph; 1.508 +} 1.509 + 1.510 +/* virtual */ 1.511 +bool 1.512 +nsOpenTypeTable::HasPartsOf(gfxContext* aThebesContext, 1.513 + int32_t aAppUnitsPerDevPixel, 1.514 + gfxFontGroup* aFontGroup, 1.515 + char16_t aChar, 1.516 + bool aVertical) 1.517 +{ 1.518 + UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar); 1.519 + 1.520 + uint32_t parts[4]; 1.521 + if (!mFontEntry->GetMathVariantsParts(mGlyphID, aVertical, parts)) { 1.522 + return false; 1.523 + } 1.524 + 1.525 + return parts[0] || parts[1] || parts[2] || parts[3]; 1.526 +} 1.527 + 1.528 +/* virtual */ 1.529 +gfxTextRun* 1.530 +nsOpenTypeTable::MakeTextRun(gfxContext* aThebesContext, 1.531 + int32_t aAppUnitsPerDevPixel, 1.532 + gfxFontGroup* aFontGroup, 1.533 + const nsGlyphCode& aGlyph) 1.534 +{ 1.535 + NS_ASSERTION(aGlyph.IsGlyphID(), 1.536 + "nsOpenTypeTable can only access glyphs by id"); 1.537 + 1.538 + gfxTextRunFactory::Parameters params = { 1.539 + aThebesContext, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel 1.540 + }; 1.541 + gfxTextRun* textRun = gfxTextRun::Create(¶ms, 1, aFontGroup, 0); 1.542 + textRun->AddGlyphRun(aFontGroup->GetFontAt(0), gfxTextRange::kFontGroup, 0, 1.543 + false); 1.544 + gfxTextRun::DetailedGlyph detailedGlyph; 1.545 + detailedGlyph.mGlyphID = aGlyph.glyphID; 1.546 + detailedGlyph.mAdvance = 1.547 + NSToCoordRound(aAppUnitsPerDevPixel * 1.548 + aFontGroup->GetFontAt(0)-> 1.549 + GetGlyphHAdvance(aThebesContext, aGlyph.glyphID)); 1.550 + detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0; 1.551 + gfxShapedText::CompressedGlyph g; 1.552 + g.SetComplex(true, true, 1); 1.553 + textRun->SetGlyphs(0, g, &detailedGlyph); 1.554 + 1.555 + return textRun; 1.556 +} 1.557 + 1.558 +// ----------------------------------------------------------------------------- 1.559 +// This is the list of all the applicable glyph tables. 1.560 +// We will maintain a single global instance that will only reveal those 1.561 +// glyph tables that are associated to fonts currently installed on the 1.562 +// user' system. The class is an XPCOM shutdown observer to allow us to 1.563 +// free its allocated data at shutdown 1.564 + 1.565 +class nsGlyphTableList : public nsIObserver 1.566 +{ 1.567 +public: 1.568 + NS_DECL_ISUPPORTS 1.569 + NS_DECL_NSIOBSERVER 1.570 + 1.571 + nsPropertiesTable mUnicodeTable; 1.572 + 1.573 + nsGlyphTableList() 1.574 + : mUnicodeTable(NS_LITERAL_STRING("Unicode")) 1.575 + { 1.576 + MOZ_COUNT_CTOR(nsGlyphTableList); 1.577 + } 1.578 + 1.579 + virtual ~nsGlyphTableList() 1.580 + { 1.581 + MOZ_COUNT_DTOR(nsGlyphTableList); 1.582 + } 1.583 + 1.584 + nsresult Initialize(); 1.585 + nsresult Finalize(); 1.586 + 1.587 + // Add a glyph table in the list, return the new table that was added 1.588 + nsGlyphTable* 1.589 + AddGlyphTable(const nsString& aPrimaryFontName); 1.590 + 1.591 + // Find the glyph table in the list corresponding to the given font family. 1.592 + nsGlyphTable* 1.593 + GetGlyphTableFor(const nsAString& aFamily); 1.594 + 1.595 +private: 1.596 + nsPropertiesTable* PropertiesTableAt(int32_t aIndex) { 1.597 + return &mPropertiesTableList.ElementAt(aIndex); 1.598 + } 1.599 + int32_t PropertiesTableCount() { 1.600 + return mPropertiesTableList.Length(); 1.601 + } 1.602 + // List of glyph tables; 1.603 + nsTArray<nsPropertiesTable> mPropertiesTableList; 1.604 +}; 1.605 + 1.606 +NS_IMPL_ISUPPORTS(nsGlyphTableList, nsIObserver) 1.607 + 1.608 +// ----------------------------------------------------------------------------- 1.609 +// Here is the global list of applicable glyph tables that we will be using 1.610 +static nsGlyphTableList* gGlyphTableList = nullptr; 1.611 + 1.612 +static bool gGlyphTableInitialized = false; 1.613 + 1.614 +// XPCOM shutdown observer 1.615 +NS_IMETHODIMP 1.616 +nsGlyphTableList::Observe(nsISupports* aSubject, 1.617 + const char* aTopic, 1.618 + const char16_t* someData) 1.619 +{ 1.620 + Finalize(); 1.621 + return NS_OK; 1.622 +} 1.623 + 1.624 +// Add an observer to XPCOM shutdown so that we can free our data at shutdown 1.625 +nsresult 1.626 +nsGlyphTableList::Initialize() 1.627 +{ 1.628 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.629 + if (!obs) 1.630 + return NS_ERROR_FAILURE; 1.631 + 1.632 + nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); 1.633 + NS_ENSURE_SUCCESS(rv, rv); 1.634 + 1.635 + return NS_OK; 1.636 +} 1.637 + 1.638 +// Remove our observer and free the memory that were allocated for us 1.639 +nsresult 1.640 +nsGlyphTableList::Finalize() 1.641 +{ 1.642 + // Remove our observer from the observer service 1.643 + nsresult rv = NS_OK; 1.644 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.645 + if (obs) 1.646 + rv = obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); 1.647 + else 1.648 + rv = NS_ERROR_FAILURE; 1.649 + 1.650 + gGlyphTableInitialized = false; 1.651 + // our oneself will be destroyed when our |Release| is called by the observer 1.652 + return rv; 1.653 +} 1.654 + 1.655 +nsGlyphTable* 1.656 +nsGlyphTableList::AddGlyphTable(const nsString& aPrimaryFontName) 1.657 +{ 1.658 + // See if there is already a special table for this family. 1.659 + nsGlyphTable* glyphTable = GetGlyphTableFor(aPrimaryFontName); 1.660 + if (glyphTable != &mUnicodeTable) 1.661 + return glyphTable; 1.662 + 1.663 + // allocate a table 1.664 + glyphTable = mPropertiesTableList.AppendElement(aPrimaryFontName); 1.665 + return glyphTable; 1.666 +} 1.667 + 1.668 +nsGlyphTable* 1.669 +nsGlyphTableList::GetGlyphTableFor(const nsAString& aFamily) 1.670 +{ 1.671 + for (int32_t i = 0; i < PropertiesTableCount(); i++) { 1.672 + nsPropertiesTable* glyphTable = PropertiesTableAt(i); 1.673 + const nsAString& fontName = glyphTable->PrimaryFontName(); 1.674 + // TODO: would be nice to consider StripWhitespace and other aliasing 1.675 + if (fontName.Equals(aFamily, nsCaseInsensitiveStringComparator())) { 1.676 + return glyphTable; 1.677 + } 1.678 + } 1.679 + // Fall back to default Unicode table 1.680 + return &mUnicodeTable; 1.681 +} 1.682 + 1.683 +// ----------------------------------------------------------------------------- 1.684 + 1.685 +static nsresult 1.686 +InitGlobals(nsPresContext* aPresContext) 1.687 +{ 1.688 + NS_ASSERTION(!gGlyphTableInitialized, "Error -- already initialized"); 1.689 + gGlyphTableInitialized = true; 1.690 + 1.691 + // Allocate the placeholders for the preferred parts and variants 1.692 + nsresult rv = NS_ERROR_OUT_OF_MEMORY; 1.693 + gGlyphTableList = new nsGlyphTableList(); 1.694 + if (gGlyphTableList) { 1.695 + rv = gGlyphTableList->Initialize(); 1.696 + } 1.697 + if (NS_FAILED(rv)) { 1.698 + delete gGlyphTableList; 1.699 + gGlyphTableList = nullptr; 1.700 + return rv; 1.701 + } 1.702 + // The gGlyphTableList has been successfully registered as a shutdown 1.703 + // observer and will be deleted at shutdown. We now add some private 1.704 + // per font-family tables for stretchy operators, in order of preference. 1.705 + // Do not include the Unicode table in this list. 1.706 + if (!gGlyphTableList->AddGlyphTable(NS_LITERAL_STRING("MathJax_Main")) || 1.707 + !gGlyphTableList->AddGlyphTable(NS_LITERAL_STRING("STIXNonUnicode")) || 1.708 + !gGlyphTableList->AddGlyphTable(NS_LITERAL_STRING("STIXSizeOneSym")) || 1.709 + !gGlyphTableList->AddGlyphTable(NS_LITERAL_STRING("Standard Symbols L")) 1.710 +#ifdef XP_WIN 1.711 + || !gGlyphTableList->AddGlyphTable(NS_LITERAL_STRING("Symbol")) 1.712 +#endif 1.713 + ) { 1.714 + rv = NS_ERROR_OUT_OF_MEMORY; 1.715 + } 1.716 + 1.717 + return rv; 1.718 +} 1.719 + 1.720 +// ----------------------------------------------------------------------------- 1.721 +// And now the implementation of nsMathMLChar 1.722 + 1.723 +nsMathMLChar::~nsMathMLChar() 1.724 +{ 1.725 + MOZ_COUNT_DTOR(nsMathMLChar); 1.726 + mStyleContext->Release(); 1.727 +} 1.728 + 1.729 +nsStyleContext* 1.730 +nsMathMLChar::GetStyleContext() const 1.731 +{ 1.732 + NS_ASSERTION(mStyleContext, "chars should always have style context"); 1.733 + return mStyleContext; 1.734 +} 1.735 + 1.736 +void 1.737 +nsMathMLChar::SetStyleContext(nsStyleContext* aStyleContext) 1.738 +{ 1.739 + NS_PRECONDITION(aStyleContext, "null ptr"); 1.740 + if (aStyleContext != mStyleContext) { 1.741 + if (mStyleContext) 1.742 + mStyleContext->Release(); 1.743 + if (aStyleContext) { 1.744 + mStyleContext = aStyleContext; 1.745 + aStyleContext->AddRef(); 1.746 + } 1.747 + } 1.748 +} 1.749 + 1.750 +void 1.751 +nsMathMLChar::SetData(nsPresContext* aPresContext, 1.752 + nsString& aData) 1.753 +{ 1.754 + if (!gGlyphTableInitialized) { 1.755 + InitGlobals(aPresContext); 1.756 + } 1.757 + mData = aData; 1.758 + // some assumptions until proven otherwise 1.759 + // note that mGlyph is not initialized 1.760 + mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED; 1.761 + mBoundingMetrics = nsBoundingMetrics(); 1.762 + // check if stretching is applicable ... 1.763 + if (gGlyphTableList && (1 == mData.Length())) { 1.764 + mDirection = nsMathMLOperators::GetStretchyDirection(mData); 1.765 + // default tentative table (not the one that is necessarily going 1.766 + // to be used) 1.767 + } 1.768 +} 1.769 + 1.770 +// ----------------------------------------------------------------------------- 1.771 +/* 1.772 + The Stretch: 1.773 + @param aContainerSize - suggested size for the stretched char 1.774 + @param aDesiredStretchSize - OUT parameter. The desired size 1.775 + after stretching. If no stretching is done, the output will 1.776 + simply give the base size. 1.777 + 1.778 + How it works? 1.779 + Summary:- 1.780 + The Stretch() method first looks for a glyph of appropriate 1.781 + size; If a glyph is found, it is cached by this object and 1.782 + its size is returned in aDesiredStretchSize. The cached 1.783 + glyph will then be used at the painting stage. 1.784 + If no glyph of appropriate size is found, a search is made 1.785 + to see if the char can be built by parts. 1.786 + 1.787 + Details:- 1.788 + A character gets stretched through the following pipeline : 1.789 + 1.790 + 1) If the base size of the char is sufficient to cover the 1.791 + container' size, we use that. If not, it will still be 1.792 + used as a fallback if the other stages in the pipeline fail. 1.793 + Issues : 1.794 + a) The base size, the parts and the variants of a char can 1.795 + be in different fonts. For eg., the base size for '(' should 1.796 + come from a normal ascii font if CMEX10 is used, since CMEX10 1.797 + only contains the stretched versions. Hence, there are two 1.798 + style contexts in use throughout the process. The leaf style 1.799 + context of the char holds fonts with which to try to stretch 1.800 + the char. The parent style context of the char contains fonts 1.801 + for normal rendering. So the parent context is the one used 1.802 + to get the initial base size at the start of the pipeline. 1.803 + b) For operators that can be largeop's in display mode, 1.804 + we will skip the base size even if it fits, so that 1.805 + the next stage in the pipeline is given a chance to find 1.806 + a largeop variant. If the next stage fails, we fallback 1.807 + to the base size. 1.808 + 1.809 + 2) We search for the first larger variant of the char that fits the 1.810 + container' size. We first search for larger variants using the glyph 1.811 + table corresponding to the first existing font specified in the list of 1.812 + stretchy fonts held by the leaf style context (from -moz-math-stretchy in 1.813 + mathml.css). Generic fonts are resolved by the preference 1.814 + "font.mathfont-family". 1.815 + Issues : 1.816 + a) the largeop and display settings determine the starting 1.817 + size when we do the above search, regardless of whether 1.818 + smaller variants already fit the container' size. 1.819 + b) if it is a largeopOnly request (i.e., a displaystyle operator 1.820 + with largeop=true and stretchy=false), we break after finding 1.821 + the first starting variant, regardless of whether that 1.822 + variant fits the container's size. 1.823 + 1.824 + 3) If a variant of appropriate size wasn't found, we see if the char 1.825 + can be built by parts using the same glyph table. 1.826 + Issue: 1.827 + There are chars that have no middle and glue glyphs. For 1.828 + such chars, the parts need to be joined using the rule. 1.829 + By convention (TeXbook p.225), the descent of the parts is 1.830 + zero while their ascent gives the thickness of the rule that 1.831 + should be used to join them. 1.832 + 1.833 + 4) If a match was not found in that glyph table, repeat from 2 to search the 1.834 + ordered list of stretchy fonts for the first font with a glyph table that 1.835 + provides a fit to the container size. If no fit is found, the closest fit 1.836 + is used. 1.837 + 1.838 + Of note: 1.839 + When the pipeline completes successfully, the desired size of the 1.840 + stretched char can actually be slightly larger or smaller than 1.841 + aContainerSize. But it is the responsibility of the caller to 1.842 + account for the spacing when setting aContainerSize, and to leave 1.843 + any extra margin when placing the stretched char. 1.844 +*/ 1.845 +// ----------------------------------------------------------------------------- 1.846 + 1.847 + 1.848 +// plain TeX settings (TeXbook p.152) 1.849 +#define NS_MATHML_DELIMITER_FACTOR 0.901f 1.850 +#define NS_MATHML_DELIMITER_SHORTFALL_POINTS 5.0f 1.851 + 1.852 +static bool 1.853 +IsSizeOK(nsPresContext* aPresContext, nscoord a, nscoord b, uint32_t aHint) 1.854 +{ 1.855 + // Normal: True if 'a' is around +/-10% of the target 'b' (10% is 1.856 + // 1-DelimiterFactor). This often gives a chance to the base size to 1.857 + // win, especially in the context of <mfenced> without tall elements 1.858 + // or in sloppy markups without protective <mrow></mrow> 1.859 + bool isNormal = 1.860 + (aHint & NS_STRETCH_NORMAL) && 1.861 + Abs<float>(a - b) < (1.0f - NS_MATHML_DELIMITER_FACTOR) * float(b); 1.862 + 1.863 + // Nearer: True if 'a' is around max{ +/-10% of 'b' , 'b' - 5pt }, 1.864 + // as documented in The TeXbook, Ch.17, p.152. 1.865 + // i.e. within 10% and within 5pt 1.866 + bool isNearer = false; 1.867 + if (aHint & (NS_STRETCH_NEARER | NS_STRETCH_LARGEOP)) { 1.868 + float c = std::max(float(b) * NS_MATHML_DELIMITER_FACTOR, 1.869 + float(b) - nsPresContext:: 1.870 + CSSPointsToAppUnits(NS_MATHML_DELIMITER_SHORTFALL_POINTS)); 1.871 + isNearer = Abs<float>(b - a) <= float(b) - c; 1.872 + } 1.873 + 1.874 + // Smaller: Mainly for transitory use, to compare two candidate 1.875 + // choices 1.876 + bool isSmaller = 1.877 + (aHint & NS_STRETCH_SMALLER) && 1.878 + float(a) >= NS_MATHML_DELIMITER_FACTOR * float(b) && 1.879 + a <= b; 1.880 + 1.881 + // Larger: Critical to the sqrt code to ensure that the radical 1.882 + // size is tall enough 1.883 + bool isLarger = 1.884 + (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP)) && 1.885 + a >= b; 1.886 + 1.887 + return (isNormal || isSmaller || isNearer || isLarger); 1.888 +} 1.889 + 1.890 +static bool 1.891 +IsSizeBetter(nscoord a, nscoord olda, nscoord b, uint32_t aHint) 1.892 +{ 1.893 + if (0 == olda) 1.894 + return true; 1.895 + if (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP)) 1.896 + return (a >= olda) ? (olda < b) : (a >= b); 1.897 + if (aHint & NS_STRETCH_SMALLER) 1.898 + return (a <= olda) ? (olda > b) : (a <= b); 1.899 + 1.900 + // XXXkt prob want log scale here i.e. 1.5 is closer to 1 than 0.5 1.901 + return Abs(a - b) < Abs(olda - b); 1.902 +} 1.903 + 1.904 +// We want to place the glyphs even when they don't fit at their 1.905 +// full extent, i.e., we may clip to tolerate a small amount of 1.906 +// overlap between the parts. This is important to cater for fonts 1.907 +// with long glues. 1.908 +static nscoord 1.909 +ComputeSizeFromParts(nsPresContext* aPresContext, 1.910 + nsGlyphCode* aGlyphs, 1.911 + nscoord* aSizes, 1.912 + nscoord aTargetSize) 1.913 +{ 1.914 + enum {first, middle, last, glue}; 1.915 + // Add the parts that cannot be left out. 1.916 + nscoord sum = 0; 1.917 + for (int32_t i = first; i <= last; i++) { 1.918 + if (aGlyphs[i] != aGlyphs[glue]) { 1.919 + sum += aSizes[i]; 1.920 + } 1.921 + } 1.922 + 1.923 + // Determine how much is used in joins 1.924 + nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel(); 1.925 + int32_t joins = aGlyphs[middle] == aGlyphs[glue] ? 1 : 2; 1.926 + 1.927 + // Pick a maximum size using a maximum number of glue glyphs that we are 1.928 + // prepared to draw for one character. 1.929 + const int32_t maxGlyphs = 1000; 1.930 + 1.931 + // This also takes into account the fact that, if the glue has no size, 1.932 + // then the character can't be lengthened. 1.933 + nscoord maxSize = sum - 2 * joins * oneDevPixel + maxGlyphs * aSizes[glue]; 1.934 + if (maxSize < aTargetSize) 1.935 + return maxSize; // settle with the maximum size 1.936 + 1.937 + // Get the minimum allowable size using some flex. 1.938 + nscoord minSize = NSToCoordRound(NS_MATHML_DELIMITER_FACTOR * sum); 1.939 + 1.940 + if (minSize > aTargetSize) 1.941 + return minSize; // settle with the minimum size 1.942 + 1.943 + // Fill-up the target area 1.944 + return aTargetSize; 1.945 +} 1.946 + 1.947 +// Insert aFallbackFamilies before the first generic family in or at the end 1.948 +// of a CSS aFontName. 1.949 +static void 1.950 +AddFallbackFonts(nsAString& aFontName, const nsAString& aFallbackFamilies) 1.951 +{ 1.952 + if (aFallbackFamilies.IsEmpty()) 1.953 + return; 1.954 + 1.955 + if (aFontName.IsEmpty()) { 1.956 + return; 1.957 + } 1.958 + 1.959 + static const char16_t kSingleQuote = char16_t('\''); 1.960 + static const char16_t kDoubleQuote = char16_t('\"'); 1.961 + static const char16_t kComma = char16_t(','); 1.962 + 1.963 + const char16_t *p_begin, *p_end; 1.964 + aFontName.BeginReading(p_begin); 1.965 + aFontName.EndReading(p_end); 1.966 + 1.967 + const char16_t *p = p_begin; 1.968 + const char16_t *p_name = nullptr; 1.969 + while (p < p_end) { 1.970 + while (nsCRT::IsAsciiSpace(*p)) 1.971 + if (++p == p_end) 1.972 + goto insert; 1.973 + 1.974 + p_name = p; 1.975 + if (*p == kSingleQuote || *p == kDoubleQuote) { 1.976 + // quoted font family 1.977 + char16_t quoteMark = *p; 1.978 + if (++p == p_end) 1.979 + goto insert; 1.980 + 1.981 + // XXX What about CSS character escapes? 1.982 + while (*p != quoteMark) 1.983 + if (++p == p_end) 1.984 + goto insert; 1.985 + 1.986 + while (++p != p_end && *p != kComma) 1.987 + /* nothing */ ; 1.988 + 1.989 + } else { 1.990 + // unquoted font family 1.991 + const char16_t *nameStart = p; 1.992 + while (++p != p_end && *p != kComma) 1.993 + /* nothing */ ; 1.994 + 1.995 + nsAutoString family; 1.996 + family = Substring(nameStart, p); 1.997 + family.CompressWhitespace(false, true); 1.998 + 1.999 + uint8_t generic; 1.1000 + nsFont::GetGenericID(family, &generic); 1.1001 + if (generic != kGenericFont_NONE) 1.1002 + goto insert; 1.1003 + } 1.1004 + 1.1005 + ++p; // may advance past p_end 1.1006 + } 1.1007 + 1.1008 + aFontName.Append(NS_LITERAL_STRING(",") + aFallbackFamilies); 1.1009 + return; 1.1010 + 1.1011 +insert: 1.1012 + if (p_name) { 1.1013 + aFontName.Insert(aFallbackFamilies + NS_LITERAL_STRING(","), 1.1014 + p_name - p_begin); 1.1015 + } 1.1016 + else { // whitespace or empty 1.1017 + aFontName = aFallbackFamilies; 1.1018 + } 1.1019 +} 1.1020 + 1.1021 +// Update the font if there is a family change and returns the font group. 1.1022 +bool 1.1023 +nsMathMLChar::SetFontFamily(nsPresContext* aPresContext, 1.1024 + const nsGlyphTable* aGlyphTable, 1.1025 + const nsGlyphCode& aGlyphCode, 1.1026 + const nsAString& aDefaultFamily, 1.1027 + nsFont& aFont, 1.1028 + nsRefPtr<gfxFontGroup>* aFontGroup) 1.1029 +{ 1.1030 + const nsAString& family = 1.1031 + aGlyphCode.font ? aGlyphTable->FontNameFor(aGlyphCode) : aDefaultFamily; 1.1032 + if (!*aFontGroup || !family.Equals(aFont.name)) { 1.1033 + nsFont font = aFont; 1.1034 + font.name = family; 1.1035 + nsRefPtr<nsFontMetrics> fm; 1.1036 + aPresContext->DeviceContext()-> 1.1037 + GetMetricsFor(font, 1.1038 + mStyleContext->StyleFont()->mLanguage, 1.1039 + aPresContext->GetUserFontSet(), 1.1040 + aPresContext->GetTextPerfMetrics(), 1.1041 + *getter_AddRefs(fm)); 1.1042 + // Set the font if it is an unicode table 1.1043 + // or if the same family name has been found 1.1044 + if (aGlyphTable == &gGlyphTableList->mUnicodeTable || 1.1045 + fm->GetThebesFontGroup()->GetFontAt(0)->GetFontEntry()-> 1.1046 + FamilyName() == family) { 1.1047 + aFont.name = family; 1.1048 + *aFontGroup = fm->GetThebesFontGroup(); 1.1049 + } else { 1.1050 + return false; // We did not set the font 1.1051 + } 1.1052 + } 1.1053 + return true; 1.1054 +} 1.1055 + 1.1056 +static nsBoundingMetrics 1.1057 +MeasureTextRun(gfxContext* aThebesContext, gfxTextRun* aTextRun) 1.1058 +{ 1.1059 + gfxTextRun::Metrics metrics = 1.1060 + aTextRun->MeasureText(0, aTextRun->GetLength(), 1.1061 + gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS, 1.1062 + aThebesContext, nullptr); 1.1063 + 1.1064 + nsBoundingMetrics bm; 1.1065 + bm.leftBearing = NSToCoordFloor(metrics.mBoundingBox.X()); 1.1066 + bm.rightBearing = NSToCoordCeil(metrics.mBoundingBox.XMost()); 1.1067 + bm.ascent = NSToCoordCeil(-metrics.mBoundingBox.Y()); 1.1068 + bm.descent = NSToCoordCeil(metrics.mBoundingBox.YMost()); 1.1069 + bm.width = NSToCoordRound(metrics.mAdvanceWidth); 1.1070 + 1.1071 + return bm; 1.1072 +} 1.1073 + 1.1074 +class nsMathMLChar::StretchEnumContext { 1.1075 +public: 1.1076 + StretchEnumContext(nsMathMLChar* aChar, 1.1077 + nsPresContext* aPresContext, 1.1078 + gfxContext* aThebesContext, 1.1079 + nsStretchDirection aStretchDirection, 1.1080 + nscoord aTargetSize, 1.1081 + uint32_t aStretchHint, 1.1082 + nsBoundingMetrics& aStretchedMetrics, 1.1083 + const nsAString& aFamilies, 1.1084 + bool& aGlyphFound) 1.1085 + : mChar(aChar), 1.1086 + mPresContext(aPresContext), 1.1087 + mThebesContext(aThebesContext), 1.1088 + mDirection(aStretchDirection), 1.1089 + mTargetSize(aTargetSize), 1.1090 + mStretchHint(aStretchHint), 1.1091 + mBoundingMetrics(aStretchedMetrics), 1.1092 + mFamilies(aFamilies), 1.1093 + mTryVariants(true), 1.1094 + mTryParts(true), 1.1095 + mGlyphFound(aGlyphFound) {} 1.1096 + 1.1097 + static bool 1.1098 + EnumCallback(const nsString& aFamily, bool aGeneric, void *aData); 1.1099 + 1.1100 +private: 1.1101 + bool TryVariants(nsGlyphTable* aGlyphTable, 1.1102 + nsRefPtr<gfxFontGroup>* aFontGroup, 1.1103 + const nsAString& aFamily); 1.1104 + bool TryParts(nsGlyphTable* aGlyphTable, 1.1105 + nsRefPtr<gfxFontGroup>* aFontGroup, 1.1106 + const nsAString& aFamily); 1.1107 + 1.1108 + nsMathMLChar* mChar; 1.1109 + nsPresContext* mPresContext; 1.1110 + gfxContext* mThebesContext; 1.1111 + const nsStretchDirection mDirection; 1.1112 + const nscoord mTargetSize; 1.1113 + const uint32_t mStretchHint; 1.1114 + nsBoundingMetrics& mBoundingMetrics; 1.1115 + // Font families to search 1.1116 + const nsAString& mFamilies; 1.1117 + 1.1118 +public: 1.1119 + bool mTryVariants; 1.1120 + bool mTryParts; 1.1121 + 1.1122 +private: 1.1123 + nsAutoTArray<nsGlyphTable*,16> mTablesTried; 1.1124 + bool& mGlyphFound; 1.1125 +}; 1.1126 + 1.1127 + 1.1128 +// 2. See if there are any glyphs of the appropriate size. 1.1129 +// Returns true if the size is OK, false to keep searching. 1.1130 +// Always updates the char if a better match is found. 1.1131 +bool 1.1132 +nsMathMLChar:: 1.1133 +StretchEnumContext::TryVariants(nsGlyphTable* aGlyphTable, 1.1134 + nsRefPtr<gfxFontGroup>* aFontGroup, 1.1135 + const nsAString& aFamily) 1.1136 +{ 1.1137 + // Use our stretchy style context now that stretching is in progress 1.1138 + nsStyleContext *sc = mChar->mStyleContext; 1.1139 + nsFont font = sc->StyleFont()->mFont; 1.1140 + 1.1141 + bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL); 1.1142 + nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel(); 1.1143 + char16_t uchar = mChar->mData[0]; 1.1144 + bool largeop = (NS_STRETCH_LARGEOP & mStretchHint) != 0; 1.1145 + bool largeopOnly = 1.1146 + largeop && (NS_STRETCH_VARIABLE_MASK & mStretchHint) == 0; 1.1147 + bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0; 1.1148 + 1.1149 + nscoord bestSize = 1.1150 + isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent 1.1151 + : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing; 1.1152 + bool haveBetter = false; 1.1153 + 1.1154 + // start at size = 1 (size = 0 is the char at its normal size) 1.1155 + int32_t size = 1; 1.1156 + nsGlyphCode ch; 1.1157 + nscoord displayOperatorMinHeight = 0; 1.1158 + if (largeopOnly) { 1.1159 + NS_ASSERTION(isVertical, "Stretching should be in the vertical direction"); 1.1160 + ch = aGlyphTable->BigOf(mThebesContext, oneDevPixel, *aFontGroup, uchar, 1.1161 + isVertical, 0); 1.1162 + if (ch.IsGlyphID()) { 1.1163 + gfxFont* mathFont = aFontGroup->get()->GetFontAt(0); 1.1164 + // For OpenType MATH fonts, we will rely on the DisplayOperatorMinHeight 1.1165 + // to select the right size variant. Note that the value is sometimes too 1.1166 + // small so we use kLargeOpFactor/kIntegralFactor as a minimum value. 1.1167 + displayOperatorMinHeight = 1.1168 + NSToCoordRound(mathFont->GetFontEntry()-> 1.1169 + GetMathConstant(gfxFontEntry::DisplayOperatorMinHeight) * 1.1170 + mathFont->GetAdjustedSize() * oneDevPixel); 1.1171 + nsAutoPtr<gfxTextRun> textRun; 1.1172 + textRun = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel, 1.1173 + *aFontGroup, ch); 1.1174 + nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun); 1.1175 + float largeopFactor = kLargeOpFactor; 1.1176 + if (NS_STRETCH_INTEGRAL & mStretchHint) { 1.1177 + // integrals are drawn taller 1.1178 + largeopFactor = kIntegralFactor; 1.1179 + } 1.1180 + nscoord minHeight = largeopFactor * (bm.ascent + bm.descent); 1.1181 + if (displayOperatorMinHeight < minHeight) { 1.1182 + displayOperatorMinHeight = minHeight; 1.1183 + } 1.1184 + } 1.1185 + } 1.1186 +#ifdef NOISY_SEARCH 1.1187 + printf(" searching in %s ...\n", 1.1188 + NS_LossyConvertUTF16toASCII(aFamily).get()); 1.1189 +#endif 1.1190 + while ((ch = aGlyphTable->BigOf(mThebesContext, oneDevPixel, *aFontGroup, 1.1191 + uchar, isVertical, size)).Exists()) { 1.1192 + 1.1193 + if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamily, font, 1.1194 + aFontGroup)) { 1.1195 + // if largeopOnly is set, break now 1.1196 + if (largeopOnly) break; 1.1197 + ++size; 1.1198 + continue; 1.1199 + } 1.1200 + 1.1201 + nsAutoPtr<gfxTextRun> textRun; 1.1202 + textRun = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel, 1.1203 + *aFontGroup, ch); 1.1204 + nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun); 1.1205 + if (ch.IsGlyphID()) { 1.1206 + gfxFont* mathFont = aFontGroup->get()->GetFontAt(0); 1.1207 + if (mathFont->GetFontEntry()->TryGetMathTable(mathFont)) { 1.1208 + // MeasureTextRun should have set the advance width to the right 1.1209 + // bearing for OpenType MATH fonts. We now subtract the italic 1.1210 + // correction, so that nsMathMLmmultiscripts will place the scripts 1.1211 + // correctly. 1.1212 + // Note that STIX-Word does not provide italic corrections but its 1.1213 + // advance widths do not match right bearings. 1.1214 + // (http://sourceforge.net/p/stixfonts/tracking/50/) 1.1215 + gfxFloat italicCorrection; 1.1216 + if (mathFont->GetFontEntry()-> 1.1217 + GetMathItalicsCorrection(ch.glyphID, &italicCorrection)) { 1.1218 + bm.width -= 1.1219 + NSToCoordRound(italicCorrection * 1.1220 + mathFont->GetAdjustedSize() * oneDevPixel); 1.1221 + if (bm.width < 0) { 1.1222 + bm.width = 0; 1.1223 + } 1.1224 + } 1.1225 + } 1.1226 + } 1.1227 + 1.1228 + nscoord charSize = 1.1229 + isVertical ? bm.ascent + bm.descent 1.1230 + : bm.rightBearing - bm.leftBearing; 1.1231 + 1.1232 + if (largeopOnly || 1.1233 + IsSizeBetter(charSize, bestSize, mTargetSize, mStretchHint)) { 1.1234 + mGlyphFound = true; 1.1235 + if (maxWidth) { 1.1236 + // IsSizeBetter() checked that charSize < maxsize; 1.1237 + // Leave ascent, descent, and bestsize as these contain maxsize. 1.1238 + if (mBoundingMetrics.width < bm.width) 1.1239 + mBoundingMetrics.width = bm.width; 1.1240 + if (mBoundingMetrics.leftBearing > bm.leftBearing) 1.1241 + mBoundingMetrics.leftBearing = bm.leftBearing; 1.1242 + if (mBoundingMetrics.rightBearing < bm.rightBearing) 1.1243 + mBoundingMetrics.rightBearing = bm.rightBearing; 1.1244 + // Continue to check other sizes unless largeopOnly 1.1245 + haveBetter = largeopOnly; 1.1246 + } 1.1247 + else { 1.1248 + mBoundingMetrics = bm; 1.1249 + haveBetter = true; 1.1250 + bestSize = charSize; 1.1251 + mChar->mGlyphs[0] = textRun; 1.1252 + mChar->mDraw = DRAW_VARIANT; 1.1253 + } 1.1254 +#ifdef NOISY_SEARCH 1.1255 + printf(" size:%d Current best\n", size); 1.1256 +#endif 1.1257 + } 1.1258 + else { 1.1259 +#ifdef NOISY_SEARCH 1.1260 + printf(" size:%d Rejected!\n", size); 1.1261 +#endif 1.1262 + if (haveBetter) 1.1263 + break; // Not making an futher progress, stop searching 1.1264 + } 1.1265 + 1.1266 + // If this a largeop only operator, we stop if the glyph is large enough. 1.1267 + if (largeopOnly && (bm.ascent + bm.descent) >= displayOperatorMinHeight) { 1.1268 + break; 1.1269 + } 1.1270 + ++size; 1.1271 + } 1.1272 + 1.1273 + return haveBetter && 1.1274 + (largeopOnly || 1.1275 + IsSizeOK(mPresContext, bestSize, mTargetSize, mStretchHint)); 1.1276 +} 1.1277 + 1.1278 +// 3. Build by parts. 1.1279 +// Returns true if the size is OK, false to keep searching. 1.1280 +// Always updates the char if a better match is found. 1.1281 +bool 1.1282 +nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable* aGlyphTable, 1.1283 + nsRefPtr<gfxFontGroup>* aFontGroup, 1.1284 + const nsAString& aFamily) 1.1285 +{ 1.1286 + // Use our stretchy style context now that stretching is in progress 1.1287 + nsFont font = mChar->mStyleContext->StyleFont()->mFont; 1.1288 + 1.1289 + // Compute the bounding metrics of all partial glyphs 1.1290 + nsAutoPtr<gfxTextRun> textRun[4]; 1.1291 + nsGlyphCode chdata[4]; 1.1292 + nsBoundingMetrics bmdata[4]; 1.1293 + nscoord sizedata[4]; 1.1294 + 1.1295 + bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL); 1.1296 + nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel(); 1.1297 + char16_t uchar = mChar->mData[0]; 1.1298 + bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0; 1.1299 + if (!aGlyphTable->HasPartsOf(mThebesContext, oneDevPixel, *aFontGroup, 1.1300 + uchar, isVertical)) 1.1301 + return false; // to next table 1.1302 + 1.1303 + for (int32_t i = 0; i < 4; i++) { 1.1304 + nsGlyphCode ch = aGlyphTable->ElementAt(mThebesContext, oneDevPixel, 1.1305 + *aFontGroup, uchar, isVertical, i); 1.1306 + chdata[i] = ch; 1.1307 + if (ch.Exists()) { 1.1308 + if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamily, font, 1.1309 + aFontGroup)) 1.1310 + return false; 1.1311 + 1.1312 + textRun[i] = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel, 1.1313 + *aFontGroup, ch); 1.1314 + nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun[i]); 1.1315 + 1.1316 + // TODO: For the generic Unicode table, ideally we should check that the 1.1317 + // glyphs are actually found and that they each come from the same 1.1318 + // font. 1.1319 + bmdata[i] = bm; 1.1320 + sizedata[i] = isVertical ? bm.ascent + bm.descent 1.1321 + : bm.rightBearing - bm.leftBearing; 1.1322 + } else { 1.1323 + // Null glue indicates that a rule will be drawn, which can stretch to 1.1324 + // fill any space. 1.1325 + textRun[i] = nullptr; 1.1326 + bmdata[i] = nsBoundingMetrics(); 1.1327 + sizedata[i] = i == 3 ? mTargetSize : 0; 1.1328 + } 1.1329 + } 1.1330 + 1.1331 + // Build by parts if we have successfully computed the 1.1332 + // bounding metrics of all parts. 1.1333 + nscoord computedSize = ComputeSizeFromParts(mPresContext, chdata, sizedata, 1.1334 + mTargetSize); 1.1335 + 1.1336 + nscoord currentSize = 1.1337 + isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent 1.1338 + : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing; 1.1339 + 1.1340 + if (!IsSizeBetter(computedSize, currentSize, mTargetSize, mStretchHint)) { 1.1341 +#ifdef NOISY_SEARCH 1.1342 + printf(" Font %s Rejected!\n", 1.1343 + NS_LossyConvertUTF16toASCII(fontName).get()); 1.1344 +#endif 1.1345 + return false; // to next table 1.1346 + } 1.1347 + 1.1348 +#ifdef NOISY_SEARCH 1.1349 + printf(" Font %s Current best!\n", 1.1350 + NS_LossyConvertUTF16toASCII(fontName).get()); 1.1351 +#endif 1.1352 + 1.1353 + // The computed size is the best we have found so far... 1.1354 + // now is the time to compute and cache our bounding metrics 1.1355 + if (isVertical) { 1.1356 + int32_t i; 1.1357 + // Try and find the first existing part and then determine the extremal 1.1358 + // horizontal metrics of the parts. 1.1359 + for (i = 0; i <= 3 && !textRun[i]; i++); 1.1360 + if (i == 4) { 1.1361 + NS_ERROR("Cannot stretch - All parts missing"); 1.1362 + return false; 1.1363 + } 1.1364 + nscoord lbearing = bmdata[i].leftBearing; 1.1365 + nscoord rbearing = bmdata[i].rightBearing; 1.1366 + nscoord width = bmdata[i].width; 1.1367 + i++; 1.1368 + for (; i <= 3; i++) { 1.1369 + if (!textRun[i]) continue; 1.1370 + lbearing = std::min(lbearing, bmdata[i].leftBearing); 1.1371 + rbearing = std::max(rbearing, bmdata[i].rightBearing); 1.1372 + width = std::max(width, bmdata[i].width); 1.1373 + } 1.1374 + if (maxWidth) { 1.1375 + lbearing = std::min(lbearing, mBoundingMetrics.leftBearing); 1.1376 + rbearing = std::max(rbearing, mBoundingMetrics.rightBearing); 1.1377 + width = std::max(width, mBoundingMetrics.width); 1.1378 + } 1.1379 + mBoundingMetrics.width = width; 1.1380 + // When maxWidth, updating ascent and descent indicates that no characters 1.1381 + // larger than this character's minimum size need to be checked as they 1.1382 + // will not be used. 1.1383 + mBoundingMetrics.ascent = bmdata[0].ascent; // not used except with descent 1.1384 + // for height 1.1385 + mBoundingMetrics.descent = computedSize - mBoundingMetrics.ascent; 1.1386 + mBoundingMetrics.leftBearing = lbearing; 1.1387 + mBoundingMetrics.rightBearing = rbearing; 1.1388 + } 1.1389 + else { 1.1390 + int32_t i; 1.1391 + // Try and find the first existing part and then determine the extremal 1.1392 + // vertical metrics of the parts. 1.1393 + for (i = 0; i <= 3 && !textRun[i]; i++); 1.1394 + if (i == 4) { 1.1395 + NS_ERROR("Cannot stretch - All parts missing"); 1.1396 + return false; 1.1397 + } 1.1398 + nscoord ascent = bmdata[i].ascent; 1.1399 + nscoord descent = bmdata[i].descent; 1.1400 + i++; 1.1401 + for (; i <= 3; i++) { 1.1402 + if (!textRun[i]) continue; 1.1403 + ascent = std::max(ascent, bmdata[i].ascent); 1.1404 + descent = std::max(descent, bmdata[i].descent); 1.1405 + } 1.1406 + mBoundingMetrics.width = computedSize; 1.1407 + mBoundingMetrics.ascent = ascent; 1.1408 + mBoundingMetrics.descent = descent; 1.1409 + mBoundingMetrics.leftBearing = 0; 1.1410 + mBoundingMetrics.rightBearing = computedSize; 1.1411 + } 1.1412 + mGlyphFound = true; 1.1413 + if (maxWidth) 1.1414 + return false; // Continue to check other sizes 1.1415 + 1.1416 + // reset 1.1417 + mChar->mDraw = DRAW_PARTS; 1.1418 + for (int32_t i = 0; i < 4; i++) { 1.1419 + mChar->mGlyphs[i] = textRun[i]; 1.1420 + mChar->mBmData[i] = bmdata[i]; 1.1421 + } 1.1422 + 1.1423 + return IsSizeOK(mPresContext, computedSize, mTargetSize, mStretchHint); 1.1424 +} 1.1425 + 1.1426 +// This is called for each family, whether it exists or not 1.1427 +bool 1.1428 +nsMathMLChar::StretchEnumContext::EnumCallback(const nsString& aFamily, 1.1429 + bool aGeneric, void *aData) 1.1430 +{ 1.1431 + StretchEnumContext* context = static_cast<StretchEnumContext*>(aData); 1.1432 + 1.1433 + // Check font family if it is not a generic one 1.1434 + // We test with the kNullGlyph 1.1435 + nsStyleContext *sc = context->mChar->mStyleContext; 1.1436 + nsFont font = sc->StyleFont()->mFont; 1.1437 + nsRefPtr<gfxFontGroup> fontGroup; 1.1438 + if (!aGeneric && !context->mChar->SetFontFamily(context->mPresContext, 1.1439 + nullptr, kNullGlyph, aFamily, 1.1440 + font, &fontGroup)) 1.1441 + return true; // Could not set the family 1.1442 + 1.1443 + // Determine the glyph table to use for this font. 1.1444 + nsAutoPtr<nsOpenTypeTable> openTypeTable; 1.1445 + nsGlyphTable* glyphTable; 1.1446 + if (aGeneric) { 1.1447 + // This is a generic font, use the Unicode table. 1.1448 + glyphTable = &gGlyphTableList->mUnicodeTable; 1.1449 + } else { 1.1450 + // If the font contains an Open Type MATH table, use it. 1.1451 + openTypeTable = nsOpenTypeTable::Create(fontGroup->GetFontAt(0)); 1.1452 + if (openTypeTable) { 1.1453 + glyphTable = openTypeTable; 1.1454 + } else { 1.1455 + // Otherwise try to find a .properties file corresponding to that font 1.1456 + // family or fallback to the Unicode table. 1.1457 + glyphTable = gGlyphTableList->GetGlyphTableFor(aFamily); 1.1458 + } 1.1459 + } 1.1460 + 1.1461 + if (!openTypeTable) { 1.1462 + if (context->mTablesTried.Contains(glyphTable)) 1.1463 + return true; // already tried this one 1.1464 + 1.1465 + // Only try this table once. 1.1466 + context->mTablesTried.AppendElement(glyphTable); 1.1467 + } 1.1468 + 1.1469 + // If the unicode table is being used, then search all font families. If a 1.1470 + // special table is being used then the font in this family should have the 1.1471 + // specified glyphs. 1.1472 + const nsAString& family = glyphTable == &gGlyphTableList->mUnicodeTable ? 1.1473 + context->mFamilies : aFamily; 1.1474 + 1.1475 + if((context->mTryVariants && 1.1476 + context->TryVariants(glyphTable, &fontGroup, family)) || 1.1477 + (context->mTryParts && context->TryParts(glyphTable, &fontGroup, family))) 1.1478 + return false; // no need to continue 1.1479 + 1.1480 + return true; // true means continue 1.1481 +} 1.1482 + 1.1483 +nsresult 1.1484 +nsMathMLChar::StretchInternal(nsPresContext* aPresContext, 1.1485 + gfxContext* aThebesContext, 1.1486 + nsStretchDirection& aStretchDirection, 1.1487 + const nsBoundingMetrics& aContainerSize, 1.1488 + nsBoundingMetrics& aDesiredStretchSize, 1.1489 + uint32_t aStretchHint, 1.1490 + // These are currently only used when 1.1491 + // aStretchHint & NS_STRETCH_MAXWIDTH: 1.1492 + float aMaxSize, 1.1493 + bool aMaxSizeIsAbsolute) 1.1494 +{ 1.1495 + // if we have been called before, and we didn't actually stretch, our 1.1496 + // direction may have been set to NS_STRETCH_DIRECTION_UNSUPPORTED. 1.1497 + // So first set our direction back to its instrinsic value 1.1498 + nsStretchDirection direction = nsMathMLOperators::GetStretchyDirection(mData); 1.1499 + 1.1500 + // Set default font and get the default bounding metrics 1.1501 + // mStyleContext is a leaf context used only when stretching happens. 1.1502 + // For the base size, the default font should come from the parent context 1.1503 + nsFont font = mStyleContext->GetParent()->StyleFont()->mFont; 1.1504 + 1.1505 + nsRefPtr<nsFontMetrics> fm; 1.1506 + aPresContext->DeviceContext()-> 1.1507 + GetMetricsFor(font, 1.1508 + mStyleContext->StyleFont()->mLanguage, 1.1509 + aPresContext->GetUserFontSet(), 1.1510 + aPresContext->GetTextPerfMetrics(), 1.1511 + *getter_AddRefs(fm)); 1.1512 + uint32_t len = uint32_t(mData.Length()); 1.1513 + nsAutoPtr<gfxTextRun> textRun; 1.1514 + textRun = fm->GetThebesFontGroup()-> 1.1515 + MakeTextRun(static_cast<const char16_t*>(mData.get()), len, aThebesContext, 1.1516 + aPresContext->AppUnitsPerDevPixel(), 0); 1.1517 + aDesiredStretchSize = MeasureTextRun(aThebesContext, textRun); 1.1518 + mGlyphs[0] = textRun; 1.1519 + 1.1520 + bool maxWidth = (NS_STRETCH_MAXWIDTH & aStretchHint) != 0; 1.1521 + if (!maxWidth) { 1.1522 + mUnscaledAscent = aDesiredStretchSize.ascent; 1.1523 + } 1.1524 + 1.1525 + ////////////////////////////////////////////////////////////////////////////// 1.1526 + // 1. Check the common situations where stretching is not actually needed 1.1527 + ////////////////////////////////////////////////////////////////////////////// 1.1528 + 1.1529 + // quick return if there is nothing special about this char 1.1530 + if ((aStretchDirection != direction && 1.1531 + aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT) || 1.1532 + (aStretchHint & ~NS_STRETCH_MAXWIDTH) == NS_STRETCH_NONE) { 1.1533 + mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED; 1.1534 + return NS_OK; 1.1535 + } 1.1536 + 1.1537 + // if no specified direction, attempt to stretch in our preferred direction 1.1538 + if (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT) { 1.1539 + aStretchDirection = direction; 1.1540 + } 1.1541 + 1.1542 + // see if this is a particular largeop or largeopOnly request 1.1543 + bool largeop = (NS_STRETCH_LARGEOP & aStretchHint) != 0; 1.1544 + bool stretchy = (NS_STRETCH_VARIABLE_MASK & aStretchHint) != 0; 1.1545 + bool largeopOnly = largeop && !stretchy; 1.1546 + 1.1547 + bool isVertical = (direction == NS_STRETCH_DIRECTION_VERTICAL); 1.1548 + 1.1549 + nscoord targetSize = 1.1550 + isVertical ? aContainerSize.ascent + aContainerSize.descent 1.1551 + : aContainerSize.rightBearing - aContainerSize.leftBearing; 1.1552 + 1.1553 + if (maxWidth) { 1.1554 + // See if it is only necessary to consider glyphs up to some maximum size. 1.1555 + // Set the current height to the maximum size, and set aStretchHint to 1.1556 + // NS_STRETCH_SMALLER if the size is variable, so that only smaller sizes 1.1557 + // are considered. targetSize from GetMaxWidth() is 0. 1.1558 + if (stretchy) { 1.1559 + // variable size stretch - consider all sizes < maxsize 1.1560 + aStretchHint = 1.1561 + (aStretchHint & ~NS_STRETCH_VARIABLE_MASK) | NS_STRETCH_SMALLER; 1.1562 + } 1.1563 + 1.1564 + // Use NS_MATHML_DELIMITER_FACTOR to allow some slightly larger glyphs as 1.1565 + // maxsize is not enforced exactly. 1.1566 + if (aMaxSize == NS_MATHML_OPERATOR_SIZE_INFINITY) { 1.1567 + aDesiredStretchSize.ascent = nscoord_MAX; 1.1568 + aDesiredStretchSize.descent = 0; 1.1569 + } 1.1570 + else { 1.1571 + nscoord height = aDesiredStretchSize.ascent + aDesiredStretchSize.descent; 1.1572 + if (height == 0) { 1.1573 + if (aMaxSizeIsAbsolute) { 1.1574 + aDesiredStretchSize.ascent = 1.1575 + NSToCoordRound(aMaxSize / NS_MATHML_DELIMITER_FACTOR); 1.1576 + aDesiredStretchSize.descent = 0; 1.1577 + } 1.1578 + // else: leave height as 0 1.1579 + } 1.1580 + else { 1.1581 + float scale = aMaxSizeIsAbsolute ? aMaxSize / height : aMaxSize; 1.1582 + scale /= NS_MATHML_DELIMITER_FACTOR; 1.1583 + aDesiredStretchSize.ascent = 1.1584 + NSToCoordRound(scale * aDesiredStretchSize.ascent); 1.1585 + aDesiredStretchSize.descent = 1.1586 + NSToCoordRound(scale * aDesiredStretchSize.descent); 1.1587 + } 1.1588 + } 1.1589 + } 1.1590 + 1.1591 + nsBoundingMetrics initialSize = aDesiredStretchSize; 1.1592 + nscoord charSize = 1.1593 + isVertical ? initialSize.ascent + initialSize.descent 1.1594 + : initialSize.rightBearing - initialSize.leftBearing; 1.1595 + 1.1596 + bool done = false; 1.1597 + 1.1598 + if (!maxWidth && !largeop) { 1.1599 + // Doing Stretch() not GetMaxWidth(), 1.1600 + // and not a largeop in display mode; we're done if size fits 1.1601 + if ((targetSize <= 0) || 1.1602 + ((isVertical && charSize >= targetSize) || 1.1603 + IsSizeOK(aPresContext, charSize, targetSize, aStretchHint))) 1.1604 + done = true; 1.1605 + } 1.1606 + 1.1607 + ////////////////////////////////////////////////////////////////////////////// 1.1608 + // 2/3. Search for a glyph or set of part glyphs of appropriate size 1.1609 + ////////////////////////////////////////////////////////////////////////////// 1.1610 + 1.1611 + bool glyphFound = false; 1.1612 + 1.1613 + if (!done) { // normal case 1.1614 + // Use the css font-family but add preferred fallback fonts. 1.1615 + font = mStyleContext->StyleFont()->mFont; 1.1616 + NS_NAMED_LITERAL_CSTRING(defaultKey, "font.mathfont-family"); 1.1617 + nsAdoptingString fallbackFonts = Preferences::GetString(defaultKey.get()); 1.1618 + if (!fallbackFonts.IsEmpty()) { 1.1619 + AddFallbackFonts(font.name, fallbackFonts); 1.1620 + } 1.1621 + 1.1622 +#ifdef NOISY_SEARCH 1.1623 + printf("Searching in "%s" for a glyph of appropriate size for: 0x%04X:%c\n", 1.1624 + font.name, mData[0], mData[0]&0x00FF); 1.1625 +#endif 1.1626 + StretchEnumContext enumData(this, aPresContext, aThebesContext, 1.1627 + aStretchDirection, targetSize, aStretchHint, 1.1628 + aDesiredStretchSize, font.name, glyphFound); 1.1629 + enumData.mTryParts = !largeopOnly; 1.1630 + 1.1631 + font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData); 1.1632 + } 1.1633 + 1.1634 + if (!maxWidth) { 1.1635 + // Now, we know how we are going to draw the char. Update the member 1.1636 + // variables accordingly. 1.1637 + mUnscaledAscent = aDesiredStretchSize.ascent; 1.1638 + } 1.1639 + 1.1640 + if (glyphFound) { 1.1641 + return NS_OK; 1.1642 + } 1.1643 + 1.1644 + // stretchy character 1.1645 + if (stretchy) { 1.1646 + if (isVertical) { 1.1647 + float scale = 1.1648 + float(aContainerSize.ascent + aContainerSize.descent) / 1.1649 + (aDesiredStretchSize.ascent + aDesiredStretchSize.descent); 1.1650 + if (!largeop || scale > 1.0) { 1.1651 + // make the character match the desired height. 1.1652 + if (!maxWidth) { 1.1653 + mScaleY *= scale; 1.1654 + } 1.1655 + aDesiredStretchSize.ascent *= scale; 1.1656 + aDesiredStretchSize.descent *= scale; 1.1657 + } 1.1658 + } else { 1.1659 + float scale = 1.1660 + float(aContainerSize.rightBearing - aContainerSize.leftBearing) / 1.1661 + (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing); 1.1662 + if (!largeop || scale > 1.0) { 1.1663 + // make the character match the desired width. 1.1664 + if (!maxWidth) { 1.1665 + mScaleX *= scale; 1.1666 + } 1.1667 + aDesiredStretchSize.leftBearing *= scale; 1.1668 + aDesiredStretchSize.rightBearing *= scale; 1.1669 + aDesiredStretchSize.width *= scale; 1.1670 + } 1.1671 + } 1.1672 + } 1.1673 + 1.1674 + // We do not have a char variant for this largeop in display mode, so we 1.1675 + // apply a scale transform to the base char. 1.1676 + if (largeop) { 1.1677 + float scale; 1.1678 + float largeopFactor = kLargeOpFactor; 1.1679 + 1.1680 + // increase the width if it is not largeopFactor times larger 1.1681 + // than the initial one. 1.1682 + if ((aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing) < 1.1683 + largeopFactor * (initialSize.rightBearing - initialSize.leftBearing)) { 1.1684 + scale = (largeopFactor * 1.1685 + (initialSize.rightBearing - initialSize.leftBearing)) / 1.1686 + (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing); 1.1687 + if (!maxWidth) { 1.1688 + mScaleX *= scale; 1.1689 + } 1.1690 + aDesiredStretchSize.leftBearing *= scale; 1.1691 + aDesiredStretchSize.rightBearing *= scale; 1.1692 + aDesiredStretchSize.width *= scale; 1.1693 + } 1.1694 + 1.1695 + // increase the height if it is not largeopFactor times larger 1.1696 + // than the initial one. 1.1697 + if (NS_STRETCH_INTEGRAL & aStretchHint) { 1.1698 + // integrals are drawn taller 1.1699 + largeopFactor = kIntegralFactor; 1.1700 + } 1.1701 + if ((aDesiredStretchSize.ascent + aDesiredStretchSize.descent) < 1.1702 + largeopFactor * (initialSize.ascent + initialSize.descent)) { 1.1703 + scale = (largeopFactor * 1.1704 + (initialSize.ascent + initialSize.descent)) / 1.1705 + (aDesiredStretchSize.ascent + aDesiredStretchSize.descent); 1.1706 + if (!maxWidth) { 1.1707 + mScaleY *= scale; 1.1708 + } 1.1709 + aDesiredStretchSize.ascent *= scale; 1.1710 + aDesiredStretchSize.descent *= scale; 1.1711 + } 1.1712 + } 1.1713 + 1.1714 + return NS_OK; 1.1715 +} 1.1716 + 1.1717 +nsresult 1.1718 +nsMathMLChar::Stretch(nsPresContext* aPresContext, 1.1719 + nsRenderingContext& aRenderingContext, 1.1720 + nsStretchDirection aStretchDirection, 1.1721 + const nsBoundingMetrics& aContainerSize, 1.1722 + nsBoundingMetrics& aDesiredStretchSize, 1.1723 + uint32_t aStretchHint, 1.1724 + bool aRTL) 1.1725 +{ 1.1726 + NS_ASSERTION(!(aStretchHint & 1.1727 + ~(NS_STRETCH_VARIABLE_MASK | NS_STRETCH_LARGEOP | 1.1728 + NS_STRETCH_INTEGRAL)), 1.1729 + "Unexpected stretch flags"); 1.1730 + 1.1731 + mDraw = DRAW_NORMAL; 1.1732 + mMirrored = aRTL && nsMathMLOperators::IsMirrorableOperator(mData); 1.1733 + mScaleY = mScaleX = 1.0; 1.1734 + mDirection = aStretchDirection; 1.1735 + nsresult rv = 1.1736 + StretchInternal(aPresContext, aRenderingContext.ThebesContext(), mDirection, 1.1737 + aContainerSize, aDesiredStretchSize, aStretchHint); 1.1738 + 1.1739 + // Record the metrics 1.1740 + mBoundingMetrics = aDesiredStretchSize; 1.1741 + 1.1742 + return rv; 1.1743 +} 1.1744 + 1.1745 +// What happens here is that the StretchInternal algorithm is used but 1.1746 +// modified by passing the NS_STRETCH_MAXWIDTH stretch hint. That causes 1.1747 +// StretchInternal to return horizontal bounding metrics that are the maximum 1.1748 +// that might be returned from a Stretch. 1.1749 +// 1.1750 +// In order to avoid considering widths of some characters in fonts that will 1.1751 +// not be used for any stretch size, StretchInternal sets the initial height 1.1752 +// to infinity and looks for any characters smaller than this height. When a 1.1753 +// character built from parts is considered, (it will be used by Stretch for 1.1754 +// any characters greater than its minimum size, so) the height is set to its 1.1755 +// minimum size, so that only widths of smaller subsequent characters are 1.1756 +// considered. 1.1757 +nscoord 1.1758 +nsMathMLChar::GetMaxWidth(nsPresContext* aPresContext, 1.1759 + nsRenderingContext& aRenderingContext, 1.1760 + uint32_t aStretchHint, 1.1761 + float aMaxSize, bool aMaxSizeIsAbsolute) 1.1762 +{ 1.1763 + nsBoundingMetrics bm; 1.1764 + nsStretchDirection direction = NS_STRETCH_DIRECTION_VERTICAL; 1.1765 + const nsBoundingMetrics container; // zero target size 1.1766 + 1.1767 + StretchInternal(aPresContext, aRenderingContext.ThebesContext(), direction, 1.1768 + container, bm, aStretchHint | NS_STRETCH_MAXWIDTH); 1.1769 + 1.1770 + return std::max(bm.width, bm.rightBearing) - std::min(0, bm.leftBearing); 1.1771 +} 1.1772 + 1.1773 +class nsDisplayMathMLSelectionRect : public nsDisplayItem { 1.1774 +public: 1.1775 + nsDisplayMathMLSelectionRect(nsDisplayListBuilder* aBuilder, 1.1776 + nsIFrame* aFrame, const nsRect& aRect) 1.1777 + : nsDisplayItem(aBuilder, aFrame), mRect(aRect) { 1.1778 + MOZ_COUNT_CTOR(nsDisplayMathMLSelectionRect); 1.1779 + } 1.1780 +#ifdef NS_BUILD_REFCNT_LOGGING 1.1781 + virtual ~nsDisplayMathMLSelectionRect() { 1.1782 + MOZ_COUNT_DTOR(nsDisplayMathMLSelectionRect); 1.1783 + } 1.1784 +#endif 1.1785 + 1.1786 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.1787 + nsRenderingContext* aCtx); 1.1788 + NS_DISPLAY_DECL_NAME("MathMLSelectionRect", TYPE_MATHML_SELECTION_RECT) 1.1789 +private: 1.1790 + nsRect mRect; 1.1791 +}; 1.1792 + 1.1793 +void nsDisplayMathMLSelectionRect::Paint(nsDisplayListBuilder* aBuilder, 1.1794 + nsRenderingContext* aCtx) 1.1795 +{ 1.1796 + // get color to use for selection from the look&feel object 1.1797 + nscolor bgColor = 1.1798 + LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground, 1.1799 + NS_RGB(0, 0, 0)); 1.1800 + aCtx->SetColor(bgColor); 1.1801 + aCtx->FillRect(mRect + ToReferenceFrame()); 1.1802 +} 1.1803 + 1.1804 +class nsDisplayMathMLCharBackground : public nsDisplayItem { 1.1805 +public: 1.1806 + nsDisplayMathMLCharBackground(nsDisplayListBuilder* aBuilder, 1.1807 + nsIFrame* aFrame, const nsRect& aRect, 1.1808 + nsStyleContext* aStyleContext) 1.1809 + : nsDisplayItem(aBuilder, aFrame), mStyleContext(aStyleContext), 1.1810 + mRect(aRect) { 1.1811 + MOZ_COUNT_CTOR(nsDisplayMathMLCharBackground); 1.1812 + } 1.1813 +#ifdef NS_BUILD_REFCNT_LOGGING 1.1814 + virtual ~nsDisplayMathMLCharBackground() { 1.1815 + MOZ_COUNT_DTOR(nsDisplayMathMLCharBackground); 1.1816 + } 1.1817 +#endif 1.1818 + 1.1819 + virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, 1.1820 + const nsDisplayItemGeometry* aGeometry, 1.1821 + nsRegion *aInvalidRegion) MOZ_OVERRIDE; 1.1822 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.1823 + nsRenderingContext* aCtx); 1.1824 + NS_DISPLAY_DECL_NAME("MathMLCharBackground", TYPE_MATHML_CHAR_BACKGROUND) 1.1825 +private: 1.1826 + nsStyleContext* mStyleContext; 1.1827 + nsRect mRect; 1.1828 +}; 1.1829 + 1.1830 +void 1.1831 +nsDisplayMathMLCharBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, 1.1832 + const nsDisplayItemGeometry* aGeometry, 1.1833 + nsRegion *aInvalidRegion) 1.1834 +{ 1.1835 + AddInvalidRegionForSyncDecodeBackgroundImages(aBuilder, aGeometry, aInvalidRegion); 1.1836 + 1.1837 + nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); 1.1838 +} 1.1839 + 1.1840 +void nsDisplayMathMLCharBackground::Paint(nsDisplayListBuilder* aBuilder, 1.1841 + nsRenderingContext* aCtx) 1.1842 +{ 1.1843 + const nsStyleBorder* border = mStyleContext->StyleBorder(); 1.1844 + nsRect rect(mRect + ToReferenceFrame()); 1.1845 + nsCSSRendering::PaintBackgroundWithSC(mFrame->PresContext(), *aCtx, mFrame, 1.1846 + mVisibleRect, rect, 1.1847 + mStyleContext, *border, 1.1848 + aBuilder->GetBackgroundPaintFlags()); 1.1849 +} 1.1850 + 1.1851 +class nsDisplayMathMLCharForeground : public nsDisplayItem { 1.1852 +public: 1.1853 + nsDisplayMathMLCharForeground(nsDisplayListBuilder* aBuilder, 1.1854 + nsIFrame* aFrame, nsMathMLChar* aChar, 1.1855 + uint32_t aIndex, bool aIsSelected) 1.1856 + : nsDisplayItem(aBuilder, aFrame), mChar(aChar), 1.1857 + mIndex(aIndex), mIsSelected(aIsSelected) { 1.1858 + MOZ_COUNT_CTOR(nsDisplayMathMLCharForeground); 1.1859 + } 1.1860 +#ifdef NS_BUILD_REFCNT_LOGGING 1.1861 + virtual ~nsDisplayMathMLCharForeground() { 1.1862 + MOZ_COUNT_DTOR(nsDisplayMathMLCharForeground); 1.1863 + } 1.1864 +#endif 1.1865 + 1.1866 + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { 1.1867 + *aSnap = false; 1.1868 + nsRect rect; 1.1869 + mChar->GetRect(rect); 1.1870 + nsPoint offset = ToReferenceFrame() + rect.TopLeft(); 1.1871 + nsBoundingMetrics bm; 1.1872 + mChar->GetBoundingMetrics(bm); 1.1873 + nsRect temp(offset.x + bm.leftBearing, offset.y, 1.1874 + bm.rightBearing - bm.leftBearing, bm.ascent + bm.descent); 1.1875 + // Bug 748220 1.1876 + temp.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel()); 1.1877 + return temp; 1.1878 + } 1.1879 + 1.1880 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.1881 + nsRenderingContext* aCtx) 1.1882 + { 1.1883 + mChar->PaintForeground(mFrame->PresContext(), *aCtx, 1.1884 + ToReferenceFrame(), mIsSelected); 1.1885 + } 1.1886 + 1.1887 + NS_DISPLAY_DECL_NAME("MathMLCharForeground", TYPE_MATHML_CHAR_FOREGROUND) 1.1888 + 1.1889 + virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) 1.1890 + { 1.1891 + bool snap; 1.1892 + return GetBounds(aBuilder, &snap); 1.1893 + } 1.1894 + 1.1895 + virtual uint32_t GetPerFrameKey() { 1.1896 + return (mIndex << nsDisplayItem::TYPE_BITS) 1.1897 + | nsDisplayItem::GetPerFrameKey(); 1.1898 + } 1.1899 + 1.1900 +private: 1.1901 + nsMathMLChar* mChar; 1.1902 + uint32_t mIndex; 1.1903 + bool mIsSelected; 1.1904 +}; 1.1905 + 1.1906 +#ifdef DEBUG 1.1907 +class nsDisplayMathMLCharDebug : public nsDisplayItem { 1.1908 +public: 1.1909 + nsDisplayMathMLCharDebug(nsDisplayListBuilder* aBuilder, 1.1910 + nsIFrame* aFrame, const nsRect& aRect) 1.1911 + : nsDisplayItem(aBuilder, aFrame), mRect(aRect) { 1.1912 + MOZ_COUNT_CTOR(nsDisplayMathMLCharDebug); 1.1913 + } 1.1914 +#ifdef NS_BUILD_REFCNT_LOGGING 1.1915 + virtual ~nsDisplayMathMLCharDebug() { 1.1916 + MOZ_COUNT_DTOR(nsDisplayMathMLCharDebug); 1.1917 + } 1.1918 +#endif 1.1919 + 1.1920 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.1921 + nsRenderingContext* aCtx); 1.1922 + NS_DISPLAY_DECL_NAME("MathMLCharDebug", TYPE_MATHML_CHAR_DEBUG) 1.1923 + 1.1924 +private: 1.1925 + nsRect mRect; 1.1926 +}; 1.1927 + 1.1928 +void nsDisplayMathMLCharDebug::Paint(nsDisplayListBuilder* aBuilder, 1.1929 + nsRenderingContext* aCtx) 1.1930 +{ 1.1931 + // for visual debug 1.1932 + int skipSides = 0; 1.1933 + nsPresContext* presContext = mFrame->PresContext(); 1.1934 + nsStyleContext* styleContext = mFrame->StyleContext(); 1.1935 + nsRect rect = mRect + ToReferenceFrame(); 1.1936 + nsCSSRendering::PaintBorder(presContext, *aCtx, mFrame, 1.1937 + mVisibleRect, rect, styleContext, skipSides); 1.1938 + nsCSSRendering::PaintOutline(presContext, *aCtx, mFrame, 1.1939 + mVisibleRect, rect, styleContext); 1.1940 +} 1.1941 +#endif 1.1942 + 1.1943 + 1.1944 +void 1.1945 +nsMathMLChar::Display(nsDisplayListBuilder* aBuilder, 1.1946 + nsIFrame* aForFrame, 1.1947 + const nsDisplayListSet& aLists, 1.1948 + uint32_t aIndex, 1.1949 + const nsRect* aSelectedRect) 1.1950 +{ 1.1951 + nsStyleContext* parentContext = mStyleContext->GetParent(); 1.1952 + nsStyleContext* styleContext = mStyleContext; 1.1953 + 1.1954 + if (mDraw == DRAW_NORMAL) { 1.1955 + // normal drawing if there is nothing special about this char 1.1956 + // Set default context to the parent context 1.1957 + styleContext = parentContext; 1.1958 + } 1.1959 + 1.1960 + if (!styleContext->StyleVisibility()->IsVisible()) 1.1961 + return; 1.1962 + 1.1963 + // if the leaf style context that we use for stretchy chars has a background 1.1964 + // color we use it -- this feature is mostly used for testing and debugging 1.1965 + // purposes. Normally, users will set the background on the container frame. 1.1966 + // paint the selection background -- beware MathML frames overlap a lot 1.1967 + if (aSelectedRect && !aSelectedRect->IsEmpty()) { 1.1968 + aLists.BorderBackground()->AppendNewToTop(new (aBuilder) 1.1969 + nsDisplayMathMLSelectionRect(aBuilder, aForFrame, *aSelectedRect)); 1.1970 + } 1.1971 + else if (mRect.width && mRect.height) { 1.1972 + const nsStyleBackground* backg = styleContext->StyleBackground(); 1.1973 + if (styleContext != parentContext && 1.1974 + NS_GET_A(backg->mBackgroundColor) > 0) { 1.1975 + aLists.BorderBackground()->AppendNewToTop(new (aBuilder) 1.1976 + nsDisplayMathMLCharBackground(aBuilder, aForFrame, mRect, 1.1977 + styleContext)); 1.1978 + } 1.1979 + //else 1.1980 + // our container frame will take care of painting its background 1.1981 + 1.1982 +#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX) 1.1983 + // for visual debug 1.1984 + aLists.BorderBackground()->AppendToTop(new (aBuilder) 1.1985 + nsDisplayMathMLCharDebug(aBuilder, aForFrame, mRect)); 1.1986 +#endif 1.1987 + } 1.1988 + aLists.Content()->AppendNewToTop(new (aBuilder) 1.1989 + nsDisplayMathMLCharForeground(aBuilder, aForFrame, this, 1.1990 + aIndex, 1.1991 + aSelectedRect && 1.1992 + !aSelectedRect->IsEmpty())); 1.1993 +} 1.1994 + 1.1995 +void 1.1996 +nsMathMLChar::ApplyTransforms(gfxContext* aThebesContext, 1.1997 + int32_t aAppUnitsPerGfxUnit, 1.1998 + nsRect &r) 1.1999 +{ 1.2000 + // apply the transforms 1.2001 + if (mMirrored) { 1.2002 + nsPoint pt = r.TopRight(); 1.2003 + aThebesContext-> 1.2004 + Translate(gfxPoint(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit), 1.2005 + NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit))); 1.2006 + aThebesContext->Scale(-mScaleX, mScaleY); 1.2007 + } else { 1.2008 + nsPoint pt = r.TopLeft(); 1.2009 + aThebesContext-> 1.2010 + Translate(gfxPoint(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit), 1.2011 + NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit))); 1.2012 + aThebesContext->Scale(mScaleX, mScaleY); 1.2013 + } 1.2014 + 1.2015 + // update the bounding rectangle. 1.2016 + r.x = r.y = 0; 1.2017 + r.width /= mScaleX; 1.2018 + r.height /= mScaleY; 1.2019 +} 1.2020 + 1.2021 +void 1.2022 +nsMathMLChar::PaintForeground(nsPresContext* aPresContext, 1.2023 + nsRenderingContext& aRenderingContext, 1.2024 + nsPoint aPt, 1.2025 + bool aIsSelected) 1.2026 +{ 1.2027 + nsStyleContext* parentContext = mStyleContext->GetParent(); 1.2028 + nsStyleContext* styleContext = mStyleContext; 1.2029 + 1.2030 + if (mDraw == DRAW_NORMAL) { 1.2031 + // normal drawing if there is nothing special about this char 1.2032 + // Set default context to the parent context 1.2033 + styleContext = parentContext; 1.2034 + } 1.2035 + 1.2036 + nsRefPtr<gfxContext> thebesContext = aRenderingContext.ThebesContext(); 1.2037 + 1.2038 + // Set color ... 1.2039 + nscolor fgColor = styleContext->GetVisitedDependentColor(eCSSProperty_color); 1.2040 + if (aIsSelected) { 1.2041 + // get color to use for selection from the look&feel object 1.2042 + fgColor = LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground, 1.2043 + fgColor); 1.2044 + } 1.2045 + thebesContext->SetColor(fgColor); 1.2046 + thebesContext->Save(); 1.2047 + nsRect r = mRect + aPt; 1.2048 + ApplyTransforms(thebesContext, aPresContext->AppUnitsPerDevPixel(), r); 1.2049 + 1.2050 + switch(mDraw) 1.2051 + { 1.2052 + case DRAW_NORMAL: 1.2053 + case DRAW_VARIANT: 1.2054 + // draw a single glyph (base size or size variant) 1.2055 + // XXXfredw verify if mGlyphs[0] is non-null to workaround bug 973322. 1.2056 + if (mGlyphs[0]) { 1.2057 + mGlyphs[0]->Draw(thebesContext, gfxPoint(0.0, mUnscaledAscent), 1.2058 + DrawMode::GLYPH_FILL, 0, mGlyphs[0]->GetLength(), 1.2059 + nullptr, nullptr, nullptr); 1.2060 + } 1.2061 + break; 1.2062 + case DRAW_PARTS: { 1.2063 + // paint by parts 1.2064 + if (NS_STRETCH_DIRECTION_VERTICAL == mDirection) 1.2065 + PaintVertically(aPresContext, thebesContext, r); 1.2066 + else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection) 1.2067 + PaintHorizontally(aPresContext, thebesContext, r); 1.2068 + break; 1.2069 + } 1.2070 + default: 1.2071 + NS_NOTREACHED("Unknown drawing method"); 1.2072 + break; 1.2073 + } 1.2074 + 1.2075 + thebesContext->Restore(); 1.2076 +} 1.2077 + 1.2078 +/* ============================================================================= 1.2079 + Helper routines that actually do the job of painting the char by parts 1.2080 + */ 1.2081 + 1.2082 +class AutoPushClipRect { 1.2083 + gfxContext* mThebesContext; 1.2084 +public: 1.2085 + AutoPushClipRect(gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit, 1.2086 + const nsRect& aRect) 1.2087 + : mThebesContext(aThebesContext) { 1.2088 + mThebesContext->Save(); 1.2089 + mThebesContext->NewPath(); 1.2090 + gfxRect clip = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerGfxUnit); 1.2091 + mThebesContext->SnappedRectangle(clip); 1.2092 + mThebesContext->Clip(); 1.2093 + } 1.2094 + ~AutoPushClipRect() { 1.2095 + mThebesContext->Restore(); 1.2096 + } 1.2097 +}; 1.2098 + 1.2099 +static nsPoint 1.2100 +SnapToDevPixels(const gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit, 1.2101 + const nsPoint& aPt) 1.2102 +{ 1.2103 + gfxPoint pt(NSAppUnitsToFloatPixels(aPt.x, aAppUnitsPerGfxUnit), 1.2104 + NSAppUnitsToFloatPixels(aPt.y, aAppUnitsPerGfxUnit)); 1.2105 + pt = aThebesContext->UserToDevice(pt); 1.2106 + pt.Round(); 1.2107 + pt = aThebesContext->DeviceToUser(pt); 1.2108 + return nsPoint(NSFloatPixelsToAppUnits(pt.x, aAppUnitsPerGfxUnit), 1.2109 + NSFloatPixelsToAppUnits(pt.y, aAppUnitsPerGfxUnit)); 1.2110 +} 1.2111 + 1.2112 +static void 1.2113 +PaintRule(gfxContext* aThebesContext, 1.2114 + int32_t aAppUnitsPerGfxUnit, 1.2115 + nsRect& aRect) 1.2116 +{ 1.2117 + aThebesContext->NewPath(); 1.2118 + gfxRect rect = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerGfxUnit); 1.2119 + aThebesContext->SnappedRectangle(rect); 1.2120 + aThebesContext->Fill(); 1.2121 +} 1.2122 + 1.2123 +// paint a stretchy char by assembling glyphs vertically 1.2124 +nsresult 1.2125 +nsMathMLChar::PaintVertically(nsPresContext* aPresContext, 1.2126 + gfxContext* aThebesContext, 1.2127 + nsRect& aRect) 1.2128 +{ 1.2129 + // Get the device pixel size in the vertical direction. 1.2130 + // (This makes no effort to optimize for non-translation transformations.) 1.2131 + nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel(); 1.2132 + 1.2133 + // get metrics data to be re-used later 1.2134 + int32_t i = 0; 1.2135 + nscoord dx = aRect.x; 1.2136 + nscoord offset[3], start[3], end[3]; 1.2137 + for (i = 0; i <= 2; ++i) { 1.2138 + const nsBoundingMetrics& bm = mBmData[i]; 1.2139 + nscoord dy; 1.2140 + if (0 == i) { // top 1.2141 + dy = aRect.y + bm.ascent; 1.2142 + } 1.2143 + else if (2 == i) { // bottom 1.2144 + dy = aRect.y + aRect.height - bm.descent; 1.2145 + } 1.2146 + else { // middle 1.2147 + dy = aRect.y + bm.ascent + (aRect.height - (bm.ascent + bm.descent))/2; 1.2148 + } 1.2149 + // _cairo_scaled_font_show_glyphs snaps origins to device pixels. 1.2150 + // Do this now so that we can get the other dimensions right. 1.2151 + // (This may not achieve much with non-rectangular transformations.) 1.2152 + dy = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).y; 1.2153 + // abcissa passed to Draw 1.2154 + offset[i] = dy; 1.2155 + // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest 1.2156 + // pixel, so the bm values can include 1 row of faint pixels on each edge. 1.2157 + // Don't rely on this pixel as it can look like a gap. 1.2158 + if (bm.ascent + bm.descent >= 2 * oneDevPixel) { 1.2159 + start[i] = dy - bm.ascent + oneDevPixel; // top join 1.2160 + end[i] = dy + bm.descent - oneDevPixel; // bottom join 1.2161 + } else { 1.2162 + // To avoid overlaps, we don't add one pixel on each side when the part 1.2163 + // is too small. 1.2164 + start[i] = dy - bm.ascent; // top join 1.2165 + end[i] = dy + bm.descent; // bottom join 1.2166 + } 1.2167 + } 1.2168 + 1.2169 + // If there are overlaps, then join at the mid point 1.2170 + for (i = 0; i < 2; ++i) { 1.2171 + if (end[i] > start[i+1]) { 1.2172 + end[i] = (end[i] + start[i+1]) / 2; 1.2173 + start[i+1] = end[i]; 1.2174 + } 1.2175 + } 1.2176 + 1.2177 + nsRect unionRect = aRect; 1.2178 + unionRect.x += mBoundingMetrics.leftBearing; 1.2179 + unionRect.width = 1.2180 + mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing; 1.2181 + unionRect.Inflate(oneDevPixel, oneDevPixel); 1.2182 + 1.2183 + ///////////////////////////////////// 1.2184 + // draw top, middle, bottom 1.2185 + for (i = 0; i <= 2; ++i) { 1.2186 + // glue can be null 1.2187 + if (mGlyphs[i]) { 1.2188 + nscoord dy = offset[i]; 1.2189 + // Draw a glyph in a clipped area so that we don't have hairy chars 1.2190 + // pending outside 1.2191 + nsRect clipRect = unionRect; 1.2192 + // Clip at the join to get a solid edge (without overlap or gap), when 1.2193 + // this won't change the glyph too much. If the glyph is too small to 1.2194 + // clip then we'll overlap rather than have a gap. 1.2195 + nscoord height = mBmData[i].ascent + mBmData[i].descent; 1.2196 + if (height * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) { 1.2197 + if (0 == i) { // top 1.2198 + clipRect.height = end[i] - clipRect.y; 1.2199 + } 1.2200 + else if (2 == i) { // bottom 1.2201 + clipRect.height -= start[i] - clipRect.y; 1.2202 + clipRect.y = start[i]; 1.2203 + } 1.2204 + else { // middle 1.2205 + clipRect.y = start[i]; 1.2206 + clipRect.height = end[i] - start[i]; 1.2207 + } 1.2208 + } 1.2209 + if (!clipRect.IsEmpty()) { 1.2210 + AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect); 1.2211 + mGlyphs[i]->Draw(aThebesContext, gfxPoint(dx, dy), 1.2212 + DrawMode::GLYPH_FILL, 0, mGlyphs[i]->GetLength(), 1.2213 + nullptr, nullptr, nullptr); 1.2214 + } 1.2215 + } 1.2216 + } 1.2217 + 1.2218 + /////////////// 1.2219 + // fill the gap between top and middle, and between middle and bottom. 1.2220 + if (!mGlyphs[3]) { // null glue : draw a rule 1.2221 + // figure out the dimensions of the rule to be drawn : 1.2222 + // set lbearing to rightmost lbearing among the two current successive 1.2223 + // parts. 1.2224 + // set rbearing to leftmost rbearing among the two current successive parts. 1.2225 + // this not only satisfies the convention used for over/underbraces 1.2226 + // in TeX, but also takes care of broken fonts like the stretchy integral 1.2227 + // in Symbol for small font sizes in unix. 1.2228 + nscoord lbearing, rbearing; 1.2229 + int32_t first = 0, last = 1; 1.2230 + while (last <= 2) { 1.2231 + if (mGlyphs[last]) { 1.2232 + lbearing = mBmData[last].leftBearing; 1.2233 + rbearing = mBmData[last].rightBearing; 1.2234 + if (mGlyphs[first]) { 1.2235 + if (lbearing < mBmData[first].leftBearing) 1.2236 + lbearing = mBmData[first].leftBearing; 1.2237 + if (rbearing > mBmData[first].rightBearing) 1.2238 + rbearing = mBmData[first].rightBearing; 1.2239 + } 1.2240 + } 1.2241 + else if (mGlyphs[first]) { 1.2242 + lbearing = mBmData[first].leftBearing; 1.2243 + rbearing = mBmData[first].rightBearing; 1.2244 + } 1.2245 + else { 1.2246 + NS_ERROR("Cannot stretch - All parts missing"); 1.2247 + return NS_ERROR_UNEXPECTED; 1.2248 + } 1.2249 + // paint the rule between the parts 1.2250 + nsRect rule(aRect.x + lbearing, end[first], 1.2251 + rbearing - lbearing, start[last] - end[first]); 1.2252 + PaintRule(aThebesContext, oneDevPixel, rule); 1.2253 + first = last; 1.2254 + last++; 1.2255 + } 1.2256 + } 1.2257 + else if (mBmData[3].ascent + mBmData[3].descent > 0) { 1.2258 + // glue is present 1.2259 + nsBoundingMetrics& bm = mBmData[3]; 1.2260 + // Ensure the stride for the glue is not reduced to less than one pixel 1.2261 + if (bm.ascent + bm.descent >= 3 * oneDevPixel) { 1.2262 + // To protect against gaps, pretend the glue is smaller than it is, 1.2263 + // in order to trim off ends and thus get a solid edge for the join. 1.2264 + bm.ascent -= oneDevPixel; 1.2265 + bm.descent -= oneDevPixel; 1.2266 + } 1.2267 + 1.2268 + nsRect clipRect = unionRect; 1.2269 + 1.2270 + for (i = 0; i < 2; ++i) { 1.2271 + // Make sure not to draw outside the character 1.2272 + nscoord dy = std::max(end[i], aRect.y); 1.2273 + nscoord fillEnd = std::min(start[i+1], aRect.YMost()); 1.2274 + while (dy < fillEnd) { 1.2275 + clipRect.y = dy; 1.2276 + clipRect.height = std::min(bm.ascent + bm.descent, fillEnd - dy); 1.2277 + AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect); 1.2278 + dy += bm.ascent; 1.2279 + mGlyphs[3]->Draw(aThebesContext, gfxPoint(dx, dy), 1.2280 + DrawMode::GLYPH_FILL, 0, mGlyphs[3]->GetLength(), 1.2281 + nullptr, nullptr, nullptr); 1.2282 + dy += bm.descent; 1.2283 + } 1.2284 + } 1.2285 + } 1.2286 +#ifdef DEBUG 1.2287 + else { 1.2288 + for (i = 0; i < 2; ++i) { 1.2289 + NS_ASSERTION(end[i] >= start[i+1], 1.2290 + "gap between parts with missing glue glyph"); 1.2291 + } 1.2292 + } 1.2293 +#endif 1.2294 + return NS_OK; 1.2295 +} 1.2296 + 1.2297 +// paint a stretchy char by assembling glyphs horizontally 1.2298 +nsresult 1.2299 +nsMathMLChar::PaintHorizontally(nsPresContext* aPresContext, 1.2300 + gfxContext* aThebesContext, 1.2301 + nsRect& aRect) 1.2302 +{ 1.2303 + // Get the device pixel size in the horizontal direction. 1.2304 + // (This makes no effort to optimize for non-translation transformations.) 1.2305 + nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel(); 1.2306 + 1.2307 + // get metrics data to be re-used later 1.2308 + int32_t i = 0; 1.2309 + nscoord dy = aRect.y + mBoundingMetrics.ascent; 1.2310 + nscoord offset[3], start[3], end[3]; 1.2311 + for (i = 0; i <= 2; ++i) { 1.2312 + const nsBoundingMetrics& bm = mBmData[i]; 1.2313 + nscoord dx; 1.2314 + if (0 == i) { // left 1.2315 + dx = aRect.x - bm.leftBearing; 1.2316 + } 1.2317 + else if (2 == i) { // right 1.2318 + dx = aRect.x + aRect.width - bm.rightBearing; 1.2319 + } 1.2320 + else { // middle 1.2321 + dx = aRect.x + (aRect.width - bm.width)/2; 1.2322 + } 1.2323 + // _cairo_scaled_font_show_glyphs snaps origins to device pixels. 1.2324 + // Do this now so that we can get the other dimensions right. 1.2325 + // (This may not achieve much with non-rectangular transformations.) 1.2326 + dx = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).x; 1.2327 + // abcissa passed to Draw 1.2328 + offset[i] = dx; 1.2329 + // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest 1.2330 + // pixel, so the bm values can include 1 row of faint pixels on each edge. 1.2331 + // Don't rely on this pixel as it can look like a gap. 1.2332 + if (bm.rightBearing - bm.leftBearing >= 2 * oneDevPixel) { 1.2333 + start[i] = dx + bm.leftBearing + oneDevPixel; // left join 1.2334 + end[i] = dx + bm.rightBearing - oneDevPixel; // right join 1.2335 + } else { 1.2336 + // To avoid overlaps, we don't add one pixel on each side when the part 1.2337 + // is too small. 1.2338 + start[i] = dx + bm.leftBearing; // left join 1.2339 + end[i] = dx + bm.rightBearing; // right join 1.2340 + } 1.2341 + } 1.2342 + 1.2343 + // If there are overlaps, then join at the mid point 1.2344 + for (i = 0; i < 2; ++i) { 1.2345 + if (end[i] > start[i+1]) { 1.2346 + end[i] = (end[i] + start[i+1]) / 2; 1.2347 + start[i+1] = end[i]; 1.2348 + } 1.2349 + } 1.2350 + 1.2351 + nsRect unionRect = aRect; 1.2352 + unionRect.Inflate(oneDevPixel, oneDevPixel); 1.2353 + 1.2354 + /////////////////////////// 1.2355 + // draw left, middle, right 1.2356 + for (i = 0; i <= 2; ++i) { 1.2357 + // glue can be null 1.2358 + if (mGlyphs[i]) { 1.2359 + nscoord dx = offset[i]; 1.2360 + nsRect clipRect = unionRect; 1.2361 + // Clip at the join to get a solid edge (without overlap or gap), when 1.2362 + // this won't change the glyph too much. If the glyph is too small to 1.2363 + // clip then we'll overlap rather than have a gap. 1.2364 + nscoord width = mBmData[i].rightBearing - mBmData[i].leftBearing; 1.2365 + if (width * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) { 1.2366 + if (0 == i) { // left 1.2367 + clipRect.width = end[i] - clipRect.x; 1.2368 + } 1.2369 + else if (2 == i) { // right 1.2370 + clipRect.width -= start[i] - clipRect.x; 1.2371 + clipRect.x = start[i]; 1.2372 + } 1.2373 + else { // middle 1.2374 + clipRect.x = start[i]; 1.2375 + clipRect.width = end[i] - start[i]; 1.2376 + } 1.2377 + } 1.2378 + if (!clipRect.IsEmpty()) { 1.2379 + AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect); 1.2380 + mGlyphs[i]->Draw(aThebesContext, gfxPoint(dx, dy), 1.2381 + DrawMode::GLYPH_FILL, 0, mGlyphs[i]->GetLength(), 1.2382 + nullptr, nullptr, nullptr); 1.2383 + } 1.2384 + } 1.2385 + } 1.2386 + 1.2387 + //////////////// 1.2388 + // fill the gap between left and middle, and between middle and right. 1.2389 + if (!mGlyphs[3]) { // null glue : draw a rule 1.2390 + // figure out the dimensions of the rule to be drawn : 1.2391 + // set ascent to lowest ascent among the two current successive parts. 1.2392 + // set descent to highest descent among the two current successive parts. 1.2393 + // this satisfies the convention used for over/underbraces, and helps 1.2394 + // fix broken fonts. 1.2395 + nscoord ascent, descent; 1.2396 + int32_t first = 0, last = 1; 1.2397 + while (last <= 2) { 1.2398 + if (mGlyphs[last]) { 1.2399 + ascent = mBmData[last].ascent; 1.2400 + descent = mBmData[last].descent; 1.2401 + if (mGlyphs[first]) { 1.2402 + if (ascent > mBmData[first].ascent) 1.2403 + ascent = mBmData[first].ascent; 1.2404 + if (descent > mBmData[first].descent) 1.2405 + descent = mBmData[first].descent; 1.2406 + } 1.2407 + } 1.2408 + else if (mGlyphs[first]) { 1.2409 + ascent = mBmData[first].ascent; 1.2410 + descent = mBmData[first].descent; 1.2411 + } 1.2412 + else { 1.2413 + NS_ERROR("Cannot stretch - All parts missing"); 1.2414 + return NS_ERROR_UNEXPECTED; 1.2415 + } 1.2416 + // paint the rule between the parts 1.2417 + nsRect rule(end[first], dy - ascent, 1.2418 + start[last] - end[first], ascent + descent); 1.2419 + PaintRule(aThebesContext, oneDevPixel, rule); 1.2420 + first = last; 1.2421 + last++; 1.2422 + } 1.2423 + } 1.2424 + else if (mBmData[3].rightBearing - mBmData[3].leftBearing > 0) { 1.2425 + // glue is present 1.2426 + nsBoundingMetrics& bm = mBmData[3]; 1.2427 + // Ensure the stride for the glue is not reduced to less than one pixel 1.2428 + if (bm.rightBearing - bm.leftBearing >= 3 * oneDevPixel) { 1.2429 + // To protect against gaps, pretend the glue is smaller than it is, 1.2430 + // in order to trim off ends and thus get a solid edge for the join. 1.2431 + bm.leftBearing += oneDevPixel; 1.2432 + bm.rightBearing -= oneDevPixel; 1.2433 + } 1.2434 + 1.2435 + nsRect clipRect = unionRect; 1.2436 + 1.2437 + for (i = 0; i < 2; ++i) { 1.2438 + // Make sure not to draw outside the character 1.2439 + nscoord dx = std::max(end[i], aRect.x); 1.2440 + nscoord fillEnd = std::min(start[i+1], aRect.XMost()); 1.2441 + while (dx < fillEnd) { 1.2442 + clipRect.x = dx; 1.2443 + clipRect.width = std::min(bm.rightBearing - bm.leftBearing, fillEnd - dx); 1.2444 + AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect); 1.2445 + dx -= bm.leftBearing; 1.2446 + mGlyphs[3]->Draw(aThebesContext, gfxPoint(dx, dy), 1.2447 + DrawMode::GLYPH_FILL, 0, mGlyphs[3]->GetLength(), 1.2448 + nullptr, nullptr, nullptr); 1.2449 + dx += bm.rightBearing; 1.2450 + } 1.2451 + } 1.2452 + } 1.2453 +#ifdef DEBUG 1.2454 + else { // no glue 1.2455 + for (i = 0; i < 2; ++i) { 1.2456 + NS_ASSERTION(end[i] >= start[i+1], 1.2457 + "gap between parts with missing glue glyph"); 1.2458 + } 1.2459 + } 1.2460 +#endif 1.2461 + return NS_OK; 1.2462 +}