Coverage Report

Created: 2018-09-25 14:53

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