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