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