layout/mathml/nsMathMLChar.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

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

mercurial