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 , 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 +}