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