tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gfxHarfBuzzShaper.cpp (69805B)


      1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "nsString.h"
      7 #include "gfxContext.h"
      8 #include "gfxFontConstants.h"
      9 #include "gfxHarfBuzzShaper.h"
     10 #include "gfxFontUtils.h"
     11 #include "gfxTextRun.h"
     12 #include "mozilla/Sprintf.h"
     13 #include "mozilla/intl/String.h"
     14 #include "mozilla/intl/UnicodeProperties.h"
     15 #include "mozilla/intl/UnicodeScriptCodes.h"
     16 #include "nsUnicodeProperties.h"
     17 
     18 #include "harfbuzz/hb.h"
     19 #include "harfbuzz/hb-ot.h"
     20 
     21 #include <algorithm>
     22 
     23 #define FloatToFixed(f) (65536 * (f))
     24 #define FixedToFloat(f) ((f) * (1.0 / 65536.0))
     25 // Right shifts of negative (signed) integers are undefined, as are overflows
     26 // when converting unsigned to negative signed integers.
     27 // (If speed were an issue we could make some 2's complement assumptions.)
     28 #define FixedToIntRound(f) \
     29  ((f) > 0 ? ((32768 + (f)) >> 16) : -((32767 - (f)) >> 16))
     30 
     31 using namespace mozilla;           // for AutoSwap_* types
     32 using namespace mozilla::unicode;  // for Unicode property lookup
     33 
     34 /*
     35 * Creation and destruction; on deletion, release any font tables we're holding
     36 */
     37 
     38 gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfxFont* aFont)
     39    : gfxFontShaper(aFont),
     40      mHBFont(nullptr),
     41      mBuffer(nullptr),
     42      mCallbackData(),
     43      mKernTable(nullptr),
     44      mHmtxTable(nullptr),
     45      mVmtxTable(nullptr),
     46      mVORGTable(nullptr),
     47      mLocaTable(nullptr),
     48      mGlyfTable(nullptr),
     49      mCmapTable(nullptr),
     50      mCmapFormat(-1),
     51      mSubtableOffset(0),
     52      mUVSTableOffset(0),
     53      mNumLongHMetrics(0),
     54      mNumLongVMetrics(0),
     55      mDefaultVOrg(-1.0),
     56      mUseFontGetGlyph(aFont->ProvidesGetGlyph()),
     57      mIsSymbolFont(false),
     58      mUseFontGlyphWidths(aFont->ProvidesGlyphWidths()),
     59      mInitialized(false),
     60      mVerticalInitialized(false),
     61      mUseVerticalPresentationForms(false),
     62      mLoadedLocaGlyf(false),
     63      mLocaLongOffsets(false) {}
     64 
     65 gfxHarfBuzzShaper::~gfxHarfBuzzShaper() {
     66  // hb_*_destroy functions are safe to call on nullptr
     67  hb_blob_destroy(mCmapTable);
     68  hb_blob_destroy(mHmtxTable);
     69  hb_blob_destroy(mKernTable);
     70  hb_blob_destroy(mVmtxTable);
     71  hb_blob_destroy(mVORGTable);
     72  hb_blob_destroy(mLocaTable);
     73  hb_blob_destroy(mGlyfTable);
     74  hb_font_destroy(mHBFont);
     75  hb_buffer_destroy(mBuffer);
     76 }
     77 
     78 #define UNICODE_BMP_LIMIT 0x10000
     79 
     80 hb_codepoint_t gfxHarfBuzzShaper::GetGlyphUncached(
     81    hb_codepoint_t unicode) const {
     82  hb_codepoint_t gid = 0;
     83 
     84  if (mUseFontGetGlyph) {
     85    MutexAutoUnlock unlock(mCacheLock);
     86    gid = mFont->GetGlyph(unicode, 0);
     87  } else {
     88    // we only instantiate a harfbuzz shaper if there's a cmap available
     89    NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
     90                 "cmap data not correctly set up, expect disaster");
     91 
     92    uint32_t length;
     93    const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &length);
     94 
     95    switch (mCmapFormat) {
     96      case 4:
     97        gid =
     98            unicode < UNICODE_BMP_LIMIT
     99                ? gfxFontUtils::MapCharToGlyphFormat4(
    100                      data + mSubtableOffset, length - mSubtableOffset, unicode)
    101                : 0;
    102        break;
    103      case 10:
    104        gid = gfxFontUtils::MapCharToGlyphFormat10(data + mSubtableOffset,
    105                                                   unicode);
    106        break;
    107      case 12:
    108      case 13:
    109        gid = gfxFontUtils::MapCharToGlyphFormat12or13(data + mSubtableOffset,
    110                                                       unicode);
    111        break;
    112      default:
    113        NS_WARNING("unsupported cmap format, glyphs will be missing");
    114        break;
    115    }
    116  }
    117 
    118  if (!gid) {
    119    if (mIsSymbolFont) {
    120      // For legacy MS Symbol fonts, we try mapping the given character code
    121      // to the PUA range used by these fonts' cmaps.
    122      if (auto pua = gfxFontUtils::MapLegacySymbolFontCharToPUA(unicode)) {
    123        gid = GetGlyphUncached(pua);
    124      }
    125      if (gid) {
    126        return gid;
    127      }
    128    }
    129    switch (unicode) {
    130      case 0xA0: {
    131        // if there's no glyph for &nbsp;, just use the space glyph instead.
    132        gid = mFont->GetSpaceGlyph();
    133        break;
    134      }
    135      case 0x2010:
    136      case 0x2011: {
    137        // For Unicode HYPHEN and NON-BREAKING HYPHEN, fall back to the ASCII
    138        // HYPHEN-MINUS as a substitute.
    139        gid = GetGlyphUncached('-');
    140        break;
    141      }
    142    }
    143  }
    144 
    145  return gid;
    146 }
    147 
    148 hb_codepoint_t gfxHarfBuzzShaper::GetNominalGlyph(
    149    hb_codepoint_t unicode) const {
    150  MutexAutoLock lock(mCacheLock);
    151  auto cached = mCmapCache->Lookup(unicode);
    152  if (cached) {
    153    return cached.Data().mGlyphId;
    154  }
    155 
    156  // This call can temporarily unlock the cache if mUseFontGetGlyph is true.
    157  hb_codepoint_t gid = GetGlyphUncached(unicode);
    158 
    159  if (mUseFontGetGlyph) {
    160    // GetGlyphUncached may have invalidated our earlier cache lookup!
    161    mCmapCache->Put(unicode, CmapCacheData{unicode, gid});
    162  } else {
    163    cached.Set(CmapCacheData{unicode, gid});
    164  }
    165 
    166  return gid;
    167 }
    168 
    169 unsigned int gfxHarfBuzzShaper::GetNominalGlyphs(
    170    unsigned int count, const hb_codepoint_t* first_unicode,
    171    unsigned int unicode_stride, hb_codepoint_t* first_glyph,
    172    unsigned int glyph_stride) {
    173  MutexAutoLock lock(mCacheLock);
    174  unsigned int result = 0;
    175  while (result < count) {
    176    hb_codepoint_t usv = *first_unicode;
    177    auto cached = mCmapCache->Lookup(usv);
    178    if (cached) {
    179      // Cache hit :)
    180      *first_glyph = cached.Data().mGlyphId;
    181    } else {
    182      // Cache miss: call GetGlyphUncached (which handles things like symbol-
    183      // encoding fallback) and fill in the cache entry with the result.
    184      hb_codepoint_t gid = GetGlyphUncached(usv);
    185      if (mUseFontGetGlyph) {
    186        mCmapCache->Put(usv, CmapCacheData{usv, gid});
    187      } else {
    188        cached.Set(CmapCacheData{usv, gid});
    189      }
    190      *first_glyph = gid;
    191    }
    192    first_unicode = reinterpret_cast<const hb_codepoint_t*>(
    193        reinterpret_cast<const char*>(first_unicode) + unicode_stride);
    194    first_glyph = reinterpret_cast<hb_codepoint_t*>(
    195        reinterpret_cast<char*>(first_glyph) + glyph_stride);
    196    result++;
    197  }
    198  return result;
    199 }
    200 
    201 hb_codepoint_t gfxHarfBuzzShaper::GetVariationGlyph(
    202    hb_codepoint_t unicode, hb_codepoint_t variation_selector) const {
    203  if (mUseFontGetGlyph) {
    204    return mFont->GetGlyph(unicode, variation_selector);
    205  }
    206 
    207  NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(),
    208               "we cannot be using this font!");
    209  NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
    210               "cmap data not correctly set up, expect disaster");
    211 
    212  uint32_t length;
    213  const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &length);
    214 
    215  uint32_t ch = 0;
    216  if (mUVSTableOffset) {
    217    hb_codepoint_t gid = gfxFontUtils::MapUVSToGlyphFormat14(
    218        data + mUVSTableOffset, unicode, variation_selector);
    219    if (gid) {
    220      return gid;
    221    }
    222    // If <unicode, variation_selector> is a "default UVS sequence" for this
    223    // font, we'll return the result of looking up the bare unicode codepoint.
    224    if (gfxFontUtils::IsDefaultUVSSequence(data + mUVSTableOffset, unicode,
    225                                           variation_selector)) {
    226      ch = unicode;
    227    }
    228  }
    229  if (!ch) {
    230    ch = gfxFontUtils::GetUVSFallback(unicode, variation_selector);
    231  }
    232  if (!ch) {
    233    return 0;
    234  }
    235 
    236  switch (mCmapFormat) {
    237    case 4:
    238      if (ch < UNICODE_BMP_LIMIT) {
    239        return gfxFontUtils::MapCharToGlyphFormat4(
    240            data + mSubtableOffset, length - mSubtableOffset, ch);
    241      }
    242      break;
    243    case 10:
    244      return gfxFontUtils::MapCharToGlyphFormat10(data + mSubtableOffset, ch);
    245      break;
    246    case 12:
    247    case 13:
    248      return gfxFontUtils::MapCharToGlyphFormat12or13(data + mSubtableOffset,
    249                                                      ch);
    250      break;
    251  }
    252 
    253  return 0;
    254 }
    255 
    256 static int VertFormsGlyphCompare(const void* aKey, const void* aElem) {
    257  return int(*((hb_codepoint_t*)(aKey))) - int(*((uint16_t*)(aElem)));
    258 }
    259 
    260 // Return a vertical presentation-form codepoint corresponding to the
    261 // given Unicode value, or 0 if no such form is available.
    262 hb_codepoint_t gfxHarfBuzzShaper::GetVerticalPresentationForm(
    263    hb_codepoint_t aUnicode) {
    264  static const uint16_t sVerticalForms[][2] = {
    265      {0x2013, 0xfe32},  // EN DASH
    266      {0x2014, 0xfe31},  // EM DASH
    267      {0x2025, 0xfe30},  // TWO DOT LEADER
    268      {0x2026, 0xfe19},  // HORIZONTAL ELLIPSIS
    269      {0x3001, 0xfe11},  // IDEOGRAPHIC COMMA
    270      {0x3002, 0xfe12},  // IDEOGRAPHIC FULL STOP
    271      {0x3008, 0xfe3f},  // LEFT ANGLE BRACKET
    272      {0x3009, 0xfe40},  // RIGHT ANGLE BRACKET
    273      {0x300a, 0xfe3d},  // LEFT DOUBLE ANGLE BRACKET
    274      {0x300b, 0xfe3e},  // RIGHT DOUBLE ANGLE BRACKET
    275      {0x300c, 0xfe41},  // LEFT CORNER BRACKET
    276      {0x300d, 0xfe42},  // RIGHT CORNER BRACKET
    277      {0x300e, 0xfe43},  // LEFT WHITE CORNER BRACKET
    278      {0x300f, 0xfe44},  // RIGHT WHITE CORNER BRACKET
    279      {0x3010, 0xfe3b},  // LEFT BLACK LENTICULAR BRACKET
    280      {0x3011, 0xfe3c},  // RIGHT BLACK LENTICULAR BRACKET
    281      {0x3014, 0xfe39},  // LEFT TORTOISE SHELL BRACKET
    282      {0x3015, 0xfe3a},  // RIGHT TORTOISE SHELL BRACKET
    283      {0x3016, 0xfe17},  // LEFT WHITE LENTICULAR BRACKET
    284      {0x3017, 0xfe18},  // RIGHT WHITE LENTICULAR BRACKET
    285      {0xfe4f, 0xfe34},  // WAVY LOW LINE
    286      {0xff01, 0xfe15},  // FULLWIDTH EXCLAMATION MARK
    287      {0xff08, 0xfe35},  // FULLWIDTH LEFT PARENTHESIS
    288      {0xff09, 0xfe36},  // FULLWIDTH RIGHT PARENTHESIS
    289      {0xff0c, 0xfe10},  // FULLWIDTH COMMA
    290      {0xff1a, 0xfe13},  // FULLWIDTH COLON
    291      {0xff1b, 0xfe14},  // FULLWIDTH SEMICOLON
    292      {0xff1f, 0xfe16},  // FULLWIDTH QUESTION MARK
    293      {0xff3b, 0xfe47},  // FULLWIDTH LEFT SQUARE BRACKET
    294      {0xff3d, 0xfe48},  // FULLWIDTH RIGHT SQUARE BRACKET
    295      {0xff3f, 0xfe33},  // FULLWIDTH LOW LINE
    296      {0xff5b, 0xfe37},  // FULLWIDTH LEFT CURLY BRACKET
    297      {0xff5d, 0xfe38}   // FULLWIDTH RIGHT CURLY BRACKET
    298  };
    299  const uint16_t* charPair = static_cast<const uint16_t*>(
    300      bsearch(&aUnicode, sVerticalForms, std::size(sVerticalForms),
    301              sizeof(sVerticalForms[0]), VertFormsGlyphCompare));
    302  return charPair ? charPair[1] : 0;
    303 }
    304 
    305 static hb_bool_t HBGetNominalGlyph(hb_font_t* font, void* font_data,
    306                                   hb_codepoint_t unicode,
    307                                   hb_codepoint_t* glyph, void* user_data) {
    308  const gfxHarfBuzzShaper::FontCallbackData* fcd =
    309      static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
    310 
    311  if (fcd->mShaper->UseVerticalPresentationForms()) {
    312    hb_codepoint_t verticalForm =
    313        gfxHarfBuzzShaper::GetVerticalPresentationForm(unicode);
    314    if (verticalForm) {
    315      *glyph = fcd->mShaper->GetNominalGlyph(verticalForm);
    316      if (*glyph != 0) {
    317        return true;
    318      }
    319    }
    320    // fall back to the non-vertical form if we didn't find an alternate
    321  }
    322 
    323  *glyph = fcd->mShaper->GetNominalGlyph(unicode);
    324  return *glyph != 0;
    325 }
    326 
    327 static unsigned int HBGetNominalGlyphs(
    328    hb_font_t* font, void* font_data, unsigned int count,
    329    const hb_codepoint_t* first_unicode, unsigned int unicode_stride,
    330    hb_codepoint_t* first_glyph, unsigned int glyph_stride, void* user_data) {
    331  const gfxHarfBuzzShaper::FontCallbackData* fcd =
    332      static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
    333  if (fcd->mShaper->UseVerticalPresentationForms()) {
    334    return 0;
    335  }
    336 
    337  return fcd->mShaper->GetNominalGlyphs(count, first_unicode, unicode_stride,
    338                                        first_glyph, glyph_stride);
    339 }
    340 
    341 static hb_bool_t HBGetVariationGlyph(hb_font_t* font, void* font_data,
    342                                     hb_codepoint_t unicode,
    343                                     hb_codepoint_t variation_selector,
    344                                     hb_codepoint_t* glyph, void* user_data) {
    345  const gfxHarfBuzzShaper::FontCallbackData* fcd =
    346      static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
    347 
    348  if (fcd->mShaper->UseVerticalPresentationForms()) {
    349    hb_codepoint_t verticalForm =
    350        gfxHarfBuzzShaper::GetVerticalPresentationForm(unicode);
    351    if (verticalForm) {
    352      *glyph =
    353          fcd->mShaper->GetVariationGlyph(verticalForm, variation_selector);
    354      if (*glyph != 0) {
    355        return true;
    356      }
    357    }
    358    // fall back to the non-vertical form if we didn't find an alternate
    359  }
    360 
    361  *glyph = fcd->mShaper->GetVariationGlyph(unicode, variation_selector);
    362  return *glyph != 0;
    363 }
    364 
    365 // Glyph metrics structures, shared (with appropriate reinterpretation of
    366 // field names) by horizontal and vertical metrics tables.
    367 struct LongMetric {
    368  AutoSwap_PRUint16 advanceWidth;  // or advanceHeight, when vertical
    369  AutoSwap_PRInt16 lsb;            // or tsb, when vertical
    370 };
    371 
    372 struct GlyphMetrics {
    373  LongMetric metrics[1];  // actually numberOfLongMetrics
    374  // the variable-length metrics[] array is immediately followed by:
    375  //  AutoSwap_PRUint16    leftSideBearing[];
    376 };
    377 
    378 hb_position_t gfxHarfBuzzShaper::GetGlyphHAdvanceUncached(
    379    hb_codepoint_t glyph) const {
    380  if (mUseFontGlyphWidths) {
    381    return GetFont()->GetGlyphWidth(glyph);
    382  }
    383 
    384  // Get an unhinted value directly from the font tables.
    385  NS_ASSERTION((mNumLongHMetrics > 0) && mHmtxTable != nullptr,
    386               "font is lacking metrics, we shouldn't be here");
    387 
    388  if (glyph >= uint32_t(mNumLongHMetrics)) {
    389    if (glyph >= mNumGlyphs) {
    390      // Return 0 for out-of-range glyph ID. In particular, AAT shaping uses
    391      // GID 0xFFFF to represent a deleted glyph.
    392      return 0;
    393    }
    394    glyph = mNumLongHMetrics - 1;
    395  }
    396 
    397  // glyph must be valid now, because we checked during initialization
    398  // that mNumLongHMetrics is > 0, and that the metrics table is large enough
    399  // to contain mNumLongHMetrics records
    400  const ::GlyphMetrics* metrics = reinterpret_cast<const ::GlyphMetrics*>(
    401      hb_blob_get_data(mHmtxTable, nullptr));
    402  return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
    403                      uint16_t(metrics->metrics[glyph].advanceWidth));
    404 }
    405 
    406 hb_position_t gfxHarfBuzzShaper::GetGlyphHAdvance(hb_codepoint_t glyph) const {
    407  if (mUseFontGlyphWidths) {
    408    MutexAutoLock lock(mCacheLock);
    409    if (auto cached = mWidthCache->Lookup(glyph)) {
    410      return cached.Data().mAdvance;
    411    }
    412    mCacheLock.Unlock();
    413    hb_position_t advance = GetFont()->GetGlyphWidth(glyph);
    414    mCacheLock.Lock();
    415    mWidthCache->Put(glyph, WidthCacheData{glyph, advance});
    416    return advance;
    417  }
    418 
    419  return GetGlyphHAdvanceUncached(glyph);
    420 }
    421 
    422 void gfxHarfBuzzShaper::GetGlyphHAdvances(unsigned int count,
    423                                          const hb_codepoint_t* first_glyph,
    424                                          unsigned int glyph_stride,
    425                                          hb_position_t* first_advance,
    426                                          unsigned int advance_stride) const {
    427  if (mUseFontGlyphWidths) {
    428    // Take the cache lock here, hoping we'll be able to retrieve a bunch of
    429    // widths from the cache for the cost of a single locking operation.
    430    MutexAutoLock lock(mCacheLock);
    431    for (unsigned int i = 0; i < count; ++i) {
    432      hb_codepoint_t gid = *first_glyph;
    433      if (auto cached = mWidthCache->Lookup(gid)) {
    434        *first_advance = cached.Data().mAdvance;
    435      } else {
    436        // Unlock to avoid deadlock if the font needs internal locking.
    437        mCacheLock.Unlock();
    438        hb_position_t advance = GetFont()->GetGlyphWidth(gid);
    439        mCacheLock.Lock();
    440        mWidthCache->Put(gid, WidthCacheData{gid, advance});
    441        *first_advance = advance;
    442      }
    443      first_glyph = reinterpret_cast<const hb_codepoint_t*>(
    444          reinterpret_cast<const char*>(first_glyph) + glyph_stride);
    445      first_advance = reinterpret_cast<hb_position_t*>(
    446          reinterpret_cast<char*>(first_advance) + advance_stride);
    447    }
    448    return;
    449  }
    450 
    451  for (unsigned int i = 0; i < count; ++i) {
    452    *first_advance = GetGlyphHAdvanceUncached(*first_glyph);
    453    first_glyph = reinterpret_cast<const hb_codepoint_t*>(
    454        reinterpret_cast<const char*>(first_glyph) + glyph_stride);
    455    first_advance = reinterpret_cast<hb_position_t*>(
    456        reinterpret_cast<char*>(first_advance) + advance_stride);
    457  }
    458 }
    459 
    460 hb_position_t gfxHarfBuzzShaper::GetGlyphVAdvance(hb_codepoint_t glyph) {
    461  InitializeVertical();
    462 
    463  if (!mVmtxTable) {
    464    // Must be a "vertical" font that doesn't actually have vertical metrics.
    465    // Return an invalid (negative) value to tell the caller to fall back to
    466    // something else.
    467    return -1;
    468  }
    469 
    470  NS_ASSERTION(mNumLongVMetrics > 0,
    471               "font is lacking metrics, we shouldn't be here");
    472 
    473  if (glyph >= uint32_t(mNumLongVMetrics)) {
    474    glyph = mNumLongVMetrics - 1;
    475  }
    476 
    477  // glyph must be valid now, because we checked during initialization
    478  // that mNumLongVMetrics is > 0, and that the metrics table is large enough
    479  // to contain mNumLongVMetrics records
    480  const ::GlyphMetrics* metrics = reinterpret_cast<const ::GlyphMetrics*>(
    481      hb_blob_get_data(mVmtxTable, nullptr));
    482  return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
    483                      uint16_t(metrics->metrics[glyph].advanceWidth));
    484 }
    485 
    486 static hb_position_t HBGetGlyphHAdvance(hb_font_t* font, void* font_data,
    487                                        hb_codepoint_t glyph, void* user_data) {
    488  const gfxHarfBuzzShaper::FontCallbackData* fcd =
    489      static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
    490  return fcd->mShaper->GetGlyphHAdvance(glyph);
    491 }
    492 
    493 static void HBGetGlyphHAdvances(hb_font_t* font, void* font_data,
    494                                unsigned int count,
    495                                const hb_codepoint_t* first_glyph,
    496                                unsigned int glyph_stride,
    497                                hb_position_t* first_advance,
    498                                unsigned int advance_stride, void* user_data) {
    499  const gfxHarfBuzzShaper::FontCallbackData* fcd =
    500      static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
    501  fcd->mShaper->GetGlyphHAdvances(count, first_glyph, glyph_stride,
    502                                  first_advance, advance_stride);
    503 }
    504 
    505 static hb_position_t HBGetGlyphVAdvance(hb_font_t* font, void* font_data,
    506                                        hb_codepoint_t glyph, void* user_data) {
    507  const gfxHarfBuzzShaper::FontCallbackData* fcd =
    508      static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
    509  // Currently, we don't offer gfxFont subclasses a method to override this
    510  // and provide hinted platform-specific vertical advances (analogous to the
    511  // GetGlyphWidth method for horizontal advances). If that proves necessary,
    512  // we'll add a new gfxFont method and call it from here.
    513  hb_position_t advance = fcd->mShaper->GetGlyphVAdvance(glyph);
    514  if (advance < 0) {
    515    // Not available (e.g. broken metrics in the font); use a fallback value.
    516    advance = FloatToFixed(fcd->mShaper->GetFont()
    517                               ->GetMetrics(nsFontMetrics::eVertical)
    518                               .aveCharWidth);
    519  }
    520  // We negate the value from GetGlyphVAdvance here because harfbuzz shapes
    521  // with a coordinate system where positive is upwards, whereas the inline
    522  // direction in which glyphs advance is downwards.
    523  return -advance;
    524 }
    525 
    526 struct VORG {
    527  AutoSwap_PRUint16 majorVersion;
    528  AutoSwap_PRUint16 minorVersion;
    529  AutoSwap_PRInt16 defaultVertOriginY;
    530  AutoSwap_PRUint16 numVertOriginYMetrics;
    531 };
    532 
    533 struct VORGrec {
    534  AutoSwap_PRUint16 glyphIndex;
    535  AutoSwap_PRInt16 vertOriginY;
    536 };
    537 
    538 static hb_bool_t HBGetGlyphVOrigin(hb_font_t* font, void* font_data,
    539                                   hb_codepoint_t glyph, hb_position_t* x,
    540                                   hb_position_t* y, void* user_data) {
    541  const gfxHarfBuzzShaper::FontCallbackData* fcd =
    542      static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
    543  fcd->mShaper->GetGlyphVOrigin(glyph, x, y);
    544  return true;
    545 }
    546 
    547 void gfxHarfBuzzShaper::GetGlyphVOrigin(hb_codepoint_t aGlyph,
    548                                        hb_position_t* aX,
    549                                        hb_position_t* aY) const {
    550  *aX = 0.5 * GetGlyphHAdvance(aGlyph);
    551 
    552  if (mVORGTable) {
    553    // We checked in Initialize() that the VORG table is safely readable,
    554    // so no length/bounds-check needed here.
    555    const VORG* vorg =
    556        reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable, nullptr));
    557 
    558    const VORGrec* lo = reinterpret_cast<const VORGrec*>(vorg + 1);
    559    const VORGrec* hi = lo + uint16_t(vorg->numVertOriginYMetrics);
    560    const VORGrec* limit = hi;
    561    while (lo < hi) {
    562      const VORGrec* mid = lo + (hi - lo) / 2;
    563      if (uint16_t(mid->glyphIndex) < aGlyph) {
    564        lo = mid + 1;
    565      } else {
    566        hi = mid;
    567      }
    568    }
    569 
    570    if (lo < limit && uint16_t(lo->glyphIndex) == aGlyph) {
    571      *aY = FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
    572                         int16_t(lo->vertOriginY));
    573    } else {
    574      *aY = FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
    575                         int16_t(vorg->defaultVertOriginY));
    576    }
    577    return;
    578  }
    579 
    580  if (mVmtxTable) {
    581    bool emptyGlyf;
    582    const Glyf* glyf = FindGlyf(aGlyph, &emptyGlyf);
    583    // If we didn't find any 'glyf' data, fall through to the default below;
    584    // note that the glyph might still actually render (via SVG or COLR data),
    585    // so we need to provide a reasonable origin.
    586    if (glyf && !emptyGlyf) {
    587      const ::GlyphMetrics* metrics = reinterpret_cast<const ::GlyphMetrics*>(
    588          hb_blob_get_data(mVmtxTable, nullptr));
    589      int16_t lsb;
    590      if (aGlyph < hb_codepoint_t(mNumLongVMetrics)) {
    591        // Glyph is covered by the first (advance & sidebearing) array
    592        lsb = int16_t(metrics->metrics[aGlyph].lsb);
    593      } else {
    594        // Glyph is covered by the second (sidebearing-only) array
    595        const AutoSwap_PRInt16* sidebearings =
    596            reinterpret_cast<const AutoSwap_PRInt16*>(
    597                &metrics->metrics[mNumLongVMetrics]);
    598        lsb = int16_t(sidebearings[aGlyph - mNumLongVMetrics]);
    599      }
    600      *aY = FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
    601                         (lsb + int16_t(glyf->yMax)));
    602      return;
    603    } else {
    604      // XXX TODO: not a truetype font; need to get glyph extents
    605      // via some other API?
    606      // For now, fall through to default code below.
    607    }
    608  }
    609 
    610  if (mDefaultVOrg < 0.0) {
    611    // XXX should we consider using OS/2 sTypo* metrics if available?
    612 
    613    gfxFontEntry::AutoTable hheaTable(GetFont()->GetFontEntry(),
    614                                      TRUETYPE_TAG('h', 'h', 'e', 'a'));
    615    if (hheaTable) {
    616      uint32_t len;
    617      const MetricsHeader* hhea = reinterpret_cast<const MetricsHeader*>(
    618          hb_blob_get_data(hheaTable, &len));
    619      if (len >= sizeof(MetricsHeader)) {
    620        // divide up the default advance we're using (1em) in proportion
    621        // to ascender:descender from the hhea table
    622        int16_t a = int16_t(hhea->ascender);
    623        int16_t d = int16_t(hhea->descender);
    624        mDefaultVOrg = FloatToFixed(GetFont()->GetAdjustedSize() * a / (a - d));
    625      }
    626    }
    627 
    628    if (mDefaultVOrg < 0.0) {
    629      // Last resort, for non-sfnt fonts: get the horizontal metrics and
    630      // compute a default VOrg from their ascent and descent.
    631      const gfxFont::Metrics& mtx = mFont->GetHorizontalMetrics();
    632      gfxFloat advance =
    633          mFont->GetMetrics(nsFontMetrics::eVertical).aveCharWidth;
    634      gfxFloat ascent = mtx.emAscent;
    635      gfxFloat height = ascent + mtx.emDescent;
    636      // vOrigin that will place the glyph so that its origin is shifted
    637      // down most of the way within overall (vertical) advance, in
    638      // proportion to the font ascent as a part of the overall font
    639      // height.
    640      mDefaultVOrg = FloatToFixed(advance * ascent / height);
    641    }
    642  }
    643 
    644  *aY = mDefaultVOrg;
    645 }
    646 
    647 static hb_bool_t HBGetGlyphExtents(hb_font_t* font, void* font_data,
    648                                   hb_codepoint_t glyph,
    649                                   hb_glyph_extents_t* extents,
    650                                   void* user_data) {
    651  const gfxHarfBuzzShaper::FontCallbackData* fcd =
    652      static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
    653  return fcd->mShaper->GetGlyphExtents(glyph, extents);
    654 }
    655 
    656 // Find the data for glyph ID |aGlyph| in the 'glyf' table, if present.
    657 // Returns null if not found, otherwise pointer to the beginning of the
    658 // glyph's data. Sets aEmptyGlyf true if there is no actual data;
    659 // otherwise, it's guaranteed that we can read at least the bounding box.
    660 const gfxHarfBuzzShaper::Glyf* gfxHarfBuzzShaper::FindGlyf(
    661    hb_codepoint_t aGlyph, bool* aEmptyGlyf) const {
    662  if (!mLoadedLocaGlyf) {
    663    mLoadedLocaGlyf = true;  // only try this once; if it fails, this
    664                             // isn't a truetype font
    665    gfxFontEntry* entry = mFont->GetFontEntry();
    666    uint32_t len;
    667    gfxFontEntry::AutoTable headTable(entry, TRUETYPE_TAG('h', 'e', 'a', 'd'));
    668    if (!headTable) {
    669      return nullptr;
    670    }
    671    const HeadTable* head =
    672        reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable, &len));
    673    if (len < sizeof(HeadTable)) {
    674      return nullptr;
    675    }
    676    mLocaLongOffsets = int16_t(head->indexToLocFormat) > 0;
    677    mLocaTable = entry->GetFontTable(TRUETYPE_TAG('l', 'o', 'c', 'a'));
    678    mGlyfTable = entry->GetFontTable(TRUETYPE_TAG('g', 'l', 'y', 'f'));
    679  }
    680 
    681  if (!mLocaTable || !mGlyfTable) {
    682    // it's not a truetype font
    683    return nullptr;
    684  }
    685 
    686  uint32_t offset;  // offset of glyph record in the 'glyf' table
    687  uint32_t len;
    688  const char* data = hb_blob_get_data(mLocaTable, &len);
    689  if (mLocaLongOffsets) {
    690    if ((aGlyph + 1) * sizeof(AutoSwap_PRUint32) > len) {
    691      return nullptr;
    692    }
    693    const AutoSwap_PRUint32* offsets =
    694        reinterpret_cast<const AutoSwap_PRUint32*>(data);
    695    offset = offsets[aGlyph];
    696    *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
    697  } else {
    698    if ((aGlyph + 1) * sizeof(AutoSwap_PRUint16) > len) {
    699      return nullptr;
    700    }
    701    const AutoSwap_PRUint16* offsets =
    702        reinterpret_cast<const AutoSwap_PRUint16*>(data);
    703    offset = uint16_t(offsets[aGlyph]);
    704    *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
    705    offset *= 2;
    706  }
    707 
    708  data = hb_blob_get_data(mGlyfTable, &len);
    709  if (offset + sizeof(Glyf) > len) {
    710    return nullptr;
    711  }
    712 
    713  return reinterpret_cast<const Glyf*>(data + offset);
    714 }
    715 
    716 hb_bool_t gfxHarfBuzzShaper::GetGlyphExtents(
    717    hb_codepoint_t aGlyph, hb_glyph_extents_t* aExtents) const {
    718  bool emptyGlyf;
    719  const Glyf* glyf = FindGlyf(aGlyph, &emptyGlyf);
    720  if (!glyf) {
    721    // TODO: for non-truetype fonts, get extents some other way?
    722    return false;
    723  }
    724 
    725  if (emptyGlyf) {
    726    aExtents->x_bearing = 0;
    727    aExtents->y_bearing = 0;
    728    aExtents->width = 0;
    729    aExtents->height = 0;
    730    return true;
    731  }
    732 
    733  double f = mFont->FUnitsToDevUnitsFactor();
    734  aExtents->x_bearing = FloatToFixed(int16_t(glyf->xMin) * f);
    735  aExtents->width =
    736      FloatToFixed((int16_t(glyf->xMax) - int16_t(glyf->xMin)) * f);
    737 
    738  // Our y-coordinates are positive-downwards, whereas harfbuzz assumes
    739  // positive-upwards; hence the apparently-reversed subtractions here.
    740  aExtents->y_bearing = FloatToFixed(int16_t(glyf->yMax) * f -
    741                                     mFont->GetHorizontalMetrics().emAscent);
    742  aExtents->height =
    743      FloatToFixed((int16_t(glyf->yMin) - int16_t(glyf->yMax)) * f);
    744 
    745  return true;
    746 }
    747 
    748 static hb_bool_t HBGetContourPoint(hb_font_t* font, void* font_data,
    749                                   unsigned int point_index,
    750                                   hb_codepoint_t glyph, hb_position_t* x,
    751                                   hb_position_t* y, void* user_data) {
    752  /* not yet implemented - no support for used of hinted contour points
    753     to fine-tune anchor positions in GPOS AnchorFormat2 */
    754  return false;
    755 }
    756 
    757 struct KernHeaderFmt0 {
    758  AutoSwap_PRUint16 nPairs;
    759  AutoSwap_PRUint16 searchRange;
    760  AutoSwap_PRUint16 entrySelector;
    761  AutoSwap_PRUint16 rangeShift;
    762 };
    763 
    764 struct KernPair {
    765  AutoSwap_PRUint16 left;
    766  AutoSwap_PRUint16 right;
    767  AutoSwap_PRInt16 value;
    768 };
    769 
    770 // Find a kern pair in a Format 0 subtable.
    771 // The aSubtable parameter points to the subtable itself, NOT its header,
    772 // as the header structure differs between Windows and Mac (v0 and v1.0)
    773 // versions of the 'kern' table.
    774 // aSubtableLen is the length of the subtable EXCLUDING its header.
    775 // If the pair <aFirstGlyph,aSecondGlyph> is found, the kerning value is
    776 // added to aValue, so that multiple subtables can accumulate a total
    777 // kerning value for a given pair.
    778 static void GetKernValueFmt0(const void* aSubtable, uint32_t aSubtableLen,
    779                             uint16_t aFirstGlyph, uint16_t aSecondGlyph,
    780                             int32_t& aValue, bool aIsOverride = false,
    781                             bool aIsMinimum = false) {
    782  const KernHeaderFmt0* hdr =
    783      reinterpret_cast<const KernHeaderFmt0*>(aSubtable);
    784 
    785  const KernPair* lo = reinterpret_cast<const KernPair*>(hdr + 1);
    786  const KernPair* hi = lo + uint16_t(hdr->nPairs);
    787  const KernPair* limit = hi;
    788 
    789  if (reinterpret_cast<const char*>(aSubtable) + aSubtableLen <
    790      reinterpret_cast<const char*>(hi)) {
    791    // subtable is not large enough to contain the claimed number
    792    // of kern pairs, so just ignore it
    793    return;
    794  }
    795 
    796 #define KERN_PAIR_KEY(l, r) (uint32_t((uint16_t(l) << 16) + uint16_t(r)))
    797 
    798  uint32_t key = KERN_PAIR_KEY(aFirstGlyph, aSecondGlyph);
    799  while (lo < hi) {
    800    const KernPair* mid = lo + (hi - lo) / 2;
    801    if (KERN_PAIR_KEY(mid->left, mid->right) < key) {
    802      lo = mid + 1;
    803    } else {
    804      hi = mid;
    805    }
    806  }
    807 
    808  if (lo < limit && KERN_PAIR_KEY(lo->left, lo->right) == key) {
    809    if (aIsOverride) {
    810      aValue = int16_t(lo->value);
    811    } else if (aIsMinimum) {
    812      aValue = std::max(aValue, int32_t(lo->value));
    813    } else {
    814      aValue += int16_t(lo->value);
    815    }
    816  }
    817 }
    818 
    819 // Get kerning value from Apple (version 1.0) kern table,
    820 // subtable format 2 (simple N x M array of kerning values)
    821 
    822 // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
    823 // for details of version 1.0 format 2 subtable.
    824 
    825 struct KernHeaderVersion1Fmt2 {
    826  KernTableSubtableHeaderVersion1 header;
    827  AutoSwap_PRUint16 rowWidth;
    828  AutoSwap_PRUint16 leftOffsetTable;
    829  AutoSwap_PRUint16 rightOffsetTable;
    830  AutoSwap_PRUint16 array;
    831 };
    832 
    833 struct KernClassTableHdr {
    834  AutoSwap_PRUint16 firstGlyph;
    835  AutoSwap_PRUint16 nGlyphs;
    836  AutoSwap_PRUint16 offsets[1];  // actually an array of nGlyphs entries
    837 };
    838 
    839 static int16_t GetKernValueVersion1Fmt2(const void* aSubtable,
    840                                        uint32_t aSubtableLen,
    841                                        uint16_t aFirstGlyph,
    842                                        uint16_t aSecondGlyph) {
    843  if (aSubtableLen < sizeof(KernHeaderVersion1Fmt2)) {
    844    return 0;
    845  }
    846 
    847  const char* base = reinterpret_cast<const char*>(aSubtable);
    848  const char* subtableEnd = base + aSubtableLen;
    849 
    850  const KernHeaderVersion1Fmt2* h =
    851      reinterpret_cast<const KernHeaderVersion1Fmt2*>(aSubtable);
    852  uint32_t offset = h->array;
    853 
    854  const KernClassTableHdr* leftClassTable =
    855      reinterpret_cast<const KernClassTableHdr*>(base +
    856                                                 uint16_t(h->leftOffsetTable));
    857  if (reinterpret_cast<const char*>(leftClassTable) +
    858          sizeof(KernClassTableHdr) >
    859      subtableEnd) {
    860    return 0;
    861  }
    862  if (aFirstGlyph >= uint16_t(leftClassTable->firstGlyph)) {
    863    aFirstGlyph -= uint16_t(leftClassTable->firstGlyph);
    864    if (aFirstGlyph < uint16_t(leftClassTable->nGlyphs)) {
    865      if (reinterpret_cast<const char*>(leftClassTable) +
    866              sizeof(KernClassTableHdr) + aFirstGlyph * sizeof(uint16_t) >=
    867          subtableEnd) {
    868        return 0;
    869      }
    870      offset = uint16_t(leftClassTable->offsets[aFirstGlyph]);
    871    }
    872  }
    873 
    874  const KernClassTableHdr* rightClassTable =
    875      reinterpret_cast<const KernClassTableHdr*>(base +
    876                                                 uint16_t(h->rightOffsetTable));
    877  if (reinterpret_cast<const char*>(rightClassTable) +
    878          sizeof(KernClassTableHdr) >
    879      subtableEnd) {
    880    return 0;
    881  }
    882  if (aSecondGlyph >= uint16_t(rightClassTable->firstGlyph)) {
    883    aSecondGlyph -= uint16_t(rightClassTable->firstGlyph);
    884    if (aSecondGlyph < uint16_t(rightClassTable->nGlyphs)) {
    885      if (reinterpret_cast<const char*>(rightClassTable) +
    886              sizeof(KernClassTableHdr) + aSecondGlyph * sizeof(uint16_t) >=
    887          subtableEnd) {
    888        return 0;
    889      }
    890      offset += uint16_t(rightClassTable->offsets[aSecondGlyph]);
    891    }
    892  }
    893 
    894  const AutoSwap_PRInt16* pval =
    895      reinterpret_cast<const AutoSwap_PRInt16*>(base + offset);
    896  if (reinterpret_cast<const char*>(pval + 1) >= subtableEnd) {
    897    return 0;
    898  }
    899  return *pval;
    900 }
    901 
    902 // Get kerning value from Apple (version 1.0) kern table,
    903 // subtable format 3 (simple N x M array of kerning values)
    904 
    905 // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
    906 // for details of version 1.0 format 3 subtable.
    907 
    908 struct KernHeaderVersion1Fmt3 {
    909  KernTableSubtableHeaderVersion1 header;
    910  AutoSwap_PRUint16 glyphCount;
    911  uint8_t kernValueCount;
    912  uint8_t leftClassCount;
    913  uint8_t rightClassCount;
    914  uint8_t flags;
    915 };
    916 
    917 static int16_t GetKernValueVersion1Fmt3(const void* aSubtable,
    918                                        uint32_t aSubtableLen,
    919                                        uint16_t aFirstGlyph,
    920                                        uint16_t aSecondGlyph) {
    921  // check that we can safely read the header fields
    922  if (aSubtableLen < sizeof(KernHeaderVersion1Fmt3)) {
    923    return 0;
    924  }
    925 
    926  const KernHeaderVersion1Fmt3* hdr =
    927      reinterpret_cast<const KernHeaderVersion1Fmt3*>(aSubtable);
    928  if (hdr->flags != 0) {
    929    return 0;
    930  }
    931 
    932  uint16_t glyphCount = hdr->glyphCount;
    933 
    934  // check that table is large enough for the arrays
    935  if (sizeof(KernHeaderVersion1Fmt3) + hdr->kernValueCount * sizeof(int16_t) +
    936          glyphCount + glyphCount + hdr->leftClassCount * hdr->rightClassCount >
    937      aSubtableLen) {
    938    return 0;
    939  }
    940 
    941  if (aFirstGlyph >= glyphCount || aSecondGlyph >= glyphCount) {
    942    // glyphs are out of range for the class tables
    943    return 0;
    944  }
    945 
    946  // get pointers to the four arrays within the subtable
    947  const AutoSwap_PRInt16* kernValue =
    948      reinterpret_cast<const AutoSwap_PRInt16*>(hdr + 1);
    949  const uint8_t* leftClass =
    950      reinterpret_cast<const uint8_t*>(kernValue + hdr->kernValueCount);
    951  const uint8_t* rightClass = leftClass + glyphCount;
    952  const uint8_t* kernIndex = rightClass + glyphCount;
    953 
    954  uint8_t lc = leftClass[aFirstGlyph];
    955  uint8_t rc = rightClass[aSecondGlyph];
    956  if (lc >= hdr->leftClassCount || rc >= hdr->rightClassCount) {
    957    return 0;
    958  }
    959 
    960  uint8_t ki = kernIndex[leftClass[aFirstGlyph] * hdr->rightClassCount +
    961                         rightClass[aSecondGlyph]];
    962  if (ki >= hdr->kernValueCount) {
    963    return 0;
    964  }
    965 
    966  return kernValue[ki];
    967 }
    968 
    969 #define KERN0_COVERAGE_HORIZONTAL 0x0001
    970 #define KERN0_COVERAGE_MINIMUM 0x0002
    971 #define KERN0_COVERAGE_CROSS_STREAM 0x0004
    972 #define KERN0_COVERAGE_OVERRIDE 0x0008
    973 #define KERN0_COVERAGE_RESERVED 0x00F0
    974 
    975 #define KERN1_COVERAGE_VERTICAL 0x8000
    976 #define KERN1_COVERAGE_CROSS_STREAM 0x4000
    977 #define KERN1_COVERAGE_VARIATION 0x2000
    978 #define KERN1_COVERAGE_RESERVED 0x1F00
    979 
    980 hb_position_t gfxHarfBuzzShaper::GetHKerning(uint16_t aFirstGlyph,
    981                                             uint16_t aSecondGlyph) const {
    982  // We want to ignore any kern pairs involving <space>, because we are
    983  // handling words in isolation, the only space characters seen here are
    984  // the ones artificially added by the textRun code.
    985  uint32_t spaceGlyph = mFont->GetSpaceGlyph();
    986  if (aFirstGlyph == spaceGlyph || aSecondGlyph == spaceGlyph) {
    987    return 0;
    988  }
    989 
    990  if (!mKernTable) {
    991    mKernTable =
    992        mFont->GetFontEntry()->GetFontTable(TRUETYPE_TAG('k', 'e', 'r', 'n'));
    993    if (!mKernTable) {
    994      mKernTable = hb_blob_get_empty();
    995    }
    996  }
    997 
    998  uint32_t len;
    999  const char* base = hb_blob_get_data(mKernTable, &len);
   1000  if (len < sizeof(KernTableVersion0)) {
   1001    return 0;
   1002  }
   1003  int32_t value = 0;
   1004 
   1005  // First try to interpret as "version 0" kern table
   1006  // (see http://www.microsoft.com/typography/otspec/kern.htm)
   1007  const KernTableVersion0* kern0 =
   1008      reinterpret_cast<const KernTableVersion0*>(base);
   1009  if (uint16_t(kern0->version) == 0) {
   1010    uint16_t nTables = kern0->nTables;
   1011    uint32_t offs = sizeof(KernTableVersion0);
   1012    for (uint16_t i = 0; i < nTables; ++i) {
   1013      if (offs + sizeof(KernTableSubtableHeaderVersion0) > len) {
   1014        break;
   1015      }
   1016      const KernTableSubtableHeaderVersion0* st0 =
   1017          reinterpret_cast<const KernTableSubtableHeaderVersion0*>(base + offs);
   1018      uint16_t subtableLen = uint16_t(st0->length);
   1019      if (offs + subtableLen > len) {
   1020        break;
   1021      }
   1022      offs += subtableLen;
   1023      uint16_t coverage = st0->coverage;
   1024      if (!(coverage & KERN0_COVERAGE_HORIZONTAL)) {
   1025        // we only care about horizontal kerning (for now)
   1026        continue;
   1027      }
   1028      if (coverage & (KERN0_COVERAGE_CROSS_STREAM | KERN0_COVERAGE_RESERVED)) {
   1029        // we don't support cross-stream kerning, and
   1030        // reserved bits should be zero;
   1031        // ignore the subtable if not
   1032        continue;
   1033      }
   1034      uint8_t format = (coverage >> 8);
   1035      switch (format) {
   1036        case 0:
   1037          GetKernValueFmt0(st0 + 1, subtableLen - sizeof(*st0), aFirstGlyph,
   1038                           aSecondGlyph, value,
   1039                           (coverage & KERN0_COVERAGE_OVERRIDE) != 0,
   1040                           (coverage & KERN0_COVERAGE_MINIMUM) != 0);
   1041          break;
   1042        default:
   1043          // TODO: implement support for other formats,
   1044          // if they're ever used in practice
   1045 #if DEBUG
   1046        {
   1047          char buf[1024];
   1048          SprintfLiteral(buf,
   1049                         "unknown kern subtable in %s: "
   1050                         "ver 0 format %d\n",
   1051                         mFont->GetName().get(), format);
   1052          NS_WARNING(buf);
   1053        }
   1054 #endif
   1055        break;
   1056      }
   1057    }
   1058  } else {
   1059    // It wasn't a "version 0" table; check if it is Apple version 1.0
   1060    // (see http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html)
   1061    const KernTableVersion1* kern1 =
   1062        reinterpret_cast<const KernTableVersion1*>(base);
   1063    if (uint32_t(kern1->version) == 0x00010000) {
   1064      uint32_t nTables = kern1->nTables;
   1065      uint32_t offs = sizeof(KernTableVersion1);
   1066      for (uint32_t i = 0; i < nTables; ++i) {
   1067        if (offs + sizeof(KernTableSubtableHeaderVersion1) > len) {
   1068          break;
   1069        }
   1070        const KernTableSubtableHeaderVersion1* st1 =
   1071            reinterpret_cast<const KernTableSubtableHeaderVersion1*>(base +
   1072                                                                     offs);
   1073        uint32_t subtableLen = uint32_t(st1->length);
   1074        offs += subtableLen;
   1075        uint16_t coverage = st1->coverage;
   1076        if (coverage & (KERN1_COVERAGE_VERTICAL | KERN1_COVERAGE_CROSS_STREAM |
   1077                        KERN1_COVERAGE_VARIATION | KERN1_COVERAGE_RESERVED)) {
   1078          // we only care about horizontal kerning (for now),
   1079          // we don't support cross-stream kerning,
   1080          // we don't support variations,
   1081          // reserved bits should be zero;
   1082          // ignore the subtable if not
   1083          continue;
   1084        }
   1085        uint8_t format = (coverage & 0xff);
   1086        switch (format) {
   1087          case 0:
   1088            GetKernValueFmt0(st1 + 1, subtableLen - sizeof(*st1), aFirstGlyph,
   1089                             aSecondGlyph, value);
   1090            break;
   1091          case 2:
   1092            value = GetKernValueVersion1Fmt2(st1, subtableLen, aFirstGlyph,
   1093                                             aSecondGlyph);
   1094            break;
   1095          case 3:
   1096            value = GetKernValueVersion1Fmt3(st1, subtableLen, aFirstGlyph,
   1097                                             aSecondGlyph);
   1098            break;
   1099          default:
   1100            // TODO: implement support for other formats.
   1101            // Note that format 1 cannot be supported here,
   1102            // as it requires the full glyph array to run the FSM,
   1103            // not just the current glyph pair.
   1104 #if DEBUG
   1105          {
   1106            char buf[1024];
   1107            SprintfLiteral(buf,
   1108                           "unknown kern subtable in %s: "
   1109                           "ver 0 format %d\n",
   1110                           mFont->GetName().get(), format);
   1111            NS_WARNING(buf);
   1112          }
   1113 #endif
   1114          break;
   1115        }
   1116      }
   1117    }
   1118  }
   1119 
   1120  if (value != 0) {
   1121    return FloatToFixed(mFont->FUnitsToDevUnitsFactor() * value);
   1122  }
   1123  return 0;
   1124 }
   1125 
   1126 static hb_position_t HBGetHKerning(hb_font_t* font, void* font_data,
   1127                                   hb_codepoint_t first_glyph,
   1128                                   hb_codepoint_t second_glyph,
   1129                                   void* user_data) {
   1130  const gfxHarfBuzzShaper::FontCallbackData* fcd =
   1131      static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
   1132  return fcd->mShaper->GetHKerning(first_glyph, second_glyph);
   1133 }
   1134 
   1135 /*
   1136 * HarfBuzz unicode property callbacks
   1137 */
   1138 
   1139 static hb_codepoint_t HBGetMirroring(hb_unicode_funcs_t* ufuncs,
   1140                                     hb_codepoint_t aCh, void* user_data) {
   1141  return intl::UnicodeProperties::CharMirror(aCh);
   1142 }
   1143 
   1144 static hb_unicode_general_category_t HBGetGeneralCategory(
   1145    hb_unicode_funcs_t* ufuncs, hb_codepoint_t aCh, void* user_data) {
   1146  return hb_unicode_general_category_t(GetGeneralCategory(aCh));
   1147 }
   1148 
   1149 static hb_script_t HBGetScript(hb_unicode_funcs_t* ufuncs, hb_codepoint_t aCh,
   1150                               void* user_data) {
   1151  return hb_script_t(
   1152      GetScriptTagForCode(intl::UnicodeProperties::GetScriptCode(aCh)));
   1153 }
   1154 
   1155 static hb_unicode_combining_class_t HBGetCombiningClass(
   1156    hb_unicode_funcs_t* ufuncs, hb_codepoint_t aCh, void* user_data) {
   1157  return hb_unicode_combining_class_t(
   1158      intl::UnicodeProperties::GetCombiningClass(aCh));
   1159 }
   1160 
   1161 static hb_bool_t HBUnicodeCompose(hb_unicode_funcs_t* ufuncs, hb_codepoint_t a,
   1162                                  hb_codepoint_t b, hb_codepoint_t* ab,
   1163                                  void* user_data) {
   1164  char32_t ch = intl::String::ComposePairNFC(a, b);
   1165  if (ch > 0) {
   1166    *ab = ch;
   1167    return true;
   1168  }
   1169 
   1170  return false;
   1171 }
   1172 
   1173 static hb_bool_t HBUnicodeDecompose(hb_unicode_funcs_t* ufuncs,
   1174                                    hb_codepoint_t ab, hb_codepoint_t* a,
   1175                                    hb_codepoint_t* b, void* user_data) {
   1176 #if defined(MOZ_WIDGET_ANDROID) && !defined(NIGHTLY_BUILD)
   1177  // Hack for the SamsungDevanagari font, bug 1012365:
   1178  // support U+0972 by decomposing it.
   1179  if (ab == 0x0972) {
   1180    *a = 0x0905;
   1181    *b = 0x0945;
   1182    return true;
   1183  }
   1184 #endif
   1185 
   1186  char32_t decomp[2] = {0};
   1187  if (intl::String::DecomposeRawNFD(ab, decomp)) {
   1188    if (decomp[1] || decomp[0] != ab) {
   1189      *a = decomp[0];
   1190      *b = decomp[1];
   1191      return true;
   1192    }
   1193  }
   1194 
   1195  return false;
   1196 }
   1197 
   1198 static void AddOpenTypeFeature(uint32_t aTag, uint32_t aValue, void* aUserArg) {
   1199  nsTArray<hb_feature_t>* features =
   1200      static_cast<nsTArray<hb_feature_t>*>(aUserArg);
   1201 
   1202  hb_feature_t feat = {0, 0, 0, UINT_MAX};
   1203  feat.tag = aTag;
   1204  feat.value = aValue;
   1205  features->AppendElement(feat);
   1206 }
   1207 
   1208 /*
   1209 * gfxFontShaper override to initialize the text run using HarfBuzz
   1210 */
   1211 
   1212 static hb_font_funcs_t* sHBFontFuncs = nullptr;
   1213 static hb_font_funcs_t* sNominalGlyphFunc = nullptr;
   1214 static hb_unicode_funcs_t* sHBUnicodeFuncs = nullptr;
   1215 MOZ_RUNINIT static const hb_script_t sMathScript =
   1216    hb_ot_tag_to_script(HB_TAG('m', 'a', 't', 'h'));
   1217 
   1218 bool gfxHarfBuzzShaper::Initialize() {
   1219  if (mInitialized) {
   1220    return mHBFont != nullptr;
   1221  }
   1222  mInitialized = true;
   1223  mCallbackData.mShaper = this;
   1224 
   1225  if (!sHBFontFuncs) {
   1226    // static function callback pointers, initialized by the first
   1227    // harfbuzz shaper used
   1228    sHBFontFuncs = hb_font_funcs_create();
   1229    hb_font_funcs_set_nominal_glyph_func(sHBFontFuncs, HBGetNominalGlyph,
   1230                                         nullptr, nullptr);
   1231    hb_font_funcs_set_nominal_glyphs_func(sHBFontFuncs, HBGetNominalGlyphs,
   1232                                          nullptr, nullptr);
   1233    hb_font_funcs_set_variation_glyph_func(sHBFontFuncs, HBGetVariationGlyph,
   1234                                           nullptr, nullptr);
   1235    hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs, HBGetGlyphHAdvance,
   1236                                           nullptr, nullptr);
   1237    hb_font_funcs_set_glyph_h_advances_func(sHBFontFuncs, HBGetGlyphHAdvances,
   1238                                            nullptr, nullptr);
   1239    hb_font_funcs_set_glyph_v_advance_func(sHBFontFuncs, HBGetGlyphVAdvance,
   1240                                           nullptr, nullptr);
   1241    hb_font_funcs_set_glyph_v_origin_func(sHBFontFuncs, HBGetGlyphVOrigin,
   1242                                          nullptr, nullptr);
   1243    hb_font_funcs_set_glyph_extents_func(sHBFontFuncs, HBGetGlyphExtents,
   1244                                         nullptr, nullptr);
   1245    hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs, HBGetContourPoint,
   1246                                               nullptr, nullptr);
   1247    hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs, HBGetHKerning, nullptr,
   1248                                           nullptr);
   1249    hb_font_funcs_make_immutable(sHBFontFuncs);
   1250 
   1251    sNominalGlyphFunc = hb_font_funcs_create();
   1252    hb_font_funcs_set_nominal_glyph_func(sNominalGlyphFunc, HBGetNominalGlyph,
   1253                                         nullptr, nullptr);
   1254    hb_font_funcs_make_immutable(sNominalGlyphFunc);
   1255 
   1256    sHBUnicodeFuncs = hb_unicode_funcs_create(hb_unicode_funcs_get_empty());
   1257    hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs, HBGetMirroring,
   1258                                        nullptr, nullptr);
   1259    hb_unicode_funcs_set_script_func(sHBUnicodeFuncs, HBGetScript, nullptr,
   1260                                     nullptr);
   1261    hb_unicode_funcs_set_general_category_func(
   1262        sHBUnicodeFuncs, HBGetGeneralCategory, nullptr, nullptr);
   1263    hb_unicode_funcs_set_combining_class_func(
   1264        sHBUnicodeFuncs, HBGetCombiningClass, nullptr, nullptr);
   1265    hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs, HBUnicodeCompose,
   1266                                      nullptr, nullptr);
   1267    hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs, HBUnicodeDecompose,
   1268                                        nullptr, nullptr);
   1269    hb_unicode_funcs_make_immutable(sHBUnicodeFuncs);
   1270  }
   1271 
   1272  gfxFontEntry* entry = mFont->GetFontEntry();
   1273  if (!mUseFontGetGlyph) {
   1274    // get the cmap table and find offset to our subtable
   1275    mCmapTable = entry->GetFontTable(TRUETYPE_TAG('c', 'm', 'a', 'p'));
   1276    if (!mCmapTable) {
   1277      NS_WARNING("failed to load cmap, glyphs will be missing");
   1278      return false;
   1279    }
   1280    uint32_t len;
   1281    const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &len);
   1282    mCmapFormat = gfxFontUtils::FindPreferredSubtable(
   1283        data, len, &mSubtableOffset, &mUVSTableOffset, &mIsSymbolFont);
   1284    if (mCmapFormat <= 0) {
   1285      return false;
   1286    }
   1287  }
   1288 
   1289  gfxFontEntry::AutoTable maxpTable(entry, TRUETYPE_TAG('m', 'a', 'x', 'p'));
   1290  if (maxpTable && hb_blob_get_length(maxpTable) >= sizeof(MaxpTableHeader)) {
   1291    const MaxpTableHeader* maxp = reinterpret_cast<const MaxpTableHeader*>(
   1292        hb_blob_get_data(maxpTable, nullptr));
   1293    mNumGlyphs = uint16_t(maxp->numGlyphs);
   1294  }
   1295 
   1296  // We don't need to take the cache lock here, as we're just initializing the
   1297  // shaper and no other thread can yet be using it.
   1298  MOZ_PUSH_IGNORE_THREAD_SAFETY
   1299  mCmapCache = MakeUnique<CmapCache>();
   1300 
   1301  if (mUseFontGlyphWidths) {
   1302    mWidthCache = MakeUnique<WidthCache>();
   1303  } else {
   1304    // If font doesn't implement GetGlyphWidth, we will be reading
   1305    // the metrics table directly, so make sure we can load it.
   1306    if (!LoadHmtxTable()) {
   1307      return false;
   1308    }
   1309  }
   1310  MOZ_POP_THREAD_SAFETY
   1311 
   1312  mBuffer = hb_buffer_create();
   1313  hb_buffer_set_unicode_funcs(mBuffer, sHBUnicodeFuncs);
   1314  hb_buffer_set_cluster_level(mBuffer,
   1315                              HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
   1316 
   1317  auto* funcs =
   1318      mFont->GetFontEntry()->HasFontTable(TRUETYPE_TAG('C', 'F', 'F', ' '))
   1319          ? sNominalGlyphFunc
   1320          : sHBFontFuncs;
   1321  mHBFont = CreateHBFont(mFont, funcs, &mCallbackData);
   1322 
   1323  return true;
   1324 }
   1325 
   1326 hb_font_t* gfxHarfBuzzShaper::CreateHBFont(gfxFont* aFont,
   1327                                           hb_font_funcs_t* aFontFuncs,
   1328                                           FontCallbackData* aCallbackData) {
   1329  auto face(aFont->GetFontEntry()->GetHBFace());
   1330  hb_font_t* result = hb_font_create(face);
   1331 
   1332  if (aFontFuncs && aCallbackData) {
   1333    if (aFontFuncs == sNominalGlyphFunc) {
   1334      hb_font_t* subfont = hb_font_create_sub_font(result);
   1335      hb_font_destroy(result);
   1336      result = subfont;
   1337    }
   1338    hb_font_set_funcs(result, aFontFuncs, aCallbackData, nullptr);
   1339  }
   1340  hb_font_set_ppem(result, aFont->GetAdjustedSize(), aFont->GetAdjustedSize());
   1341  uint32_t scale = FloatToFixed(aFont->GetAdjustedSize());  // 16.16 fixed-point
   1342  hb_font_set_scale(result, scale, scale);
   1343 
   1344  AutoTArray<gfxFontVariation, 8> vars;
   1345  aFont->GetFontEntry()->GetVariationsForStyle(vars, *aFont->GetStyle());
   1346  if (vars.Length() > 0) {
   1347    // Fortunately, the hb_variation_t struct is compatible with our
   1348    // gfxFontVariation, so we can simply cast here.
   1349    static_assert(
   1350        sizeof(gfxFontVariation) == sizeof(hb_variation_t) &&
   1351            offsetof(gfxFontVariation, mTag) == offsetof(hb_variation_t, tag) &&
   1352            offsetof(gfxFontVariation, mValue) ==
   1353                offsetof(hb_variation_t, value),
   1354        "Gecko vs HarfBuzz struct mismatch!");
   1355    auto hbVars = reinterpret_cast<const hb_variation_t*>(vars.Elements());
   1356    hb_font_set_variations(result, hbVars, vars.Length());
   1357  }
   1358 
   1359  return result;
   1360 }
   1361 
   1362 bool gfxHarfBuzzShaper::LoadHmtxTable() {
   1363  // Read mNumLongHMetrics from metrics-head table without caching its
   1364  // blob, and preload/cache the metrics table.
   1365  gfxFontEntry* entry = mFont->GetFontEntry();
   1366  gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h', 'h', 'e', 'a'));
   1367  if (hheaTable) {
   1368    uint32_t len;
   1369    const MetricsHeader* hhea = reinterpret_cast<const MetricsHeader*>(
   1370        hb_blob_get_data(hheaTable, &len));
   1371    if (len >= sizeof(MetricsHeader)) {
   1372      mNumLongHMetrics = hhea->numOfLongMetrics;
   1373      if (mNumLongHMetrics > 0 && int16_t(hhea->metricDataFormat) == 0) {
   1374        // no point reading metrics if number of entries is zero!
   1375        // in that case, we won't be able to use this font
   1376        // (this method will return FALSE below if mHmtxTable
   1377        // is null)
   1378        mHmtxTable = entry->GetFontTable(TRUETYPE_TAG('h', 'm', 't', 'x'));
   1379        if (mHmtxTable && hb_blob_get_length(mHmtxTable) <
   1380                              mNumLongHMetrics * sizeof(LongMetric)) {
   1381          // metrics table is not large enough for the claimed
   1382          // number of entries: invalid, do not use.
   1383          hb_blob_destroy(mHmtxTable);
   1384          mHmtxTable = nullptr;
   1385        }
   1386      }
   1387    }
   1388  }
   1389  if (!mHmtxTable) {
   1390    return false;
   1391  }
   1392  return true;
   1393 }
   1394 
   1395 void gfxHarfBuzzShaper::InitializeVertical() {
   1396  // We only do this once. If we don't have a mHmtxTable after that,
   1397  // we'll be making up fallback metrics.
   1398  if (mVerticalInitialized) {
   1399    return;
   1400  }
   1401  mVerticalInitialized = true;
   1402 
   1403  if (!mHmtxTable) {
   1404    if (!LoadHmtxTable()) {
   1405      return;
   1406    }
   1407  }
   1408 
   1409  // Load vertical metrics if present in the font; if not, we'll synthesize
   1410  // vertical glyph advances based on (horizontal) ascent/descent metrics.
   1411  gfxFontEntry* entry = mFont->GetFontEntry();
   1412  gfxFontEntry::AutoTable vheaTable(entry, TRUETYPE_TAG('v', 'h', 'e', 'a'));
   1413  if (vheaTable) {
   1414    uint32_t len;
   1415    const MetricsHeader* vhea = reinterpret_cast<const MetricsHeader*>(
   1416        hb_blob_get_data(vheaTable, &len));
   1417    if (len >= sizeof(MetricsHeader)) {
   1418      mNumLongVMetrics = vhea->numOfLongMetrics;
   1419      if (mNumLongVMetrics > 0 && mNumLongVMetrics <= int32_t(mNumGlyphs) &&
   1420          int16_t(vhea->metricDataFormat) == 0) {
   1421        mVmtxTable = entry->GetFontTable(TRUETYPE_TAG('v', 'm', 't', 'x'));
   1422        if (mVmtxTable &&
   1423            hb_blob_get_length(mVmtxTable) <
   1424                mNumLongVMetrics * sizeof(LongMetric) +
   1425                    (mNumGlyphs - mNumLongVMetrics) * sizeof(int16_t)) {
   1426          // metrics table is not large enough for the claimed
   1427          // number of entries: invalid, do not use.
   1428          hb_blob_destroy(mVmtxTable);
   1429          mVmtxTable = nullptr;
   1430        }
   1431      }
   1432    }
   1433  }
   1434 
   1435  // For CFF fonts only, load a VORG table if present.
   1436  if (entry->HasFontTable(TRUETYPE_TAG('C', 'F', 'F', ' '))) {
   1437    mVORGTable = entry->GetFontTable(TRUETYPE_TAG('V', 'O', 'R', 'G'));
   1438    if (mVORGTable) {
   1439      uint32_t len;
   1440      const VORG* vorg =
   1441          reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable, &len));
   1442      if (len < sizeof(VORG) || uint16_t(vorg->majorVersion) != 1 ||
   1443          uint16_t(vorg->minorVersion) != 0 ||
   1444          len < sizeof(VORG) +
   1445                    uint16_t(vorg->numVertOriginYMetrics) * sizeof(VORGrec)) {
   1446        // VORG table is an unknown version, or not large enough
   1447        // to be valid -- discard it.
   1448        NS_WARNING("discarding invalid VORG table");
   1449        hb_blob_destroy(mVORGTable);
   1450        mVORGTable = nullptr;
   1451      }
   1452    }
   1453  }
   1454 }
   1455 
   1456 bool gfxHarfBuzzShaper::ShapeText(DrawTarget* aDrawTarget,
   1457                                  const char16_t* aText, uint32_t aOffset,
   1458                                  uint32_t aLength, Script aScript,
   1459                                  nsAtom* aLanguage, bool aVertical,
   1460                                  RoundingFlags aRounding,
   1461                                  gfxShapedText* aShapedText) {
   1462  mUseVerticalPresentationForms = false;
   1463 
   1464  if (!Initialize()) {
   1465    return false;
   1466  }
   1467 
   1468  if (aVertical) {
   1469    InitializeVertical();
   1470    if (!mFont->GetFontEntry()->SupportsOpenTypeFeature(
   1471            aScript, HB_TAG('v', 'e', 'r', 't'))) {
   1472      mUseVerticalPresentationForms = true;
   1473    }
   1474  }
   1475 
   1476  const gfxFontStyle* style = mFont->GetStyle();
   1477 
   1478  // determine whether petite-caps falls back to small-caps
   1479  bool addSmallCaps = false;
   1480  if (style->variantCaps != NS_FONT_VARIANT_CAPS_NORMAL) {
   1481    switch (style->variantCaps) {
   1482      case NS_FONT_VARIANT_CAPS_ALLPETITE:
   1483      case NS_FONT_VARIANT_CAPS_PETITECAPS:
   1484        bool synLower, synUpper;
   1485        mFont->SupportsVariantCaps(aScript, style->variantCaps, addSmallCaps,
   1486                                   synLower, synUpper);
   1487        break;
   1488      default:
   1489        break;
   1490    }
   1491  }
   1492 
   1493  gfxFontEntry* entry = mFont->GetFontEntry();
   1494 
   1495  // insert any merged features into hb_feature array
   1496  AutoTArray<hb_feature_t, 20> features;
   1497  MergeFontFeatures(style, entry->mFeatureSettings,
   1498                    aShapedText->DisableLigatures(), entry->FamilyName(),
   1499                    addSmallCaps, AddOpenTypeFeature, &features);
   1500 
   1501  // For CJK script, match kerning and proportional-alternates (palt) features
   1502  // (and their vertical counterparts) as per spec:
   1503  // https://learn.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-palt
   1504  // and disable kerning by default (for font-kerning:auto).
   1505  if (gfxTextRun::IsCJKScript(aScript)) {
   1506    hb_tag_t kern =
   1507        aVertical ? HB_TAG('v', 'k', 'r', 'n') : HB_TAG('k', 'e', 'r', 'n');
   1508    hb_tag_t alt =
   1509        aVertical ? HB_TAG('v', 'p', 'a', 'l') : HB_TAG('p', 'a', 'l', 't');
   1510    struct Cmp {
   1511      bool Equals(const hb_feature_t& a, const hb_tag_t& b) const {
   1512        return a.tag == b;
   1513      }
   1514    };
   1515    constexpr auto NoIndex = nsTArray<hb_feature_t>::NoIndex;
   1516    nsTArray<hb_feature_t>::index_type i = features.IndexOf(kern, 0, Cmp());
   1517    if (i == NoIndex) {
   1518      // Kerning was not explicitly set; override harfbuzz's default to disable
   1519      // it.
   1520      features.AppendElement(hb_feature_t{kern, 0, HB_FEATURE_GLOBAL_START,
   1521                                          HB_FEATURE_GLOBAL_END});
   1522    } else if (features[i].value) {
   1523      // If kerning was explicitly enabled), we also turn on proportional
   1524      // alternates, as per the OpenType feature registry.
   1525      // Bug 1798297: for the Yu Gothic UI font, we don't do this, because its
   1526      // 'palt' feature produces badly-spaced (overcrowded) kana glyphs.
   1527      if (!entry->FamilyName().EqualsLiteral("Yu Gothic UI")) {
   1528        if (features.IndexOf(alt, 0, Cmp()) == NoIndex) {
   1529          features.AppendElement(hb_feature_t{alt, 1, HB_FEATURE_GLOBAL_START,
   1530                                              HB_FEATURE_GLOBAL_END});
   1531        }
   1532      }
   1533    }
   1534  }
   1535 
   1536  bool isRightToLeft = aShapedText->IsRightToLeft();
   1537 
   1538  hb_buffer_set_direction(
   1539      mBuffer, aVertical
   1540                   ? HB_DIRECTION_TTB
   1541                   : (isRightToLeft ? HB_DIRECTION_RTL : HB_DIRECTION_LTR));
   1542  hb_script_t scriptTag;
   1543  if (aShapedText->GetFlags() & gfx::ShapedTextFlags::TEXT_USE_MATH_SCRIPT) {
   1544    scriptTag = sMathScript;
   1545  } else {
   1546    scriptTag = GetHBScriptUsedForShaping(aScript);
   1547  }
   1548  hb_buffer_set_script(mBuffer, scriptTag);
   1549 
   1550  hb_language_t language;
   1551  if (style->languageOverride._0) {
   1552    language = hb_ot_tag_to_language(style->languageOverride._0);
   1553  } else if (entry->mLanguageOverride) {
   1554    language = hb_ot_tag_to_language(entry->mLanguageOverride);
   1555  } else if (aLanguage) {
   1556    nsCString langString;
   1557    aLanguage->ToUTF8String(langString);
   1558    language = hb_language_from_string(langString.get(), langString.Length());
   1559  } else {
   1560    language = hb_ot_tag_to_language(HB_OT_TAG_DEFAULT_LANGUAGE);
   1561  }
   1562  hb_buffer_set_language(mBuffer, language);
   1563 
   1564  uint32_t length = aLength;
   1565  hb_buffer_add_utf16(mBuffer, reinterpret_cast<const uint16_t*>(aText), length,
   1566                      0, length);
   1567 
   1568  hb_shape(mHBFont, mBuffer, features.Elements(), features.Length());
   1569 
   1570  if (isRightToLeft) {
   1571    hb_buffer_reverse(mBuffer);
   1572  }
   1573 
   1574  nsresult rv = SetGlyphsFromRun(aShapedText, aOffset, aLength, aText,
   1575                                 aVertical, aRounding);
   1576 
   1577  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1578                       "failed to store glyphs into gfxShapedWord");
   1579  hb_buffer_clear_contents(mBuffer);
   1580 
   1581  return NS_SUCCEEDED(rv);
   1582 }
   1583 
   1584 #define SMALL_GLYPH_RUN \
   1585  128  // some testing indicates that 90%+ of text runs
   1586       // will fit without requiring separate allocation
   1587       // for charToGlyphArray
   1588 
   1589 nsresult gfxHarfBuzzShaper::SetGlyphsFromRun(gfxShapedText* aShapedText,
   1590                                             uint32_t aOffset, uint32_t aLength,
   1591                                             const char16_t* aText,
   1592                                             bool aVertical,
   1593                                             RoundingFlags aRounding) {
   1594  typedef gfxShapedText::CompressedGlyph CompressedGlyph;
   1595 
   1596  uint32_t numGlyphs;
   1597  const hb_glyph_info_t* ginfo = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
   1598  if (numGlyphs == 0) {
   1599    return NS_OK;
   1600  }
   1601 
   1602  AutoTArray<gfxTextRun::DetailedGlyph, 1> detailedGlyphs;
   1603 
   1604  uint32_t wordLength = aLength;
   1605  static const int32_t NO_GLYPH = -1;
   1606  AutoTArray<int32_t, SMALL_GLYPH_RUN> charToGlyphArray;
   1607  if (!charToGlyphArray.SetLength(wordLength, fallible)) {
   1608    return NS_ERROR_OUT_OF_MEMORY;
   1609  }
   1610 
   1611  int32_t* charToGlyph = charToGlyphArray.Elements();
   1612  for (uint32_t offset = 0; offset < wordLength; ++offset) {
   1613    charToGlyph[offset] = NO_GLYPH;
   1614  }
   1615 
   1616  for (uint32_t i = 0; i < numGlyphs; ++i) {
   1617    uint32_t loc = ginfo[i].cluster;
   1618    if (loc < wordLength) {
   1619      charToGlyph[loc] = i;
   1620    }
   1621  }
   1622 
   1623  int32_t glyphStart = 0;  // looking for a clump that starts at this glyph
   1624  int32_t charStart = 0;   // and this char index within the range of the run
   1625 
   1626  bool roundI, roundB;
   1627  if (aVertical) {
   1628    roundI = bool(aRounding & RoundingFlags::kRoundY);
   1629    roundB = bool(aRounding & RoundingFlags::kRoundX);
   1630  } else {
   1631    roundI = bool(aRounding & RoundingFlags::kRoundX);
   1632    roundB = bool(aRounding & RoundingFlags::kRoundY);
   1633  }
   1634 
   1635  int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
   1636  CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset;
   1637 
   1638  // factor to convert 16.16 fixed-point pixels to app units
   1639  // (only used if not rounding)
   1640  double hb2appUnits = FixedToFloat(aShapedText->GetAppUnitsPerDevUnit());
   1641 
   1642  // Residual from rounding of previous advance, for use in rounding the
   1643  // subsequent offset or advance appropriately.  16.16 fixed-point
   1644  //
   1645  // When rounding, the goal is to make the distance between glyphs and
   1646  // their base glyph equal to the integral number of pixels closest to that
   1647  // suggested by that shaper.
   1648  // i.e. posInfo[n].x_advance - posInfo[n].x_offset + posInfo[n+1].x_offset
   1649  //
   1650  // The value of the residual is the part of the desired distance that has
   1651  // not been included in integer offsets.
   1652  hb_position_t residual = 0;
   1653 
   1654  // keep track of y-position to set glyph offsets if needed
   1655  nscoord bPos = 0;
   1656 
   1657  const hb_glyph_position_t* posInfo =
   1658      hb_buffer_get_glyph_positions(mBuffer, nullptr);
   1659  if (!posInfo) {
   1660    // Some kind of unexpected failure inside harfbuzz?
   1661    return NS_ERROR_UNEXPECTED;
   1662  }
   1663 
   1664  while (glyphStart < int32_t(numGlyphs)) {
   1665    int32_t charEnd = ginfo[glyphStart].cluster;
   1666    int32_t glyphEnd = glyphStart;
   1667    int32_t charLimit = wordLength;
   1668    while (charEnd < charLimit) {
   1669      // This is normally executed once for each iteration of the outer loop,
   1670      // but in unusual cases where the character/glyph association is complex,
   1671      // the initial character range might correspond to a non-contiguous
   1672      // glyph range with "holes" in it. If so, we will repeat this loop to
   1673      // extend the character range until we have a contiguous glyph sequence.
   1674      charEnd += 1;
   1675      while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) {
   1676        charEnd += 1;
   1677      }
   1678 
   1679      // find the maximum glyph index covered by the clump so far
   1680      for (int32_t i = charStart; i < charEnd; ++i) {
   1681        if (charToGlyph[i] != NO_GLYPH) {
   1682          glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
   1683          // update extent of glyph range
   1684        }
   1685      }
   1686 
   1687      if (glyphEnd == glyphStart + 1) {
   1688        // for the common case of a single-glyph clump,
   1689        // we can skip the following checks
   1690        break;
   1691      }
   1692 
   1693      if (glyphEnd == glyphStart) {
   1694        // no glyphs, try to extend the clump
   1695        continue;
   1696      }
   1697 
   1698      // check whether all glyphs in the range are associated with the
   1699      // characters in our clump; if not, we have a discontinuous range, and
   1700      // should extend it unless we've reached the end of the text
   1701      bool allGlyphsAreWithinCluster = true;
   1702      for (int32_t i = glyphStart; i < glyphEnd; ++i) {
   1703        int32_t glyphCharIndex = ginfo[i].cluster;
   1704        if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) {
   1705          allGlyphsAreWithinCluster = false;
   1706          break;
   1707        }
   1708      }
   1709      if (allGlyphsAreWithinCluster) {
   1710        break;
   1711      }
   1712    }
   1713 
   1714    NS_ASSERTION(glyphStart < glyphEnd,
   1715                 "character/glyph clump contains no glyphs!");
   1716    NS_ASSERTION(charStart != charEnd,
   1717                 "character/glyph clump contains no characters!");
   1718 
   1719    // Now charStart..charEnd is a ligature clump, corresponding to
   1720    // glyphStart..glyphEnd; Set baseCharIndex to the char we'll actually attach
   1721    // the glyphs to (1st of ligature), and endCharIndex to the limit (position
   1722    // beyond the last char), adjusting for the offset of the stringRange
   1723    // relative to the textRun.
   1724    int32_t baseCharIndex, endCharIndex;
   1725    while (charEnd < int32_t(wordLength) && charToGlyph[charEnd] == NO_GLYPH)
   1726      charEnd++;
   1727    baseCharIndex = charStart;
   1728    endCharIndex = charEnd;
   1729 
   1730    // Then we check if the clump falls outside our actual string range;
   1731    // if so, just go to the next.
   1732    if (baseCharIndex >= int32_t(wordLength)) {
   1733      glyphStart = glyphEnd;
   1734      charStart = charEnd;
   1735      continue;
   1736    }
   1737    // Ensure we won't try to go beyond the valid length of the textRun's text
   1738    endCharIndex = std::min<int32_t>(endCharIndex, wordLength);
   1739 
   1740    // Now we're ready to set the glyph info in the textRun
   1741    int32_t glyphsInClump = glyphEnd - glyphStart;
   1742 
   1743    // Check for default-ignorable char that didn't get filtered, combined,
   1744    // etc by the shaping process, and remove from the run.
   1745    // (This may be done within harfbuzz eventually.)
   1746    if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex &&
   1747        aShapedText->FilterIfIgnorable(aOffset + baseCharIndex,
   1748                                       aText[baseCharIndex])) {
   1749      glyphStart = glyphEnd;
   1750      charStart = charEnd;
   1751      continue;
   1752    }
   1753 
   1754    // HarfBuzz gives us physical x- and y-coordinates, but we will store
   1755    // them as logical inline- and block-direction values in the textrun.
   1756 
   1757    hb_position_t i_offset, i_advance;  // inline-direction offset/advance
   1758    hb_position_t b_offset, b_advance;  // block-direction offset/advance
   1759    if (aVertical) {
   1760      // our coordinate directions are the opposite of harfbuzz's
   1761      // when doing top-to-bottom shaping
   1762      i_offset = -posInfo[glyphStart].y_offset;
   1763      i_advance = -posInfo[glyphStart].y_advance;
   1764      b_offset = -posInfo[glyphStart].x_offset;
   1765      b_advance = -posInfo[glyphStart].x_advance;
   1766    } else {
   1767      i_offset = posInfo[glyphStart].x_offset;
   1768      i_advance = posInfo[glyphStart].x_advance;
   1769      b_offset = posInfo[glyphStart].y_offset;
   1770      b_advance = posInfo[glyphStart].y_advance;
   1771    }
   1772 
   1773    nscoord iOffset, advance;
   1774    if (roundI) {
   1775      iOffset = appUnitsPerDevUnit * FixedToIntRound(i_offset + residual);
   1776      // Desired distance from the base glyph to the next reference point.
   1777      hb_position_t width = i_advance - i_offset;
   1778      int intWidth = FixedToIntRound(width);
   1779      residual = width - FloatToFixed(intWidth);
   1780      advance = appUnitsPerDevUnit * intWidth + iOffset;
   1781    } else {
   1782      iOffset = floor(hb2appUnits * i_offset + 0.5);
   1783      advance = floor(hb2appUnits * i_advance + 0.5);
   1784    }
   1785    // Check if it's a simple one-to-one mapping
   1786    if (glyphsInClump == 1 &&
   1787        CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
   1788        CompressedGlyph::IsSimpleAdvance(advance) &&
   1789        charGlyphs[baseCharIndex].IsClusterStart() && iOffset == 0 &&
   1790        b_offset == 0 && b_advance == 0 && bPos == 0) {
   1791      charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
   1792                                               ginfo[glyphStart].codepoint);
   1793    } else {
   1794      // Collect all glyphs in a list to be assigned to the first char;
   1795      // there must be at least one in the clump, and we already measured
   1796      // its advance, hence the placement of the loop-exit test and the
   1797      // measurement of the next glyph.
   1798      while (1) {
   1799        gfxTextRun::DetailedGlyph* details = detailedGlyphs.AppendElement();
   1800        details->mGlyphID = ginfo[glyphStart].codepoint;
   1801 
   1802        details->mAdvance = advance;
   1803 
   1804        if (aVertical) {
   1805          details->mOffset.x =
   1806              bPos - (roundB ? appUnitsPerDevUnit * FixedToIntRound(b_offset)
   1807                             : floor(hb2appUnits * b_offset + 0.5));
   1808          details->mOffset.y = iOffset;
   1809        } else {
   1810          details->mOffset.x = iOffset;
   1811          details->mOffset.y =
   1812              bPos - (roundB ? appUnitsPerDevUnit * FixedToIntRound(b_offset)
   1813                             : floor(hb2appUnits * b_offset + 0.5));
   1814        }
   1815 
   1816        if (b_advance != 0) {
   1817          bPos -= roundB ? appUnitsPerDevUnit * FixedToIntRound(b_advance)
   1818                         : floor(hb2appUnits * b_advance + 0.5);
   1819        }
   1820        if (++glyphStart >= glyphEnd) {
   1821          break;
   1822        }
   1823 
   1824        if (aVertical) {
   1825          i_offset = -posInfo[glyphStart].y_offset;
   1826          i_advance = -posInfo[glyphStart].y_advance;
   1827          b_offset = -posInfo[glyphStart].x_offset;
   1828          b_advance = -posInfo[glyphStart].x_advance;
   1829        } else {
   1830          i_offset = posInfo[glyphStart].x_offset;
   1831          i_advance = posInfo[glyphStart].x_advance;
   1832          b_offset = posInfo[glyphStart].y_offset;
   1833          b_advance = posInfo[glyphStart].y_advance;
   1834        }
   1835 
   1836        if (roundI) {
   1837          iOffset = appUnitsPerDevUnit * FixedToIntRound(i_offset + residual);
   1838          // Desired distance to the next reference point.  The
   1839          // residual is considered here, and includes the residual
   1840          // from the base glyph offset and subsequent advances, so
   1841          // that the distance from the base glyph is optimized
   1842          // rather than the distance from combining marks.
   1843          i_advance += residual;
   1844          int intAdvance = FixedToIntRound(i_advance);
   1845          residual = i_advance - FloatToFixed(intAdvance);
   1846          advance = appUnitsPerDevUnit * intAdvance;
   1847        } else {
   1848          iOffset = floor(hb2appUnits * i_offset + 0.5);
   1849          advance = floor(hb2appUnits * i_advance + 0.5);
   1850        }
   1851      }
   1852 
   1853      aShapedText->SetDetailedGlyphs(aOffset + baseCharIndex,
   1854                                     detailedGlyphs.Length(),
   1855                                     detailedGlyphs.Elements());
   1856 
   1857      detailedGlyphs.Clear();
   1858    }
   1859 
   1860    // the rest of the chars in the group are ligature continuations,
   1861    // no associated glyphs
   1862    while (++baseCharIndex != endCharIndex &&
   1863           baseCharIndex < int32_t(wordLength)) {
   1864      CompressedGlyph& g = charGlyphs[baseCharIndex];
   1865      NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
   1866      g.SetComplex(g.IsClusterStart(), false);
   1867    }
   1868 
   1869    glyphStart = glyphEnd;
   1870    charStart = charEnd;
   1871  }
   1872 
   1873  return NS_OK;
   1874 }