LCOV - code coverage report
Current view: top level - gfx/thebes - gfxHarfBuzzShaper.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 174 734 23.7 %
Date: 2017-07-14 16:53:18 Functions: 8 36 22.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13