gfx/thebes/gfxHarfBuzzShaper.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     6 #include "nsString.h"
     7 #include "gfxContext.h"
     8 #include "gfxHarfBuzzShaper.h"
     9 #include "gfxFontUtils.h"
    10 #include "nsUnicodeProperties.h"
    11 #include "nsUnicodeScriptCodes.h"
    12 #include "nsUnicodeNormalizer.h"
    14 #include "harfbuzz/hb.h"
    15 #include "harfbuzz/hb-ot.h"
    17 #include <algorithm>
    19 #define FloatToFixed(f) (65536 * (f))
    20 #define FixedToFloat(f) ((f) * (1.0 / 65536.0))
    21 // Right shifts of negative (signed) integers are undefined, as are overflows
    22 // when converting unsigned to negative signed integers.
    23 // (If speed were an issue we could make some 2's complement assumptions.)
    24 #define FixedToIntRound(f) ((f) > 0 ?  ((32768 + (f)) >> 16) \
    25                                     : -((32767 - (f)) >> 16))
    27 using namespace mozilla; // for AutoSwap_* types
    28 using namespace mozilla::unicode; // for Unicode property lookup
    30 /*
    31  * Creation and destruction; on deletion, release any font tables we're holding
    32  */
    34 gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfxFont *aFont)
    35     : gfxFontShaper(aFont),
    36       mHBFace(aFont->GetFontEntry()->GetHBFace()),
    37       mHBFont(nullptr),
    38       mKernTable(nullptr),
    39       mHmtxTable(nullptr),
    40       mNumLongMetrics(0),
    41       mCmapTable(nullptr),
    42       mCmapFormat(-1),
    43       mSubtableOffset(0),
    44       mUVSTableOffset(0),
    45       mUseFontGetGlyph(aFont->ProvidesGetGlyph()),
    46       mUseFontGlyphWidths(false),
    47       mInitialized(false)
    48 {
    49 }
    51 gfxHarfBuzzShaper::~gfxHarfBuzzShaper()
    52 {
    53     if (mCmapTable) {
    54         hb_blob_destroy(mCmapTable);
    55     }
    56     if (mHmtxTable) {
    57         hb_blob_destroy(mHmtxTable);
    58     }
    59     if (mKernTable) {
    60         hb_blob_destroy(mKernTable);
    61     }
    62     if (mHBFont) {
    63         hb_font_destroy(mHBFont);
    64     }
    65     if (mHBFace) {
    66         hb_face_destroy(mHBFace);
    67     }
    68 }
    70 #define UNICODE_BMP_LIMIT 0x10000
    72 hb_codepoint_t
    73 gfxHarfBuzzShaper::GetGlyph(hb_codepoint_t unicode,
    74                             hb_codepoint_t variation_selector) const
    75 {
    76     hb_codepoint_t gid = 0;
    78     if (mUseFontGetGlyph) {
    79         gid = mFont->GetGlyph(unicode, variation_selector);
    80     } else {
    81         // we only instantiate a harfbuzz shaper if there's a cmap available
    82         NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(),
    83                      "we cannot be using this font!");
    85         NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
    86                      "cmap data not correctly set up, expect disaster");
    88         const uint8_t* data =
    89             (const uint8_t*)hb_blob_get_data(mCmapTable, nullptr);
    91         if (variation_selector) {
    92             if (mUVSTableOffset) {
    93                 gid =
    94                     gfxFontUtils::MapUVSToGlyphFormat14(data + mUVSTableOffset,
    95                                                         unicode,
    96                                                         variation_selector);
    97             }
    98             if (!gid) {
    99                 uint32_t compat =
   100                     gfxFontUtils::GetUVSFallback(unicode, variation_selector);
   101                 if (compat) {
   102                     switch (mCmapFormat) {
   103                     case 4:
   104                         if (compat < UNICODE_BMP_LIMIT) {
   105                             gid = gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset,
   106                                                                       compat);
   107                         }
   108                         break;
   109                     case 12:
   110                         gid = gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset,
   111                                                                    compat);
   112                         break;
   113                     }
   114                 }
   115             }
   116             // If the variation sequence was not supported, return zero here;
   117             // harfbuzz will call us again for the base character alone
   118             return gid;
   119         }
   121         switch (mCmapFormat) {
   122         case 4:
   123             gid = unicode < UNICODE_BMP_LIMIT ?
   124                 gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset,
   125                                                     unicode) : 0;
   126             break;
   127         case 12:
   128             gid = gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset,
   129                                                        unicode);
   130             break;
   131         default:
   132             NS_WARNING("unsupported cmap format, glyphs will be missing");
   133             break;
   134         }
   135     }
   137     if (!gid) {
   138         // if there's no glyph for &nbsp;, just use the space glyph instead
   139         if (unicode == 0xA0) {
   140             gid = mFont->GetSpaceGlyph();
   141         }
   142     }
   144     return gid;
   145 }
   147 static hb_bool_t
   148 HBGetGlyph(hb_font_t *font, void *font_data,
   149            hb_codepoint_t unicode, hb_codepoint_t variation_selector,
   150            hb_codepoint_t *glyph,
   151            void *user_data)
   152 {
   153     const gfxHarfBuzzShaper::FontCallbackData *fcd =
   154         static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
   155     *glyph = fcd->mShaper->GetGlyph(unicode, variation_selector);
   156     return *glyph != 0;
   157 }
   159 struct HMetricsHeader {
   160     AutoSwap_PRUint32    tableVersionNumber;
   161     AutoSwap_PRInt16     ascender;
   162     AutoSwap_PRInt16     descender;
   163     AutoSwap_PRInt16     lineGap;
   164     AutoSwap_PRUint16    advanceWidthMax;
   165     AutoSwap_PRInt16     minLeftSideBearing;
   166     AutoSwap_PRInt16     minRightSideBearing;
   167     AutoSwap_PRInt16     xMaxExtent;
   168     AutoSwap_PRInt16     caretSlopeRise;
   169     AutoSwap_PRInt16     caretSlopeRun;
   170     AutoSwap_PRInt16     caretOffset;
   171     AutoSwap_PRInt16     reserved[4];
   172     AutoSwap_PRInt16     metricDataFormat;
   173     AutoSwap_PRUint16    numberOfHMetrics;
   174 };
   176 struct HLongMetric {
   177     AutoSwap_PRUint16    advanceWidth;
   178     AutoSwap_PRInt16     lsb;
   179 };
   181 struct HMetrics {
   182     HLongMetric          metrics[1]; // actually numberOfHMetrics
   183 // the variable-length metrics[] array is immediately followed by:
   184 //  AutoSwap_PRUint16    leftSideBearing[];
   185 };
   187 hb_position_t
   188 gfxHarfBuzzShaper::GetGlyphHAdvance(gfxContext *aContext,
   189                                     hb_codepoint_t glyph) const
   190 {
   191     // font did not implement GetHintedGlyphWidth, so get an unhinted value
   192     // directly from the font tables
   194     NS_ASSERTION((mNumLongMetrics > 0) && mHmtxTable != nullptr,
   195                  "font is lacking metrics, we shouldn't be here");
   197     if (glyph >= uint32_t(mNumLongMetrics)) {
   198         glyph = mNumLongMetrics - 1;
   199     }
   201     // glyph must be valid now, because we checked during initialization
   202     // that mNumLongMetrics is > 0, and that the hmtx table is large enough
   203     // to contain mNumLongMetrics records
   204     const HMetrics* hmtx =
   205         reinterpret_cast<const HMetrics*>(hb_blob_get_data(mHmtxTable, nullptr));
   206     return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
   207                         uint16_t(hmtx->metrics[glyph].advanceWidth));
   208 }
   210 /* static */
   211 hb_position_t
   212 gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
   213                                       hb_codepoint_t glyph, void *user_data)
   214 {
   215     const gfxHarfBuzzShaper::FontCallbackData *fcd =
   216         static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
   217     gfxFont *gfxfont = fcd->mShaper->GetFont();
   218     if (gfxfont->ProvidesGlyphWidths()) {
   219         return gfxfont->GetGlyphWidth(fcd->mContext, glyph);
   220     } else {
   221         return fcd->mShaper->GetGlyphHAdvance(fcd->mContext, glyph);
   222     }
   223 }
   225 static hb_bool_t
   226 HBGetContourPoint(hb_font_t *font, void *font_data,
   227                   unsigned int point_index, hb_codepoint_t glyph,
   228                   hb_position_t *x, hb_position_t *y,
   229                   void *user_data)
   230 {
   231     /* not yet implemented - no support for used of hinted contour points
   232        to fine-tune anchor positions in GPOS AnchorFormat2 */
   233     return false;
   234 }
   236 struct KernHeaderFmt0 {
   237     AutoSwap_PRUint16 nPairs;
   238     AutoSwap_PRUint16 searchRange;
   239     AutoSwap_PRUint16 entrySelector;
   240     AutoSwap_PRUint16 rangeShift;
   241 };
   243 struct KernPair {
   244     AutoSwap_PRUint16 left;
   245     AutoSwap_PRUint16 right;
   246     AutoSwap_PRInt16  value;
   247 };
   249 // Find a kern pair in a Format 0 subtable.
   250 // The aSubtable parameter points to the subtable itself, NOT its header,
   251 // as the header structure differs between Windows and Mac (v0 and v1.0)
   252 // versions of the 'kern' table.
   253 // aSubtableLen is the length of the subtable EXCLUDING its header.
   254 // If the pair <aFirstGlyph,aSecondGlyph> is found, the kerning value is
   255 // added to aValue, so that multiple subtables can accumulate a total
   256 // kerning value for a given pair.
   257 static void
   258 GetKernValueFmt0(const void* aSubtable,
   259                  uint32_t aSubtableLen,
   260                  uint16_t aFirstGlyph,
   261                  uint16_t aSecondGlyph,
   262                  int32_t& aValue,
   263                  bool     aIsOverride = false,
   264                  bool     aIsMinimum = false)
   265 {
   266     const KernHeaderFmt0* hdr =
   267         reinterpret_cast<const KernHeaderFmt0*>(aSubtable);
   269     const KernPair *lo = reinterpret_cast<const KernPair*>(hdr + 1);
   270     const KernPair *hi = lo + uint16_t(hdr->nPairs);
   271     const KernPair *limit = hi;
   273     if (reinterpret_cast<const char*>(aSubtable) + aSubtableLen <
   274         reinterpret_cast<const char*>(hi)) {
   275         // subtable is not large enough to contain the claimed number
   276         // of kern pairs, so just ignore it
   277         return;
   278     }
   280 #define KERN_PAIR_KEY(l,r) (uint32_t((uint16_t(l) << 16) + uint16_t(r)))
   282     uint32_t key = KERN_PAIR_KEY(aFirstGlyph, aSecondGlyph);
   283     while (lo < hi) {
   284         const KernPair *mid = lo + (hi - lo) / 2;
   285         if (KERN_PAIR_KEY(mid->left, mid->right) < key) {
   286             lo = mid + 1;
   287         } else {
   288             hi = mid;
   289         }
   290     }
   292     if (lo < limit && KERN_PAIR_KEY(lo->left, lo->right) == key) {
   293         if (aIsOverride) {
   294             aValue = int16_t(lo->value);
   295         } else if (aIsMinimum) {
   296             aValue = std::max(aValue, int32_t(lo->value));
   297         } else {
   298             aValue += int16_t(lo->value);
   299         }
   300     }
   301 }
   303 // Get kerning value from Apple (version 1.0) kern table,
   304 // subtable format 2 (simple N x M array of kerning values)
   306 // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
   307 // for details of version 1.0 format 2 subtable.
   309 struct KernHeaderVersion1Fmt2 {
   310     KernTableSubtableHeaderVersion1 header;
   311     AutoSwap_PRUint16 rowWidth;
   312     AutoSwap_PRUint16 leftOffsetTable;
   313     AutoSwap_PRUint16 rightOffsetTable;
   314     AutoSwap_PRUint16 array;
   315 };
   317 struct KernClassTableHdr {
   318     AutoSwap_PRUint16 firstGlyph;
   319     AutoSwap_PRUint16 nGlyphs;
   320     AutoSwap_PRUint16 offsets[1]; // actually an array of nGlyphs entries	
   321 };
   323 static int16_t
   324 GetKernValueVersion1Fmt2(const void* aSubtable,
   325                          uint32_t aSubtableLen,
   326                          uint16_t aFirstGlyph,
   327                          uint16_t aSecondGlyph)
   328 {
   329     if (aSubtableLen < sizeof(KernHeaderVersion1Fmt2)) {
   330         return 0;
   331     }
   333     const char* base = reinterpret_cast<const char*>(aSubtable);
   334     const char* subtableEnd = base + aSubtableLen;
   336     const KernHeaderVersion1Fmt2* h =
   337         reinterpret_cast<const KernHeaderVersion1Fmt2*>(aSubtable);
   338     uint32_t offset = h->array;
   340     const KernClassTableHdr* leftClassTable =
   341         reinterpret_cast<const KernClassTableHdr*>(base +
   342                                                    uint16_t(h->leftOffsetTable));
   343     if (reinterpret_cast<const char*>(leftClassTable) +
   344         sizeof(KernClassTableHdr) > subtableEnd) {
   345         return 0;
   346     }
   347     if (aFirstGlyph >= uint16_t(leftClassTable->firstGlyph)) {
   348         aFirstGlyph -= uint16_t(leftClassTable->firstGlyph);
   349         if (aFirstGlyph < uint16_t(leftClassTable->nGlyphs)) {
   350             if (reinterpret_cast<const char*>(leftClassTable) +
   351                 sizeof(KernClassTableHdr) +
   352                 aFirstGlyph * sizeof(uint16_t) >= subtableEnd) {
   353                 return 0;
   354             }
   355             offset = uint16_t(leftClassTable->offsets[aFirstGlyph]);
   356         }
   357     }
   359     const KernClassTableHdr* rightClassTable =
   360         reinterpret_cast<const KernClassTableHdr*>(base +
   361                                                    uint16_t(h->rightOffsetTable));
   362     if (reinterpret_cast<const char*>(rightClassTable) +
   363         sizeof(KernClassTableHdr) > subtableEnd) {
   364         return 0;
   365     }
   366     if (aSecondGlyph >= uint16_t(rightClassTable->firstGlyph)) {
   367         aSecondGlyph -= uint16_t(rightClassTable->firstGlyph);
   368         if (aSecondGlyph < uint16_t(rightClassTable->nGlyphs)) {
   369             if (reinterpret_cast<const char*>(rightClassTable) +
   370                 sizeof(KernClassTableHdr) +
   371                 aSecondGlyph * sizeof(uint16_t) >= subtableEnd) {
   372                 return 0;
   373             }
   374             offset += uint16_t(rightClassTable->offsets[aSecondGlyph]);
   375         }
   376     }
   378     const AutoSwap_PRInt16* pval =
   379         reinterpret_cast<const AutoSwap_PRInt16*>(base + offset);
   380     if (reinterpret_cast<const char*>(pval + 1) >= subtableEnd) {
   381         return 0;
   382     }
   383     return *pval;
   384 }
   386 // Get kerning value from Apple (version 1.0) kern table,
   387 // subtable format 3 (simple N x M array of kerning values)
   389 // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
   390 // for details of version 1.0 format 3 subtable.
   392 struct KernHeaderVersion1Fmt3 {
   393     KernTableSubtableHeaderVersion1 header;
   394     AutoSwap_PRUint16 glyphCount;
   395     uint8_t kernValueCount;
   396     uint8_t leftClassCount;
   397     uint8_t rightClassCount;
   398     uint8_t flags;
   399 };
   401 static int16_t
   402 GetKernValueVersion1Fmt3(const void* aSubtable,
   403                          uint32_t aSubtableLen,
   404                          uint16_t aFirstGlyph,
   405                          uint16_t aSecondGlyph)
   406 {
   407     // check that we can safely read the header fields
   408     if (aSubtableLen < sizeof(KernHeaderVersion1Fmt3)) {
   409         return 0;
   410     }
   412     const KernHeaderVersion1Fmt3* hdr =
   413         reinterpret_cast<const KernHeaderVersion1Fmt3*>(aSubtable);
   414     if (hdr->flags != 0) {
   415         return 0;
   416     }
   418     uint16_t glyphCount = hdr->glyphCount;
   420     // check that table is large enough for the arrays
   421     if (sizeof(KernHeaderVersion1Fmt3) +
   422         hdr->kernValueCount * sizeof(int16_t) +
   423         glyphCount + glyphCount +
   424         hdr->leftClassCount * hdr->rightClassCount > aSubtableLen) {
   425         return 0;
   426     }
   428     if (aFirstGlyph >= glyphCount || aSecondGlyph >= glyphCount) {
   429         // glyphs are out of range for the class tables
   430         return 0;
   431     }
   433     // get pointers to the four arrays within the subtable
   434     const AutoSwap_PRInt16* kernValue =
   435         reinterpret_cast<const AutoSwap_PRInt16*>(hdr + 1);
   436     const uint8_t* leftClass =
   437         reinterpret_cast<const uint8_t*>(kernValue + hdr->kernValueCount);
   438     const uint8_t* rightClass = leftClass + glyphCount;
   439     const uint8_t* kernIndex = rightClass + glyphCount;
   441     uint8_t lc = leftClass[aFirstGlyph];
   442     uint8_t rc = rightClass[aSecondGlyph];
   443     if (lc >= hdr->leftClassCount || rc >= hdr->rightClassCount) {
   444         return 0;
   445     }
   447     uint8_t ki = kernIndex[leftClass[aFirstGlyph] * hdr->rightClassCount +
   448                            rightClass[aSecondGlyph]];
   449     if (ki >= hdr->kernValueCount) {
   450         return 0;
   451     }
   453     return kernValue[ki];
   454 }
   456 #define KERN0_COVERAGE_HORIZONTAL   0x0001
   457 #define KERN0_COVERAGE_MINIMUM      0x0002
   458 #define KERN0_COVERAGE_CROSS_STREAM 0x0004
   459 #define KERN0_COVERAGE_OVERRIDE     0x0008
   460 #define KERN0_COVERAGE_RESERVED     0x00F0
   462 #define KERN1_COVERAGE_VERTICAL     0x8000
   463 #define KERN1_COVERAGE_CROSS_STREAM 0x4000
   464 #define KERN1_COVERAGE_VARIATION    0x2000
   465 #define KERN1_COVERAGE_RESERVED     0x1F00
   467 hb_position_t
   468 gfxHarfBuzzShaper::GetHKerning(uint16_t aFirstGlyph,
   469                                uint16_t aSecondGlyph) const
   470 {
   471     // We want to ignore any kern pairs involving <space>, because we are
   472     // handling words in isolation, the only space characters seen here are
   473     // the ones artificially added by the textRun code.
   474     uint32_t spaceGlyph = mFont->GetSpaceGlyph();
   475     if (aFirstGlyph == spaceGlyph || aSecondGlyph == spaceGlyph) {
   476         return 0;
   477     }
   479     if (!mKernTable) {
   480         mKernTable = mFont->GetFontEntry()->GetFontTable(TRUETYPE_TAG('k','e','r','n'));
   481         if (!mKernTable) {
   482             mKernTable = hb_blob_get_empty();
   483         }
   484     }
   486     uint32_t len;
   487     const char* base = hb_blob_get_data(mKernTable, &len);
   488     if (len < sizeof(KernTableVersion0)) {
   489         return 0;
   490     }
   491     int32_t value = 0;
   493     // First try to interpret as "version 0" kern table
   494     // (see http://www.microsoft.com/typography/otspec/kern.htm)
   495     const KernTableVersion0* kern0 =
   496         reinterpret_cast<const KernTableVersion0*>(base);
   497     if (uint16_t(kern0->version) == 0) {
   498         uint16_t nTables = kern0->nTables;
   499         uint32_t offs = sizeof(KernTableVersion0);
   500         for (uint16_t i = 0; i < nTables; ++i) {
   501             if (offs + sizeof(KernTableSubtableHeaderVersion0) > len) {
   502                 break;
   503             }
   504             const KernTableSubtableHeaderVersion0* st0 =
   505                 reinterpret_cast<const KernTableSubtableHeaderVersion0*>
   506                                 (base + offs);
   507             uint16_t subtableLen = uint16_t(st0->length);
   508             if (offs + subtableLen > len) {
   509                 break;
   510             }
   511             offs += subtableLen;
   512             uint16_t coverage = st0->coverage;
   513             if (!(coverage & KERN0_COVERAGE_HORIZONTAL)) {
   514                 // we only care about horizontal kerning (for now)
   515                 continue;
   516             }
   517             if (coverage &
   518                 (KERN0_COVERAGE_CROSS_STREAM | KERN0_COVERAGE_RESERVED)) {
   519                 // we don't support cross-stream kerning, and
   520                 // reserved bits should be zero;
   521                 // ignore the subtable if not
   522                 continue;
   523             }
   524             uint8_t format = (coverage >> 8);
   525             switch (format) {
   526             case 0:
   527                 GetKernValueFmt0(st0 + 1, subtableLen - sizeof(*st0),
   528                                  aFirstGlyph, aSecondGlyph, value,
   529                                  (coverage & KERN0_COVERAGE_OVERRIDE) != 0,
   530                                  (coverage & KERN0_COVERAGE_MINIMUM) != 0);
   531                 break;
   532             default:
   533                 // TODO: implement support for other formats,
   534                 // if they're ever used in practice
   535 #if DEBUG
   536                 {
   537                     char buf[1024];
   538                     sprintf(buf, "unknown kern subtable in %s: "
   539                                  "ver 0 format %d\n",
   540                             NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
   541                             format);
   542                     NS_WARNING(buf);
   543                 }
   544 #endif
   545                 break;
   546             }
   547         }
   548     } else {
   549         // It wasn't a "version 0" table; check if it is Apple version 1.0
   550         // (see http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html)
   551         const KernTableVersion1* kern1 =
   552             reinterpret_cast<const KernTableVersion1*>(base);
   553         if (uint32_t(kern1->version) == 0x00010000) {
   554             uint32_t nTables = kern1->nTables;
   555             uint32_t offs = sizeof(KernTableVersion1);
   556             for (uint32_t i = 0; i < nTables; ++i) {
   557                 if (offs + sizeof(KernTableSubtableHeaderVersion1) > len) {
   558                     break;
   559                 }
   560                 const KernTableSubtableHeaderVersion1* st1 =
   561                     reinterpret_cast<const KernTableSubtableHeaderVersion1*>
   562                                     (base + offs);
   563                 uint32_t subtableLen = uint32_t(st1->length);
   564                 offs += subtableLen;
   565                 uint16_t coverage = st1->coverage;
   566                 if (coverage &
   567                     (KERN1_COVERAGE_VERTICAL     |
   568                      KERN1_COVERAGE_CROSS_STREAM |
   569                      KERN1_COVERAGE_VARIATION    |
   570                      KERN1_COVERAGE_RESERVED)) {
   571                     // we only care about horizontal kerning (for now),
   572                     // we don't support cross-stream kerning,
   573                     // we don't support variations,
   574                     // reserved bits should be zero;
   575                     // ignore the subtable if not
   576                     continue;
   577                 }
   578                 uint8_t format = (coverage & 0xff);
   579                 switch (format) {
   580                 case 0:
   581                     GetKernValueFmt0(st1 + 1, subtableLen - sizeof(*st1),
   582                                      aFirstGlyph, aSecondGlyph, value);
   583                     break;
   584                 case 2:
   585                     value = GetKernValueVersion1Fmt2(st1, subtableLen,
   586                                                      aFirstGlyph, aSecondGlyph);
   587                     break;
   588                 case 3:
   589                     value = GetKernValueVersion1Fmt3(st1, subtableLen,
   590                                                      aFirstGlyph, aSecondGlyph);
   591                     break;
   592                 default:
   593                     // TODO: implement support for other formats.
   594                     // Note that format 1 cannot be supported here,
   595                     // as it requires the full glyph array to run the FSM,
   596                     // not just the current glyph pair.
   597 #if DEBUG
   598                     {
   599                         char buf[1024];
   600                         sprintf(buf, "unknown kern subtable in %s: "
   601                                      "ver 0 format %d\n",
   602                                 NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
   603                                 format);
   604                         NS_WARNING(buf);
   605                     }
   606 #endif
   607                     break;
   608                 }
   609             }
   610         }
   611     }
   613     if (value != 0) {
   614         return FloatToFixed(mFont->FUnitsToDevUnitsFactor() * value);
   615     }
   616     return 0;
   617 }
   619 static hb_position_t
   620 HBGetHKerning(hb_font_t *font, void *font_data,
   621               hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
   622               void *user_data)
   623 {
   624     const gfxHarfBuzzShaper::FontCallbackData *fcd =
   625         static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
   626     return fcd->mShaper->GetHKerning(first_glyph, second_glyph);
   627 }
   629 /*
   630  * HarfBuzz unicode property callbacks
   631  */
   633 static hb_codepoint_t
   634 HBGetMirroring(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh,
   635                void *user_data)
   636 {
   637     return GetMirroredChar(aCh);
   638 }
   640 static hb_unicode_general_category_t
   641 HBGetGeneralCategory(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh,
   642                      void *user_data)
   643 {
   644     return hb_unicode_general_category_t(GetGeneralCategory(aCh));
   645 }
   647 static hb_script_t
   648 HBGetScript(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data)
   649 {
   650     return hb_script_t(GetScriptTagForCode(GetScriptCode(aCh)));
   651 }
   653 static hb_unicode_combining_class_t
   654 HBGetCombiningClass(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh,
   655                     void *user_data)
   656 {
   657     return hb_unicode_combining_class_t(GetCombiningClass(aCh));
   658 }
   660 static unsigned int
   661 HBGetEastAsianWidth(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh,
   662                     void *user_data)
   663 {
   664     return GetEastAsianWidth(aCh);
   665 }
   667 // Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
   668 // note that some letters do not have a dagesh presForm encoded
   669 static const char16_t sDageshForms[0x05EA - 0x05D0 + 1] = {
   670     0xFB30, // ALEF
   671     0xFB31, // BET
   672     0xFB32, // GIMEL
   673     0xFB33, // DALET
   674     0xFB34, // HE
   675     0xFB35, // VAV
   676     0xFB36, // ZAYIN
   677     0, // HET
   678     0xFB38, // TET
   679     0xFB39, // YOD
   680     0xFB3A, // FINAL KAF
   681     0xFB3B, // KAF
   682     0xFB3C, // LAMED
   683     0, // FINAL MEM
   684     0xFB3E, // MEM
   685     0, // FINAL NUN
   686     0xFB40, // NUN
   687     0xFB41, // SAMEKH
   688     0, // AYIN
   689     0xFB43, // FINAL PE
   690     0xFB44, // PE
   691     0, // FINAL TSADI
   692     0xFB46, // TSADI
   693     0xFB47, // QOF
   694     0xFB48, // RESH
   695     0xFB49, // SHIN
   696     0xFB4A // TAV
   697 };
   699 static hb_bool_t
   700 HBUnicodeCompose(hb_unicode_funcs_t *ufuncs,
   701                  hb_codepoint_t      a,
   702                  hb_codepoint_t      b,
   703                  hb_codepoint_t     *ab,
   704                  void               *user_data)
   705 {
   706     hb_bool_t found = nsUnicodeNormalizer::Compose(a, b, ab);
   708     if (!found && (b & 0x1fff80) == 0x0580) {
   709         // special-case Hebrew presentation forms that are excluded from
   710         // standard normalization, but wanted for old fonts
   711         switch (b) {
   712         case 0x05B4: // HIRIQ
   713             if (a == 0x05D9) { // YOD
   714                 *ab = 0xFB1D;
   715                 found = true;
   716             }
   717             break;
   718         case 0x05B7: // patah
   719             if (a == 0x05F2) { // YIDDISH YOD YOD
   720                 *ab = 0xFB1F;
   721                 found = true;
   722             } else if (a == 0x05D0) { // ALEF
   723                 *ab = 0xFB2E;
   724                 found = true;
   725             }
   726             break;
   727         case 0x05B8: // QAMATS
   728             if (a == 0x05D0) { // ALEF
   729                 *ab = 0xFB2F;
   730                 found = true;
   731             }
   732             break;
   733         case 0x05B9: // HOLAM
   734             if (a == 0x05D5) { // VAV
   735                 *ab = 0xFB4B;
   736                 found = true;
   737             }
   738             break;
   739         case 0x05BC: // DAGESH
   740             if (a >= 0x05D0 && a <= 0x05EA) {
   741                 *ab = sDageshForms[a - 0x05D0];
   742                 found = (*ab != 0);
   743             } else if (a == 0xFB2A) { // SHIN WITH SHIN DOT
   744                 *ab = 0xFB2C;
   745                 found = true;
   746             } else if (a == 0xFB2B) { // SHIN WITH SIN DOT
   747                 *ab = 0xFB2D;
   748                 found = true;
   749             }
   750             break;
   751         case 0x05BF: // RAFE
   752             switch (a) {
   753             case 0x05D1: // BET
   754                 *ab = 0xFB4C;
   755                 found = true;
   756                 break;
   757             case 0x05DB: // KAF
   758                 *ab = 0xFB4D;
   759                 found = true;
   760                 break;
   761             case 0x05E4: // PE
   762                 *ab = 0xFB4E;
   763                 found = true;
   764                 break;
   765             }
   766             break;
   767         case 0x05C1: // SHIN DOT
   768             if (a == 0x05E9) { // SHIN
   769                 *ab = 0xFB2A;
   770                 found = true;
   771             } else if (a == 0xFB49) { // SHIN WITH DAGESH
   772                 *ab = 0xFB2C;
   773                 found = true;
   774             }
   775             break;
   776         case 0x05C2: // SIN DOT
   777             if (a == 0x05E9) { // SHIN
   778                 *ab = 0xFB2B;
   779                 found = true;
   780             } else if (a == 0xFB49) { // SHIN WITH DAGESH
   781                 *ab = 0xFB2D;
   782                 found = true;
   783             }
   784             break;
   785         }
   786     }
   788     return found;
   789 }
   791 static hb_bool_t
   792 HBUnicodeDecompose(hb_unicode_funcs_t *ufuncs,
   793                    hb_codepoint_t      ab,
   794                    hb_codepoint_t     *a,
   795                    hb_codepoint_t     *b,
   796                    void               *user_data)
   797 {
   798 #ifdef MOZ_WIDGET_ANDROID
   799     // Hack for the SamsungDevanagari font, bug 1012365:
   800     // support U+0972 by decomposing it.
   801     if (ab == 0x0972) {
   802         *a = 0x0905;
   803         *b = 0x0945;
   804         return true;
   805     }
   806 #endif
   807     return nsUnicodeNormalizer::DecomposeNonRecursively(ab, a, b);
   808 }
   810 static PLDHashOperator
   811 AddOpenTypeFeature(const uint32_t& aTag, uint32_t& aValue, void *aUserArg)
   812 {
   813     nsTArray<hb_feature_t>* features = static_cast<nsTArray<hb_feature_t>*> (aUserArg);
   815     hb_feature_t feat = { 0, 0, 0, UINT_MAX };
   816     feat.tag = aTag;
   817     feat.value = aValue;
   818     features->AppendElement(feat);
   819     return PL_DHASH_NEXT;
   820 }
   822 /*
   823  * gfxFontShaper override to initialize the text run using HarfBuzz
   824  */
   826 static hb_font_funcs_t * sHBFontFuncs = nullptr;
   827 static hb_unicode_funcs_t * sHBUnicodeFuncs = nullptr;
   828 static const hb_script_t sMathScript =
   829     hb_ot_tag_to_script(HB_TAG('m','a','t','h'));
   831 bool
   832 gfxHarfBuzzShaper::Initialize()
   833 {
   834     if (mInitialized) {
   835         return mHBFont != nullptr;
   836     }
   837     mInitialized = true;
   838     mCallbackData.mShaper = this;
   840     mUseFontGlyphWidths = mFont->ProvidesGlyphWidths();
   842     if (!sHBFontFuncs) {
   843         // static function callback pointers, initialized by the first
   844         // harfbuzz shaper used
   845         sHBFontFuncs = hb_font_funcs_create();
   846         hb_font_funcs_set_glyph_func(sHBFontFuncs, HBGetGlyph,
   847                                      nullptr, nullptr);
   848         hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs,
   849                                                HBGetGlyphHAdvance,
   850                                                nullptr, nullptr);
   851         hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
   852                                                    HBGetContourPoint,
   853                                                    nullptr, nullptr);
   854         hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs,
   855                                                HBGetHKerning,
   856                                                nullptr, nullptr);
   858         sHBUnicodeFuncs =
   859             hb_unicode_funcs_create(hb_unicode_funcs_get_empty());
   860         hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs,
   861                                             HBGetMirroring,
   862                                             nullptr, nullptr);
   863         hb_unicode_funcs_set_script_func(sHBUnicodeFuncs, HBGetScript,
   864                                          nullptr, nullptr);
   865         hb_unicode_funcs_set_general_category_func(sHBUnicodeFuncs,
   866                                                    HBGetGeneralCategory,
   867                                                    nullptr, nullptr);
   868         hb_unicode_funcs_set_combining_class_func(sHBUnicodeFuncs,
   869                                                   HBGetCombiningClass,
   870                                                   nullptr, nullptr);
   871         hb_unicode_funcs_set_eastasian_width_func(sHBUnicodeFuncs,
   872                                                   HBGetEastAsianWidth,
   873                                                   nullptr, nullptr);
   874         hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs,
   875                                           HBUnicodeCompose,
   876                                           nullptr, nullptr);
   877         hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs,
   878                                             HBUnicodeDecompose,
   879                                             nullptr, nullptr);
   880     }
   882     gfxFontEntry *entry = mFont->GetFontEntry();
   883     if (!mUseFontGetGlyph) {
   884         // get the cmap table and find offset to our subtable
   885         mCmapTable = entry->GetFontTable(TRUETYPE_TAG('c','m','a','p'));
   886         if (!mCmapTable) {
   887             NS_WARNING("failed to load cmap, glyphs will be missing");
   888             return false;
   889         }
   890         uint32_t len;
   891         const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &len);
   892         bool symbol;
   893         mCmapFormat = gfxFontUtils::
   894             FindPreferredSubtable(data, len,
   895                                   &mSubtableOffset, &mUVSTableOffset,
   896                                   &symbol);
   897         if (mCmapFormat <= 0) {
   898             return false;
   899         }
   900     }
   902     if (!mUseFontGlyphWidths) {
   903         // if font doesn't implement GetGlyphWidth, we will be reading
   904         // the hmtx table directly;
   905         // read mNumLongMetrics from hhea table without caching its blob,
   906         // and preload/cache the hmtx table
   907         gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h','h','e','a'));
   908         if (hheaTable) {
   909             uint32_t len;
   910             const HMetricsHeader* hhea =
   911                 reinterpret_cast<const HMetricsHeader*>
   912                 (hb_blob_get_data(hheaTable, &len));
   913             if (len >= sizeof(HMetricsHeader)) {
   914                 mNumLongMetrics = hhea->numberOfHMetrics;
   915                 if (mNumLongMetrics > 0 &&
   916                     int16_t(hhea->metricDataFormat) == 0) {
   917                     // no point reading hmtx if number of entries is zero!
   918                     // in that case, we won't be able to use this font
   919                     // (this method will return FALSE below if mHmtx is null)
   920                     mHmtxTable =
   921                         entry->GetFontTable(TRUETYPE_TAG('h','m','t','x'));
   922                     if (hb_blob_get_length(mHmtxTable) <
   923                         mNumLongMetrics * sizeof(HLongMetric)) {
   924                         // hmtx table is not large enough for the claimed
   925                         // number of entries: invalid, do not use.
   926                         hb_blob_destroy(mHmtxTable);
   927                         mHmtxTable = nullptr;
   928                     }
   929                 }
   930             }
   931         }
   932         if (!mHmtxTable) {
   933             return false;
   934         }
   935     }
   937     mHBFont = hb_font_create(mHBFace);
   938     hb_font_set_funcs(mHBFont, sHBFontFuncs, &mCallbackData, nullptr);
   939     hb_font_set_ppem(mHBFont, mFont->GetAdjustedSize(), mFont->GetAdjustedSize());
   940     uint32_t scale = FloatToFixed(mFont->GetAdjustedSize()); // 16.16 fixed-point
   941     hb_font_set_scale(mHBFont, scale, scale);
   943     return true;
   944 }
   946 bool
   947 gfxHarfBuzzShaper::ShapeText(gfxContext      *aContext,
   948                              const char16_t *aText,
   949                              uint32_t         aOffset,
   950                              uint32_t         aLength,
   951                              int32_t          aScript,
   952                              gfxShapedText   *aShapedText)
   953 {
   954     // some font back-ends require this in order to get proper hinted metrics
   955     if (!mFont->SetupCairoFont(aContext)) {
   956         return false;
   957     }
   959     mCallbackData.mContext = aContext;
   961     if (!Initialize()) {
   962         return false;
   963     }
   965     const gfxFontStyle *style = mFont->GetStyle();
   967     nsAutoTArray<hb_feature_t,20> features;
   968     nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures;
   970     gfxFontEntry *entry = mFont->GetFontEntry();
   971     if (MergeFontFeatures(style,
   972                           entry->mFeatureSettings,
   973                           aShapedText->DisableLigatures(),
   974                           entry->FamilyName(),
   975                           mergedFeatures))
   976     {
   977         // enumerate result and insert into hb_feature array
   978         mergedFeatures.Enumerate(AddOpenTypeFeature, &features);
   979     }
   981     bool isRightToLeft = aShapedText->IsRightToLeft();
   982     hb_buffer_t *buffer = hb_buffer_create();
   983     hb_buffer_set_unicode_funcs(buffer, sHBUnicodeFuncs);
   984     hb_buffer_set_direction(buffer, isRightToLeft ? HB_DIRECTION_RTL :
   985                                                     HB_DIRECTION_LTR);
   986     hb_script_t scriptTag;
   987     if (aShapedText->Flags() & gfxTextRunFactory::TEXT_USE_MATH_SCRIPT) {
   988         scriptTag = sMathScript;
   989     } else if (aScript <= MOZ_SCRIPT_INHERITED) {
   990         // For unresolved "common" or "inherited" runs, default to Latin for
   991         // now.  (Should we somehow use the language or locale to try and infer
   992         // a better default?)
   993         scriptTag = HB_SCRIPT_LATIN;
   994     } else {
   995         scriptTag = hb_script_t(GetScriptTagForCode(aScript));
   996     }
   997     hb_buffer_set_script(buffer, scriptTag);
   999     hb_language_t language;
  1000     if (style->languageOverride) {
  1001         language = hb_ot_tag_to_language(style->languageOverride);
  1002     } else if (entry->mLanguageOverride) {
  1003         language = hb_ot_tag_to_language(entry->mLanguageOverride);
  1004     } else {
  1005         nsCString langString;
  1006         style->language->ToUTF8String(langString);
  1007         language =
  1008             hb_language_from_string(langString.get(), langString.Length());
  1010     hb_buffer_set_language(buffer, language);
  1012     uint32_t length = aLength;
  1013     hb_buffer_add_utf16(buffer,
  1014                         reinterpret_cast<const uint16_t*>(aText),
  1015                         length, 0, length);
  1017     hb_shape(mHBFont, buffer, features.Elements(), features.Length());
  1019     if (isRightToLeft) {
  1020         hb_buffer_reverse(buffer);
  1023     nsresult rv = SetGlyphsFromRun(aContext, aShapedText, aOffset, aLength,
  1024                                    aText, buffer);
  1026     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store glyphs into gfxShapedWord");
  1027     hb_buffer_destroy(buffer);
  1029     return NS_SUCCEEDED(rv);
  1032 #define SMALL_GLYPH_RUN 128 // some testing indicates that 90%+ of text runs
  1033                             // will fit without requiring separate allocation
  1034                             // for charToGlyphArray
  1036 nsresult
  1037 gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext      *aContext,
  1038                                     gfxShapedText   *aShapedText,
  1039                                     uint32_t         aOffset,
  1040                                     uint32_t         aLength,
  1041                                     const char16_t *aText,
  1042                                     hb_buffer_t     *aBuffer)
  1044     uint32_t numGlyphs;
  1045     const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(aBuffer, &numGlyphs);
  1046     if (numGlyphs == 0) {
  1047         return NS_OK;
  1050     nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
  1052     uint32_t wordLength = aLength;
  1053     static const int32_t NO_GLYPH = -1;
  1054     AutoFallibleTArray<int32_t,SMALL_GLYPH_RUN> charToGlyphArray;
  1055     if (!charToGlyphArray.SetLength(wordLength)) {
  1056         return NS_ERROR_OUT_OF_MEMORY;
  1059     int32_t *charToGlyph = charToGlyphArray.Elements();
  1060     for (uint32_t offset = 0; offset < wordLength; ++offset) {
  1061         charToGlyph[offset] = NO_GLYPH;
  1064     for (uint32_t i = 0; i < numGlyphs; ++i) {
  1065         uint32_t loc = ginfo[i].cluster;
  1066         if (loc < wordLength) {
  1067             charToGlyph[loc] = i;
  1071     int32_t glyphStart = 0; // looking for a clump that starts at this glyph
  1072     int32_t charStart = 0; // and this char index within the range of the run
  1074     bool roundX;
  1075     bool roundY;
  1076     aContext->GetRoundOffsetsToPixels(&roundX, &roundY);
  1078     int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
  1079     gfxShapedText::CompressedGlyph *charGlyphs =
  1080         aShapedText->GetCharacterGlyphs() + aOffset;
  1082     // factor to convert 16.16 fixed-point pixels to app units
  1083     // (only used if not rounding)
  1084     double hb2appUnits = FixedToFloat(aShapedText->GetAppUnitsPerDevUnit());
  1086     // Residual from rounding of previous advance, for use in rounding the
  1087     // subsequent offset or advance appropriately.  16.16 fixed-point
  1088     //
  1089     // When rounding, the goal is to make the distance between glyphs and
  1090     // their base glyph equal to the integral number of pixels closest to that
  1091     // suggested by that shaper.
  1092     // i.e. posInfo[n].x_advance - posInfo[n].x_offset + posInfo[n+1].x_offset
  1093     //
  1094     // The value of the residual is the part of the desired distance that has
  1095     // not been included in integer offsets.
  1096     hb_position_t x_residual = 0;
  1098     // keep track of y-position to set glyph offsets if needed
  1099     nscoord yPos = 0;
  1101     const hb_glyph_position_t *posInfo =
  1102         hb_buffer_get_glyph_positions(aBuffer, nullptr);
  1104     while (glyphStart < int32_t(numGlyphs)) {
  1106         int32_t charEnd = ginfo[glyphStart].cluster;
  1107         int32_t glyphEnd = glyphStart;
  1108         int32_t charLimit = wordLength;
  1109         while (charEnd < charLimit) {
  1110             // This is normally executed once for each iteration of the outer loop,
  1111             // but in unusual cases where the character/glyph association is complex,
  1112             // the initial character range might correspond to a non-contiguous
  1113             // glyph range with "holes" in it. If so, we will repeat this loop to
  1114             // extend the character range until we have a contiguous glyph sequence.
  1115             charEnd += 1;
  1116             while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) {
  1117                 charEnd += 1;
  1120             // find the maximum glyph index covered by the clump so far
  1121             for (int32_t i = charStart; i < charEnd; ++i) {
  1122                 if (charToGlyph[i] != NO_GLYPH) {
  1123                     glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
  1124                     // update extent of glyph range
  1128             if (glyphEnd == glyphStart + 1) {
  1129                 // for the common case of a single-glyph clump,
  1130                 // we can skip the following checks
  1131                 break;
  1134             if (glyphEnd == glyphStart) {
  1135                 // no glyphs, try to extend the clump
  1136                 continue;
  1139             // check whether all glyphs in the range are associated with the characters
  1140             // in our clump; if not, we have a discontinuous range, and should extend it
  1141             // unless we've reached the end of the text
  1142             bool allGlyphsAreWithinCluster = true;
  1143             for (int32_t i = glyphStart; i < glyphEnd; ++i) {
  1144                 int32_t glyphCharIndex = ginfo[i].cluster;
  1145                 if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) {
  1146                     allGlyphsAreWithinCluster = false;
  1147                     break;
  1150             if (allGlyphsAreWithinCluster) {
  1151                 break;
  1155         NS_ASSERTION(glyphStart < glyphEnd,
  1156                      "character/glyph clump contains no glyphs!");
  1157         NS_ASSERTION(charStart != charEnd,
  1158                      "character/glyph clump contains no characters!");
  1160         // Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd;
  1161         // Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature),
  1162         // and endCharIndex to the limit (position beyond the last char),
  1163         // adjusting for the offset of the stringRange relative to the textRun.
  1164         int32_t baseCharIndex, endCharIndex;
  1165         while (charEnd < int32_t(wordLength) && charToGlyph[charEnd] == NO_GLYPH)
  1166             charEnd++;
  1167         baseCharIndex = charStart;
  1168         endCharIndex = charEnd;
  1170         // Then we check if the clump falls outside our actual string range;
  1171         // if so, just go to the next.
  1172         if (baseCharIndex >= int32_t(wordLength)) {
  1173             glyphStart = glyphEnd;
  1174             charStart = charEnd;
  1175             continue;
  1177         // Ensure we won't try to go beyond the valid length of the textRun's text
  1178         endCharIndex = std::min<int32_t>(endCharIndex, wordLength);
  1180         // Now we're ready to set the glyph info in the textRun
  1181         int32_t glyphsInClump = glyphEnd - glyphStart;
  1183         // Check for default-ignorable char that didn't get filtered, combined,
  1184         // etc by the shaping process, and remove from the run.
  1185         // (This may be done within harfbuzz eventually.)
  1186         if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex &&
  1187             aShapedText->FilterIfIgnorable(aOffset + baseCharIndex,
  1188                                            aText[baseCharIndex])) {
  1189             glyphStart = glyphEnd;
  1190             charStart = charEnd;
  1191             continue;
  1194         hb_position_t x_offset = posInfo[glyphStart].x_offset;
  1195         hb_position_t x_advance = posInfo[glyphStart].x_advance;
  1196         nscoord xOffset, advance;
  1197         if (roundX) {
  1198             xOffset =
  1199                 appUnitsPerDevUnit * FixedToIntRound(x_offset + x_residual);
  1200             // Desired distance from the base glyph to the next reference point.
  1201             hb_position_t width = x_advance - x_offset;
  1202             int intWidth = FixedToIntRound(width);
  1203             x_residual = width - FloatToFixed(intWidth);
  1204             advance = appUnitsPerDevUnit * intWidth + xOffset;
  1205         } else {
  1206             xOffset = floor(hb2appUnits * x_offset + 0.5);
  1207             advance = floor(hb2appUnits * x_advance + 0.5);
  1209         // Check if it's a simple one-to-one mapping
  1210         if (glyphsInClump == 1 &&
  1211             gfxTextRun::CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
  1212             gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
  1213             charGlyphs[baseCharIndex].IsClusterStart() &&
  1214             xOffset == 0 &&
  1215             posInfo[glyphStart].y_offset == 0 && yPos == 0)
  1217             charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
  1218                                                      ginfo[glyphStart].codepoint);
  1219         } else {
  1220             // collect all glyphs in a list to be assigned to the first char;
  1221             // there must be at least one in the clump, and we already measured
  1222             // its advance, hence the placement of the loop-exit test and the
  1223             // measurement of the next glyph
  1224             while (1) {
  1225                 gfxTextRun::DetailedGlyph* details =
  1226                     detailedGlyphs.AppendElement();
  1227                 details->mGlyphID = ginfo[glyphStart].codepoint;
  1229                 details->mXOffset = xOffset;
  1230                 details->mAdvance = advance;
  1232                 hb_position_t y_offset = posInfo[glyphStart].y_offset;
  1233                 details->mYOffset = yPos -
  1234                     (roundY ? appUnitsPerDevUnit * FixedToIntRound(y_offset)
  1235                      : floor(hb2appUnits * y_offset + 0.5));
  1237                 hb_position_t y_advance = posInfo[glyphStart].y_advance;
  1238                 if (y_advance != 0) {
  1239                     yPos -=
  1240                         roundY ? appUnitsPerDevUnit * FixedToIntRound(y_advance)
  1241                         : floor(hb2appUnits * y_advance + 0.5);
  1243                 if (++glyphStart >= glyphEnd) {
  1244                     break;
  1247                 x_offset = posInfo[glyphStart].x_offset;
  1248                 x_advance = posInfo[glyphStart].x_advance;
  1249                 if (roundX) {
  1250                     xOffset = appUnitsPerDevUnit *
  1251                         FixedToIntRound(x_offset + x_residual);
  1252                     // Desired distance to the next reference point.  The
  1253                     // residual is considered here, and includes the residual
  1254                     // from the base glyph offset and subsequent advances, so
  1255                     // that the distance from the base glyph is optimized
  1256                     // rather than the distance from combining marks.
  1257                     x_advance += x_residual;
  1258                     int intAdvance = FixedToIntRound(x_advance);
  1259                     x_residual = x_advance - FloatToFixed(intAdvance);
  1260                     advance = appUnitsPerDevUnit * intAdvance;
  1261                 } else {
  1262                     xOffset = floor(hb2appUnits * x_offset + 0.5);
  1263                     advance = floor(hb2appUnits * x_advance + 0.5);
  1267             gfxShapedText::CompressedGlyph g;
  1268             g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
  1269                          true, detailedGlyphs.Length());
  1270             aShapedText->SetGlyphs(aOffset + baseCharIndex,
  1271                                    g, detailedGlyphs.Elements());
  1273             detailedGlyphs.Clear();
  1276         // the rest of the chars in the group are ligature continuations,
  1277         // no associated glyphs
  1278         while (++baseCharIndex != endCharIndex &&
  1279                baseCharIndex < int32_t(wordLength)) {
  1280             gfxShapedText::CompressedGlyph &g = charGlyphs[baseCharIndex];
  1281             NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
  1282             g.SetComplex(g.IsClusterStart(), false, 0);
  1285         glyphStart = glyphEnd;
  1286         charStart = charEnd;
  1289     return NS_OK;

mercurial