gfx/thebes/gfxHarfBuzzShaper.cpp

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

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

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

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

mercurial