gfx/thebes/gfxHarfBuzzShaper.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/thebes/gfxHarfBuzzShaper.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1290 @@
     1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsString.h"
    1.10 +#include "gfxContext.h"
    1.11 +#include "gfxHarfBuzzShaper.h"
    1.12 +#include "gfxFontUtils.h"
    1.13 +#include "nsUnicodeProperties.h"
    1.14 +#include "nsUnicodeScriptCodes.h"
    1.15 +#include "nsUnicodeNormalizer.h"
    1.16 +
    1.17 +#include "harfbuzz/hb.h"
    1.18 +#include "harfbuzz/hb-ot.h"
    1.19 +
    1.20 +#include <algorithm>
    1.21 +
    1.22 +#define FloatToFixed(f) (65536 * (f))
    1.23 +#define FixedToFloat(f) ((f) * (1.0 / 65536.0))
    1.24 +// Right shifts of negative (signed) integers are undefined, as are overflows
    1.25 +// when converting unsigned to negative signed integers.
    1.26 +// (If speed were an issue we could make some 2's complement assumptions.)
    1.27 +#define FixedToIntRound(f) ((f) > 0 ?  ((32768 + (f)) >> 16) \
    1.28 +                                    : -((32767 - (f)) >> 16))
    1.29 +
    1.30 +using namespace mozilla; // for AutoSwap_* types
    1.31 +using namespace mozilla::unicode; // for Unicode property lookup
    1.32 +
    1.33 +/*
    1.34 + * Creation and destruction; on deletion, release any font tables we're holding
    1.35 + */
    1.36 +
    1.37 +gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfxFont *aFont)
    1.38 +    : gfxFontShaper(aFont),
    1.39 +      mHBFace(aFont->GetFontEntry()->GetHBFace()),
    1.40 +      mHBFont(nullptr),
    1.41 +      mKernTable(nullptr),
    1.42 +      mHmtxTable(nullptr),
    1.43 +      mNumLongMetrics(0),
    1.44 +      mCmapTable(nullptr),
    1.45 +      mCmapFormat(-1),
    1.46 +      mSubtableOffset(0),
    1.47 +      mUVSTableOffset(0),
    1.48 +      mUseFontGetGlyph(aFont->ProvidesGetGlyph()),
    1.49 +      mUseFontGlyphWidths(false),
    1.50 +      mInitialized(false)
    1.51 +{
    1.52 +}
    1.53 +
    1.54 +gfxHarfBuzzShaper::~gfxHarfBuzzShaper()
    1.55 +{
    1.56 +    if (mCmapTable) {
    1.57 +        hb_blob_destroy(mCmapTable);
    1.58 +    }
    1.59 +    if (mHmtxTable) {
    1.60 +        hb_blob_destroy(mHmtxTable);
    1.61 +    }
    1.62 +    if (mKernTable) {
    1.63 +        hb_blob_destroy(mKernTable);
    1.64 +    }
    1.65 +    if (mHBFont) {
    1.66 +        hb_font_destroy(mHBFont);
    1.67 +    }
    1.68 +    if (mHBFace) {
    1.69 +        hb_face_destroy(mHBFace);
    1.70 +    }
    1.71 +}
    1.72 +
    1.73 +#define UNICODE_BMP_LIMIT 0x10000
    1.74 +
    1.75 +hb_codepoint_t
    1.76 +gfxHarfBuzzShaper::GetGlyph(hb_codepoint_t unicode,
    1.77 +                            hb_codepoint_t variation_selector) const
    1.78 +{
    1.79 +    hb_codepoint_t gid = 0;
    1.80 +
    1.81 +    if (mUseFontGetGlyph) {
    1.82 +        gid = mFont->GetGlyph(unicode, variation_selector);
    1.83 +    } else {
    1.84 +        // we only instantiate a harfbuzz shaper if there's a cmap available
    1.85 +        NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(),
    1.86 +                     "we cannot be using this font!");
    1.87 +
    1.88 +        NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
    1.89 +                     "cmap data not correctly set up, expect disaster");
    1.90 +
    1.91 +        const uint8_t* data =
    1.92 +            (const uint8_t*)hb_blob_get_data(mCmapTable, nullptr);
    1.93 +
    1.94 +        if (variation_selector) {
    1.95 +            if (mUVSTableOffset) {
    1.96 +                gid =
    1.97 +                    gfxFontUtils::MapUVSToGlyphFormat14(data + mUVSTableOffset,
    1.98 +                                                        unicode,
    1.99 +                                                        variation_selector);
   1.100 +            }
   1.101 +            if (!gid) {
   1.102 +                uint32_t compat =
   1.103 +                    gfxFontUtils::GetUVSFallback(unicode, variation_selector);
   1.104 +                if (compat) {
   1.105 +                    switch (mCmapFormat) {
   1.106 +                    case 4:
   1.107 +                        if (compat < UNICODE_BMP_LIMIT) {
   1.108 +                            gid = gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset,
   1.109 +                                                                      compat);
   1.110 +                        }
   1.111 +                        break;
   1.112 +                    case 12:
   1.113 +                        gid = gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset,
   1.114 +                                                                   compat);
   1.115 +                        break;
   1.116 +                    }
   1.117 +                }
   1.118 +            }
   1.119 +            // If the variation sequence was not supported, return zero here;
   1.120 +            // harfbuzz will call us again for the base character alone
   1.121 +            return gid;
   1.122 +        }
   1.123 +
   1.124 +        switch (mCmapFormat) {
   1.125 +        case 4:
   1.126 +            gid = unicode < UNICODE_BMP_LIMIT ?
   1.127 +                gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset,
   1.128 +                                                    unicode) : 0;
   1.129 +            break;
   1.130 +        case 12:
   1.131 +            gid = gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset,
   1.132 +                                                       unicode);
   1.133 +            break;
   1.134 +        default:
   1.135 +            NS_WARNING("unsupported cmap format, glyphs will be missing");
   1.136 +            break;
   1.137 +        }
   1.138 +    }
   1.139 +
   1.140 +    if (!gid) {
   1.141 +        // if there's no glyph for &nbsp;, just use the space glyph instead
   1.142 +        if (unicode == 0xA0) {
   1.143 +            gid = mFont->GetSpaceGlyph();
   1.144 +        }
   1.145 +    }
   1.146 +
   1.147 +    return gid;
   1.148 +}
   1.149 +
   1.150 +static hb_bool_t
   1.151 +HBGetGlyph(hb_font_t *font, void *font_data,
   1.152 +           hb_codepoint_t unicode, hb_codepoint_t variation_selector,
   1.153 +           hb_codepoint_t *glyph,
   1.154 +           void *user_data)
   1.155 +{
   1.156 +    const gfxHarfBuzzShaper::FontCallbackData *fcd =
   1.157 +        static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
   1.158 +    *glyph = fcd->mShaper->GetGlyph(unicode, variation_selector);
   1.159 +    return *glyph != 0;
   1.160 +}
   1.161 +
   1.162 +struct HMetricsHeader {
   1.163 +    AutoSwap_PRUint32    tableVersionNumber;
   1.164 +    AutoSwap_PRInt16     ascender;
   1.165 +    AutoSwap_PRInt16     descender;
   1.166 +    AutoSwap_PRInt16     lineGap;
   1.167 +    AutoSwap_PRUint16    advanceWidthMax;
   1.168 +    AutoSwap_PRInt16     minLeftSideBearing;
   1.169 +    AutoSwap_PRInt16     minRightSideBearing;
   1.170 +    AutoSwap_PRInt16     xMaxExtent;
   1.171 +    AutoSwap_PRInt16     caretSlopeRise;
   1.172 +    AutoSwap_PRInt16     caretSlopeRun;
   1.173 +    AutoSwap_PRInt16     caretOffset;
   1.174 +    AutoSwap_PRInt16     reserved[4];
   1.175 +    AutoSwap_PRInt16     metricDataFormat;
   1.176 +    AutoSwap_PRUint16    numberOfHMetrics;
   1.177 +};
   1.178 +
   1.179 +struct HLongMetric {
   1.180 +    AutoSwap_PRUint16    advanceWidth;
   1.181 +    AutoSwap_PRInt16     lsb;
   1.182 +};
   1.183 +
   1.184 +struct HMetrics {
   1.185 +    HLongMetric          metrics[1]; // actually numberOfHMetrics
   1.186 +// the variable-length metrics[] array is immediately followed by:
   1.187 +//  AutoSwap_PRUint16    leftSideBearing[];
   1.188 +};
   1.189 +
   1.190 +hb_position_t
   1.191 +gfxHarfBuzzShaper::GetGlyphHAdvance(gfxContext *aContext,
   1.192 +                                    hb_codepoint_t glyph) const
   1.193 +{
   1.194 +    // font did not implement GetHintedGlyphWidth, so get an unhinted value
   1.195 +    // directly from the font tables
   1.196 +
   1.197 +    NS_ASSERTION((mNumLongMetrics > 0) && mHmtxTable != nullptr,
   1.198 +                 "font is lacking metrics, we shouldn't be here");
   1.199 +
   1.200 +    if (glyph >= uint32_t(mNumLongMetrics)) {
   1.201 +        glyph = mNumLongMetrics - 1;
   1.202 +    }
   1.203 +
   1.204 +    // glyph must be valid now, because we checked during initialization
   1.205 +    // that mNumLongMetrics is > 0, and that the hmtx table is large enough
   1.206 +    // to contain mNumLongMetrics records
   1.207 +    const HMetrics* hmtx =
   1.208 +        reinterpret_cast<const HMetrics*>(hb_blob_get_data(mHmtxTable, nullptr));
   1.209 +    return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
   1.210 +                        uint16_t(hmtx->metrics[glyph].advanceWidth));
   1.211 +}
   1.212 +
   1.213 +/* static */
   1.214 +hb_position_t
   1.215 +gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
   1.216 +                                      hb_codepoint_t glyph, void *user_data)
   1.217 +{
   1.218 +    const gfxHarfBuzzShaper::FontCallbackData *fcd =
   1.219 +        static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
   1.220 +    gfxFont *gfxfont = fcd->mShaper->GetFont();
   1.221 +    if (gfxfont->ProvidesGlyphWidths()) {
   1.222 +        return gfxfont->GetGlyphWidth(fcd->mContext, glyph);
   1.223 +    } else {
   1.224 +        return fcd->mShaper->GetGlyphHAdvance(fcd->mContext, glyph);
   1.225 +    }
   1.226 +}
   1.227 +
   1.228 +static hb_bool_t
   1.229 +HBGetContourPoint(hb_font_t *font, void *font_data,
   1.230 +                  unsigned int point_index, hb_codepoint_t glyph,
   1.231 +                  hb_position_t *x, hb_position_t *y,
   1.232 +                  void *user_data)
   1.233 +{
   1.234 +    /* not yet implemented - no support for used of hinted contour points
   1.235 +       to fine-tune anchor positions in GPOS AnchorFormat2 */
   1.236 +    return false;
   1.237 +}
   1.238 +
   1.239 +struct KernHeaderFmt0 {
   1.240 +    AutoSwap_PRUint16 nPairs;
   1.241 +    AutoSwap_PRUint16 searchRange;
   1.242 +    AutoSwap_PRUint16 entrySelector;
   1.243 +    AutoSwap_PRUint16 rangeShift;
   1.244 +};
   1.245 +
   1.246 +struct KernPair {
   1.247 +    AutoSwap_PRUint16 left;
   1.248 +    AutoSwap_PRUint16 right;
   1.249 +    AutoSwap_PRInt16  value;
   1.250 +};
   1.251 +
   1.252 +// Find a kern pair in a Format 0 subtable.
   1.253 +// The aSubtable parameter points to the subtable itself, NOT its header,
   1.254 +// as the header structure differs between Windows and Mac (v0 and v1.0)
   1.255 +// versions of the 'kern' table.
   1.256 +// aSubtableLen is the length of the subtable EXCLUDING its header.
   1.257 +// If the pair <aFirstGlyph,aSecondGlyph> is found, the kerning value is
   1.258 +// added to aValue, so that multiple subtables can accumulate a total
   1.259 +// kerning value for a given pair.
   1.260 +static void
   1.261 +GetKernValueFmt0(const void* aSubtable,
   1.262 +                 uint32_t aSubtableLen,
   1.263 +                 uint16_t aFirstGlyph,
   1.264 +                 uint16_t aSecondGlyph,
   1.265 +                 int32_t& aValue,
   1.266 +                 bool     aIsOverride = false,
   1.267 +                 bool     aIsMinimum = false)
   1.268 +{
   1.269 +    const KernHeaderFmt0* hdr =
   1.270 +        reinterpret_cast<const KernHeaderFmt0*>(aSubtable);
   1.271 +
   1.272 +    const KernPair *lo = reinterpret_cast<const KernPair*>(hdr + 1);
   1.273 +    const KernPair *hi = lo + uint16_t(hdr->nPairs);
   1.274 +    const KernPair *limit = hi;
   1.275 +
   1.276 +    if (reinterpret_cast<const char*>(aSubtable) + aSubtableLen <
   1.277 +        reinterpret_cast<const char*>(hi)) {
   1.278 +        // subtable is not large enough to contain the claimed number
   1.279 +        // of kern pairs, so just ignore it
   1.280 +        return;
   1.281 +    }
   1.282 +
   1.283 +#define KERN_PAIR_KEY(l,r) (uint32_t((uint16_t(l) << 16) + uint16_t(r)))
   1.284 +
   1.285 +    uint32_t key = KERN_PAIR_KEY(aFirstGlyph, aSecondGlyph);
   1.286 +    while (lo < hi) {
   1.287 +        const KernPair *mid = lo + (hi - lo) / 2;
   1.288 +        if (KERN_PAIR_KEY(mid->left, mid->right) < key) {
   1.289 +            lo = mid + 1;
   1.290 +        } else {
   1.291 +            hi = mid;
   1.292 +        }
   1.293 +    }
   1.294 +
   1.295 +    if (lo < limit && KERN_PAIR_KEY(lo->left, lo->right) == key) {
   1.296 +        if (aIsOverride) {
   1.297 +            aValue = int16_t(lo->value);
   1.298 +        } else if (aIsMinimum) {
   1.299 +            aValue = std::max(aValue, int32_t(lo->value));
   1.300 +        } else {
   1.301 +            aValue += int16_t(lo->value);
   1.302 +        }
   1.303 +    }
   1.304 +}
   1.305 +
   1.306 +// Get kerning value from Apple (version 1.0) kern table,
   1.307 +// subtable format 2 (simple N x M array of kerning values)
   1.308 +
   1.309 +// See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
   1.310 +// for details of version 1.0 format 2 subtable.
   1.311 +
   1.312 +struct KernHeaderVersion1Fmt2 {
   1.313 +    KernTableSubtableHeaderVersion1 header;
   1.314 +    AutoSwap_PRUint16 rowWidth;
   1.315 +    AutoSwap_PRUint16 leftOffsetTable;
   1.316 +    AutoSwap_PRUint16 rightOffsetTable;
   1.317 +    AutoSwap_PRUint16 array;
   1.318 +};
   1.319 +
   1.320 +struct KernClassTableHdr {
   1.321 +    AutoSwap_PRUint16 firstGlyph;
   1.322 +    AutoSwap_PRUint16 nGlyphs;
   1.323 +    AutoSwap_PRUint16 offsets[1]; // actually an array of nGlyphs entries	
   1.324 +};
   1.325 +
   1.326 +static int16_t
   1.327 +GetKernValueVersion1Fmt2(const void* aSubtable,
   1.328 +                         uint32_t aSubtableLen,
   1.329 +                         uint16_t aFirstGlyph,
   1.330 +                         uint16_t aSecondGlyph)
   1.331 +{
   1.332 +    if (aSubtableLen < sizeof(KernHeaderVersion1Fmt2)) {
   1.333 +        return 0;
   1.334 +    }
   1.335 +
   1.336 +    const char* base = reinterpret_cast<const char*>(aSubtable);
   1.337 +    const char* subtableEnd = base + aSubtableLen;
   1.338 +
   1.339 +    const KernHeaderVersion1Fmt2* h =
   1.340 +        reinterpret_cast<const KernHeaderVersion1Fmt2*>(aSubtable);
   1.341 +    uint32_t offset = h->array;
   1.342 +
   1.343 +    const KernClassTableHdr* leftClassTable =
   1.344 +        reinterpret_cast<const KernClassTableHdr*>(base +
   1.345 +                                                   uint16_t(h->leftOffsetTable));
   1.346 +    if (reinterpret_cast<const char*>(leftClassTable) +
   1.347 +        sizeof(KernClassTableHdr) > subtableEnd) {
   1.348 +        return 0;
   1.349 +    }
   1.350 +    if (aFirstGlyph >= uint16_t(leftClassTable->firstGlyph)) {
   1.351 +        aFirstGlyph -= uint16_t(leftClassTable->firstGlyph);
   1.352 +        if (aFirstGlyph < uint16_t(leftClassTable->nGlyphs)) {
   1.353 +            if (reinterpret_cast<const char*>(leftClassTable) +
   1.354 +                sizeof(KernClassTableHdr) +
   1.355 +                aFirstGlyph * sizeof(uint16_t) >= subtableEnd) {
   1.356 +                return 0;
   1.357 +            }
   1.358 +            offset = uint16_t(leftClassTable->offsets[aFirstGlyph]);
   1.359 +        }
   1.360 +    }
   1.361 +
   1.362 +    const KernClassTableHdr* rightClassTable =
   1.363 +        reinterpret_cast<const KernClassTableHdr*>(base +
   1.364 +                                                   uint16_t(h->rightOffsetTable));
   1.365 +    if (reinterpret_cast<const char*>(rightClassTable) +
   1.366 +        sizeof(KernClassTableHdr) > subtableEnd) {
   1.367 +        return 0;
   1.368 +    }
   1.369 +    if (aSecondGlyph >= uint16_t(rightClassTable->firstGlyph)) {
   1.370 +        aSecondGlyph -= uint16_t(rightClassTable->firstGlyph);
   1.371 +        if (aSecondGlyph < uint16_t(rightClassTable->nGlyphs)) {
   1.372 +            if (reinterpret_cast<const char*>(rightClassTable) +
   1.373 +                sizeof(KernClassTableHdr) +
   1.374 +                aSecondGlyph * sizeof(uint16_t) >= subtableEnd) {
   1.375 +                return 0;
   1.376 +            }
   1.377 +            offset += uint16_t(rightClassTable->offsets[aSecondGlyph]);
   1.378 +        }
   1.379 +    }
   1.380 +
   1.381 +    const AutoSwap_PRInt16* pval =
   1.382 +        reinterpret_cast<const AutoSwap_PRInt16*>(base + offset);
   1.383 +    if (reinterpret_cast<const char*>(pval + 1) >= subtableEnd) {
   1.384 +        return 0;
   1.385 +    }
   1.386 +    return *pval;
   1.387 +}
   1.388 +
   1.389 +// Get kerning value from Apple (version 1.0) kern table,
   1.390 +// subtable format 3 (simple N x M array of kerning values)
   1.391 +
   1.392 +// See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
   1.393 +// for details of version 1.0 format 3 subtable.
   1.394 +
   1.395 +struct KernHeaderVersion1Fmt3 {
   1.396 +    KernTableSubtableHeaderVersion1 header;
   1.397 +    AutoSwap_PRUint16 glyphCount;
   1.398 +    uint8_t kernValueCount;
   1.399 +    uint8_t leftClassCount;
   1.400 +    uint8_t rightClassCount;
   1.401 +    uint8_t flags;
   1.402 +};
   1.403 +
   1.404 +static int16_t
   1.405 +GetKernValueVersion1Fmt3(const void* aSubtable,
   1.406 +                         uint32_t aSubtableLen,
   1.407 +                         uint16_t aFirstGlyph,
   1.408 +                         uint16_t aSecondGlyph)
   1.409 +{
   1.410 +    // check that we can safely read the header fields
   1.411 +    if (aSubtableLen < sizeof(KernHeaderVersion1Fmt3)) {
   1.412 +        return 0;
   1.413 +    }
   1.414 +
   1.415 +    const KernHeaderVersion1Fmt3* hdr =
   1.416 +        reinterpret_cast<const KernHeaderVersion1Fmt3*>(aSubtable);
   1.417 +    if (hdr->flags != 0) {
   1.418 +        return 0;
   1.419 +    }
   1.420 +
   1.421 +    uint16_t glyphCount = hdr->glyphCount;
   1.422 +
   1.423 +    // check that table is large enough for the arrays
   1.424 +    if (sizeof(KernHeaderVersion1Fmt3) +
   1.425 +        hdr->kernValueCount * sizeof(int16_t) +
   1.426 +        glyphCount + glyphCount +
   1.427 +        hdr->leftClassCount * hdr->rightClassCount > aSubtableLen) {
   1.428 +        return 0;
   1.429 +    }
   1.430 +        
   1.431 +    if (aFirstGlyph >= glyphCount || aSecondGlyph >= glyphCount) {
   1.432 +        // glyphs are out of range for the class tables
   1.433 +        return 0;
   1.434 +    }
   1.435 +
   1.436 +    // get pointers to the four arrays within the subtable
   1.437 +    const AutoSwap_PRInt16* kernValue =
   1.438 +        reinterpret_cast<const AutoSwap_PRInt16*>(hdr + 1);
   1.439 +    const uint8_t* leftClass =
   1.440 +        reinterpret_cast<const uint8_t*>(kernValue + hdr->kernValueCount);
   1.441 +    const uint8_t* rightClass = leftClass + glyphCount;
   1.442 +    const uint8_t* kernIndex = rightClass + glyphCount;
   1.443 +
   1.444 +    uint8_t lc = leftClass[aFirstGlyph];
   1.445 +    uint8_t rc = rightClass[aSecondGlyph];
   1.446 +    if (lc >= hdr->leftClassCount || rc >= hdr->rightClassCount) {
   1.447 +        return 0;
   1.448 +    }
   1.449 +
   1.450 +    uint8_t ki = kernIndex[leftClass[aFirstGlyph] * hdr->rightClassCount +
   1.451 +                           rightClass[aSecondGlyph]];
   1.452 +    if (ki >= hdr->kernValueCount) {
   1.453 +        return 0;
   1.454 +    }
   1.455 +
   1.456 +    return kernValue[ki];
   1.457 +}
   1.458 +
   1.459 +#define KERN0_COVERAGE_HORIZONTAL   0x0001
   1.460 +#define KERN0_COVERAGE_MINIMUM      0x0002
   1.461 +#define KERN0_COVERAGE_CROSS_STREAM 0x0004
   1.462 +#define KERN0_COVERAGE_OVERRIDE     0x0008
   1.463 +#define KERN0_COVERAGE_RESERVED     0x00F0
   1.464 +
   1.465 +#define KERN1_COVERAGE_VERTICAL     0x8000
   1.466 +#define KERN1_COVERAGE_CROSS_STREAM 0x4000
   1.467 +#define KERN1_COVERAGE_VARIATION    0x2000
   1.468 +#define KERN1_COVERAGE_RESERVED     0x1F00
   1.469 +
   1.470 +hb_position_t
   1.471 +gfxHarfBuzzShaper::GetHKerning(uint16_t aFirstGlyph,
   1.472 +                               uint16_t aSecondGlyph) const
   1.473 +{
   1.474 +    // We want to ignore any kern pairs involving <space>, because we are
   1.475 +    // handling words in isolation, the only space characters seen here are
   1.476 +    // the ones artificially added by the textRun code.
   1.477 +    uint32_t spaceGlyph = mFont->GetSpaceGlyph();
   1.478 +    if (aFirstGlyph == spaceGlyph || aSecondGlyph == spaceGlyph) {
   1.479 +        return 0;
   1.480 +    }
   1.481 +
   1.482 +    if (!mKernTable) {
   1.483 +        mKernTable = mFont->GetFontEntry()->GetFontTable(TRUETYPE_TAG('k','e','r','n'));
   1.484 +        if (!mKernTable) {
   1.485 +            mKernTable = hb_blob_get_empty();
   1.486 +        }
   1.487 +    }
   1.488 +
   1.489 +    uint32_t len;
   1.490 +    const char* base = hb_blob_get_data(mKernTable, &len);
   1.491 +    if (len < sizeof(KernTableVersion0)) {
   1.492 +        return 0;
   1.493 +    }
   1.494 +    int32_t value = 0;
   1.495 +
   1.496 +    // First try to interpret as "version 0" kern table
   1.497 +    // (see http://www.microsoft.com/typography/otspec/kern.htm)
   1.498 +    const KernTableVersion0* kern0 =
   1.499 +        reinterpret_cast<const KernTableVersion0*>(base);
   1.500 +    if (uint16_t(kern0->version) == 0) {
   1.501 +        uint16_t nTables = kern0->nTables;
   1.502 +        uint32_t offs = sizeof(KernTableVersion0);
   1.503 +        for (uint16_t i = 0; i < nTables; ++i) {
   1.504 +            if (offs + sizeof(KernTableSubtableHeaderVersion0) > len) {
   1.505 +                break;
   1.506 +            }
   1.507 +            const KernTableSubtableHeaderVersion0* st0 =
   1.508 +                reinterpret_cast<const KernTableSubtableHeaderVersion0*>
   1.509 +                                (base + offs);
   1.510 +            uint16_t subtableLen = uint16_t(st0->length);
   1.511 +            if (offs + subtableLen > len) {
   1.512 +                break;
   1.513 +            }
   1.514 +            offs += subtableLen;
   1.515 +            uint16_t coverage = st0->coverage;
   1.516 +            if (!(coverage & KERN0_COVERAGE_HORIZONTAL)) {
   1.517 +                // we only care about horizontal kerning (for now)
   1.518 +                continue;
   1.519 +            }
   1.520 +            if (coverage &
   1.521 +                (KERN0_COVERAGE_CROSS_STREAM | KERN0_COVERAGE_RESERVED)) {
   1.522 +                // we don't support cross-stream kerning, and
   1.523 +                // reserved bits should be zero;
   1.524 +                // ignore the subtable if not
   1.525 +                continue;
   1.526 +            }
   1.527 +            uint8_t format = (coverage >> 8);
   1.528 +            switch (format) {
   1.529 +            case 0:
   1.530 +                GetKernValueFmt0(st0 + 1, subtableLen - sizeof(*st0),
   1.531 +                                 aFirstGlyph, aSecondGlyph, value,
   1.532 +                                 (coverage & KERN0_COVERAGE_OVERRIDE) != 0,
   1.533 +                                 (coverage & KERN0_COVERAGE_MINIMUM) != 0);
   1.534 +                break;
   1.535 +            default:
   1.536 +                // TODO: implement support for other formats,
   1.537 +                // if they're ever used in practice
   1.538 +#if DEBUG
   1.539 +                {
   1.540 +                    char buf[1024];
   1.541 +                    sprintf(buf, "unknown kern subtable in %s: "
   1.542 +                                 "ver 0 format %d\n",
   1.543 +                            NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
   1.544 +                            format);
   1.545 +                    NS_WARNING(buf);
   1.546 +                }
   1.547 +#endif
   1.548 +                break;
   1.549 +            }
   1.550 +        }
   1.551 +    } else {
   1.552 +        // It wasn't a "version 0" table; check if it is Apple version 1.0
   1.553 +        // (see http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html)
   1.554 +        const KernTableVersion1* kern1 =
   1.555 +            reinterpret_cast<const KernTableVersion1*>(base);
   1.556 +        if (uint32_t(kern1->version) == 0x00010000) {
   1.557 +            uint32_t nTables = kern1->nTables;
   1.558 +            uint32_t offs = sizeof(KernTableVersion1);
   1.559 +            for (uint32_t i = 0; i < nTables; ++i) {
   1.560 +                if (offs + sizeof(KernTableSubtableHeaderVersion1) > len) {
   1.561 +                    break;
   1.562 +                }
   1.563 +                const KernTableSubtableHeaderVersion1* st1 =
   1.564 +                    reinterpret_cast<const KernTableSubtableHeaderVersion1*>
   1.565 +                                    (base + offs);
   1.566 +                uint32_t subtableLen = uint32_t(st1->length);
   1.567 +                offs += subtableLen;
   1.568 +                uint16_t coverage = st1->coverage;
   1.569 +                if (coverage &
   1.570 +                    (KERN1_COVERAGE_VERTICAL     |
   1.571 +                     KERN1_COVERAGE_CROSS_STREAM |
   1.572 +                     KERN1_COVERAGE_VARIATION    |
   1.573 +                     KERN1_COVERAGE_RESERVED)) {
   1.574 +                    // we only care about horizontal kerning (for now),
   1.575 +                    // we don't support cross-stream kerning,
   1.576 +                    // we don't support variations,
   1.577 +                    // reserved bits should be zero;
   1.578 +                    // ignore the subtable if not
   1.579 +                    continue;
   1.580 +                }
   1.581 +                uint8_t format = (coverage & 0xff);
   1.582 +                switch (format) {
   1.583 +                case 0:
   1.584 +                    GetKernValueFmt0(st1 + 1, subtableLen - sizeof(*st1),
   1.585 +                                     aFirstGlyph, aSecondGlyph, value);
   1.586 +                    break;
   1.587 +                case 2:
   1.588 +                    value = GetKernValueVersion1Fmt2(st1, subtableLen,
   1.589 +                                                     aFirstGlyph, aSecondGlyph);
   1.590 +                    break;
   1.591 +                case 3:
   1.592 +                    value = GetKernValueVersion1Fmt3(st1, subtableLen,
   1.593 +                                                     aFirstGlyph, aSecondGlyph);
   1.594 +                    break;
   1.595 +                default:
   1.596 +                    // TODO: implement support for other formats.
   1.597 +                    // Note that format 1 cannot be supported here,
   1.598 +                    // as it requires the full glyph array to run the FSM,
   1.599 +                    // not just the current glyph pair.
   1.600 +#if DEBUG
   1.601 +                    {
   1.602 +                        char buf[1024];
   1.603 +                        sprintf(buf, "unknown kern subtable in %s: "
   1.604 +                                     "ver 0 format %d\n",
   1.605 +                                NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
   1.606 +                                format);
   1.607 +                        NS_WARNING(buf);
   1.608 +                    }
   1.609 +#endif
   1.610 +                    break;
   1.611 +                }
   1.612 +            }
   1.613 +        }
   1.614 +    }
   1.615 +
   1.616 +    if (value != 0) {
   1.617 +        return FloatToFixed(mFont->FUnitsToDevUnitsFactor() * value);
   1.618 +    }
   1.619 +    return 0;
   1.620 +}
   1.621 +
   1.622 +static hb_position_t
   1.623 +HBGetHKerning(hb_font_t *font, void *font_data,
   1.624 +              hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
   1.625 +              void *user_data)
   1.626 +{
   1.627 +    const gfxHarfBuzzShaper::FontCallbackData *fcd =
   1.628 +        static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
   1.629 +    return fcd->mShaper->GetHKerning(first_glyph, second_glyph);
   1.630 +}
   1.631 +
   1.632 +/*
   1.633 + * HarfBuzz unicode property callbacks
   1.634 + */
   1.635 +
   1.636 +static hb_codepoint_t
   1.637 +HBGetMirroring(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh,
   1.638 +               void *user_data)
   1.639 +{
   1.640 +    return GetMirroredChar(aCh);
   1.641 +}
   1.642 +
   1.643 +static hb_unicode_general_category_t
   1.644 +HBGetGeneralCategory(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh,
   1.645 +                     void *user_data)
   1.646 +{
   1.647 +    return hb_unicode_general_category_t(GetGeneralCategory(aCh));
   1.648 +}
   1.649 +
   1.650 +static hb_script_t
   1.651 +HBGetScript(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data)
   1.652 +{
   1.653 +    return hb_script_t(GetScriptTagForCode(GetScriptCode(aCh)));
   1.654 +}
   1.655 +
   1.656 +static hb_unicode_combining_class_t
   1.657 +HBGetCombiningClass(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh,
   1.658 +                    void *user_data)
   1.659 +{
   1.660 +    return hb_unicode_combining_class_t(GetCombiningClass(aCh));
   1.661 +}
   1.662 +
   1.663 +static unsigned int
   1.664 +HBGetEastAsianWidth(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh,
   1.665 +                    void *user_data)
   1.666 +{
   1.667 +    return GetEastAsianWidth(aCh);
   1.668 +}
   1.669 +
   1.670 +// Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
   1.671 +// note that some letters do not have a dagesh presForm encoded
   1.672 +static const char16_t sDageshForms[0x05EA - 0x05D0 + 1] = {
   1.673 +    0xFB30, // ALEF
   1.674 +    0xFB31, // BET
   1.675 +    0xFB32, // GIMEL
   1.676 +    0xFB33, // DALET
   1.677 +    0xFB34, // HE
   1.678 +    0xFB35, // VAV
   1.679 +    0xFB36, // ZAYIN
   1.680 +    0, // HET
   1.681 +    0xFB38, // TET
   1.682 +    0xFB39, // YOD
   1.683 +    0xFB3A, // FINAL KAF
   1.684 +    0xFB3B, // KAF
   1.685 +    0xFB3C, // LAMED
   1.686 +    0, // FINAL MEM
   1.687 +    0xFB3E, // MEM
   1.688 +    0, // FINAL NUN
   1.689 +    0xFB40, // NUN
   1.690 +    0xFB41, // SAMEKH
   1.691 +    0, // AYIN
   1.692 +    0xFB43, // FINAL PE
   1.693 +    0xFB44, // PE
   1.694 +    0, // FINAL TSADI
   1.695 +    0xFB46, // TSADI
   1.696 +    0xFB47, // QOF
   1.697 +    0xFB48, // RESH
   1.698 +    0xFB49, // SHIN
   1.699 +    0xFB4A // TAV
   1.700 +};
   1.701 +
   1.702 +static hb_bool_t
   1.703 +HBUnicodeCompose(hb_unicode_funcs_t *ufuncs,
   1.704 +                 hb_codepoint_t      a,
   1.705 +                 hb_codepoint_t      b,
   1.706 +                 hb_codepoint_t     *ab,
   1.707 +                 void               *user_data)
   1.708 +{
   1.709 +    hb_bool_t found = nsUnicodeNormalizer::Compose(a, b, ab);
   1.710 +
   1.711 +    if (!found && (b & 0x1fff80) == 0x0580) {
   1.712 +        // special-case Hebrew presentation forms that are excluded from
   1.713 +        // standard normalization, but wanted for old fonts
   1.714 +        switch (b) {
   1.715 +        case 0x05B4: // HIRIQ
   1.716 +            if (a == 0x05D9) { // YOD
   1.717 +                *ab = 0xFB1D;
   1.718 +                found = true;
   1.719 +            }
   1.720 +            break;
   1.721 +        case 0x05B7: // patah
   1.722 +            if (a == 0x05F2) { // YIDDISH YOD YOD
   1.723 +                *ab = 0xFB1F;
   1.724 +                found = true;
   1.725 +            } else if (a == 0x05D0) { // ALEF
   1.726 +                *ab = 0xFB2E;
   1.727 +                found = true;
   1.728 +            }
   1.729 +            break;
   1.730 +        case 0x05B8: // QAMATS
   1.731 +            if (a == 0x05D0) { // ALEF
   1.732 +                *ab = 0xFB2F;
   1.733 +                found = true;
   1.734 +            }
   1.735 +            break;
   1.736 +        case 0x05B9: // HOLAM
   1.737 +            if (a == 0x05D5) { // VAV
   1.738 +                *ab = 0xFB4B;
   1.739 +                found = true;
   1.740 +            }
   1.741 +            break;
   1.742 +        case 0x05BC: // DAGESH
   1.743 +            if (a >= 0x05D0 && a <= 0x05EA) {
   1.744 +                *ab = sDageshForms[a - 0x05D0];
   1.745 +                found = (*ab != 0);
   1.746 +            } else if (a == 0xFB2A) { // SHIN WITH SHIN DOT
   1.747 +                *ab = 0xFB2C;
   1.748 +                found = true;
   1.749 +            } else if (a == 0xFB2B) { // SHIN WITH SIN DOT
   1.750 +                *ab = 0xFB2D;
   1.751 +                found = true;
   1.752 +            }
   1.753 +            break;
   1.754 +        case 0x05BF: // RAFE
   1.755 +            switch (a) {
   1.756 +            case 0x05D1: // BET
   1.757 +                *ab = 0xFB4C;
   1.758 +                found = true;
   1.759 +                break;
   1.760 +            case 0x05DB: // KAF
   1.761 +                *ab = 0xFB4D;
   1.762 +                found = true;
   1.763 +                break;
   1.764 +            case 0x05E4: // PE
   1.765 +                *ab = 0xFB4E;
   1.766 +                found = true;
   1.767 +                break;
   1.768 +            }
   1.769 +            break;
   1.770 +        case 0x05C1: // SHIN DOT
   1.771 +            if (a == 0x05E9) { // SHIN
   1.772 +                *ab = 0xFB2A;
   1.773 +                found = true;
   1.774 +            } else if (a == 0xFB49) { // SHIN WITH DAGESH
   1.775 +                *ab = 0xFB2C;
   1.776 +                found = true;
   1.777 +            }
   1.778 +            break;
   1.779 +        case 0x05C2: // SIN DOT
   1.780 +            if (a == 0x05E9) { // SHIN
   1.781 +                *ab = 0xFB2B;
   1.782 +                found = true;
   1.783 +            } else if (a == 0xFB49) { // SHIN WITH DAGESH
   1.784 +                *ab = 0xFB2D;
   1.785 +                found = true;
   1.786 +            }
   1.787 +            break;
   1.788 +        }
   1.789 +    }
   1.790 +
   1.791 +    return found;
   1.792 +}
   1.793 +
   1.794 +static hb_bool_t
   1.795 +HBUnicodeDecompose(hb_unicode_funcs_t *ufuncs,
   1.796 +                   hb_codepoint_t      ab,
   1.797 +                   hb_codepoint_t     *a,
   1.798 +                   hb_codepoint_t     *b,
   1.799 +                   void               *user_data)
   1.800 +{
   1.801 +#ifdef MOZ_WIDGET_ANDROID
   1.802 +    // Hack for the SamsungDevanagari font, bug 1012365:
   1.803 +    // support U+0972 by decomposing it.
   1.804 +    if (ab == 0x0972) {
   1.805 +        *a = 0x0905;
   1.806 +        *b = 0x0945;
   1.807 +        return true;
   1.808 +    }
   1.809 +#endif
   1.810 +    return nsUnicodeNormalizer::DecomposeNonRecursively(ab, a, b);
   1.811 +}
   1.812 +
   1.813 +static PLDHashOperator
   1.814 +AddOpenTypeFeature(const uint32_t& aTag, uint32_t& aValue, void *aUserArg)
   1.815 +{
   1.816 +    nsTArray<hb_feature_t>* features = static_cast<nsTArray<hb_feature_t>*> (aUserArg);
   1.817 +
   1.818 +    hb_feature_t feat = { 0, 0, 0, UINT_MAX };
   1.819 +    feat.tag = aTag;
   1.820 +    feat.value = aValue;
   1.821 +    features->AppendElement(feat);
   1.822 +    return PL_DHASH_NEXT;
   1.823 +}
   1.824 +
   1.825 +/*
   1.826 + * gfxFontShaper override to initialize the text run using HarfBuzz
   1.827 + */
   1.828 +
   1.829 +static hb_font_funcs_t * sHBFontFuncs = nullptr;
   1.830 +static hb_unicode_funcs_t * sHBUnicodeFuncs = nullptr;
   1.831 +static const hb_script_t sMathScript =
   1.832 +    hb_ot_tag_to_script(HB_TAG('m','a','t','h'));
   1.833 +
   1.834 +bool
   1.835 +gfxHarfBuzzShaper::Initialize()
   1.836 +{
   1.837 +    if (mInitialized) {
   1.838 +        return mHBFont != nullptr;
   1.839 +    }
   1.840 +    mInitialized = true;
   1.841 +    mCallbackData.mShaper = this;
   1.842 +
   1.843 +    mUseFontGlyphWidths = mFont->ProvidesGlyphWidths();
   1.844 +
   1.845 +    if (!sHBFontFuncs) {
   1.846 +        // static function callback pointers, initialized by the first
   1.847 +        // harfbuzz shaper used
   1.848 +        sHBFontFuncs = hb_font_funcs_create();
   1.849 +        hb_font_funcs_set_glyph_func(sHBFontFuncs, HBGetGlyph,
   1.850 +                                     nullptr, nullptr);
   1.851 +        hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs,
   1.852 +                                               HBGetGlyphHAdvance,
   1.853 +                                               nullptr, nullptr);
   1.854 +        hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
   1.855 +                                                   HBGetContourPoint,
   1.856 +                                                   nullptr, nullptr);
   1.857 +        hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs,
   1.858 +                                               HBGetHKerning,
   1.859 +                                               nullptr, nullptr);
   1.860 +
   1.861 +        sHBUnicodeFuncs =
   1.862 +            hb_unicode_funcs_create(hb_unicode_funcs_get_empty());
   1.863 +        hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs,
   1.864 +                                            HBGetMirroring,
   1.865 +                                            nullptr, nullptr);
   1.866 +        hb_unicode_funcs_set_script_func(sHBUnicodeFuncs, HBGetScript,
   1.867 +                                         nullptr, nullptr);
   1.868 +        hb_unicode_funcs_set_general_category_func(sHBUnicodeFuncs,
   1.869 +                                                   HBGetGeneralCategory,
   1.870 +                                                   nullptr, nullptr);
   1.871 +        hb_unicode_funcs_set_combining_class_func(sHBUnicodeFuncs,
   1.872 +                                                  HBGetCombiningClass,
   1.873 +                                                  nullptr, nullptr);
   1.874 +        hb_unicode_funcs_set_eastasian_width_func(sHBUnicodeFuncs,
   1.875 +                                                  HBGetEastAsianWidth,
   1.876 +                                                  nullptr, nullptr);
   1.877 +        hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs,
   1.878 +                                          HBUnicodeCompose,
   1.879 +                                          nullptr, nullptr);
   1.880 +        hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs,
   1.881 +                                            HBUnicodeDecompose,
   1.882 +                                            nullptr, nullptr);
   1.883 +    }
   1.884 +
   1.885 +    gfxFontEntry *entry = mFont->GetFontEntry();
   1.886 +    if (!mUseFontGetGlyph) {
   1.887 +        // get the cmap table and find offset to our subtable
   1.888 +        mCmapTable = entry->GetFontTable(TRUETYPE_TAG('c','m','a','p'));
   1.889 +        if (!mCmapTable) {
   1.890 +            NS_WARNING("failed to load cmap, glyphs will be missing");
   1.891 +            return false;
   1.892 +        }
   1.893 +        uint32_t len;
   1.894 +        const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &len);
   1.895 +        bool symbol;
   1.896 +        mCmapFormat = gfxFontUtils::
   1.897 +            FindPreferredSubtable(data, len,
   1.898 +                                  &mSubtableOffset, &mUVSTableOffset,
   1.899 +                                  &symbol);
   1.900 +        if (mCmapFormat <= 0) {
   1.901 +            return false;
   1.902 +        }
   1.903 +    }
   1.904 +
   1.905 +    if (!mUseFontGlyphWidths) {
   1.906 +        // if font doesn't implement GetGlyphWidth, we will be reading
   1.907 +        // the hmtx table directly;
   1.908 +        // read mNumLongMetrics from hhea table without caching its blob,
   1.909 +        // and preload/cache the hmtx table
   1.910 +        gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h','h','e','a'));
   1.911 +        if (hheaTable) {
   1.912 +            uint32_t len;
   1.913 +            const HMetricsHeader* hhea =
   1.914 +                reinterpret_cast<const HMetricsHeader*>
   1.915 +                (hb_blob_get_data(hheaTable, &len));
   1.916 +            if (len >= sizeof(HMetricsHeader)) {
   1.917 +                mNumLongMetrics = hhea->numberOfHMetrics;
   1.918 +                if (mNumLongMetrics > 0 &&
   1.919 +                    int16_t(hhea->metricDataFormat) == 0) {
   1.920 +                    // no point reading hmtx if number of entries is zero!
   1.921 +                    // in that case, we won't be able to use this font
   1.922 +                    // (this method will return FALSE below if mHmtx is null)
   1.923 +                    mHmtxTable =
   1.924 +                        entry->GetFontTable(TRUETYPE_TAG('h','m','t','x'));
   1.925 +                    if (hb_blob_get_length(mHmtxTable) <
   1.926 +                        mNumLongMetrics * sizeof(HLongMetric)) {
   1.927 +                        // hmtx table is not large enough for the claimed
   1.928 +                        // number of entries: invalid, do not use.
   1.929 +                        hb_blob_destroy(mHmtxTable);
   1.930 +                        mHmtxTable = nullptr;
   1.931 +                    }
   1.932 +                }
   1.933 +            }
   1.934 +        }
   1.935 +        if (!mHmtxTable) {
   1.936 +            return false;
   1.937 +        }
   1.938 +    }
   1.939 +
   1.940 +    mHBFont = hb_font_create(mHBFace);
   1.941 +    hb_font_set_funcs(mHBFont, sHBFontFuncs, &mCallbackData, nullptr);
   1.942 +    hb_font_set_ppem(mHBFont, mFont->GetAdjustedSize(), mFont->GetAdjustedSize());
   1.943 +    uint32_t scale = FloatToFixed(mFont->GetAdjustedSize()); // 16.16 fixed-point
   1.944 +    hb_font_set_scale(mHBFont, scale, scale);
   1.945 +
   1.946 +    return true;
   1.947 +}
   1.948 +
   1.949 +bool
   1.950 +gfxHarfBuzzShaper::ShapeText(gfxContext      *aContext,
   1.951 +                             const char16_t *aText,
   1.952 +                             uint32_t         aOffset,
   1.953 +                             uint32_t         aLength,
   1.954 +                             int32_t          aScript,
   1.955 +                             gfxShapedText   *aShapedText)
   1.956 +{
   1.957 +    // some font back-ends require this in order to get proper hinted metrics
   1.958 +    if (!mFont->SetupCairoFont(aContext)) {
   1.959 +        return false;
   1.960 +    }
   1.961 +
   1.962 +    mCallbackData.mContext = aContext;
   1.963 +
   1.964 +    if (!Initialize()) {
   1.965 +        return false;
   1.966 +    }
   1.967 +
   1.968 +    const gfxFontStyle *style = mFont->GetStyle();
   1.969 +
   1.970 +    nsAutoTArray<hb_feature_t,20> features;
   1.971 +    nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures;
   1.972 +
   1.973 +    gfxFontEntry *entry = mFont->GetFontEntry();
   1.974 +    if (MergeFontFeatures(style,
   1.975 +                          entry->mFeatureSettings,
   1.976 +                          aShapedText->DisableLigatures(),
   1.977 +                          entry->FamilyName(),
   1.978 +                          mergedFeatures))
   1.979 +    {
   1.980 +        // enumerate result and insert into hb_feature array
   1.981 +        mergedFeatures.Enumerate(AddOpenTypeFeature, &features);
   1.982 +    }
   1.983 +
   1.984 +    bool isRightToLeft = aShapedText->IsRightToLeft();
   1.985 +    hb_buffer_t *buffer = hb_buffer_create();
   1.986 +    hb_buffer_set_unicode_funcs(buffer, sHBUnicodeFuncs);
   1.987 +    hb_buffer_set_direction(buffer, isRightToLeft ? HB_DIRECTION_RTL :
   1.988 +                                                    HB_DIRECTION_LTR);
   1.989 +    hb_script_t scriptTag;
   1.990 +    if (aShapedText->Flags() & gfxTextRunFactory::TEXT_USE_MATH_SCRIPT) {
   1.991 +        scriptTag = sMathScript;
   1.992 +    } else if (aScript <= MOZ_SCRIPT_INHERITED) {
   1.993 +        // For unresolved "common" or "inherited" runs, default to Latin for
   1.994 +        // now.  (Should we somehow use the language or locale to try and infer
   1.995 +        // a better default?)
   1.996 +        scriptTag = HB_SCRIPT_LATIN;
   1.997 +    } else {
   1.998 +        scriptTag = hb_script_t(GetScriptTagForCode(aScript));
   1.999 +    }
  1.1000 +    hb_buffer_set_script(buffer, scriptTag);
  1.1001 +
  1.1002 +    hb_language_t language;
  1.1003 +    if (style->languageOverride) {
  1.1004 +        language = hb_ot_tag_to_language(style->languageOverride);
  1.1005 +    } else if (entry->mLanguageOverride) {
  1.1006 +        language = hb_ot_tag_to_language(entry->mLanguageOverride);
  1.1007 +    } else {
  1.1008 +        nsCString langString;
  1.1009 +        style->language->ToUTF8String(langString);
  1.1010 +        language =
  1.1011 +            hb_language_from_string(langString.get(), langString.Length());
  1.1012 +    }
  1.1013 +    hb_buffer_set_language(buffer, language);
  1.1014 +
  1.1015 +    uint32_t length = aLength;
  1.1016 +    hb_buffer_add_utf16(buffer,
  1.1017 +                        reinterpret_cast<const uint16_t*>(aText),
  1.1018 +                        length, 0, length);
  1.1019 +
  1.1020 +    hb_shape(mHBFont, buffer, features.Elements(), features.Length());
  1.1021 +
  1.1022 +    if (isRightToLeft) {
  1.1023 +        hb_buffer_reverse(buffer);
  1.1024 +    }
  1.1025 +
  1.1026 +    nsresult rv = SetGlyphsFromRun(aContext, aShapedText, aOffset, aLength,
  1.1027 +                                   aText, buffer);
  1.1028 +
  1.1029 +    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store glyphs into gfxShapedWord");
  1.1030 +    hb_buffer_destroy(buffer);
  1.1031 +
  1.1032 +    return NS_SUCCEEDED(rv);
  1.1033 +}
  1.1034 +
  1.1035 +#define SMALL_GLYPH_RUN 128 // some testing indicates that 90%+ of text runs
  1.1036 +                            // will fit without requiring separate allocation
  1.1037 +                            // for charToGlyphArray
  1.1038 +
  1.1039 +nsresult
  1.1040 +gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext      *aContext,
  1.1041 +                                    gfxShapedText   *aShapedText,
  1.1042 +                                    uint32_t         aOffset,
  1.1043 +                                    uint32_t         aLength,
  1.1044 +                                    const char16_t *aText,
  1.1045 +                                    hb_buffer_t     *aBuffer)
  1.1046 +{
  1.1047 +    uint32_t numGlyphs;
  1.1048 +    const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(aBuffer, &numGlyphs);
  1.1049 +    if (numGlyphs == 0) {
  1.1050 +        return NS_OK;
  1.1051 +    }
  1.1052 +
  1.1053 +    nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
  1.1054 +
  1.1055 +    uint32_t wordLength = aLength;
  1.1056 +    static const int32_t NO_GLYPH = -1;
  1.1057 +    AutoFallibleTArray<int32_t,SMALL_GLYPH_RUN> charToGlyphArray;
  1.1058 +    if (!charToGlyphArray.SetLength(wordLength)) {
  1.1059 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1060 +    }
  1.1061 +
  1.1062 +    int32_t *charToGlyph = charToGlyphArray.Elements();
  1.1063 +    for (uint32_t offset = 0; offset < wordLength; ++offset) {
  1.1064 +        charToGlyph[offset] = NO_GLYPH;
  1.1065 +    }
  1.1066 +
  1.1067 +    for (uint32_t i = 0; i < numGlyphs; ++i) {
  1.1068 +        uint32_t loc = ginfo[i].cluster;
  1.1069 +        if (loc < wordLength) {
  1.1070 +            charToGlyph[loc] = i;
  1.1071 +        }
  1.1072 +    }
  1.1073 +
  1.1074 +    int32_t glyphStart = 0; // looking for a clump that starts at this glyph
  1.1075 +    int32_t charStart = 0; // and this char index within the range of the run
  1.1076 +
  1.1077 +    bool roundX;
  1.1078 +    bool roundY;
  1.1079 +    aContext->GetRoundOffsetsToPixels(&roundX, &roundY);
  1.1080 +
  1.1081 +    int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
  1.1082 +    gfxShapedText::CompressedGlyph *charGlyphs =
  1.1083 +        aShapedText->GetCharacterGlyphs() + aOffset;
  1.1084 +
  1.1085 +    // factor to convert 16.16 fixed-point pixels to app units
  1.1086 +    // (only used if not rounding)
  1.1087 +    double hb2appUnits = FixedToFloat(aShapedText->GetAppUnitsPerDevUnit());
  1.1088 +
  1.1089 +    // Residual from rounding of previous advance, for use in rounding the
  1.1090 +    // subsequent offset or advance appropriately.  16.16 fixed-point
  1.1091 +    //
  1.1092 +    // When rounding, the goal is to make the distance between glyphs and
  1.1093 +    // their base glyph equal to the integral number of pixels closest to that
  1.1094 +    // suggested by that shaper.
  1.1095 +    // i.e. posInfo[n].x_advance - posInfo[n].x_offset + posInfo[n+1].x_offset
  1.1096 +    //
  1.1097 +    // The value of the residual is the part of the desired distance that has
  1.1098 +    // not been included in integer offsets.
  1.1099 +    hb_position_t x_residual = 0;
  1.1100 +
  1.1101 +    // keep track of y-position to set glyph offsets if needed
  1.1102 +    nscoord yPos = 0;
  1.1103 +
  1.1104 +    const hb_glyph_position_t *posInfo =
  1.1105 +        hb_buffer_get_glyph_positions(aBuffer, nullptr);
  1.1106 +
  1.1107 +    while (glyphStart < int32_t(numGlyphs)) {
  1.1108 +
  1.1109 +        int32_t charEnd = ginfo[glyphStart].cluster;
  1.1110 +        int32_t glyphEnd = glyphStart;
  1.1111 +        int32_t charLimit = wordLength;
  1.1112 +        while (charEnd < charLimit) {
  1.1113 +            // This is normally executed once for each iteration of the outer loop,
  1.1114 +            // but in unusual cases where the character/glyph association is complex,
  1.1115 +            // the initial character range might correspond to a non-contiguous
  1.1116 +            // glyph range with "holes" in it. If so, we will repeat this loop to
  1.1117 +            // extend the character range until we have a contiguous glyph sequence.
  1.1118 +            charEnd += 1;
  1.1119 +            while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) {
  1.1120 +                charEnd += 1;
  1.1121 +            }
  1.1122 +
  1.1123 +            // find the maximum glyph index covered by the clump so far
  1.1124 +            for (int32_t i = charStart; i < charEnd; ++i) {
  1.1125 +                if (charToGlyph[i] != NO_GLYPH) {
  1.1126 +                    glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
  1.1127 +                    // update extent of glyph range
  1.1128 +                }
  1.1129 +            }
  1.1130 +
  1.1131 +            if (glyphEnd == glyphStart + 1) {
  1.1132 +                // for the common case of a single-glyph clump,
  1.1133 +                // we can skip the following checks
  1.1134 +                break;
  1.1135 +            }
  1.1136 +
  1.1137 +            if (glyphEnd == glyphStart) {
  1.1138 +                // no glyphs, try to extend the clump
  1.1139 +                continue;
  1.1140 +            }
  1.1141 +
  1.1142 +            // check whether all glyphs in the range are associated with the characters
  1.1143 +            // in our clump; if not, we have a discontinuous range, and should extend it
  1.1144 +            // unless we've reached the end of the text
  1.1145 +            bool allGlyphsAreWithinCluster = true;
  1.1146 +            for (int32_t i = glyphStart; i < glyphEnd; ++i) {
  1.1147 +                int32_t glyphCharIndex = ginfo[i].cluster;
  1.1148 +                if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) {
  1.1149 +                    allGlyphsAreWithinCluster = false;
  1.1150 +                    break;
  1.1151 +                }
  1.1152 +            }
  1.1153 +            if (allGlyphsAreWithinCluster) {
  1.1154 +                break;
  1.1155 +            }
  1.1156 +        }
  1.1157 +
  1.1158 +        NS_ASSERTION(glyphStart < glyphEnd,
  1.1159 +                     "character/glyph clump contains no glyphs!");
  1.1160 +        NS_ASSERTION(charStart != charEnd,
  1.1161 +                     "character/glyph clump contains no characters!");
  1.1162 +
  1.1163 +        // Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd;
  1.1164 +        // Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature),
  1.1165 +        // and endCharIndex to the limit (position beyond the last char),
  1.1166 +        // adjusting for the offset of the stringRange relative to the textRun.
  1.1167 +        int32_t baseCharIndex, endCharIndex;
  1.1168 +        while (charEnd < int32_t(wordLength) && charToGlyph[charEnd] == NO_GLYPH)
  1.1169 +            charEnd++;
  1.1170 +        baseCharIndex = charStart;
  1.1171 +        endCharIndex = charEnd;
  1.1172 +
  1.1173 +        // Then we check if the clump falls outside our actual string range;
  1.1174 +        // if so, just go to the next.
  1.1175 +        if (baseCharIndex >= int32_t(wordLength)) {
  1.1176 +            glyphStart = glyphEnd;
  1.1177 +            charStart = charEnd;
  1.1178 +            continue;
  1.1179 +        }
  1.1180 +        // Ensure we won't try to go beyond the valid length of the textRun's text
  1.1181 +        endCharIndex = std::min<int32_t>(endCharIndex, wordLength);
  1.1182 +
  1.1183 +        // Now we're ready to set the glyph info in the textRun
  1.1184 +        int32_t glyphsInClump = glyphEnd - glyphStart;
  1.1185 +
  1.1186 +        // Check for default-ignorable char that didn't get filtered, combined,
  1.1187 +        // etc by the shaping process, and remove from the run.
  1.1188 +        // (This may be done within harfbuzz eventually.)
  1.1189 +        if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex &&
  1.1190 +            aShapedText->FilterIfIgnorable(aOffset + baseCharIndex,
  1.1191 +                                           aText[baseCharIndex])) {
  1.1192 +            glyphStart = glyphEnd;
  1.1193 +            charStart = charEnd;
  1.1194 +            continue;
  1.1195 +        }
  1.1196 +
  1.1197 +        hb_position_t x_offset = posInfo[glyphStart].x_offset;
  1.1198 +        hb_position_t x_advance = posInfo[glyphStart].x_advance;
  1.1199 +        nscoord xOffset, advance;
  1.1200 +        if (roundX) {
  1.1201 +            xOffset =
  1.1202 +                appUnitsPerDevUnit * FixedToIntRound(x_offset + x_residual);
  1.1203 +            // Desired distance from the base glyph to the next reference point.
  1.1204 +            hb_position_t width = x_advance - x_offset;
  1.1205 +            int intWidth = FixedToIntRound(width);
  1.1206 +            x_residual = width - FloatToFixed(intWidth);
  1.1207 +            advance = appUnitsPerDevUnit * intWidth + xOffset;
  1.1208 +        } else {
  1.1209 +            xOffset = floor(hb2appUnits * x_offset + 0.5);
  1.1210 +            advance = floor(hb2appUnits * x_advance + 0.5);
  1.1211 +        }
  1.1212 +        // Check if it's a simple one-to-one mapping
  1.1213 +        if (glyphsInClump == 1 &&
  1.1214 +            gfxTextRun::CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
  1.1215 +            gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
  1.1216 +            charGlyphs[baseCharIndex].IsClusterStart() &&
  1.1217 +            xOffset == 0 &&
  1.1218 +            posInfo[glyphStart].y_offset == 0 && yPos == 0)
  1.1219 +        {
  1.1220 +            charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
  1.1221 +                                                     ginfo[glyphStart].codepoint);
  1.1222 +        } else {
  1.1223 +            // collect all glyphs in a list to be assigned to the first char;
  1.1224 +            // there must be at least one in the clump, and we already measured
  1.1225 +            // its advance, hence the placement of the loop-exit test and the
  1.1226 +            // measurement of the next glyph
  1.1227 +            while (1) {
  1.1228 +                gfxTextRun::DetailedGlyph* details =
  1.1229 +                    detailedGlyphs.AppendElement();
  1.1230 +                details->mGlyphID = ginfo[glyphStart].codepoint;
  1.1231 +
  1.1232 +                details->mXOffset = xOffset;
  1.1233 +                details->mAdvance = advance;
  1.1234 +
  1.1235 +                hb_position_t y_offset = posInfo[glyphStart].y_offset;
  1.1236 +                details->mYOffset = yPos -
  1.1237 +                    (roundY ? appUnitsPerDevUnit * FixedToIntRound(y_offset)
  1.1238 +                     : floor(hb2appUnits * y_offset + 0.5));
  1.1239 +
  1.1240 +                hb_position_t y_advance = posInfo[glyphStart].y_advance;
  1.1241 +                if (y_advance != 0) {
  1.1242 +                    yPos -=
  1.1243 +                        roundY ? appUnitsPerDevUnit * FixedToIntRound(y_advance)
  1.1244 +                        : floor(hb2appUnits * y_advance + 0.5);
  1.1245 +                }
  1.1246 +                if (++glyphStart >= glyphEnd) {
  1.1247 +                    break;
  1.1248 +                }
  1.1249 +
  1.1250 +                x_offset = posInfo[glyphStart].x_offset;
  1.1251 +                x_advance = posInfo[glyphStart].x_advance;
  1.1252 +                if (roundX) {
  1.1253 +                    xOffset = appUnitsPerDevUnit *
  1.1254 +                        FixedToIntRound(x_offset + x_residual);
  1.1255 +                    // Desired distance to the next reference point.  The
  1.1256 +                    // residual is considered here, and includes the residual
  1.1257 +                    // from the base glyph offset and subsequent advances, so
  1.1258 +                    // that the distance from the base glyph is optimized
  1.1259 +                    // rather than the distance from combining marks.
  1.1260 +                    x_advance += x_residual;
  1.1261 +                    int intAdvance = FixedToIntRound(x_advance);
  1.1262 +                    x_residual = x_advance - FloatToFixed(intAdvance);
  1.1263 +                    advance = appUnitsPerDevUnit * intAdvance;
  1.1264 +                } else {
  1.1265 +                    xOffset = floor(hb2appUnits * x_offset + 0.5);
  1.1266 +                    advance = floor(hb2appUnits * x_advance + 0.5);
  1.1267 +                }
  1.1268 +            }
  1.1269 +
  1.1270 +            gfxShapedText::CompressedGlyph g;
  1.1271 +            g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
  1.1272 +                         true, detailedGlyphs.Length());
  1.1273 +            aShapedText->SetGlyphs(aOffset + baseCharIndex,
  1.1274 +                                   g, detailedGlyphs.Elements());
  1.1275 +
  1.1276 +            detailedGlyphs.Clear();
  1.1277 +        }
  1.1278 +
  1.1279 +        // the rest of the chars in the group are ligature continuations,
  1.1280 +        // no associated glyphs
  1.1281 +        while (++baseCharIndex != endCharIndex &&
  1.1282 +               baseCharIndex < int32_t(wordLength)) {
  1.1283 +            gfxShapedText::CompressedGlyph &g = charGlyphs[baseCharIndex];
  1.1284 +            NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
  1.1285 +            g.SetComplex(g.IsClusterStart(), false, 0);
  1.1286 +        }
  1.1287 +
  1.1288 +        glyphStart = glyphEnd;
  1.1289 +        charStart = charEnd;
  1.1290 +    }
  1.1291 +
  1.1292 +    return NS_OK;
  1.1293 +}

mercurial