gfxTextRun.cpp (153130B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=4 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "gfxTextRun.h" 8 9 #include "gfx2DGlue.h" 10 #include "gfxContext.h" 11 #include "gfxFontConstants.h" 12 #include "gfxFontMissingGlyphs.h" 13 #include "gfxGlyphExtents.h" 14 #include "gfxHarfBuzzShaper.h" 15 #include "gfxPlatformFontList.h" 16 #include "gfxScriptItemizer.h" 17 #include "gfxUserFontSet.h" 18 #include "mozilla/ClearOnShutdown.h" 19 #include "mozilla/gfx/2D.h" 20 #include "mozilla/gfx/Logging.h" // for gfxCriticalError 21 #include "mozilla/gfx/PathHelpers.h" 22 #include "mozilla/intl/Locale.h" 23 #include "mozilla/intl/String.h" 24 #include "mozilla/intl/UnicodeProperties.h" 25 #include "mozilla/Likely.h" 26 #include "mozilla/MruCache.h" 27 #include "mozilla/ServoStyleSet.h" 28 #include "mozilla/Sprintf.h" 29 #include "mozilla/StaticPresData.h" 30 #include "mozilla/UniquePtr.h" 31 #include "nsLayoutUtils.h" 32 #include "nsStyleConsts.h" 33 #include "nsStyleUtil.h" 34 #include "nsUnicodeProperties.h" 35 #include "SharedFontList-impl.h" 36 #include "TextDrawTarget.h" 37 38 #ifdef XP_WIN 39 # include "gfxWindowsPlatform.h" 40 #endif 41 42 using namespace mozilla; 43 using namespace mozilla::gfx; 44 using namespace mozilla::intl; 45 using namespace mozilla::unicode; 46 using mozilla::services::GetObserverService; 47 48 static const char16_t kEllipsisChar[] = {0x2026, 0x0}; 49 static const char16_t kASCIIPeriodsChar[] = {'.', '.', '.', 0x0}; 50 51 #ifdef DEBUG_roc 52 # define DEBUG_TEXT_RUN_STORAGE_METRICS 53 #endif 54 55 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 56 extern uint32_t gTextRunStorageHighWaterMark; 57 extern uint32_t gTextRunStorage; 58 extern uint32_t gFontCount; 59 extern uint32_t gGlyphExtentsCount; 60 extern uint32_t gGlyphExtentsWidthsTotalSize; 61 extern uint32_t gGlyphExtentsSetupEagerSimple; 62 extern uint32_t gGlyphExtentsSetupEagerTight; 63 extern uint32_t gGlyphExtentsSetupLazyTight; 64 extern uint32_t gGlyphExtentsSetupFallBackToTight; 65 #endif 66 67 void gfxTextRun::GlyphRunIterator::NextRun() { 68 if (mReverse) { 69 if (mGlyphRun == mTextRun->mGlyphRuns.begin()) { 70 mGlyphRun = nullptr; 71 return; 72 } 73 --mGlyphRun; 74 } else { 75 MOZ_DIAGNOSTIC_ASSERT(mGlyphRun != mTextRun->mGlyphRuns.end()); 76 ++mGlyphRun; 77 if (mGlyphRun == mTextRun->mGlyphRuns.end()) { 78 mGlyphRun = nullptr; 79 return; 80 } 81 } 82 if (mGlyphRun->mCharacterOffset >= mEndOffset) { 83 mGlyphRun = nullptr; 84 return; 85 } 86 uint32_t glyphRunEndOffset = mGlyphRun == mTextRun->mGlyphRuns.end() - 1 87 ? mTextRun->GetLength() 88 : (mGlyphRun + 1)->mCharacterOffset; 89 if (glyphRunEndOffset < mStartOffset) { 90 mGlyphRun = nullptr; 91 return; 92 } 93 mStringEnd = std::min(mEndOffset, glyphRunEndOffset); 94 mStringStart = std::max(mStartOffset, mGlyphRun->mCharacterOffset); 95 } 96 97 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 98 static void AccountStorageForTextRun(gfxTextRun* aTextRun, int32_t aSign) { 99 // Ignores detailed glyphs... we don't know when those have been constructed 100 // Also ignores gfxSkipChars dynamic storage (which won't be anything 101 // for preformatted text) 102 // Also ignores GlyphRun array, again because it hasn't been constructed 103 // by the time this gets called. If there's only one glyphrun that's stored 104 // directly in the textrun anyway so no additional overhead. 105 uint32_t length = aTextRun->GetLength(); 106 int32_t bytes = length * sizeof(gfxTextRun::CompressedGlyph); 107 bytes += sizeof(gfxTextRun); 108 gTextRunStorage += bytes * aSign; 109 gTextRunStorageHighWaterMark = 110 std::max(gTextRunStorageHighWaterMark, gTextRunStorage); 111 } 112 #endif 113 114 bool gfxTextRun::NeedsGlyphExtents() const { 115 if (GetFlags() & gfx::ShapedTextFlags::TEXT_NEED_BOUNDING_BOX) { 116 return true; 117 } 118 for (const auto& run : mGlyphRuns) { 119 if (run.mFont->GetFontEntry()->IsUserFont()) { 120 return true; 121 } 122 } 123 return false; 124 } 125 126 // Helper for textRun creation to preallocate storage for glyph records; 127 // this function returns a pointer to the newly-allocated glyph storage. 128 // Returns nullptr if allocation fails. 129 void* gfxTextRun::AllocateStorageForTextRun(size_t aSize, uint32_t aLength) { 130 // Allocate the storage we need, returning nullptr on failure rather than 131 // throwing an exception (because web content can create huge runs). 132 void* storage = malloc(aSize + aLength * sizeof(CompressedGlyph)); 133 if (!storage) { 134 NS_WARNING("failed to allocate storage for text run!"); 135 return nullptr; 136 } 137 138 // Initialize the glyph storage (beyond aSize) to zero 139 memset(reinterpret_cast<char*>(storage) + aSize, 0, 140 aLength * sizeof(CompressedGlyph)); 141 142 return storage; 143 } 144 145 already_AddRefed<gfxTextRun> gfxTextRun::Create( 146 const gfxTextRunFactory::Parameters* aParams, uint32_t aLength, 147 gfxFontGroup* aFontGroup, gfx::ShapedTextFlags aFlags, 148 nsTextFrameUtils::Flags aFlags2) { 149 void* storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength); 150 if (!storage) { 151 return nullptr; 152 } 153 154 RefPtr<gfxTextRun> result = 155 new (storage) gfxTextRun(aParams, aLength, aFontGroup, aFlags, aFlags2); 156 return result.forget(); 157 } 158 159 gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters* aParams, 160 uint32_t aLength, gfxFontGroup* aFontGroup, 161 gfx::ShapedTextFlags aFlags, 162 nsTextFrameUtils::Flags aFlags2) 163 : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit), 164 mUserData(aParams->mUserData), 165 mFontGroup(aFontGroup), 166 mFlags2(aFlags2), 167 mReleasedFontGroup(false), 168 mReleasedFontGroupSkippedDrawing(false), 169 mShapingState(eShapingState_Normal) { 170 NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale"); 171 NS_ADDREF(mFontGroup); 172 173 #ifndef RELEASE_OR_BETA 174 gfxTextPerfMetrics* tp = aFontGroup->GetTextPerfMetrics(); 175 if (tp) { 176 tp->current.textrunConst++; 177 } 178 #endif 179 180 mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1); 181 182 if (aParams->mSkipChars) { 183 mSkipChars.TakeFrom(aParams->mSkipChars); 184 } 185 186 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 187 AccountStorageForTextRun(this, 1); 188 #endif 189 190 mDontSkipDrawing = 191 !!(aFlags2 & nsTextFrameUtils::Flags::DontSkipDrawingForPendingUserFonts); 192 } 193 194 gfxTextRun::~gfxTextRun() { 195 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 196 AccountStorageForTextRun(this, -1); 197 #endif 198 #ifdef DEBUG 199 // Make it easy to detect a dead text run 200 mFlags = ~gfx::ShapedTextFlags(); 201 mFlags2 = ~nsTextFrameUtils::Flags(); 202 #endif 203 204 // The cached ellipsis textrun (if any) in a fontgroup will have already 205 // been told to release its reference to the group, so we mustn't do that 206 // again here. 207 if (!mReleasedFontGroup) { 208 #ifndef RELEASE_OR_BETA 209 gfxTextPerfMetrics* tp = mFontGroup->GetTextPerfMetrics(); 210 if (tp) { 211 tp->current.textrunDestr++; 212 } 213 #endif 214 NS_RELEASE(mFontGroup); 215 } 216 } 217 218 void gfxTextRun::ReleaseFontGroup() { 219 NS_ASSERTION(!mReleasedFontGroup, "doubly released!"); 220 221 // After dropping our reference to the font group, we'll no longer be able 222 // to get up-to-date results for ShouldSkipDrawing(). Store the current 223 // value in mReleasedFontGroupSkippedDrawing. 224 // 225 // (It doesn't actually matter that we can't get up-to-date results for 226 // ShouldSkipDrawing(), since the only text runs that we call 227 // ReleaseFontGroup() for are ellipsis text runs, and we ask the font 228 // group for a new ellipsis text run each time we want to draw one, 229 // and ensure that the cached one is cleared in ClearCachedData() when 230 // font loading status changes.) 231 mReleasedFontGroupSkippedDrawing = mFontGroup->ShouldSkipDrawing(); 232 233 NS_RELEASE(mFontGroup); 234 mReleasedFontGroup = true; 235 } 236 237 bool gfxTextRun::SetPotentialLineBreaks(Range aRange, 238 const uint8_t* aBreakBefore) { 239 NS_ASSERTION(aRange.end <= GetLength(), "Overflow"); 240 241 uint32_t changed = 0; 242 CompressedGlyph* cg = mCharacterGlyphs + aRange.start; 243 const CompressedGlyph* const end = cg + aRange.Length(); 244 while (cg < end) { 245 uint8_t canBreak = *aBreakBefore++; 246 if (canBreak && !cg->IsClusterStart()) { 247 // XXX If we replace the line-breaker with one based more closely 248 // on UAX#14 (e.g. using ICU), this may not be needed any more. 249 // Avoid possible breaks inside a cluster, EXCEPT when the previous 250 // character was a space (compare UAX#14 rules LB9, LB10). 251 if (cg == mCharacterGlyphs || !(cg - 1)->CharIsSpace()) { 252 canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE; 253 } 254 } 255 // If a break is allowed here, set the break flag, but don't clear a 256 // possible pre-existing emergency-break flag already in the run. 257 if (canBreak) { 258 changed |= cg->SetCanBreakBefore(canBreak); 259 } 260 ++cg; 261 } 262 return changed != 0; 263 } 264 265 gfxTextRun::LigatureData gfxTextRun::ComputeLigatureData( 266 Range aPartRange, const PropertyProvider* aProvider) const { 267 NS_ASSERTION(aPartRange.start < aPartRange.end, 268 "Computing ligature data for empty range"); 269 NS_ASSERTION(aPartRange.end <= GetLength(), "Character length overflow"); 270 271 LigatureData result; 272 const CompressedGlyph* charGlyphs = mCharacterGlyphs; 273 274 uint32_t i; 275 for (i = aPartRange.start; !charGlyphs[i].IsLigatureGroupStart(); --i) { 276 NS_ASSERTION(i > 0, "Ligature at the start of the run??"); 277 } 278 result.mRange.start = i; 279 for (i = aPartRange.start + 1; 280 i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) { 281 } 282 result.mRange.end = i; 283 284 int32_t ligatureWidth = GetAdvanceForGlyphs(result.mRange); 285 // Count the number of started clusters we have seen 286 uint32_t totalClusterCount = 0; 287 uint32_t partClusterIndex = 0; 288 uint32_t partClusterCount = 0; 289 for (i = result.mRange.start; i < result.mRange.end; ++i) { 290 // Treat the first character of the ligature as the start of a 291 // cluster for our purposes of allocating ligature width to its 292 // characters. 293 if (i == result.mRange.start || charGlyphs[i].IsClusterStart()) { 294 ++totalClusterCount; 295 if (i < aPartRange.start) { 296 ++partClusterIndex; 297 } else if (i < aPartRange.end) { 298 ++partClusterCount; 299 } 300 } 301 } 302 NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??"); 303 result.mPartAdvance = partClusterIndex * (ligatureWidth / totalClusterCount); 304 result.mPartWidth = partClusterCount * (ligatureWidth / totalClusterCount); 305 306 // Any rounding errors are apportioned to the final part of the ligature, 307 // so that measuring all parts of a ligature and summing them is equal to 308 // the ligature width. 309 if (aPartRange.end == result.mRange.end) { 310 gfxFloat allParts = totalClusterCount * (ligatureWidth / totalClusterCount); 311 result.mPartWidth += ligatureWidth - allParts; 312 } 313 314 if (partClusterCount == 0) { 315 // nothing to draw 316 result.mClipBeforePart = result.mClipAfterPart = true; 317 } else { 318 // Determine whether we should clip before or after this part when 319 // drawing its slice of the ligature. 320 // We need to clip before the part if any cluster is drawn before 321 // this part. 322 result.mClipBeforePart = partClusterIndex > 0; 323 // We need to clip after the part if any cluster is drawn after 324 // this part. 325 result.mClipAfterPart = 326 partClusterIndex + partClusterCount < totalClusterCount; 327 } 328 329 if (aProvider && (mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) { 330 gfxFont::Spacing spacing; 331 if (aPartRange.start == result.mRange.start) { 332 if (aProvider->GetSpacing(Range(aPartRange.start, aPartRange.start + 1), 333 &spacing)) { 334 result.mPartWidth += spacing.mBefore; 335 } 336 } 337 if (aPartRange.end == result.mRange.end) { 338 if (aProvider->GetSpacing(Range(aPartRange.end - 1, aPartRange.end), 339 &spacing)) { 340 result.mPartWidth += spacing.mAfter; 341 } 342 } 343 } 344 345 return result; 346 } 347 348 gfxFloat gfxTextRun::ComputePartialLigatureWidth( 349 Range aPartRange, const PropertyProvider* aProvider) const { 350 if (aPartRange.start >= aPartRange.end) return 0; 351 LigatureData data = ComputeLigatureData(aPartRange, aProvider); 352 return data.mPartWidth; 353 } 354 355 int32_t gfxTextRun::GetAdvanceForGlyphs(Range aRange) const { 356 int32_t advance = 0; 357 for (auto i = aRange.start; i < aRange.end; ++i) { 358 advance += GetAdvanceForGlyph(i); 359 } 360 return advance; 361 } 362 363 // Returns false if there is definitely no spacing to apply. 364 static bool GetAdjustedSpacing( 365 const gfxTextRun* aTextRun, gfxTextRun::Range aRange, 366 const gfxTextRun::PropertyProvider& aProvider, 367 gfxTextRun::PropertyProvider::Spacing* aSpacing) { 368 if (aRange.start >= aRange.end) { 369 return false; 370 } 371 372 bool result = aProvider.GetSpacing(aRange, aSpacing); 373 374 #ifdef DEBUG 375 // Check to see if we have spacing inside ligatures 376 377 const gfxTextRun::CompressedGlyph* charGlyphs = 378 aTextRun->GetCharacterGlyphs(); 379 uint32_t i; 380 381 for (i = aRange.start; i < aRange.end; ++i) { 382 if (!charGlyphs[i].IsLigatureGroupStart()) { 383 NS_ASSERTION(i == aRange.start || aSpacing[i - aRange.start].mBefore == 0, 384 "Before-spacing inside a ligature!"); 385 NS_ASSERTION( 386 i - 1 <= aRange.start || aSpacing[i - 1 - aRange.start].mAfter == 0, 387 "After-spacing inside a ligature!"); 388 } 389 } 390 #endif 391 392 return result; 393 } 394 395 bool gfxTextRun::GetAdjustedSpacingArray( 396 Range aRange, const PropertyProvider* aProvider, Range aSpacingRange, 397 nsTArray<PropertyProvider::Spacing>* aSpacing) const { 398 if (!aProvider || !(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) { 399 return false; 400 } 401 if (!aSpacing->AppendElements(aRange.Length(), fallible)) { 402 return false; 403 } 404 auto spacingOffset = aSpacingRange.start - aRange.start; 405 memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing) * spacingOffset); 406 if (!GetAdjustedSpacing(this, aSpacingRange, *aProvider, 407 aSpacing->Elements() + spacingOffset)) { 408 aSpacing->Clear(); 409 return false; 410 } 411 memset(aSpacing->Elements() + spacingOffset + aSpacingRange.Length(), 0, 412 sizeof(gfxFont::Spacing) * (aRange.end - aSpacingRange.end)); 413 return true; 414 } 415 416 bool gfxTextRun::ShrinkToLigatureBoundaries(Range* aRange) const { 417 if (aRange->start >= aRange->end) { 418 return false; 419 } 420 421 const CompressedGlyph* charGlyphs = mCharacterGlyphs; 422 bool adjusted = false; 423 while (aRange->start < aRange->end && 424 !charGlyphs[aRange->start].IsLigatureGroupStart()) { 425 ++aRange->start; 426 adjusted = true; 427 } 428 if (aRange->end < GetLength()) { 429 while (aRange->end > aRange->start && 430 !charGlyphs[aRange->end].IsLigatureGroupStart()) { 431 --aRange->end; 432 adjusted = true; 433 } 434 } 435 return adjusted; 436 } 437 438 void gfxTextRun::DrawGlyphs(gfxFont* aFont, Range aRange, gfx::Point* aPt, 439 const PropertyProvider* aProvider, 440 Range aSpacingRange, TextRunDrawParams& aParams, 441 gfx::ShapedTextFlags aOrientation) const { 442 AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer; 443 bool haveSpacing = 444 GetAdjustedSpacingArray(aRange, aProvider, aSpacingRange, &spacingBuffer); 445 aParams.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr; 446 aFont->Draw(this, aRange.start, aRange.end, aPt, aParams, aOrientation); 447 } 448 449 static void ClipPartialLigature(const gfxTextRun* aTextRun, gfxFloat* aStart, 450 gfxFloat* aEnd, gfxFloat aOrigin, 451 gfxTextRun::LigatureData* aLigature) { 452 if (aLigature->mClipBeforePart) { 453 if (aTextRun->IsRightToLeft()) { 454 *aEnd = std::min(*aEnd, aOrigin); 455 } else { 456 *aStart = std::max(*aStart, aOrigin); 457 } 458 } 459 if (aLigature->mClipAfterPart) { 460 gfxFloat endEdge = 461 aOrigin + aTextRun->GetDirection() * aLigature->mPartWidth; 462 if (aTextRun->IsRightToLeft()) { 463 *aStart = std::max(*aStart, endEdge); 464 } else { 465 *aEnd = std::min(*aEnd, endEdge); 466 } 467 } 468 } 469 470 void gfxTextRun::DrawPartialLigature(gfxFont* aFont, Range aRange, 471 gfx::Point* aPt, 472 const PropertyProvider* aProvider, 473 TextRunDrawParams& aParams, 474 gfx::ShapedTextFlags aOrientation) const { 475 if (aRange.start >= aRange.end) { 476 return; 477 } 478 479 // Draw partial ligature. We hack this by clipping the ligature. 480 LigatureData data = ComputeLigatureData(aRange, aProvider); 481 gfxRect clipExtents = aParams.context->GetClipExtents(); 482 gfxFloat start, end; 483 if (aParams.isVerticalRun) { 484 start = clipExtents.Y() * mAppUnitsPerDevUnit; 485 end = clipExtents.YMost() * mAppUnitsPerDevUnit; 486 ClipPartialLigature(this, &start, &end, aPt->y, &data); 487 } else { 488 start = clipExtents.X() * mAppUnitsPerDevUnit; 489 end = clipExtents.XMost() * mAppUnitsPerDevUnit; 490 ClipPartialLigature(this, &start, &end, aPt->x, &data); 491 } 492 493 gfxClipAutoSaveRestore autoSaveClip(aParams.context); 494 { 495 // use division here to ensure that when the rect is aligned on multiples 496 // of mAppUnitsPerDevUnit, we clip to true device unit boundaries. 497 // Also, make sure we snap the rectangle to device pixels. 498 Rect clipRect = 499 aParams.isVerticalRun 500 ? Rect(clipExtents.X(), start / mAppUnitsPerDevUnit, 501 clipExtents.Width(), (end - start) / mAppUnitsPerDevUnit) 502 : Rect(start / mAppUnitsPerDevUnit, clipExtents.Y(), 503 (end - start) / mAppUnitsPerDevUnit, clipExtents.Height()); 504 MaybeSnapToDevicePixels(clipRect, *aParams.dt, true); 505 506 autoSaveClip.Clip(clipRect); 507 } 508 509 gfx::Point pt; 510 if (aParams.isVerticalRun) { 511 pt = Point(aPt->x, aPt->y - aParams.direction * data.mPartAdvance); 512 } else { 513 pt = Point(aPt->x - aParams.direction * data.mPartAdvance, aPt->y); 514 } 515 516 DrawGlyphs(aFont, data.mRange, &pt, aProvider, aRange, aParams, aOrientation); 517 518 if (aParams.isVerticalRun) { 519 aPt->y += aParams.direction * data.mPartWidth; 520 } else { 521 aPt->x += aParams.direction * data.mPartWidth; 522 } 523 } 524 525 // Returns true if the font has synthetic bolding enabled, 526 // or is a color font (COLR/SVG/sbix/CBDT), false otherwise. This is used to 527 // check whether the text run needs to be explicitly composited in order to 528 // support opacity. 529 static bool HasSyntheticBoldOrColor(gfxFont* aFont) { 530 if (aFont->ApplySyntheticBold()) { 531 return true; 532 } 533 gfxFontEntry* fe = aFont->GetFontEntry(); 534 if (fe->TryGetSVGData(aFont) || fe->TryGetColorGlyphs()) { 535 return true; 536 } 537 #if defined(XP_MACOSX) // sbix fonts only supported via Core Text 538 if (fe->HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x'))) { 539 return true; 540 } 541 #endif 542 return false; 543 } 544 545 // helper class for double-buffering drawing with non-opaque color 546 struct MOZ_STACK_CLASS BufferAlphaColor { 547 explicit BufferAlphaColor(gfxContext* aContext) : mContext(aContext) {} 548 549 ~BufferAlphaColor() = default; 550 551 void PushSolidColor(const gfxRect& aBounds, const DeviceColor& aAlphaColor, 552 uint32_t appsPerDevUnit) { 553 mContext->Save(); 554 mContext->SnappedClip(gfxRect( 555 aBounds.X() / appsPerDevUnit, aBounds.Y() / appsPerDevUnit, 556 aBounds.Width() / appsPerDevUnit, aBounds.Height() / appsPerDevUnit)); 557 mContext->SetDeviceColor( 558 DeviceColor(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b)); 559 mContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aAlphaColor.a); 560 } 561 562 void PopAlpha() { 563 // pop the text, using the color alpha as the opacity 564 mContext->PopGroupAndBlend(); 565 mContext->Restore(); 566 } 567 568 gfxContext* mContext; 569 }; 570 571 void gfxTextRun::Draw(const Range aRange, const gfx::Point aPt, 572 const DrawParams& aParams) const { 573 NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range"); 574 NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH || 575 !(aParams.drawMode & DrawMode::GLYPH_PATH), 576 "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or " 577 "GLYPH_STROKE_UNDERNEATH"); 578 NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH || !aParams.callbacks, 579 "callback must not be specified unless using GLYPH_PATH"); 580 581 bool skipDrawing = 582 !mDontSkipDrawing && (mFontGroup ? mFontGroup->ShouldSkipDrawing() 583 : mReleasedFontGroupSkippedDrawing); 584 auto* textDrawer = aParams.context->GetTextDrawer(); 585 if (aParams.drawMode & DrawMode::GLYPH_FILL) { 586 DeviceColor currentColor; 587 if (aParams.context->GetDeviceColor(currentColor) && currentColor.a == 0 && 588 !textDrawer) { 589 skipDrawing = true; 590 } 591 } 592 593 gfxFloat direction = GetDirection(); 594 595 if (skipDrawing) { 596 // We don't need to draw anything; 597 // but if the caller wants advance width, we need to compute it here 598 if (aParams.advanceWidth) { 599 gfxTextRun::Metrics metrics = 600 MeasureText(aRange, gfxFont::LOOSE_INK_EXTENTS, 601 aParams.context->GetDrawTarget(), aParams.provider); 602 *aParams.advanceWidth = metrics.mAdvanceWidth * direction; 603 } 604 605 // return without drawing 606 return; 607 } 608 609 // synthetic bolding draws glyphs twice ==> colors with opacity won't draw 610 // correctly unless first drawn without alpha 611 BufferAlphaColor syntheticBoldBuffer(aParams.context); 612 DeviceColor currentColor; 613 bool mayNeedBuffering = 614 aParams.drawMode & DrawMode::GLYPH_FILL && 615 aParams.context->HasNonOpaqueNonTransparentColor(currentColor) && 616 !textDrawer; 617 618 // If we need to double-buffer, we'll need to measure the text first to 619 // get the bounds of the area of interest. Ideally we'd do that just for 620 // the specific glyph run(s) that need buffering, but because of bug 621 // 1612610 we currently use the extent of the entire range even when 622 // just buffering a subrange. So we'll measure the full range once and 623 // keep the metrics on hand for any subsequent subranges. 624 gfxTextRun::Metrics metrics; 625 bool gotMetrics = false; 626 627 // Set up parameters that will be constant across all glyph runs we need 628 // to draw, regardless of the font used. 629 TextRunDrawParams params(aParams.paletteCache); 630 params.context = aParams.context; 631 params.devPerApp = 1.0 / double(GetAppUnitsPerDevUnit()); 632 params.isVerticalRun = IsVertical(); 633 params.isRTL = IsRightToLeft(); 634 params.direction = direction; 635 params.strokeOpts = aParams.strokeOpts; 636 params.textStrokeColor = aParams.textStrokeColor; 637 params.fontPalette = aParams.fontPalette; 638 params.textStrokePattern = aParams.textStrokePattern; 639 params.drawOpts = aParams.drawOpts; 640 params.drawMode = aParams.drawMode; 641 params.hasTextShadow = aParams.hasTextShadow; 642 params.callbacks = aParams.callbacks; 643 params.runContextPaint = aParams.contextPaint; 644 params.paintSVGGlyphs = 645 !aParams.callbacks || aParams.callbacks->mShouldPaintSVGGlyphs; 646 params.dt = aParams.context->GetDrawTarget(); 647 params.textDrawer = textDrawer; 648 if (textDrawer) { 649 params.clipRect = textDrawer->GeckoClipRect(); 650 } 651 params.allowGDI = aParams.allowGDI; 652 653 gfxFloat advance = 0.0; 654 gfx::Point pt = aPt; 655 656 for (GlyphRunIterator iter(this, aRange); !iter.AtEnd(); iter.NextRun()) { 657 gfxFont* font = iter.GlyphRun()->mFont; 658 Range runRange(iter.StringStart(), iter.StringEnd()); 659 660 bool needToRestore = false; 661 if (mayNeedBuffering && HasSyntheticBoldOrColor(font)) { 662 needToRestore = true; 663 if (!gotMetrics) { 664 // Measure text; use the bounding box to determine the area we need 665 // to buffer. We measure the entire range, rather than just the glyph 666 // run that we're actually handling, because of bug 1612610: if the 667 // bounding box passed to PushSolidColor does not intersect the 668 // drawTarget's current clip, the skia backend fails to clip properly. 669 // This means we may use a larger buffer than actually needed, but is 670 // otherwise harmless. 671 metrics = MeasureText(aRange, gfxFont::LOOSE_INK_EXTENTS, params.dt, 672 aParams.provider); 673 if (IsRightToLeft()) { 674 metrics.mBoundingBox.MoveBy( 675 gfxPoint(aPt.x - metrics.mAdvanceWidth, aPt.y)); 676 } else { 677 metrics.mBoundingBox.MoveBy(gfxPoint(aPt.x, aPt.y)); 678 } 679 gotMetrics = true; 680 } 681 syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor, 682 GetAppUnitsPerDevUnit()); 683 } 684 685 Range ligatureRange(runRange); 686 bool adjusted = ShrinkToLigatureBoundaries(&ligatureRange); 687 688 bool drawPartial = 689 adjusted && 690 ((aParams.drawMode & (DrawMode::GLYPH_FILL | DrawMode::GLYPH_STROKE)) || 691 (aParams.drawMode == DrawMode::GLYPH_PATH && aParams.callbacks)); 692 gfx::Point origPt = pt; 693 694 if (drawPartial) { 695 DrawPartialLigature(font, Range(runRange.start, ligatureRange.start), &pt, 696 aParams.provider, params, 697 iter.GlyphRun()->mOrientation); 698 } 699 700 DrawGlyphs(font, ligatureRange, &pt, aParams.provider, ligatureRange, 701 params, iter.GlyphRun()->mOrientation); 702 703 if (drawPartial) { 704 DrawPartialLigature(font, Range(ligatureRange.end, runRange.end), &pt, 705 aParams.provider, params, 706 iter.GlyphRun()->mOrientation); 707 } 708 709 if (params.isVerticalRun) { 710 advance += (pt.y - origPt.y) * params.direction; 711 } else { 712 advance += (pt.x - origPt.x) * params.direction; 713 } 714 715 // composite result when synthetic bolding used 716 if (needToRestore) { 717 syntheticBoldBuffer.PopAlpha(); 718 } 719 } 720 721 if (aParams.advanceWidth) { 722 *aParams.advanceWidth = advance; 723 } 724 } 725 726 // This method is mostly parallel to Draw(). 727 void gfxTextRun::DrawEmphasisMarks( 728 gfxContext* aContext, gfxTextRun* aMark, gfxFloat aMarkAdvance, 729 gfx::Point aPt, Range aRange, const PropertyProvider* aProvider, 730 mozilla::gfx::PaletteCache& aPaletteCache) const { 731 MOZ_ASSERT(aRange.end <= GetLength()); 732 733 EmphasisMarkDrawParams params(aContext, aPaletteCache); 734 params.mark = aMark; 735 params.advance = aMarkAdvance; 736 params.direction = GetDirection(); 737 params.isVertical = IsVertical(); 738 739 float& inlineCoord = params.isVertical ? aPt.y.value : aPt.x.value; 740 float direction = params.direction; 741 742 for (GlyphRunIterator iter(this, aRange); !iter.AtEnd(); iter.NextRun()) { 743 gfxFont* font = iter.GlyphRun()->mFont; 744 uint32_t start = iter.StringStart(); 745 uint32_t end = iter.StringEnd(); 746 Range ligatureRange(start, end); 747 bool adjusted = ShrinkToLigatureBoundaries(&ligatureRange); 748 749 if (adjusted) { 750 inlineCoord += 751 direction * ComputePartialLigatureWidth( 752 Range(start, ligatureRange.start), aProvider); 753 } 754 755 AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer; 756 bool haveSpacing = GetAdjustedSpacingArray(ligatureRange, aProvider, 757 ligatureRange, &spacingBuffer); 758 params.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr; 759 font->DrawEmphasisMarks(this, &aPt, ligatureRange.start, 760 ligatureRange.Length(), params); 761 762 if (adjusted) { 763 inlineCoord += direction * ComputePartialLigatureWidth( 764 Range(ligatureRange.end, end), aProvider); 765 } 766 } 767 } 768 769 void gfxTextRun::AccumulateMetricsForRun( 770 gfxFont* aFont, Range aRange, gfxFont::BoundingBoxType aBoundingBoxType, 771 DrawTarget* aRefDrawTarget, const PropertyProvider* aProvider, 772 Range aSpacingRange, gfx::ShapedTextFlags aOrientation, 773 Metrics* aMetrics) const { 774 AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer; 775 bool haveSpacing = 776 GetAdjustedSpacingArray(aRange, aProvider, aSpacingRange, &spacingBuffer); 777 Metrics metrics = aFont->Measure( 778 this, aRange.start, aRange.end, aBoundingBoxType, aRefDrawTarget, 779 haveSpacing ? spacingBuffer.Elements() : nullptr, aOrientation); 780 aMetrics->CombineWith(metrics, IsRightToLeft()); 781 } 782 783 void gfxTextRun::AccumulatePartialLigatureMetrics( 784 gfxFont* aFont, Range aRange, gfxFont::BoundingBoxType aBoundingBoxType, 785 DrawTarget* aRefDrawTarget, const PropertyProvider* aProvider, 786 gfx::ShapedTextFlags aOrientation, Metrics* aMetrics) const { 787 if (aRange.start >= aRange.end) return; 788 789 // Measure partial ligature. We hack this by clipping the metrics in the 790 // same way we clip the drawing. 791 LigatureData data = ComputeLigatureData(aRange, aProvider); 792 793 // First measure the complete ligature 794 Metrics metrics; 795 AccumulateMetricsForRun(aFont, data.mRange, aBoundingBoxType, aRefDrawTarget, 796 aProvider, aRange, aOrientation, &metrics); 797 798 // Clip the bounding box to the ligature part 799 gfxFloat bboxLeft = metrics.mBoundingBox.X(); 800 gfxFloat bboxRight = metrics.mBoundingBox.XMost(); 801 // Where we are going to start "drawing" relative to our left baseline origin 802 gfxFloat origin = 803 IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0; 804 ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data); 805 metrics.mBoundingBox.SetBoxX(bboxLeft, bboxRight); 806 807 // mBoundingBox is now relative to the left baseline origin for the entire 808 // ligature. Shift it left. 809 metrics.mBoundingBox.MoveByX( 810 -(IsRightToLeft() 811 ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth) 812 : data.mPartAdvance)); 813 metrics.mAdvanceWidth = data.mPartWidth; 814 815 aMetrics->CombineWith(metrics, IsRightToLeft()); 816 } 817 818 gfxTextRun::Metrics gfxTextRun::MeasureText( 819 Range aRange, gfxFont::BoundingBoxType aBoundingBoxType, 820 DrawTarget* aRefDrawTarget, const PropertyProvider* aProvider) const { 821 NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range"); 822 823 Metrics accumulatedMetrics; 824 for (GlyphRunIterator iter(this, aRange); !iter.AtEnd(); iter.NextRun()) { 825 gfxFont* font = iter.GlyphRun()->mFont; 826 uint32_t start = iter.StringStart(); 827 uint32_t end = iter.StringEnd(); 828 Range ligatureRange(start, end); 829 bool adjusted = ShrinkToLigatureBoundaries(&ligatureRange); 830 831 if (adjusted) { 832 AccumulatePartialLigatureMetrics(font, Range(start, ligatureRange.start), 833 aBoundingBoxType, aRefDrawTarget, 834 aProvider, iter.GlyphRun()->mOrientation, 835 &accumulatedMetrics); 836 } 837 838 // XXX This sucks. We have to get glyph extents just so we can detect 839 // glyphs outside the font box, even when aBoundingBoxType is LOOSE, 840 // even though in almost all cases we could get correct results just 841 // by getting some ascent/descent from the font and using our stored 842 // advance widths. 843 AccumulateMetricsForRun(font, ligatureRange, aBoundingBoxType, 844 aRefDrawTarget, aProvider, ligatureRange, 845 iter.GlyphRun()->mOrientation, &accumulatedMetrics); 846 847 if (adjusted) { 848 AccumulatePartialLigatureMetrics( 849 font, Range(ligatureRange.end, end), aBoundingBoxType, aRefDrawTarget, 850 aProvider, iter.GlyphRun()->mOrientation, &accumulatedMetrics); 851 } 852 } 853 854 return accumulatedMetrics; 855 } 856 857 void gfxTextRun::GetLineHeightMetrics(Range aRange, gfxFloat& aAscent, 858 gfxFloat& aDescent) const { 859 Metrics accumulatedMetrics; 860 for (GlyphRunIterator iter(this, aRange); !iter.AtEnd(); iter.NextRun()) { 861 gfxFont* font = iter.GlyphRun()->mFont; 862 auto metrics = 863 font->Measure(this, 0, 0, gfxFont::LOOSE_INK_EXTENTS, nullptr, nullptr, 864 iter.GlyphRun()->mOrientation); 865 accumulatedMetrics.CombineWith(metrics, false); 866 } 867 aAscent = accumulatedMetrics.mAscent; 868 aDescent = accumulatedMetrics.mDescent; 869 } 870 871 void gfxTextRun::ClassifyAutoHyphenations(uint32_t aStart, Range aRange, 872 nsTArray<HyphenType>& aHyphenBuffer, 873 HyphenationState* aWordState) { 874 MOZ_ASSERT( 875 aRange.end - aStart <= aHyphenBuffer.Length() && aRange.start >= aStart, 876 "Range out of bounds"); 877 MOZ_ASSERT(aWordState->mostRecentBoundary >= aStart, 878 "Unexpected aMostRecentWordBoundary!!"); 879 880 uint32_t start = 881 std::min<uint32_t>(aRange.start, aWordState->mostRecentBoundary); 882 883 for (uint32_t i = start; i < aRange.end; ++i) { 884 if (aHyphenBuffer[i - aStart] == HyphenType::Explicit && 885 !aWordState->hasExplicitHyphen) { 886 aWordState->hasExplicitHyphen = true; 887 } 888 if (!aWordState->hasManualHyphen && 889 (aHyphenBuffer[i - aStart] == HyphenType::Soft || 890 aHyphenBuffer[i - aStart] == HyphenType::Explicit)) { 891 aWordState->hasManualHyphen = true; 892 // This is the first manual hyphen in the current word. We can only 893 // know if the current word has a manual hyphen until now. So, we need 894 // to run a sub loop to update the auto hyphens between the start of 895 // the current word and this manual hyphen. 896 if (aWordState->hasAutoHyphen) { 897 for (uint32_t j = aWordState->mostRecentBoundary; j < i; j++) { 898 if (aHyphenBuffer[j - aStart] == 899 HyphenType::AutoWithoutManualInSameWord) { 900 aHyphenBuffer[j - aStart] = HyphenType::AutoWithManualInSameWord; 901 } 902 } 903 } 904 } 905 if (aHyphenBuffer[i - aStart] == HyphenType::AutoWithoutManualInSameWord) { 906 if (!aWordState->hasAutoHyphen) { 907 aWordState->hasAutoHyphen = true; 908 } 909 if (aWordState->hasManualHyphen) { 910 aHyphenBuffer[i - aStart] = HyphenType::AutoWithManualInSameWord; 911 } 912 } 913 914 // If we're at the word boundary, clear/reset couple states. 915 if (mCharacterGlyphs[i].CharIsSpace() || mCharacterGlyphs[i].CharIsTab() || 916 mCharacterGlyphs[i].CharIsNewline() || 917 // Since we will not have a boundary in the end of the string, let's 918 // call the end of the string a special case for word boundary. 919 i == GetLength() - 1) { 920 // We can only get to know whether we should raise/clear an explicit 921 // manual hyphen until we get to the end of a word, because this depends 922 // on whether there exists at least one auto hyphen in the same word. 923 if (!aWordState->hasAutoHyphen && aWordState->hasExplicitHyphen) { 924 for (uint32_t j = aWordState->mostRecentBoundary; j <= i; j++) { 925 if (aHyphenBuffer[j - aStart] == HyphenType::Explicit) { 926 aHyphenBuffer[j - aStart] = HyphenType::None; 927 } 928 } 929 } 930 aWordState->mostRecentBoundary = i; 931 aWordState->hasManualHyphen = false; 932 aWordState->hasAutoHyphen = false; 933 aWordState->hasExplicitHyphen = false; 934 } 935 } 936 } 937 938 uint32_t gfxTextRun::BreakAndMeasureText( 939 uint32_t aStart, uint32_t aMaxLength, bool aLineBreakBefore, 940 gfxFloat aWidth, const PropertyProvider& aProvider, 941 SuppressBreak aSuppressBreak, gfxFont::BoundingBoxType aBoundingBoxType, 942 DrawTarget* aRefDrawTarget, bool aCanWordWrap, bool aCanWhitespaceWrap, 943 bool aIsBreakSpaces, 944 // output params: 945 TrimmableWS* aOutTrimmableWhitespace, Metrics& aOutMetrics, 946 bool& aOutUsedHyphenation, uint32_t& aOutLastBreak, 947 gfxBreakPriority& aBreakPriority) { 948 aMaxLength = std::min(aMaxLength, GetLength() - aStart); 949 950 NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range"); 951 952 constexpr uint32_t kMeasurementBufferSize = 100; 953 Range bufferRange(aStart, 954 aStart + std::min(aMaxLength, kMeasurementBufferSize)); 955 PropertyProvider::Spacing spacingBuffer[kMeasurementBufferSize]; 956 bool haveSpacing = !!(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING); 957 if (haveSpacing) { 958 GetAdjustedSpacing(this, bufferRange, aProvider, spacingBuffer); 959 } 960 AutoTArray<HyphenType, 4096> hyphenBuffer; 961 HyphenationState wordState; 962 wordState.mostRecentBoundary = aStart; 963 bool haveHyphenation = 964 (aProvider.GetHyphensOption() == StyleHyphens::Auto || 965 (aProvider.GetHyphensOption() == StyleHyphens::Manual && 966 !!(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_HYPHEN_BREAKS))); 967 if (haveHyphenation) { 968 if (hyphenBuffer.AppendElements(bufferRange.Length(), fallible)) { 969 aProvider.GetHyphenationBreaks(bufferRange, hyphenBuffer.Elements()); 970 if (aProvider.GetHyphensOption() == StyleHyphens::Auto) { 971 ClassifyAutoHyphenations(aStart, bufferRange, hyphenBuffer, &wordState); 972 } 973 } else { 974 haveHyphenation = false; 975 } 976 } 977 978 gfxFloat width = 0; 979 gfxFloat advance = 0; 980 // The number of space characters that can be trimmed or hang at a soft-wrap 981 uint32_t trimmableChars = 0; 982 // The amount of space removed by ignoring trimmableChars 983 gfxFloat trimmableAdvance = 0; 984 int32_t lastBreak = -1; 985 int32_t lastBreakTrimmableChars = -1; 986 gfxFloat lastBreakTrimmableAdvance = -1; 987 // Cache the last candidate break 988 int32_t lastCandidateBreak = -1; 989 int32_t lastCandidateBreakTrimmableChars = -1; 990 gfxFloat lastCandidateBreakTrimmableAdvance = -1; 991 bool lastCandidateBreakUsedHyphenation = false; 992 gfxBreakPriority lastCandidateBreakPriority = gfxBreakPriority::eNoBreak; 993 bool aborted = false; 994 uint32_t end = aStart + aMaxLength; 995 bool lastBreakUsedHyphenation = false; 996 Range ligatureRange(aStart, end); 997 ShrinkToLigatureBoundaries(&ligatureRange); 998 999 // We may need to move `i` backwards in the following loop, and re-scan 1000 // part of the textrun; we'll use `rescanLimit` so we can tell when that 1001 // is happening: if `i < rescanLimit` then we're rescanning. 1002 uint32_t rescanLimit = aStart; 1003 for (uint32_t i = aStart; i < end; ++i) { 1004 if (i >= bufferRange.end) { 1005 // Fetch more spacing and hyphenation data 1006 uint32_t oldHyphenBufferLength = hyphenBuffer.Length(); 1007 bufferRange.start = i; 1008 bufferRange.end = 1009 std::min(aStart + aMaxLength, i + kMeasurementBufferSize); 1010 // For spacing, we always overwrite the old data with the newly 1011 // fetched one. However, for hyphenation, hyphenation data sometimes 1012 // depends on the context in every word (if "hyphens: auto" is set). 1013 // To ensure we get enough information between neighboring buffers, 1014 // we grow the hyphenBuffer instead of overwrite it. 1015 // NOTE that this means bufferRange does not correspond to the 1016 // entire hyphenBuffer, but only to the most recently added portion. 1017 // Therefore, we need to add the old length to hyphenBuffer.Elements() 1018 // when getting more data. 1019 if (haveSpacing) { 1020 GetAdjustedSpacing(this, bufferRange, aProvider, spacingBuffer); 1021 } 1022 if (haveHyphenation) { 1023 if (hyphenBuffer.AppendElements(bufferRange.Length(), fallible)) { 1024 aProvider.GetHyphenationBreaks( 1025 bufferRange, hyphenBuffer.Elements() + oldHyphenBufferLength); 1026 if (aProvider.GetHyphensOption() == StyleHyphens::Auto) { 1027 uint32_t prevMostRecentWordBoundary = wordState.mostRecentBoundary; 1028 ClassifyAutoHyphenations(aStart, bufferRange, hyphenBuffer, 1029 &wordState); 1030 // If the buffer boundary is in the middle of a word, 1031 // we need to go back to the start of the current word. 1032 // So, we can correct the wrong candidates that we set 1033 // in the previous runs of the loop. 1034 if (prevMostRecentWordBoundary < oldHyphenBufferLength) { 1035 rescanLimit = i; 1036 i = prevMostRecentWordBoundary - 1; 1037 continue; 1038 } 1039 } 1040 } else { 1041 haveHyphenation = false; 1042 } 1043 } 1044 } 1045 1046 // There can't be a word-wrap break opportunity at the beginning of the 1047 // line: if the width is too small for even one character to fit, it 1048 // could be the first and last break opportunity on the line, and that 1049 // would trigger an infinite loop. 1050 if (aSuppressBreak != eSuppressAllBreaks && 1051 (aSuppressBreak != eSuppressInitialBreak || i > aStart)) { 1052 bool atNaturalBreak = mCharacterGlyphs[i].CanBreakBefore() == 1053 CompressedGlyph::FLAG_BREAK_TYPE_NORMAL; 1054 // atHyphenationBreak indicates we're at a "soft" hyphen, where an extra 1055 // hyphen glyph will need to be painted. It is NOT set for breaks at an 1056 // explicit hyphen present in the text. 1057 // 1058 // NOTE(emilio): If you change this condition you also need to change 1059 // nsTextFrame::AddInlineMinISizeForFlow to match. 1060 bool atHyphenationBreak = !atNaturalBreak && haveHyphenation && 1061 (!aLineBreakBefore || i > aStart) && 1062 IsOptionalHyphenBreak(hyphenBuffer[i - aStart]); 1063 bool atAutoHyphenWithManualHyphenInSameWord = 1064 atHyphenationBreak && 1065 hyphenBuffer[i - aStart] == HyphenType::AutoWithManualInSameWord; 1066 bool atBreak = atNaturalBreak || atHyphenationBreak; 1067 bool wordWrapping = 1068 (aCanWordWrap || 1069 (aCanWhitespaceWrap && 1070 mCharacterGlyphs[i].CanBreakBefore() == 1071 CompressedGlyph::FLAG_BREAK_TYPE_EMERGENCY_WRAP)) && 1072 mCharacterGlyphs[i].IsClusterStart() && 1073 aBreakPriority <= gfxBreakPriority::eWordWrapBreak; 1074 1075 bool whitespaceWrapping = false; 1076 if (i > aStart) { 1077 // The spec says the breaking opportunity is *after* whitespace. 1078 auto const& g = mCharacterGlyphs[i - 1]; 1079 whitespaceWrapping = 1080 aIsBreakSpaces && 1081 (g.CharIsSpace() || g.CharIsTab() || g.CharIsNewline()); 1082 } 1083 1084 if (atBreak || wordWrapping || whitespaceWrapping) { 1085 gfxFloat hyphenatedAdvance = advance; 1086 if (atHyphenationBreak) { 1087 hyphenatedAdvance += aProvider.GetHyphenWidth(); 1088 } 1089 1090 if (lastBreak < 0 || 1091 width + hyphenatedAdvance - trimmableAdvance <= aWidth) { 1092 // We can break here. 1093 lastBreak = i; 1094 lastBreakTrimmableChars = trimmableChars; 1095 lastBreakTrimmableAdvance = trimmableAdvance; 1096 lastBreakUsedHyphenation = atHyphenationBreak; 1097 aBreakPriority = (atBreak || whitespaceWrapping) 1098 ? gfxBreakPriority::eNormalBreak 1099 : gfxBreakPriority::eWordWrapBreak; 1100 } 1101 1102 width += advance; 1103 advance = 0; 1104 if (width - trimmableAdvance > aWidth) { 1105 // No more text fits. Abort 1106 aborted = true; 1107 break; 1108 } 1109 // There are various kinds of break opportunities: 1110 // 1. word wrap break, 1111 // 2. natural break, 1112 // 3. manual hyphenation break, 1113 // 4. auto hyphenation break without any manual hyphenation 1114 // in the same word, 1115 // 5. auto hyphenation break with another manual hyphenation 1116 // in the same word. 1117 // Allow all of them except the last one to be a candidate. 1118 // So, we can ensure that we don't use an automatic 1119 // hyphenation opportunity within a word that contains another 1120 // manual hyphenation, unless it is the only choice. 1121 if (wordWrapping || !atAutoHyphenWithManualHyphenInSameWord) { 1122 lastCandidateBreak = lastBreak; 1123 lastCandidateBreakTrimmableChars = lastBreakTrimmableChars; 1124 lastCandidateBreakTrimmableAdvance = lastBreakTrimmableAdvance; 1125 lastCandidateBreakUsedHyphenation = lastBreakUsedHyphenation; 1126 lastCandidateBreakPriority = aBreakPriority; 1127 } 1128 } 1129 } 1130 1131 // If we're re-scanning part of a word (to re-process potential 1132 // hyphenation types) then we don't want to accumulate widths again 1133 // for the characters that were already added to `advance`. 1134 if (i < rescanLimit) { 1135 continue; 1136 } 1137 1138 gfxFloat charAdvance; 1139 if (i >= ligatureRange.start && i < ligatureRange.end) { 1140 charAdvance = GetAdvanceForGlyphs(Range(i, i + 1)); 1141 if (haveSpacing) { 1142 PropertyProvider::Spacing* space = 1143 &spacingBuffer[i - bufferRange.start]; 1144 charAdvance += space->mBefore + space->mAfter; 1145 } 1146 } else { 1147 charAdvance = ComputePartialLigatureWidth(Range(i, i + 1), &aProvider); 1148 } 1149 1150 advance += charAdvance; 1151 if (aOutTrimmableWhitespace) { 1152 if (mCharacterGlyphs[i].CharIsSpace()) { 1153 ++trimmableChars; 1154 trimmableAdvance += charAdvance; 1155 } else { 1156 trimmableAdvance = 0; 1157 trimmableChars = 0; 1158 } 1159 } 1160 } 1161 1162 if (!aborted) { 1163 width += advance; 1164 } 1165 1166 // There are three possibilities: 1167 // 1) all the text fit (width <= aWidth) 1168 // 2) some of the text fit up to a break opportunity (width > aWidth && 1169 // lastBreak >= 0) 1170 // 3) none of the text fits before a break opportunity (width > aWidth && 1171 // lastBreak < 0) 1172 uint32_t charsFit; 1173 aOutUsedHyphenation = false; 1174 if (width - trimmableAdvance <= aWidth) { 1175 charsFit = aMaxLength; 1176 } else if (lastBreak >= 0) { 1177 if (lastCandidateBreak >= 0 && lastCandidateBreak != lastBreak) { 1178 lastBreak = lastCandidateBreak; 1179 lastBreakTrimmableChars = lastCandidateBreakTrimmableChars; 1180 lastBreakTrimmableAdvance = lastCandidateBreakTrimmableAdvance; 1181 lastBreakUsedHyphenation = lastCandidateBreakUsedHyphenation; 1182 aBreakPriority = lastCandidateBreakPriority; 1183 } 1184 charsFit = lastBreak - aStart; 1185 trimmableChars = lastBreakTrimmableChars; 1186 trimmableAdvance = lastBreakTrimmableAdvance; 1187 aOutUsedHyphenation = lastBreakUsedHyphenation; 1188 } else { 1189 charsFit = aMaxLength; 1190 } 1191 1192 // Get the overall metrics of the range that fit (including any potentially 1193 // trimmable or hanging whitespace). 1194 aOutMetrics = MeasureText(Range(aStart, aStart + charsFit), aBoundingBoxType, 1195 aRefDrawTarget, &aProvider); 1196 1197 if (aOutTrimmableWhitespace) { 1198 aOutTrimmableWhitespace->mAdvance = trimmableAdvance; 1199 aOutTrimmableWhitespace->mCount = trimmableChars; 1200 } 1201 1202 if (charsFit == aMaxLength) { 1203 if (lastBreak < 0) { 1204 aOutLastBreak = UINT32_MAX; 1205 } else { 1206 aOutLastBreak = lastBreak - aStart; 1207 } 1208 } 1209 1210 return charsFit; 1211 } 1212 1213 gfxFloat gfxTextRun::GetAdvanceWidth( 1214 Range aRange, const PropertyProvider* aProvider, 1215 PropertyProvider::Spacing* aSpacing) const { 1216 NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range"); 1217 1218 Range ligatureRange = aRange; 1219 bool adjusted = ShrinkToLigatureBoundaries(&ligatureRange); 1220 1221 gfxFloat result = 1222 adjusted ? ComputePartialLigatureWidth( 1223 Range(aRange.start, ligatureRange.start), aProvider) + 1224 ComputePartialLigatureWidth( 1225 Range(ligatureRange.end, aRange.end), aProvider) 1226 : 0.0; 1227 1228 if (aSpacing) { 1229 aSpacing->mBefore = aSpacing->mAfter = 0; 1230 } 1231 1232 // Account for all remaining spacing here. This is more efficient than 1233 // processing it along with the glyphs. 1234 if (aProvider && (mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) { 1235 uint32_t i; 1236 AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer; 1237 if (spacingBuffer.AppendElements(aRange.Length(), fallible)) { 1238 if (GetAdjustedSpacing(this, ligatureRange, *aProvider, 1239 spacingBuffer.Elements())) { 1240 for (i = 0; i < ligatureRange.Length(); ++i) { 1241 PropertyProvider::Spacing* space = &spacingBuffer[i]; 1242 result += space->mBefore + space->mAfter; 1243 } 1244 if (aSpacing) { 1245 aSpacing->mBefore = spacingBuffer[0].mBefore; 1246 aSpacing->mAfter = spacingBuffer.LastElement().mAfter; 1247 } 1248 } 1249 } 1250 } 1251 1252 return result + GetAdvanceForGlyphs(ligatureRange); 1253 } 1254 1255 gfxFloat gfxTextRun::GetMinAdvanceWidth(Range aRange) { 1256 MOZ_ASSERT(aRange.end <= GetLength(), "Substring out of range"); 1257 1258 Range ligatureRange = aRange; 1259 bool adjusted = ShrinkToLigatureBoundaries(&ligatureRange); 1260 1261 gfxFloat result = 1262 adjusted 1263 ? std::max(ComputePartialLigatureWidth( 1264 Range(aRange.start, ligatureRange.start), nullptr), 1265 ComputePartialLigatureWidth( 1266 Range(ligatureRange.end, aRange.end), nullptr)) 1267 : 0.0; 1268 1269 // Compute min advance width by assuming each grapheme cluster takes its own 1270 // line. 1271 gfxFloat clusterAdvance = 0; 1272 for (uint32_t i = ligatureRange.start; i < ligatureRange.end; ++i) { 1273 if (mCharacterGlyphs[i].CharIsSpace()) { 1274 // Skip space char to prevent its advance width contributing to the 1275 // result. That is, don't consider a space can be in its own line. 1276 continue; 1277 } 1278 clusterAdvance += GetAdvanceForGlyph(i); 1279 if (i + 1 == ligatureRange.end || IsClusterStart(i + 1)) { 1280 result = std::max(result, clusterAdvance); 1281 clusterAdvance = 0; 1282 } 1283 } 1284 1285 return result; 1286 } 1287 1288 bool gfxTextRun::SetLineBreaks(Range aRange, bool aLineBreakBefore, 1289 bool aLineBreakAfter, 1290 gfxFloat* aAdvanceWidthDelta) { 1291 // Do nothing because our shaping does not currently take linebreaks into 1292 // account. There is no change in advance width. 1293 if (aAdvanceWidthDelta) { 1294 *aAdvanceWidthDelta = 0; 1295 } 1296 return false; 1297 } 1298 1299 const gfxTextRun::GlyphRun* gfxTextRun::FindFirstGlyphRunContaining( 1300 uint32_t aOffset) const { 1301 MOZ_ASSERT(aOffset <= GetLength(), "Bad offset looking for glyphrun"); 1302 MOZ_ASSERT(GetLength() == 0 || !mGlyphRuns.IsEmpty(), 1303 "non-empty text but no glyph runs present!"); 1304 if (mGlyphRuns.Length() <= 1) { 1305 return mGlyphRuns.begin(); 1306 } 1307 if (aOffset == GetLength()) { 1308 return mGlyphRuns.end() - 1; 1309 } 1310 const auto* start = mGlyphRuns.begin(); 1311 const auto* limit = mGlyphRuns.end(); 1312 while (limit - start > 1) { 1313 const auto* mid = start + (limit - start) / 2; 1314 if (mid->mCharacterOffset <= aOffset) { 1315 start = mid; 1316 } else { 1317 limit = mid; 1318 } 1319 } 1320 MOZ_ASSERT(start->mCharacterOffset <= aOffset, 1321 "Hmm, something went wrong, aOffset should have been found"); 1322 return start; 1323 } 1324 1325 void gfxTextRun::AddGlyphRun(gfxFont* aFont, FontMatchType aMatchType, 1326 uint32_t aUTF16Offset, bool aForceNewRun, 1327 gfx::ShapedTextFlags aOrientation, bool aIsCJK) { 1328 MOZ_ASSERT(aFont, "adding glyph run for null font!"); 1329 MOZ_ASSERT(aOrientation != gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED, 1330 "mixed orientation should have been resolved"); 1331 if (!aFont) { 1332 return; 1333 } 1334 1335 if (mGlyphRuns.IsEmpty()) { 1336 mGlyphRuns.AppendElement( 1337 GlyphRun{aFont, aUTF16Offset, aOrientation, aMatchType, aIsCJK}); 1338 return; 1339 } 1340 1341 uint32_t numGlyphRuns = mGlyphRuns.Length(); 1342 if (!aForceNewRun) { 1343 GlyphRun* lastGlyphRun = &mGlyphRuns.LastElement(); 1344 1345 MOZ_ASSERT(lastGlyphRun->mCharacterOffset <= aUTF16Offset, 1346 "Glyph runs out of order (and run not forced)"); 1347 1348 // Don't append a run if the font is already the one we want 1349 if (lastGlyphRun->Matches(aFont, aOrientation, aIsCJK, aMatchType)) { 1350 return; 1351 } 1352 1353 // If the offset has not changed, avoid leaving a zero-length run 1354 // by overwriting the last entry instead of appending... 1355 if (lastGlyphRun->mCharacterOffset == aUTF16Offset) { 1356 // ...except that if the run before the last entry had the same 1357 // font as the new one wants, merge with it instead of creating 1358 // adjacent runs with the same font 1359 if (numGlyphRuns > 1 && mGlyphRuns[numGlyphRuns - 2].Matches( 1360 aFont, aOrientation, aIsCJK, aMatchType)) { 1361 mGlyphRuns.TruncateLength(numGlyphRuns - 1); 1362 return; 1363 } 1364 1365 lastGlyphRun->SetProperties(aFont, aOrientation, aIsCJK, aMatchType); 1366 return; 1367 } 1368 } 1369 1370 MOZ_ASSERT( 1371 aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0, 1372 "First run doesn't cover the first character (and run not forced)?"); 1373 1374 mGlyphRuns.AppendElement( 1375 GlyphRun{aFont, aUTF16Offset, aOrientation, aMatchType, aIsCJK}); 1376 } 1377 1378 void gfxTextRun::SanitizeGlyphRuns() { 1379 if (mGlyphRuns.Length() < 2) { 1380 return; 1381 } 1382 1383 auto& runs = mGlyphRuns.Array(); 1384 1385 // The runs are almost certain to be already sorted, so it's worth avoiding 1386 // the Sort() call if possible. 1387 bool isSorted = true; 1388 uint32_t prevOffset = 0; 1389 for (const auto& r : runs) { 1390 if (r.mCharacterOffset < prevOffset) { 1391 isSorted = false; 1392 break; 1393 } 1394 prevOffset = r.mCharacterOffset; 1395 } 1396 if (!isSorted) { 1397 runs.Sort(GlyphRunOffsetComparator()); 1398 } 1399 1400 // Coalesce adjacent glyph runs that have the same properties, and eliminate 1401 // any empty runs. 1402 GlyphRun* prevRun = nullptr; 1403 const CompressedGlyph* charGlyphs = mCharacterGlyphs; 1404 1405 runs.RemoveElementsBy([&](GlyphRun& aRun) -> bool { 1406 // First run is always retained. 1407 if (!prevRun) { 1408 prevRun = &aRun; 1409 return false; 1410 } 1411 1412 // Merge any run whose properties match its predecessor. 1413 if (prevRun->Matches(aRun.mFont, aRun.mOrientation, aRun.mIsCJK, 1414 aRun.mMatchType)) { 1415 return true; 1416 } 1417 1418 if (prevRun->mCharacterOffset >= aRun.mCharacterOffset) { 1419 // Preceding run is empty (or has become so due to the adjusting for 1420 // ligature boundaries), so we will overwrite it with this one, which 1421 // will then be discarded. 1422 *prevRun = aRun; 1423 return true; 1424 } 1425 1426 // If any glyph run starts with ligature-continuation characters, we need to 1427 // advance it to the first "real" character to avoid drawing partial 1428 // ligature glyphs from wrong font (seen with U+FEFF in reftest 474417-1, as 1429 // Core Text eliminates the glyph, which makes it appear as if a ligature 1430 // has been formed) 1431 while (charGlyphs[aRun.mCharacterOffset].IsLigatureContinuation() && 1432 aRun.mCharacterOffset < GetLength()) { 1433 aRun.mCharacterOffset++; 1434 } 1435 1436 // We're keeping another run, so update prevRun pointer to refer to it (in 1437 // its new position). 1438 ++prevRun; 1439 return false; 1440 }); 1441 1442 MOZ_ASSERT(prevRun == &runs.LastElement(), "lost track of prevRun!"); 1443 1444 // Drop any trailing empty run. 1445 if (runs.Length() > 1 && prevRun->mCharacterOffset == GetLength()) { 1446 runs.RemoveLastElement(); 1447 } 1448 1449 MOZ_ASSERT(!runs.IsEmpty()); 1450 if (runs.Length() == 1) { 1451 mGlyphRuns.ConvertToElement(); 1452 } 1453 } 1454 1455 void gfxTextRun::CopyGlyphDataFrom(gfxShapedWord* aShapedWord, 1456 uint32_t aOffset) { 1457 uint32_t wordLen = aShapedWord->GetLength(); 1458 MOZ_ASSERT(aOffset + wordLen <= GetLength(), "word overruns end of textrun"); 1459 1460 CompressedGlyph* charGlyphs = GetCharacterGlyphs(); 1461 const CompressedGlyph* wordGlyphs = aShapedWord->GetCharacterGlyphs(); 1462 if (aShapedWord->HasDetailedGlyphs()) { 1463 for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) { 1464 const CompressedGlyph& g = wordGlyphs[i]; 1465 if (!g.IsSimpleGlyph()) { 1466 const DetailedGlyph* details = 1467 g.GetGlyphCount() > 0 ? aShapedWord->GetDetailedGlyphs(i) : nullptr; 1468 SetDetailedGlyphs(aOffset, g.GetGlyphCount(), details); 1469 } 1470 charGlyphs[aOffset] = g; 1471 } 1472 } else { 1473 memcpy(charGlyphs + aOffset, wordGlyphs, wordLen * sizeof(CompressedGlyph)); 1474 } 1475 } 1476 1477 void gfxTextRun::CopyGlyphDataFrom(gfxTextRun* aSource, Range aRange, 1478 uint32_t aDest) { 1479 MOZ_ASSERT(aRange.end <= aSource->GetLength(), 1480 "Source substring out of range"); 1481 MOZ_ASSERT(aDest + aRange.Length() <= GetLength(), 1482 "Destination substring out of range"); 1483 1484 if (aSource->mDontSkipDrawing) { 1485 mDontSkipDrawing = true; 1486 } 1487 1488 // Copy base glyph data, and DetailedGlyph data where present 1489 const CompressedGlyph* srcGlyphs = aSource->mCharacterGlyphs + aRange.start; 1490 CompressedGlyph* dstGlyphs = mCharacterGlyphs + aDest; 1491 for (uint32_t i = 0; i < aRange.Length(); ++i) { 1492 CompressedGlyph g = srcGlyphs[i]; 1493 g.SetCanBreakBefore(!g.IsClusterStart() 1494 ? CompressedGlyph::FLAG_BREAK_TYPE_NONE 1495 : dstGlyphs[i].CanBreakBefore()); 1496 if (!g.IsSimpleGlyph()) { 1497 uint32_t count = g.GetGlyphCount(); 1498 if (count > 0) { 1499 // DetailedGlyphs allocation is infallible, so this should never be 1500 // null unless the source textrun is somehow broken. 1501 DetailedGlyph* src = aSource->GetDetailedGlyphs(i + aRange.start); 1502 MOZ_ASSERT(src, "missing DetailedGlyphs?"); 1503 if (src) { 1504 DetailedGlyph* dst = AllocateDetailedGlyphs(i + aDest, count); 1505 ::memcpy(dst, src, count * sizeof(DetailedGlyph)); 1506 } else { 1507 g.SetMissing(); 1508 } 1509 } 1510 } 1511 dstGlyphs[i] = g; 1512 } 1513 1514 // Copy glyph runs 1515 #ifdef DEBUG 1516 GlyphRun* prevRun = nullptr; 1517 #endif 1518 for (GlyphRunIterator iter(aSource, aRange); !iter.AtEnd(); iter.NextRun()) { 1519 gfxFont* font = iter.GlyphRun()->mFont; 1520 MOZ_ASSERT(!prevRun || !prevRun->Matches(iter.GlyphRun()->mFont, 1521 iter.GlyphRun()->mOrientation, 1522 iter.GlyphRun()->mIsCJK, 1523 FontMatchType::Kind::kUnspecified), 1524 "Glyphruns not coalesced?"); 1525 #ifdef DEBUG 1526 prevRun = const_cast<GlyphRun*>(iter.GlyphRun()); 1527 uint32_t end = iter.StringEnd(); 1528 #endif 1529 uint32_t start = iter.StringStart(); 1530 1531 // These used to be NS_ASSERTION()s, but WARNING is more appropriate. 1532 // Although it's unusual (and not desirable), it's possible for us to assign 1533 // different fonts to a base character and a following diacritic. 1534 // Example on OSX 10.5/10.6 with default fonts installed: 1535 // data:text/html,<p style="font-family:helvetica, arial, sans-serif;"> 1536 // &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486; 1537 // This means the rendering of the cluster will probably not be very good, 1538 // but it's the best we can do for now if the specified font only covered 1539 // the initial base character and not its applied marks. 1540 NS_WARNING_ASSERTION(aSource->IsClusterStart(start), 1541 "Started font run in the middle of a cluster"); 1542 NS_WARNING_ASSERTION( 1543 end == aSource->GetLength() || aSource->IsClusterStart(end), 1544 "Ended font run in the middle of a cluster"); 1545 1546 AddGlyphRun(font, iter.GlyphRun()->mMatchType, start - aRange.start + aDest, 1547 false, iter.GlyphRun()->mOrientation, iter.GlyphRun()->mIsCJK); 1548 } 1549 } 1550 1551 void gfxTextRun::ClearGlyphsAndCharacters() { 1552 ResetGlyphRuns(); 1553 memset(reinterpret_cast<char*>(mCharacterGlyphs), 0, 1554 mLength * sizeof(CompressedGlyph)); 1555 mDetailedGlyphs = nullptr; 1556 } 1557 1558 void gfxTextRun::SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget, 1559 uint32_t aCharIndex, 1560 gfx::ShapedTextFlags aOrientation) { 1561 if (SetSpaceGlyphIfSimple(aFont, aCharIndex, ' ', aOrientation)) { 1562 return; 1563 } 1564 1565 gfx::ShapedTextFlags flags = 1566 gfx::ShapedTextFlags::TEXT_IS_8BIT | aOrientation; 1567 bool vertical = 1568 !!(GetFlags() & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT); 1569 gfxFontShaper::RoundingFlags roundingFlags = 1570 aFont->GetRoundOffsetsToPixels(aDrawTarget); 1571 aFont->ProcessSingleSpaceShapedWord( 1572 aDrawTarget, vertical, mAppUnitsPerDevUnit, flags, roundingFlags, 1573 [&](gfxShapedWord* aShapedWord) { 1574 const GlyphRun* prevRun = TrailingGlyphRun(); 1575 bool isCJK = prevRun && prevRun->mFont == aFont && 1576 prevRun->mOrientation == aOrientation 1577 ? prevRun->mIsCJK 1578 : false; 1579 AddGlyphRun(aFont, FontMatchType::Kind::kUnspecified, aCharIndex, false, 1580 aOrientation, isCJK); 1581 CopyGlyphDataFrom(aShapedWord, aCharIndex); 1582 GetCharacterGlyphs()[aCharIndex].SetIsSpace(); 1583 }); 1584 } 1585 1586 bool gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex, 1587 char16_t aSpaceChar, 1588 gfx::ShapedTextFlags aOrientation) { 1589 uint32_t spaceGlyph = aFont->GetSpaceGlyph(); 1590 if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) { 1591 return false; 1592 } 1593 1594 gfxFont::Orientation fontOrientation = 1595 (aOrientation & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT) 1596 ? nsFontMetrics::eVertical 1597 : nsFontMetrics::eHorizontal; 1598 uint32_t spaceWidthAppUnits = NS_lroundf( 1599 aFont->GetMetrics(fontOrientation).spaceWidth * mAppUnitsPerDevUnit); 1600 if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) { 1601 return false; 1602 } 1603 1604 const GlyphRun* prevRun = TrailingGlyphRun(); 1605 bool isCJK = prevRun && prevRun->mFont == aFont && 1606 prevRun->mOrientation == aOrientation 1607 ? prevRun->mIsCJK 1608 : false; 1609 AddGlyphRun(aFont, FontMatchType::Kind::kUnspecified, aCharIndex, false, 1610 aOrientation, isCJK); 1611 CompressedGlyph g = 1612 CompressedGlyph::MakeSimpleGlyph(spaceWidthAppUnits, spaceGlyph); 1613 if (aSpaceChar == ' ') { 1614 g.SetIsSpace(); 1615 } 1616 GetCharacterGlyphs()[aCharIndex] = g; 1617 return true; 1618 } 1619 1620 void gfxTextRun::FetchGlyphExtents(DrawTarget* aRefDrawTarget) const { 1621 bool needsGlyphExtents = NeedsGlyphExtents(); 1622 if (!needsGlyphExtents && !mDetailedGlyphs) { 1623 return; 1624 } 1625 1626 uint32_t runCount; 1627 const GlyphRun* glyphRuns = GetGlyphRuns(&runCount); 1628 CompressedGlyph* charGlyphs = mCharacterGlyphs; 1629 for (uint32_t i = 0; i < runCount; ++i) { 1630 const GlyphRun& run = glyphRuns[i]; 1631 gfxFont* font = run.mFont; 1632 if (MOZ_UNLIKELY(font->GetStyle()->AdjustedSizeMustBeZero())) { 1633 continue; 1634 } 1635 1636 uint32_t start = run.mCharacterOffset; 1637 uint32_t end = 1638 i + 1 < runCount ? glyphRuns[i + 1].mCharacterOffset : GetLength(); 1639 gfxGlyphExtents* extents = 1640 font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit); 1641 1642 AutoReadLock lock(extents->mLock); 1643 for (uint32_t j = start; j < end; ++j) { 1644 const gfxTextRun::CompressedGlyph* glyphData = &charGlyphs[j]; 1645 if (glyphData->IsSimpleGlyph()) { 1646 // If we're in speed mode, don't set up glyph extents here; we'll 1647 // just return "optimistic" glyph bounds later 1648 if (needsGlyphExtents) { 1649 uint32_t glyphIndex = glyphData->GetSimpleGlyph(); 1650 if (!extents->IsGlyphKnownLocked(glyphIndex)) { 1651 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 1652 ++gGlyphExtentsSetupEagerSimple; 1653 #endif 1654 extents->mLock.ReadUnlock(); 1655 font->SetupGlyphExtents(aRefDrawTarget, glyphIndex, false, extents); 1656 extents->mLock.ReadLock(); 1657 } 1658 } 1659 } else if (!glyphData->IsMissing()) { 1660 uint32_t glyphCount = glyphData->GetGlyphCount(); 1661 if (glyphCount == 0) { 1662 continue; 1663 } 1664 const gfxTextRun::DetailedGlyph* details = GetDetailedGlyphs(j); 1665 if (!details) { 1666 continue; 1667 } 1668 for (uint32_t k = 0; k < glyphCount; ++k, ++details) { 1669 uint32_t glyphIndex = details->mGlyphID; 1670 if (!extents->IsGlyphKnownWithTightExtentsLocked(glyphIndex)) { 1671 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 1672 ++gGlyphExtentsSetupEagerTight; 1673 #endif 1674 extents->mLock.ReadUnlock(); 1675 font->SetupGlyphExtents(aRefDrawTarget, glyphIndex, true, extents); 1676 extents->mLock.ReadLock(); 1677 } 1678 } 1679 } 1680 } 1681 } 1682 } 1683 1684 size_t gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) { 1685 size_t total = mGlyphRuns.ShallowSizeOfExcludingThis(aMallocSizeOf); 1686 1687 if (mDetailedGlyphs) { 1688 total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf); 1689 } 1690 1691 return total; 1692 } 1693 1694 size_t gfxTextRun::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) { 1695 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1696 } 1697 1698 #ifdef DEBUG_FRAME_DUMP 1699 void gfxTextRun::Dump(FILE* out) { 1700 # define APPEND_FLAG(string_, enum_, field_, flag_) \ 1701 if (field_ & enum_::flag_) { \ 1702 string_.AppendPrintf(remaining != field_ ? " %s" : "%s", #flag_); \ 1703 remaining &= ~enum_::flag_; \ 1704 } 1705 # define APPEND_FLAGS(string_, enum_, field_, flags_) \ 1706 { \ 1707 auto remaining = field_; \ 1708 MOZ_FOR_EACH(APPEND_FLAG, (string_, enum_, field_, ), flags_) \ 1709 if (int(remaining)) { \ 1710 string_.AppendPrintf(" %s(0x%0x)", #enum_, int(remaining)); \ 1711 } \ 1712 } 1713 1714 nsCString flagsString; 1715 ShapedTextFlags orient = mFlags & ShapedTextFlags::TEXT_ORIENT_MASK; 1716 ShapedTextFlags otherFlags = mFlags & ~ShapedTextFlags::TEXT_ORIENT_MASK; 1717 APPEND_FLAGS(flagsString, ShapedTextFlags, otherFlags, 1718 (TEXT_IS_RTL, TEXT_ENABLE_SPACING, TEXT_IS_8BIT, 1719 TEXT_ENABLE_HYPHEN_BREAKS, TEXT_NEED_BOUNDING_BOX, 1720 TEXT_DISABLE_OPTIONAL_LIGATURES, TEXT_OPTIMIZE_SPEED, 1721 TEXT_HIDE_CONTROL_CHARACTERS, TEXT_TRAILING_ARABICCHAR, 1722 TEXT_INCOMING_ARABICCHAR, TEXT_USE_MATH_SCRIPT)) 1723 1724 if (orient != ShapedTextFlags::TEXT_ORIENT_HORIZONTAL && 1725 !flagsString.IsEmpty()) { 1726 flagsString += ' '; 1727 } 1728 1729 switch (orient) { 1730 case ShapedTextFlags::TEXT_ORIENT_HORIZONTAL: 1731 break; 1732 case ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT: 1733 flagsString += "TEXT_ORIENT_VERTICAL_UPRIGHT"; 1734 break; 1735 case ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT: 1736 flagsString += "TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT"; 1737 break; 1738 case ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED: 1739 flagsString += "TEXT_ORIENT_VERTICAL_MIXED"; 1740 break; 1741 case ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT: 1742 flagsString += "TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT"; 1743 break; 1744 default: 1745 flagsString.AppendPrintf("UNKNOWN_TEXT_ORIENT_MASK(0x%0x)", int(orient)); 1746 break; 1747 } 1748 1749 nsCString flags2String; 1750 APPEND_FLAGS( 1751 flags2String, nsTextFrameUtils::Flags, mFlags2, 1752 (HasTab, HasShy, HasNewline, DontSkipDrawingForPendingUserFonts, 1753 IsSimpleFlow, IncomingWhitespace, TrailingWhitespace, 1754 CompressedLeadingWhitespace, NoBreaks, IsTransformed, HasTrailingBreak, 1755 IsSingleCharMi, MightHaveGlyphChanges, RunSizeAccounted)) 1756 1757 # undef APPEND_FLAGS 1758 # undef APPEND_FLAG 1759 1760 nsAutoCString lang; 1761 mFontGroup->Language()->ToUTF8String(lang); 1762 fprintf(out, "gfxTextRun@%p (length %u) [%s] [%s] [%s]\n", this, mLength, 1763 flagsString.get(), flags2String.get(), lang.get()); 1764 1765 fprintf(out, " Glyph runs:\n"); 1766 for (const auto& run : mGlyphRuns) { 1767 gfxFont* font = run.mFont; 1768 const gfxFontStyle* style = font->GetStyle(); 1769 nsAutoCString styleString; 1770 style->style.ToString(styleString); 1771 fprintf(out, " offset=%d %s %f/%g/%s\n", run.mCharacterOffset, 1772 font->GetName().get(), style->size, style->weight.ToFloat(), 1773 styleString.get()); 1774 } 1775 1776 fprintf(out, " Glyphs:\n"); 1777 for (uint32_t i = 0; i < mLength; ++i) { 1778 auto glyphData = GetCharacterGlyphs()[i]; 1779 1780 nsCString line; 1781 line.AppendPrintf(" [%d] 0x%p %s", i, GetCharacterGlyphs() + i, 1782 glyphData.IsSimpleGlyph() ? "simple" : "detailed"); 1783 1784 if (glyphData.IsSimpleGlyph()) { 1785 line.AppendPrintf(" id=%d adv=%d", glyphData.GetSimpleGlyph(), 1786 glyphData.GetSimpleAdvance()); 1787 } else { 1788 uint32_t count = glyphData.GetGlyphCount(); 1789 if (count) { 1790 line += " ids="; 1791 for (uint32_t j = 0; j < count; j++) { 1792 line.AppendPrintf(j ? ",%d" : "%d", GetDetailedGlyphs(i)[j].mGlyphID); 1793 } 1794 line += " advs="; 1795 for (uint32_t j = 0; j < count; j++) { 1796 line.AppendPrintf(j ? ",%d" : "%d", GetDetailedGlyphs(i)[j].mAdvance); 1797 } 1798 line += " offsets="; 1799 for (uint32_t j = 0; j < count; j++) { 1800 auto offset = GetDetailedGlyphs(i)[j].mOffset; 1801 line.AppendPrintf(j ? ",(%g,%g)" : "(%g,%g)", offset.x.value, 1802 offset.y.value); 1803 } 1804 } else { 1805 line += " (no glyphs)"; 1806 } 1807 } 1808 1809 if (glyphData.CharIsSpace()) { 1810 line += " CHAR_IS_SPACE"; 1811 } 1812 if (glyphData.CharIsTab()) { 1813 line += " CHAR_IS_TAB"; 1814 } 1815 if (glyphData.CharIsNewline()) { 1816 line += " CHAR_IS_NEWLINE"; 1817 } 1818 if (glyphData.CharIsFormattingControl()) { 1819 line += " CHAR_IS_FORMATTING_CONTROL"; 1820 } 1821 if (glyphData.CharTypeFlags() & 1822 CompressedGlyph::FLAG_CHAR_NO_EMPHASIS_MARK) { 1823 line += " CHAR_NO_EMPHASIS_MARK"; 1824 } 1825 1826 if (!glyphData.IsSimpleGlyph()) { 1827 if (!glyphData.IsMissing()) { 1828 line += " NOT_MISSING"; 1829 } 1830 if (!glyphData.IsClusterStart()) { 1831 line += " NOT_IS_CLUSTER_START"; 1832 } 1833 if (!glyphData.IsLigatureGroupStart()) { 1834 line += " NOT_LIGATURE_GROUP_START"; 1835 } 1836 } 1837 1838 switch (glyphData.CanBreakBefore()) { 1839 case CompressedGlyph::FLAG_BREAK_TYPE_NORMAL: 1840 line += " BREAK_TYPE_NORMAL"; 1841 break; 1842 case CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN: 1843 line += " BREAK_TYPE_HYPHEN"; 1844 break; 1845 } 1846 1847 fprintf(out, "%s\n", line.get()); 1848 } 1849 } 1850 #endif 1851 1852 gfxFontGroup::gfxFontGroup(FontVisibilityProvider* aFontVisibilityProvider, 1853 const StyleFontFamilyList& aFontFamilyList, 1854 const gfxFontStyle* aStyle, nsAtom* aLanguage, 1855 bool aExplicitLanguage, 1856 gfxTextPerfMetrics* aTextPerf, 1857 gfxUserFontSet* aUserFontSet, gfxFloat aDevToCssSize, 1858 StyleFontVariantEmoji aVariantEmoji) 1859 : mFontVisibilityProvider( 1860 aFontVisibilityProvider), // Note that mFontVisibilityProvider may be 1861 // null! 1862 mFamilyList(aFontFamilyList), 1863 mStyle(*aStyle), 1864 mLanguage(aLanguage), 1865 mDevToCssSize(aDevToCssSize), 1866 mUserFontSet(aUserFontSet), 1867 mTextPerf(aTextPerf), 1868 mPageLang(gfxPlatformFontList::GetFontPrefLangFor(aLanguage)), 1869 mExplicitLanguage(aExplicitLanguage), 1870 mFontVariantEmoji(aVariantEmoji) { 1871 // We don't use SetUserFontSet() here, as we want to unconditionally call 1872 // EnsureFontList() rather than only do UpdateUserFonts() if it changed. 1873 } 1874 1875 gfxFontGroup::~gfxFontGroup() { 1876 // Should not be dropped by stylo 1877 MOZ_ASSERT(!Servo_IsWorkerThread()); 1878 } 1879 1880 static StyleGenericFontFamily GetDefaultGeneric(nsAtom* aLanguage) { 1881 return StaticPresData::Get() 1882 ->GetFontPrefsForLang(aLanguage) 1883 ->GetDefaultGeneric(); 1884 } 1885 1886 class DeferredClearResolvedFonts final : public nsIRunnable { 1887 public: 1888 NS_DECL_THREADSAFE_ISUPPORTS 1889 1890 DeferredClearResolvedFonts() = delete; 1891 explicit DeferredClearResolvedFonts( 1892 const DeferredClearResolvedFonts& aOther) = delete; 1893 explicit DeferredClearResolvedFonts( 1894 nsTArray<gfxFontGroup::FamilyFace>&& aFontList) 1895 : mFontList(std::move(aFontList)) {} 1896 1897 protected: 1898 virtual ~DeferredClearResolvedFonts() {} 1899 1900 NS_IMETHOD Run(void) override { 1901 mFontList.Clear(); 1902 return NS_OK; 1903 } 1904 1905 nsTArray<gfxFontGroup::FamilyFace> mFontList; 1906 }; 1907 1908 NS_IMPL_ISUPPORTS(DeferredClearResolvedFonts, nsIRunnable) 1909 1910 void gfxFontGroup::EnsureFontList() { 1911 // Ensure resolved font instances are valid; discard them if necessary. 1912 auto* pfl = gfxPlatformFontList::PlatformFontList(); 1913 if (mFontListGeneration != pfl->GetGeneration()) { 1914 // Forget cached fonts that may no longer be valid. 1915 mLastPrefFamily = FontFamily(); 1916 mLastPrefFont = nullptr; 1917 mDefaultFont = nullptr; 1918 mResolvedFonts = false; 1919 } 1920 1921 // If we have already resolved the font list, just return. 1922 if (mResolvedFonts) { 1923 return; 1924 } 1925 1926 // Discard existing fonts; but if we're in servo traversal, defer the actual 1927 // deletion. 1928 // XXX(jfkthame) is this really necessary, or is the assertion in 1929 // ~gfxUserFontFamily() obsolete? 1930 if (gfxFontUtils::IsInServoTraversal()) { 1931 NS_DispatchToMainThread(new DeferredClearResolvedFonts(std::move(mFonts))); 1932 } else { 1933 mFonts.Clear(); 1934 } 1935 1936 // (Re-)build the list of fonts. 1937 AutoTArray<FamilyAndGeneric, 10> fonts; 1938 1939 // lookup fonts in the fontlist 1940 for (const StyleSingleFontFamily& name : mFamilyList.list.AsSpan()) { 1941 if (name.IsFamilyName()) { 1942 const auto& familyName = name.AsFamilyName(); 1943 AddPlatformFont(nsAtomCString(familyName.name.AsAtom()), 1944 familyName.syntax == StyleFontFamilyNameSyntax::Quoted, 1945 fonts); 1946 } else { 1947 MOZ_ASSERT(name.IsGeneric()); 1948 const StyleGenericFontFamily generic = name.AsGeneric(); 1949 // system-ui is usually a single family, so it doesn't work great as 1950 // fallback. Prefer the following generic or the language default instead. 1951 if (mFallbackGeneric == StyleGenericFontFamily::None && 1952 generic != StyleGenericFontFamily::SystemUi) { 1953 mFallbackGeneric = generic; 1954 } 1955 pfl->AddGenericFonts(mFontVisibilityProvider, generic, mLanguage, fonts); 1956 if (mTextPerf) { 1957 mTextPerf->current.genericLookups++; 1958 } 1959 } 1960 } 1961 1962 // If necessary, append default language generic onto the end. 1963 if (mFallbackGeneric == StyleGenericFontFamily::None && !mStyle.systemFont) { 1964 auto defaultLanguageGeneric = GetDefaultGeneric(mLanguage); 1965 1966 pfl->AddGenericFonts(mFontVisibilityProvider, defaultLanguageGeneric, 1967 mLanguage, fonts); 1968 if (mTextPerf) { 1969 mTextPerf->current.genericLookups++; 1970 } 1971 } 1972 1973 // build the fontlist from the specified families 1974 for (const auto& f : fonts) { 1975 if (f.mFamily.mShared) { 1976 AddFamilyToFontList(f.mFamily.mShared, f.mGeneric); 1977 } else { 1978 AddFamilyToFontList(f.mFamily.mUnshared, f.mGeneric); 1979 } 1980 } 1981 1982 mFontListGeneration = pfl->GetGeneration(); 1983 mResolvedFonts = true; 1984 } 1985 1986 void gfxFontGroup::AddPlatformFont(const nsACString& aName, bool aQuotedName, 1987 nsTArray<FamilyAndGeneric>& aFamilyList) { 1988 // First, look up in the user font set... 1989 // If the fontSet matches the family, we must not look for a platform 1990 // font of the same name, even if we fail to actually get a fontEntry 1991 // here; we'll fall back to the next name in the CSS font-family list. 1992 if (mUserFontSet) { 1993 // Add userfonts to the fontlist whether already loaded 1994 // or not. Loading is initiated during font matching. 1995 RefPtr<gfxFontFamily> family = mUserFontSet->LookupFamily(aName); 1996 if (family) { 1997 aFamilyList.AppendElement(std::move(family)); 1998 return; 1999 } 2000 } 2001 2002 // Not known in the user font set ==> check system fonts 2003 gfxPlatformFontList::PlatformFontList()->FindAndAddFamilies( 2004 mFontVisibilityProvider, StyleGenericFontFamily::None, aName, 2005 &aFamilyList, 2006 aQuotedName ? gfxPlatformFontList::FindFamiliesFlags::eQuotedFamilyName 2007 : gfxPlatformFontList::FindFamiliesFlags(0), 2008 &mStyle, mLanguage.get(), mDevToCssSize); 2009 } 2010 2011 void gfxFontGroup::AddFamilyToFontList(gfxFontFamily* aFamily, 2012 StyleGenericFontFamily aGeneric) { 2013 if (!aFamily) { 2014 MOZ_ASSERT_UNREACHABLE("don't try to add a null font family!"); 2015 return; 2016 } 2017 AutoTArray<gfxFontEntry*, 4> fontEntryList; 2018 aFamily->FindAllFontsForStyle(mStyle, fontEntryList); 2019 // add these to the fontlist 2020 for (gfxFontEntry* fe : fontEntryList) { 2021 if (!HasFont(fe)) { 2022 FamilyFace ff(aFamily, fe, aGeneric); 2023 if (fe->mIsUserFontContainer) { 2024 ff.CheckState(mSkipDrawing); 2025 } 2026 mFonts.AppendElement(ff); 2027 } 2028 } 2029 // for a family marked as "check fallback faces", only mark the last 2030 // entry so that fallbacks for a family are only checked once 2031 if (aFamily->CheckForFallbackFaces() && !fontEntryList.IsEmpty() && 2032 !mFonts.IsEmpty()) { 2033 mFonts.LastElement().SetCheckForFallbackFaces(); 2034 } 2035 } 2036 2037 void gfxFontGroup::AddFamilyToFontList(fontlist::Family* aFamily, 2038 StyleGenericFontFamily aGeneric) { 2039 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList(); 2040 if (!aFamily->IsInitialized()) { 2041 if (ServoStyleSet* set = gfxFontUtils::CurrentServoStyleSet()) { 2042 // If we need to initialize a Family record, but we're on a style 2043 // worker thread, we have to defer it. 2044 set->AppendTask(PostTraversalTask::InitializeFamily(aFamily)); 2045 set->AppendTask(PostTraversalTask::FontInfoUpdate(set)); 2046 return; 2047 } 2048 if (!pfl->InitializeFamily(aFamily)) { 2049 return; 2050 } 2051 } 2052 AutoTArray<fontlist::Face*, 4> faceList; 2053 aFamily->FindAllFacesForStyle(pfl->SharedFontList(), mStyle, faceList); 2054 for (auto* face : faceList) { 2055 gfxFontEntry* fe = pfl->GetOrCreateFontEntry(face, aFamily); 2056 if (fe && !HasFont(fe)) { 2057 FamilyFace ff(aFamily, fe, aGeneric); 2058 mFonts.AppendElement(ff); 2059 } 2060 } 2061 } 2062 2063 bool gfxFontGroup::HasFont(const gfxFontEntry* aFontEntry) { 2064 for (auto& f : mFonts) { 2065 if (f.FontEntry() == aFontEntry) { 2066 return true; 2067 } 2068 } 2069 return false; 2070 } 2071 2072 already_AddRefed<gfxFont> gfxFontGroup::GetFontAt(uint32_t i, uint32_t aCh, 2073 bool* aLoading) { 2074 if (i >= mFonts.Length()) { 2075 return nullptr; 2076 } 2077 2078 FamilyFace& ff = mFonts[i]; 2079 if (ff.IsInvalid() || ff.IsLoading()) { 2080 return nullptr; 2081 } 2082 2083 RefPtr<gfxFont> font = ff.Font(); 2084 if (!font) { 2085 gfxFontEntry* fe = ff.FontEntry(); 2086 if (!fe) { 2087 return nullptr; 2088 } 2089 gfxCharacterMap* unicodeRangeMap = nullptr; 2090 if (fe->mIsUserFontContainer) { 2091 gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe); 2092 if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED && 2093 ufe->CharacterInUnicodeRange(aCh) && !*aLoading) { 2094 ufe->Load(); 2095 ff.CheckState(mSkipDrawing); 2096 *aLoading = ff.IsLoading(); 2097 } 2098 fe = ufe->GetPlatformFontEntry(); 2099 if (!fe) { 2100 return nullptr; 2101 } 2102 unicodeRangeMap = ufe->GetUnicodeRangeMap(); 2103 } 2104 font = fe->FindOrMakeFont(&mStyle, unicodeRangeMap); 2105 if (!font || !font->Valid()) { 2106 ff.SetInvalid(); 2107 return nullptr; 2108 } 2109 ff.SetFont(font); 2110 } 2111 return font.forget(); 2112 } 2113 2114 void gfxFontGroup::FamilyFace::CheckState(bool& aSkipDrawing) { 2115 gfxFontEntry* fe = FontEntry(); 2116 if (!fe) { 2117 return; 2118 } 2119 if (fe->mIsUserFontContainer) { 2120 gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe); 2121 gfxUserFontEntry::UserFontLoadState state = ufe->LoadState(); 2122 switch (state) { 2123 case gfxUserFontEntry::STATUS_LOAD_PENDING: 2124 case gfxUserFontEntry::STATUS_LOADING: 2125 SetLoading(true); 2126 break; 2127 case gfxUserFontEntry::STATUS_FAILED: 2128 SetInvalid(); 2129 // fall-thru to the default case 2130 [[fallthrough]]; 2131 default: 2132 SetLoading(false); 2133 } 2134 if (ufe->WaitForUserFont()) { 2135 aSkipDrawing = true; 2136 } 2137 } 2138 } 2139 2140 bool gfxFontGroup::FamilyFace::EqualsUserFont( 2141 const gfxUserFontEntry* aUserFont) const { 2142 gfxFontEntry* fe = FontEntry(); 2143 // if there's a font, the entry is the underlying platform font 2144 if (mFontCreated) { 2145 gfxFontEntry* pfe = aUserFont->GetPlatformFontEntry(); 2146 if (pfe == fe) { 2147 return true; 2148 } 2149 } else if (fe == aUserFont) { 2150 return true; 2151 } 2152 return false; 2153 } 2154 2155 static nsAutoCString FamilyListToString( 2156 const StyleFontFamilyList& aFamilyList) { 2157 return StringJoin(","_ns, aFamilyList.list.AsSpan(), 2158 [](nsACString& dst, const StyleSingleFontFamily& name) { 2159 name.AppendToString(dst); 2160 }); 2161 } 2162 2163 already_AddRefed<gfxFont> gfxFontGroup::GetDefaultFont() { 2164 if (mDefaultFont) { 2165 return do_AddRef(mDefaultFont); 2166 } 2167 2168 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList(); 2169 FontFamily family = pfl->GetDefaultFont(mFontVisibilityProvider, &mStyle); 2170 MOZ_ASSERT(!family.IsNull(), 2171 "invalid default font returned by GetDefaultFont"); 2172 2173 gfxFontEntry* fe = nullptr; 2174 if (family.mShared) { 2175 fontlist::Family* fam = family.mShared; 2176 if (!fam->IsInitialized()) { 2177 // If this fails, FindFaceForStyle will just safely return nullptr 2178 (void)pfl->InitializeFamily(fam); 2179 } 2180 fontlist::Face* face = fam->FindFaceForStyle(pfl->SharedFontList(), mStyle); 2181 if (face) { 2182 fe = pfl->GetOrCreateFontEntry(face, fam); 2183 } 2184 } else { 2185 fe = family.mUnshared->FindFontForStyle(mStyle); 2186 } 2187 if (fe) { 2188 mDefaultFont = fe->FindOrMakeFont(&mStyle); 2189 } 2190 2191 uint32_t numInits, loaderState; 2192 pfl->GetFontlistInitInfo(numInits, loaderState); 2193 2194 MOZ_ASSERT(numInits != 0, 2195 "must initialize system fontlist before getting default font!"); 2196 2197 uint32_t numFonts = 0; 2198 if (!mDefaultFont) { 2199 // Try for a "font of last resort...." 2200 // Because an empty font list would be Really Bad for later code 2201 // that assumes it will be able to get valid metrics for layout, 2202 // just look for the first usable font and put in the list. 2203 // (see bug 554544) 2204 if (pfl->SharedFontList()) { 2205 fontlist::FontList* list = pfl->SharedFontList(); 2206 numFonts = list->NumFamilies(); 2207 fontlist::Family* families = list->Families(); 2208 for (uint32_t i = 0; i < numFonts; ++i) { 2209 fontlist::Family* fam = &families[i]; 2210 if (!fam->IsInitialized()) { 2211 (void)pfl->InitializeFamily(fam); 2212 } 2213 fontlist::Face* face = 2214 fam->FindFaceForStyle(pfl->SharedFontList(), mStyle); 2215 if (face) { 2216 fe = pfl->GetOrCreateFontEntry(face, fam); 2217 if (fe) { 2218 mDefaultFont = fe->FindOrMakeFont(&mStyle); 2219 if (mDefaultFont) { 2220 break; 2221 } 2222 NS_WARNING("FindOrMakeFont failed"); 2223 } 2224 } 2225 } 2226 } else { 2227 AutoTArray<RefPtr<gfxFontFamily>, 200> familyList; 2228 pfl->GetFontFamilyList(familyList); 2229 numFonts = familyList.Length(); 2230 for (uint32_t i = 0; i < numFonts; ++i) { 2231 gfxFontEntry* fe = familyList[i]->FindFontForStyle(mStyle, true); 2232 if (fe) { 2233 mDefaultFont = fe->FindOrMakeFont(&mStyle); 2234 if (mDefaultFont) { 2235 break; 2236 } 2237 } 2238 } 2239 } 2240 } 2241 2242 if (!mDefaultFont) { 2243 // We must have failed to find anything usable in our font-family list, 2244 // or it's badly broken. One more last-ditch effort to make a font: 2245 if (gfxFontEntry* fe = pfl->GetDefaultFontEntry()) { 2246 if (RefPtr<gfxFont> f = fe->FindOrMakeFont(&mStyle)) { 2247 return f.forget(); 2248 } 2249 } 2250 2251 // an empty font list at this point is fatal; we're not going to 2252 // be able to do even the most basic layout operations 2253 2254 // annotate crash report with fontlist info 2255 nsAutoCString fontInitInfo; 2256 fontInitInfo.AppendPrintf("no fonts - init: %d fonts: %d loader: %d", 2257 numInits, numFonts, loaderState); 2258 #ifdef XP_WIN 2259 bool dwriteEnabled = gfxWindowsPlatform::GetPlatform()->DWriteEnabled(); 2260 double upTime = (double)GetTickCount(); 2261 fontInitInfo.AppendPrintf(" backend: %s system-uptime: %9.3f sec", 2262 dwriteEnabled ? "directwrite" : "gdi", 2263 upTime / 1000); 2264 #endif 2265 gfxCriticalError() << fontInitInfo.get(); 2266 2267 char msg[256]; // CHECK buffer length if revising message below 2268 SprintfLiteral(msg, "unable to find a usable font (%.220s)", 2269 FamilyListToString(mFamilyList).get()); 2270 MOZ_CRASH_UNSAFE(msg); 2271 } 2272 2273 return do_AddRef(mDefaultFont); 2274 } 2275 2276 already_AddRefed<gfxFont> gfxFontGroup::GetFirstValidFont( 2277 uint32_t aCh, StyleGenericFontFamily* aGeneric, bool* aIsFirst) { 2278 EnsureFontList(); 2279 2280 uint32_t count = mFonts.Length(); 2281 bool loading = false; 2282 2283 // Check whether the font supports the given character, unless aCh is the 2284 // kCSSFirstAvailableFont constant, in which case (as per CSS Fonts spec) 2285 // we want the first font whose unicode-range does not exclude <space>, 2286 // regardless of whether it in fact supports the <space> character. 2287 auto isValidForChar = [](gfxFont* aFont, uint32_t aCh) -> bool { 2288 if (!aFont) { 2289 return false; 2290 } 2291 if (aCh == kCSSFirstAvailableFont) { 2292 if (const auto* unicodeRange = aFont->GetUnicodeRangeMap()) { 2293 return unicodeRange->test(' '); 2294 } 2295 return true; 2296 } 2297 return aFont->HasCharacter(aCh); 2298 }; 2299 2300 for (uint32_t i = 0; i < count; ++i) { 2301 FamilyFace& ff = mFonts[i]; 2302 if (ff.IsInvalid()) { 2303 continue; 2304 } 2305 2306 // already have a font? 2307 RefPtr<gfxFont> font = ff.Font(); 2308 if (isValidForChar(font, aCh)) { 2309 if (aGeneric) { 2310 *aGeneric = ff.Generic(); 2311 } 2312 if (aIsFirst) { 2313 *aIsFirst = (i == 0); 2314 } 2315 return font.forget(); 2316 } 2317 2318 // Need to build a font, loading userfont if not loaded. In 2319 // cases where unicode range might apply, use the character 2320 // provided. 2321 gfxFontEntry* fe = ff.FontEntry(); 2322 if (fe && fe->mIsUserFontContainer) { 2323 gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe); 2324 bool inRange = ufe->CharacterInUnicodeRange( 2325 aCh == kCSSFirstAvailableFont ? ' ' : aCh); 2326 if (inRange) { 2327 if (!loading && 2328 ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED) { 2329 ufe->Load(); 2330 ff.CheckState(mSkipDrawing); 2331 } 2332 if (ff.IsLoading()) { 2333 loading = true; 2334 } 2335 } 2336 if (ufe->LoadState() != gfxUserFontEntry::STATUS_LOADED || !inRange) { 2337 continue; 2338 } 2339 } 2340 2341 font = GetFontAt(i, aCh, &loading); 2342 if (isValidForChar(font, aCh)) { 2343 if (aGeneric) { 2344 *aGeneric = ff.Generic(); 2345 } 2346 if (aIsFirst) { 2347 *aIsFirst = (i == 0); 2348 } 2349 return font.forget(); 2350 } 2351 } 2352 if (aGeneric) { 2353 *aGeneric = StyleGenericFontFamily::None; 2354 } 2355 if (aIsFirst) { 2356 *aIsFirst = false; 2357 } 2358 return GetDefaultFont(); 2359 } 2360 2361 already_AddRefed<gfxFont> gfxFontGroup::GetFirstMathFont() { 2362 EnsureFontList(); 2363 uint32_t count = mFonts.Length(); 2364 for (uint32_t i = 0; i < count; ++i) { 2365 RefPtr<gfxFont> font = GetFontAt(i); 2366 if (font && font->TryGetMathTable()) { 2367 return font.forget(); 2368 } 2369 } 2370 return nullptr; 2371 } 2372 2373 bool gfxFontGroup::IsInvalidChar(uint8_t ch) { 2374 return ((ch & 0x7f) < 0x20 || ch == 0x7f); 2375 } 2376 2377 bool gfxFontGroup::IsInvalidChar(char16_t ch) { 2378 // All printable 7-bit ASCII values are OK 2379 if (ch >= ' ' && ch < 0x7f) { 2380 return false; 2381 } 2382 // No point in sending non-printing control chars through font shaping 2383 if (ch <= 0x9f) { 2384 return true; 2385 } 2386 // Word-separating format/bidi control characters are not shaped as part 2387 // of words. 2388 return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ && 2389 (ch == 0x200B /*ZWSP*/ || ch == 0x2028 /*LSEP*/ || 2390 ch == 0x2029 /*PSEP*/ || ch == 0x2060 /*WJ*/)) || 2391 ch == 0xfeff /*ZWNBSP*/ || IsBidiControl(ch)); 2392 } 2393 2394 already_AddRefed<gfxTextRun> gfxFontGroup::MakeEmptyTextRun( 2395 const Parameters* aParams, gfx::ShapedTextFlags aFlags, 2396 nsTextFrameUtils::Flags aFlags2) { 2397 aFlags |= ShapedTextFlags::TEXT_IS_8BIT; 2398 return gfxTextRun::Create(aParams, 0, this, aFlags, aFlags2); 2399 } 2400 2401 already_AddRefed<gfxTextRun> gfxFontGroup::MakeSpaceTextRun( 2402 const Parameters* aParams, gfx::ShapedTextFlags aFlags, 2403 nsTextFrameUtils::Flags aFlags2) { 2404 aFlags |= ShapedTextFlags::TEXT_IS_8BIT; 2405 2406 RefPtr<gfxTextRun> textRun = 2407 gfxTextRun::Create(aParams, 1, this, aFlags, aFlags2); 2408 if (!textRun) { 2409 return nullptr; 2410 } 2411 2412 gfx::ShapedTextFlags orientation = aFlags & ShapedTextFlags::TEXT_ORIENT_MASK; 2413 if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) { 2414 orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT; 2415 } 2416 2417 RefPtr<gfxFont> font = GetFirstValidFont(); 2418 if (MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero())) { 2419 // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle 2420 // them, and always create at least size 1 fonts, i.e. they still 2421 // render something for size 0 fonts. 2422 textRun->AddGlyphRun(font, FontMatchType::Kind::kUnspecified, 0, false, 2423 orientation, false); 2424 } else { 2425 if (font->GetSpaceGlyph()) { 2426 // Normally, the font has a cached space glyph, so we can avoid 2427 // the cost of calling FindFontForChar. 2428 textRun->SetSpaceGlyph(font, aParams->mDrawTarget, 0, orientation); 2429 } else { 2430 // In case the primary font doesn't have <space> (bug 970891), 2431 // find one that does. 2432 FontMatchType matchType; 2433 RefPtr<gfxFont> spaceFont = 2434 FindFontForChar(' ', 0, 0, Script::LATIN, nullptr, &matchType); 2435 if (spaceFont) { 2436 textRun->SetSpaceGlyph(spaceFont, aParams->mDrawTarget, 0, orientation); 2437 } 2438 } 2439 } 2440 2441 // Note that the gfxGlyphExtents glyph bounds storage for the font will 2442 // always contain an entry for the font's space glyph, so we don't have 2443 // to call FetchGlyphExtents here. 2444 return textRun.forget(); 2445 } 2446 2447 template <typename T> 2448 already_AddRefed<gfxTextRun> gfxFontGroup::MakeBlankTextRun( 2449 const T* aString, uint32_t aLength, const Parameters* aParams, 2450 gfx::ShapedTextFlags aFlags, nsTextFrameUtils::Flags aFlags2) { 2451 RefPtr<gfxTextRun> textRun = 2452 gfxTextRun::Create(aParams, aLength, this, aFlags, aFlags2); 2453 if (!textRun) { 2454 return nullptr; 2455 } 2456 2457 gfx::ShapedTextFlags orientation = aFlags & ShapedTextFlags::TEXT_ORIENT_MASK; 2458 if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) { 2459 orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT; 2460 } 2461 RefPtr<gfxFont> font = GetFirstValidFont(); 2462 textRun->AddGlyphRun(font, FontMatchType::Kind::kUnspecified, 0, false, 2463 orientation, false); 2464 2465 textRun->SetupClusterBoundaries(0, aString, aLength); 2466 2467 for (uint32_t i = 0; i < aLength; i++) { 2468 if (aString[i] == '\n') { 2469 textRun->SetIsNewline(i); 2470 } else if (aString[i] == '\t') { 2471 textRun->SetIsTab(i); 2472 } 2473 } 2474 2475 return textRun.forget(); 2476 } 2477 2478 already_AddRefed<gfxTextRun> gfxFontGroup::MakeHyphenTextRun( 2479 DrawTarget* aDrawTarget, gfx::ShapedTextFlags aFlags, 2480 uint32_t aAppUnitsPerDevUnit) { 2481 // only use U+2010 if it is supported by the first font in the group; 2482 // it's better to use ASCII '-' from the primary font than to fall back to 2483 // U+2010 from some other, possibly poorly-matching face 2484 static const char16_t hyphen = 0x2010; 2485 RefPtr<gfxFont> font = GetFirstValidFont(uint32_t(hyphen)); 2486 if (font->HasCharacter(hyphen)) { 2487 return MakeTextRun(&hyphen, 1, aDrawTarget, aAppUnitsPerDevUnit, aFlags, 2488 nsTextFrameUtils::Flags(), nullptr); 2489 } 2490 2491 static const uint8_t dash = '-'; 2492 return MakeTextRun(&dash, 1, aDrawTarget, aAppUnitsPerDevUnit, aFlags, 2493 nsTextFrameUtils::Flags(), nullptr); 2494 } 2495 2496 gfxFloat gfxFontGroup::GetHyphenWidth( 2497 const gfxTextRun::PropertyProvider* aProvider) { 2498 if (mHyphenWidth < 0) { 2499 RefPtr<DrawTarget> dt(aProvider->GetDrawTarget()); 2500 if (dt) { 2501 RefPtr<gfxTextRun> hyphRun( 2502 MakeHyphenTextRun(dt, aProvider->GetShapedTextFlags(), 2503 aProvider->GetAppUnitsPerDevUnit())); 2504 mHyphenWidth = hyphRun.get() ? hyphRun->GetAdvanceWidth() : 0; 2505 } 2506 } 2507 return mHyphenWidth; 2508 } 2509 2510 template <typename T> 2511 already_AddRefed<gfxTextRun> gfxFontGroup::MakeTextRun( 2512 const T* aString, uint32_t aLength, const Parameters* aParams, 2513 gfx::ShapedTextFlags aFlags, nsTextFrameUtils::Flags aFlags2, 2514 gfxMissingFontRecorder* aMFR) { 2515 if (aLength == 0) { 2516 return MakeEmptyTextRun(aParams, aFlags, aFlags2); 2517 } 2518 if (aLength == 1 && aString[0] == ' ') { 2519 return MakeSpaceTextRun(aParams, aFlags, aFlags2); 2520 } 2521 2522 if (sizeof(T) == 1) { 2523 aFlags |= ShapedTextFlags::TEXT_IS_8BIT; 2524 } 2525 2526 if (MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero())) { 2527 // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle 2528 // them, and always create at least size 1 fonts, i.e. they still 2529 // render something for size 0 fonts. 2530 return MakeBlankTextRun(aString, aLength, aParams, aFlags, aFlags2); 2531 } 2532 2533 RefPtr<gfxTextRun> textRun = 2534 gfxTextRun::Create(aParams, aLength, this, aFlags, aFlags2); 2535 if (!textRun) { 2536 return nullptr; 2537 } 2538 2539 InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR); 2540 2541 textRun->FetchGlyphExtents(aParams->mDrawTarget); 2542 2543 return textRun.forget(); 2544 } 2545 2546 // MakeTextRun instantiations (needed by Linux64 base-toolchain build). 2547 template already_AddRefed<gfxTextRun> gfxFontGroup::MakeTextRun( 2548 const uint8_t* aString, uint32_t aLength, const Parameters* aParams, 2549 gfx::ShapedTextFlags aFlags, nsTextFrameUtils::Flags aFlags2, 2550 gfxMissingFontRecorder* aMFR); 2551 template already_AddRefed<gfxTextRun> gfxFontGroup::MakeTextRun( 2552 const char16_t* aString, uint32_t aLength, const Parameters* aParams, 2553 gfx::ShapedTextFlags aFlags, nsTextFrameUtils::Flags aFlags2, 2554 gfxMissingFontRecorder* aMFR); 2555 2556 // Helper to get a hashtable that maps tags to Script codes, created on first 2557 // use. 2558 static const nsTHashMap<nsUint32HashKey, Script>* ScriptTagToCodeTable() { 2559 using TableT = nsTHashMap<nsUint32HashKey, Script>; 2560 2561 // Initialize our static var by creating the hashtable and populating it with 2562 // all the valid codes. 2563 // According to 2564 // https://en.cppreference.com/w/cpp/language/storage_duration#Static_block_variables: 2565 // "If multiple threads attempt to initialize the same static local variable 2566 // concurrently, the initialization occurs exactly once." 2567 static UniquePtr<TableT> sScriptTagToCode = []() { 2568 auto tagToCode = MakeUnique<TableT>(size_t(Script::NUM_SCRIPT_CODES)); 2569 Script scriptCount = 2570 Script(std::min<int>(UnicodeProperties::GetMaxNumberOfScripts() + 1, 2571 int(Script::NUM_SCRIPT_CODES))); 2572 for (Script s = Script::ARABIC; s < scriptCount; 2573 s = Script(static_cast<int>(s) + 1)) { 2574 uint32_t tag = GetScriptTagForCode(s); 2575 if (tag != HB_SCRIPT_UNKNOWN) { 2576 tagToCode->InsertOrUpdate(tag, s); 2577 } 2578 } 2579 // Clearing the UniquePtr at shutdown will free the table. The call to 2580 // ClearOnShutdown has to be done on the main thread, even if this 2581 // initialization happens from a worker. 2582 if (NS_IsMainThread()) { 2583 ClearOnShutdown(&sScriptTagToCode); 2584 } else { 2585 NS_DispatchToMainThread( 2586 NS_NewRunnableFunction("ClearOnShutdown(sScriptTagToCode)", 2587 []() { ClearOnShutdown(&sScriptTagToCode); })); 2588 } 2589 return tagToCode; 2590 }(); 2591 2592 return sScriptTagToCode.get(); 2593 } 2594 2595 static Script ResolveScriptForLang(const nsAtom* aLanguage, Script aDefault) { 2596 // Cache for lang-to-script lookups, to avoid constantly needing to parse 2597 // and resolve the lang code from scratch. 2598 class LangScriptCache 2599 : public MruCache<const nsAtom*, std::pair<const nsAtom*, Script>, 2600 LangScriptCache> { 2601 public: 2602 static HashNumber Hash(const nsAtom* const& aKey) { return aKey->hash(); } 2603 static bool Match(const nsAtom* const& aKey, 2604 const std::pair<const nsAtom*, Script>& aValue) { 2605 return aKey == aValue.first; 2606 } 2607 }; 2608 2609 static LangScriptCache sCache; 2610 static RWLock sLock("LangScriptCache lock"); 2611 2612 MOZ_ASSERT(aDefault != Script::INVALID && 2613 aDefault < Script::NUM_SCRIPT_CODES); 2614 2615 { 2616 // Try to use a cached value without taking an exclusive lock. 2617 AutoReadLock lock(sLock); 2618 auto p = sCache.Lookup(aLanguage); 2619 if (p) { 2620 return p.Data().second; 2621 } 2622 } 2623 2624 // Didn't find an existing entry, so lock the cache and do a full 2625 // lookup-and-update. 2626 AutoWriteLock lock(sLock); 2627 auto p = sCache.Lookup(aLanguage); 2628 if (p) { 2629 return p.Data().second; 2630 } 2631 2632 Script script = aDefault; 2633 nsAutoCString lang; 2634 aLanguage->ToUTF8String(lang); 2635 Locale locale; 2636 if (LocaleParser::TryParse(lang, locale).isOk()) { 2637 if (locale.Script().Missing()) { 2638 (void)locale.AddLikelySubtags(); 2639 } 2640 if (locale.Script().Present()) { 2641 Span span = locale.Script().Span(); 2642 MOZ_ASSERT(span.Length() == 4); 2643 uint32_t tag = TRUETYPE_TAG(span[0], span[1], span[2], span[3]); 2644 Script localeScript; 2645 if (ScriptTagToCodeTable()->Get(tag, &localeScript)) { 2646 script = localeScript; 2647 } 2648 } 2649 } 2650 p.Set(std::pair(aLanguage, script)); 2651 2652 return script; 2653 } 2654 2655 template <typename T> 2656 void gfxFontGroup::InitTextRun(DrawTarget* aDrawTarget, gfxTextRun* aTextRun, 2657 const T* aString, uint32_t aLength, 2658 gfxMissingFontRecorder* aMFR) { 2659 NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run"); 2660 2661 // we need to do numeral processing even on 8-bit text, 2662 // in case we're converting Western to Hindi/Arabic digits 2663 uint32_t numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption(); 2664 UniquePtr<char16_t[]> transformedString; 2665 if (numOption != IBMBIDI_NUMERAL_NOMINAL) { 2666 // scan the string for numerals that may need to be transformed; 2667 // if we find any, we'll make a local copy here and use that for 2668 // font matching and glyph generation/shaping 2669 bool prevIsArabic = 2670 !!(aTextRun->GetFlags() & ShapedTextFlags::TEXT_INCOMING_ARABICCHAR); 2671 for (uint32_t i = 0; i < aLength; ++i) { 2672 char16_t origCh = aString[i]; 2673 char16_t newCh = HandleNumberInChar(origCh, prevIsArabic, numOption); 2674 if (newCh != origCh) { 2675 if (!transformedString) { 2676 transformedString = MakeUnique<char16_t[]>(aLength); 2677 if constexpr (sizeof(T) == sizeof(char16_t)) { 2678 memcpy(transformedString.get(), aString, i * sizeof(char16_t)); 2679 } else { 2680 for (uint32_t j = 0; j < i; ++j) { 2681 transformedString[j] = aString[j]; 2682 } 2683 } 2684 } 2685 } 2686 if (transformedString) { 2687 transformedString[i] = newCh; 2688 } 2689 prevIsArabic = IS_ARABIC_CHAR(newCh); 2690 } 2691 } 2692 2693 LogModule* log = mStyle.systemFont ? gfxPlatform::GetLog(eGfxLog_textrunui) 2694 : gfxPlatform::GetLog(eGfxLog_textrun); 2695 2696 // variant fallback handling may end up passing through this twice 2697 bool redo; 2698 do { 2699 redo = false; 2700 2701 // split into script runs so that script can potentially influence 2702 // the font matching process below 2703 gfxScriptItemizer scriptRuns; 2704 const char16_t* textPtr = nullptr; 2705 2706 if (sizeof(T) == sizeof(uint8_t) && !transformedString) { 2707 scriptRuns.SetText(aString, aLength); 2708 } else { 2709 if (transformedString) { 2710 textPtr = transformedString.get(); 2711 } else { 2712 // typecast to avoid compilation error for the 8-bit version, 2713 // even though this is dead code in that case 2714 textPtr = reinterpret_cast<const char16_t*>(aString); 2715 } 2716 2717 scriptRuns.SetText(textPtr, aLength); 2718 } 2719 2720 while (gfxScriptItemizer::Run run = scriptRuns.Next()) { 2721 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) { 2722 nsAutoCString lang; 2723 mLanguage->ToUTF8String(lang); 2724 nsAutoCString styleString; 2725 mStyle.style.ToString(styleString); 2726 auto defaultLanguageGeneric = GetDefaultGeneric(mLanguage); 2727 MOZ_LOG( 2728 log, LogLevel::Warning, 2729 ("(%s) fontgroup: [%s] default: %s lang: %s script: %d " 2730 "len %d weight: %g stretch: %g%% style: %s size: %6.2f " 2731 "%zu-byte TEXTRUN [%s] ENDTEXTRUN\n", 2732 (mStyle.systemFont ? "textrunui" : "textrun"), 2733 FamilyListToString(mFamilyList).get(), 2734 (defaultLanguageGeneric == StyleGenericFontFamily::Serif 2735 ? "serif" 2736 : (defaultLanguageGeneric == StyleGenericFontFamily::SansSerif 2737 ? "sans-serif" 2738 : "none")), 2739 lang.get(), static_cast<int>(run.mScript), run.mLength, 2740 mStyle.weight.ToFloat(), mStyle.stretch.ToFloat(), 2741 styleString.get(), mStyle.size, sizeof(T), 2742 textPtr 2743 ? NS_ConvertUTF16toUTF8(textPtr + run.mOffset, run.mLength) 2744 .get() 2745 : nsPromiseFlatCString( 2746 nsDependentCSubstring( 2747 reinterpret_cast<const char*>(aString) + run.mOffset, 2748 run.mLength)) 2749 .get())); 2750 } 2751 2752 // If COMMON or INHERITED was not resolved, try to use the language code 2753 // to guess a likely script. 2754 if (run.mScript <= Script::INHERITED) { 2755 // This assumes Script codes begin with COMMON and INHERITED, preceding 2756 // codes for any "real" scripts. 2757 MOZ_ASSERT( 2758 run.mScript == Script::COMMON || run.mScript == Script::INHERITED, 2759 "unexpected Script code!"); 2760 run.mScript = ResolveScriptForLang(mLanguage, run.mScript); 2761 } 2762 2763 if (textPtr) { 2764 InitScriptRun(aDrawTarget, aTextRun, textPtr + run.mOffset, run.mOffset, 2765 run.mLength, run.mScript, aMFR); 2766 } else { 2767 InitScriptRun(aDrawTarget, aTextRun, aString + run.mOffset, run.mOffset, 2768 run.mLength, run.mScript, aMFR); 2769 } 2770 } 2771 2772 // if shaping was aborted due to lack of feature support, clear out 2773 // glyph runs and redo shaping with fallback forced on 2774 if (aTextRun->GetShapingState() == gfxTextRun::eShapingState_Aborted) { 2775 redo = true; 2776 aTextRun->SetShapingState(gfxTextRun::eShapingState_ForceFallbackFeature); 2777 aTextRun->ClearGlyphsAndCharacters(); 2778 } 2779 2780 } while (redo); 2781 2782 if (sizeof(T) == sizeof(char16_t) && aLength > 0) { 2783 gfxTextRun::CompressedGlyph* glyph = aTextRun->GetCharacterGlyphs(); 2784 if (!glyph->IsSimpleGlyph()) { 2785 glyph->SetClusterStart(true); 2786 } 2787 } 2788 2789 // It's possible for CoreText to omit glyph runs if it decides they contain 2790 // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we 2791 // need to eliminate them from the glyph run array to avoid drawing "partial 2792 // ligatures" with the wrong font. 2793 // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because 2794 // it will iterate back over all glyphruns in the textrun, which leads to 2795 // pathologically-bad perf in the case where a textrun contains many script 2796 // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs 2797 // every time a new script subrun is processed. 2798 aTextRun->SanitizeGlyphRuns(); 2799 } 2800 2801 static inline bool IsPUA(uint32_t aUSV) { 2802 // We could look up the General Category of the codepoint here, 2803 // but it's simpler to check PUA codepoint ranges. 2804 return (aUSV >= 0xE000 && aUSV <= 0xF8FF) || (aUSV >= 0xF0000); 2805 } 2806 2807 template <typename T> 2808 void gfxFontGroup::InitScriptRun(DrawTarget* aDrawTarget, gfxTextRun* aTextRun, 2809 const T* aString, // text for this script run, 2810 // not the entire textrun 2811 uint32_t aOffset, // position of the script 2812 // run within the textrun 2813 uint32_t aLength, // length of the script run 2814 Script aRunScript, 2815 gfxMissingFontRecorder* aMFR) { 2816 NS_ASSERTION(aLength > 0, "don't call InitScriptRun for a 0-length run"); 2817 NS_ASSERTION(aTextRun->GetShapingState() != gfxTextRun::eShapingState_Aborted, 2818 "don't call InitScriptRun with aborted shaping state"); 2819 2820 // confirm the load state of userfonts in the list 2821 if (mUserFontSet && mCurrGeneration != mUserFontSet->GetGeneration()) { 2822 UpdateUserFonts(); 2823 } 2824 2825 RefPtr<gfxFont> mainFont = GetFirstValidFont(); 2826 2827 ShapedTextFlags orientation = 2828 aTextRun->GetFlags() & ShapedTextFlags::TEXT_ORIENT_MASK; 2829 2830 if (orientation != ShapedTextFlags::TEXT_ORIENT_HORIZONTAL && 2831 (aRunScript == Script::MONGOLIAN || aRunScript == Script::PHAGS_PA)) { 2832 // Mongolian and Phags-pa text should ignore text-orientation and 2833 // always render in its "native" vertical mode, implemented by fonts 2834 // as sideways-right (i.e as if shaped horizontally, and then the 2835 // entire line is rotated to render vertically). Therefore, we ignore 2836 // the aOrientation value from the textrun's flags, and make all 2837 // vertical Mongolian/Phags-pa use sideways-right. 2838 orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT; 2839 } 2840 2841 uint32_t runStart = 0; 2842 AutoTArray<TextRange, 3> fontRanges; 2843 ComputeRanges(fontRanges, aString, aLength, aRunScript, orientation); 2844 uint32_t numRanges = fontRanges.Length(); 2845 bool missingChars = false; 2846 bool isCJK = gfxTextRun::IsCJKScript(aRunScript); 2847 2848 for (uint32_t r = 0; r < numRanges; r++) { 2849 const TextRange& range = fontRanges[r]; 2850 uint32_t matchedLength = range.Length(); 2851 RefPtr<gfxFont> matchedFont = range.font; 2852 // create the glyph run for this range 2853 if (matchedFont && mStyle.noFallbackVariantFeatures) { 2854 // common case - just do glyph layout and record the 2855 // resulting positioned glyphs 2856 aTextRun->AddGlyphRun(matchedFont, range.matchType, aOffset + runStart, 2857 (matchedLength > 0), range.orientation, isCJK); 2858 if (!matchedFont->SplitAndInitTextRun( 2859 aDrawTarget, aTextRun, aString + runStart, aOffset + runStart, 2860 matchedLength, aRunScript, mLanguage, range.orientation)) { 2861 // glyph layout failed! treat as missing glyphs 2862 matchedFont = nullptr; 2863 } 2864 } else if (matchedFont) { 2865 // shape with some variant feature that requires fallback handling 2866 bool petiteToSmallCaps = false; 2867 bool syntheticLower = false; 2868 bool syntheticUpper = false; 2869 2870 if (mStyle.variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL && 2871 mStyle.useSyntheticPosition && 2872 (aTextRun->GetShapingState() == 2873 gfxTextRun::eShapingState_ForceFallbackFeature || 2874 !matchedFont->SupportsSubSuperscript(mStyle.variantSubSuper, aString, 2875 aLength, aRunScript))) { 2876 // fallback for subscript/superscript variant glyphs 2877 2878 // if the feature was already used, abort and force 2879 // fallback across the entire textrun 2880 gfxTextRun::ShapingState ss = aTextRun->GetShapingState(); 2881 2882 if (ss == gfxTextRun::eShapingState_Normal) { 2883 aTextRun->SetShapingState( 2884 gfxTextRun::eShapingState_ShapingWithFallback); 2885 } else if (ss == gfxTextRun::eShapingState_ShapingWithFeature) { 2886 aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted); 2887 return; 2888 } 2889 2890 RefPtr<gfxFont> subSuperFont = matchedFont->GetSubSuperscriptFont( 2891 aTextRun->GetAppUnitsPerDevUnit()); 2892 aTextRun->AddGlyphRun(subSuperFont, range.matchType, aOffset + runStart, 2893 (matchedLength > 0), range.orientation, isCJK); 2894 if (!subSuperFont->SplitAndInitTextRun( 2895 aDrawTarget, aTextRun, aString + runStart, aOffset + runStart, 2896 matchedLength, aRunScript, mLanguage, range.orientation)) { 2897 // glyph layout failed! treat as missing glyphs 2898 matchedFont = nullptr; 2899 } 2900 } else if (mStyle.variantCaps != NS_FONT_VARIANT_CAPS_NORMAL && 2901 mStyle.allowSyntheticSmallCaps && 2902 !matchedFont->SupportsVariantCaps( 2903 aRunScript, mStyle.variantCaps, petiteToSmallCaps, 2904 syntheticLower, syntheticUpper)) { 2905 // fallback for small-caps variant glyphs 2906 if (!matchedFont->InitFakeSmallCapsRun( 2907 mFontVisibilityProvider, aDrawTarget, aTextRun, 2908 aString + runStart, aOffset + runStart, matchedLength, 2909 range.matchType, range.orientation, aRunScript, 2910 mExplicitLanguage ? mLanguage.get() : nullptr, syntheticLower, 2911 syntheticUpper)) { 2912 matchedFont = nullptr; 2913 } 2914 } else { 2915 // shape normally with variant feature enabled 2916 gfxTextRun::ShapingState ss = aTextRun->GetShapingState(); 2917 2918 // adjust the shaping state if necessary 2919 if (ss == gfxTextRun::eShapingState_Normal) { 2920 aTextRun->SetShapingState( 2921 gfxTextRun::eShapingState_ShapingWithFeature); 2922 } else if (ss == gfxTextRun::eShapingState_ShapingWithFallback) { 2923 // already have shaping results using fallback, need to redo 2924 aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted); 2925 return; 2926 } 2927 2928 // do glyph layout and record the resulting positioned glyphs 2929 aTextRun->AddGlyphRun(matchedFont, range.matchType, aOffset + runStart, 2930 (matchedLength > 0), range.orientation, isCJK); 2931 if (!matchedFont->SplitAndInitTextRun( 2932 aDrawTarget, aTextRun, aString + runStart, aOffset + runStart, 2933 matchedLength, aRunScript, mLanguage, range.orientation)) { 2934 // glyph layout failed! treat as missing glyphs 2935 matchedFont = nullptr; 2936 } 2937 } 2938 } else { 2939 aTextRun->AddGlyphRun(mainFont, FontMatchType::Kind::kFontGroup, 2940 aOffset + runStart, (matchedLength > 0), 2941 range.orientation, isCJK); 2942 } 2943 2944 if (!matchedFont) { 2945 // We need to set cluster boundaries (and mark spaces) so that 2946 // surrogate pairs, combining characters, etc behave properly, 2947 // even if we don't have glyphs for them 2948 aTextRun->SetupClusterBoundaries(aOffset + runStart, aString + runStart, 2949 matchedLength); 2950 2951 // various "missing" characters may need special handling, 2952 // so we check for them here 2953 uint32_t runLimit = runStart + matchedLength; 2954 for (uint32_t index = runStart; index < runLimit; index++) { 2955 T ch = aString[index]; 2956 2957 // tab and newline are not to be displayed as hexboxes, 2958 // but do need to be recorded in the textrun 2959 if (ch == '\n') { 2960 aTextRun->SetIsNewline(aOffset + index); 2961 continue; 2962 } 2963 if (ch == '\t') { 2964 aTextRun->SetIsTab(aOffset + index); 2965 continue; 2966 } 2967 2968 // for 16-bit textruns only, check for surrogate pairs and 2969 // special Unicode spaces; omit these checks in 8-bit runs 2970 if constexpr (sizeof(T) == sizeof(char16_t)) { 2971 if (index + 1 < aLength && 2972 NS_IS_SURROGATE_PAIR(ch, aString[index + 1])) { 2973 uint32_t usv = SURROGATE_TO_UCS4(ch, aString[index + 1]); 2974 aTextRun->SetMissingGlyph(aOffset + index, usv, mainFont); 2975 index++; 2976 if (!mSkipDrawing && !IsPUA(usv)) { 2977 missingChars = true; 2978 } 2979 continue; 2980 } 2981 2982 // check if this is a known Unicode whitespace character that 2983 // we can render using the space glyph with a custom width 2984 gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch); 2985 if (wid >= 0.0) { 2986 nscoord advance = 2987 aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5); 2988 if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) { 2989 aTextRun->GetCharacterGlyphs()[aOffset + index].SetSimpleGlyph( 2990 advance, mainFont->GetSpaceGlyph()); 2991 } else { 2992 gfxTextRun::DetailedGlyph detailedGlyph; 2993 detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph(); 2994 detailedGlyph.mAdvance = advance; 2995 aTextRun->SetDetailedGlyphs(aOffset + index, 1, &detailedGlyph); 2996 } 2997 continue; 2998 } 2999 } 3000 3001 if (IsInvalidChar(ch)) { 3002 // invalid chars are left as zero-width/invisible 3003 continue; 3004 } 3005 3006 // record char code so we can draw a box with the Unicode value 3007 aTextRun->SetMissingGlyph(aOffset + index, ch, mainFont); 3008 if (!mSkipDrawing && !IsPUA(ch)) { 3009 missingChars = true; 3010 } 3011 } 3012 } 3013 3014 runStart += matchedLength; 3015 } 3016 3017 if (aMFR && missingChars) { 3018 aMFR->RecordScript(aRunScript); 3019 } 3020 } 3021 3022 gfxTextRun* gfxFontGroup::GetEllipsisTextRun( 3023 int32_t aAppUnitsPerDevPixel, gfx::ShapedTextFlags aFlags, 3024 LazyReferenceDrawTargetGetter& aRefDrawTargetGetter) { 3025 MOZ_ASSERT(!(aFlags & ~ShapedTextFlags::TEXT_ORIENT_MASK), 3026 "flags here should only be used to specify orientation"); 3027 if (mCachedEllipsisTextRun && 3028 (mCachedEllipsisTextRun->GetFlags() & 3029 ShapedTextFlags::TEXT_ORIENT_MASK) == aFlags && 3030 mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) { 3031 return mCachedEllipsisTextRun.get(); 3032 } 3033 3034 // Use a Unicode ellipsis if the font supports it, 3035 // otherwise use three ASCII periods as fallback. 3036 RefPtr<gfxFont> firstFont = GetFirstValidFont(); 3037 nsString ellipsis = 3038 firstFont->HasCharacter(kEllipsisChar[0]) 3039 ? nsDependentString(kEllipsisChar, std::size(kEllipsisChar) - 1) 3040 : nsDependentString(kASCIIPeriodsChar, 3041 std::size(kASCIIPeriodsChar) - 1); 3042 3043 RefPtr<DrawTarget> refDT = aRefDrawTargetGetter.GetRefDrawTarget(); 3044 Parameters params = {refDT, nullptr, nullptr, 3045 nullptr, 0, aAppUnitsPerDevPixel}; 3046 mCachedEllipsisTextRun = 3047 MakeTextRun(ellipsis.BeginReading(), ellipsis.Length(), ¶ms, aFlags, 3048 nsTextFrameUtils::Flags(), nullptr); 3049 if (!mCachedEllipsisTextRun) { 3050 return nullptr; 3051 } 3052 // don't let the presence of a cached ellipsis textrun prolong the 3053 // fontgroup's life 3054 mCachedEllipsisTextRun->ReleaseFontGroup(); 3055 return mCachedEllipsisTextRun.get(); 3056 } 3057 3058 already_AddRefed<gfxFont> gfxFontGroup::FindFallbackFaceForChar( 3059 gfxFontFamily* aFamily, uint32_t aCh, uint32_t aNextCh, 3060 FontPresentation aPresentation) { 3061 GlobalFontMatch data(aCh, aNextCh, mStyle, aPresentation); 3062 aFamily->SearchAllFontsForChar(&data); 3063 gfxFontEntry* fe = data.mBestMatch; 3064 if (!fe) { 3065 return nullptr; 3066 } 3067 return fe->FindOrMakeFont(&mStyle); 3068 } 3069 3070 already_AddRefed<gfxFont> gfxFontGroup::FindFallbackFaceForChar( 3071 fontlist::Family* aFamily, uint32_t aCh, uint32_t aNextCh, 3072 FontPresentation aPresentation) { 3073 auto* pfl = gfxPlatformFontList::PlatformFontList(); 3074 auto* list = pfl->SharedFontList(); 3075 3076 // If async fallback is enabled, and the family isn't fully initialized yet, 3077 // just start the async cmap loading and return. 3078 if (!aFamily->IsFullyInitialized() && 3079 StaticPrefs::gfx_font_rendering_fallback_async() && 3080 !XRE_IsParentProcess()) { 3081 pfl->StartCmapLoadingFromFamily(aFamily - list->Families()); 3082 return nullptr; 3083 } 3084 3085 GlobalFontMatch data(aCh, aNextCh, mStyle, aPresentation); 3086 aFamily->SearchAllFontsForChar(list, &data); 3087 gfxFontEntry* fe = data.mBestMatch; 3088 if (!fe) { 3089 return nullptr; 3090 } 3091 return fe->FindOrMakeFont(&mStyle); 3092 } 3093 3094 already_AddRefed<gfxFont> gfxFontGroup::FindFallbackFaceForChar( 3095 const FamilyFace& aFamily, uint32_t aCh, uint32_t aNextCh, 3096 FontPresentation aPresentation) { 3097 if (aFamily.IsSharedFamily()) { 3098 return FindFallbackFaceForChar(aFamily.SharedFamily(), aCh, aNextCh, 3099 aPresentation); 3100 } 3101 return FindFallbackFaceForChar(aFamily.OwnedFamily(), aCh, aNextCh, 3102 aPresentation); 3103 } 3104 3105 gfxFloat gfxFontGroup::GetUnderlineOffset() { 3106 if (mUnderlineOffset == UNDERLINE_OFFSET_NOT_SET) { 3107 // if the fontlist contains a bad underline font, make the underline 3108 // offset the min of the first valid font and bad font underline offsets 3109 uint32_t len = mFonts.Length(); 3110 for (uint32_t i = 0; i < len; i++) { 3111 FamilyFace& ff = mFonts[i]; 3112 gfxFontEntry* fe = ff.FontEntry(); 3113 if (!fe) { 3114 continue; 3115 } 3116 if (!fe->mIsUserFontContainer && !fe->IsUserFont() && 3117 ((ff.IsSharedFamily() && ff.SharedFamily() && 3118 ff.SharedFamily()->IsBadUnderlineFamily()) || 3119 (!ff.IsSharedFamily() && ff.OwnedFamily() && 3120 ff.OwnedFamily()->IsBadUnderlineFamily()))) { 3121 RefPtr<gfxFont> font = GetFontAt(i); 3122 if (!font) { 3123 continue; 3124 } 3125 gfxFloat bad = 3126 font->GetMetrics(nsFontMetrics::eHorizontal).underlineOffset; 3127 RefPtr<gfxFont> firstValidFont = GetFirstValidFont(); 3128 gfxFloat first = firstValidFont->GetMetrics(nsFontMetrics::eHorizontal) 3129 .underlineOffset; 3130 mUnderlineOffset = std::min(first, bad); 3131 return mUnderlineOffset; 3132 } 3133 } 3134 3135 // no bad underline fonts, use the first valid font's metric 3136 RefPtr<gfxFont> firstValidFont = GetFirstValidFont(); 3137 mUnderlineOffset = 3138 firstValidFont->GetMetrics(nsFontMetrics::eHorizontal).underlineOffset; 3139 } 3140 3141 return mUnderlineOffset; 3142 } 3143 3144 #define NARROW_NO_BREAK_SPACE 0x202fu 3145 3146 already_AddRefed<gfxFont> gfxFontGroup::FindFontForChar( 3147 uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh, Script aRunScript, 3148 gfxFont* aPrevMatchedFont, FontMatchType* aMatchType) { 3149 // If the char is a cluster extender, we want to use the same font as the 3150 // preceding character if possible. This is preferable to using the font 3151 // group because it avoids breaks in shaping within a cluster. 3152 if (aPrevMatchedFont && IsClusterExtender(aCh)) { 3153 if (aPrevMatchedFont->HasCharacter(aCh) || IsDefaultIgnorable(aCh)) { 3154 return do_AddRef(aPrevMatchedFont); 3155 } 3156 // Check if this char and preceding char can compose; if so, is the 3157 // combination supported by the current font. 3158 uint32_t composed = intl::String::ComposePairNFC(aPrevCh, aCh); 3159 if (composed > 0 && aPrevMatchedFont->HasCharacter(composed)) { 3160 return do_AddRef(aPrevMatchedFont); 3161 } 3162 } 3163 3164 // Special cases for NNBSP (as used in Mongolian): 3165 if (aCh == NARROW_NO_BREAK_SPACE) { 3166 // If there is no preceding character, try the font that we'd use 3167 // for the next char (unless it's just another NNBSP; we don't try 3168 // to look ahead through a whole run of them). 3169 if (!aPrevCh && aNextCh && aNextCh != NARROW_NO_BREAK_SPACE) { 3170 RefPtr<gfxFont> nextFont = FindFontForChar(aNextCh, 0, 0, aRunScript, 3171 aPrevMatchedFont, aMatchType); 3172 if (nextFont && nextFont->HasCharacter(aCh)) { 3173 return nextFont.forget(); 3174 } 3175 } 3176 // Otherwise, treat NNBSP like a cluster extender (as above) and try 3177 // to continue the preceding font run. 3178 if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) { 3179 return do_AddRef(aPrevMatchedFont); 3180 } 3181 } 3182 3183 // To optimize common cases, try the first font in the font-group 3184 // before going into the more detailed checks below 3185 uint32_t fontListLength = mFonts.Length(); 3186 uint32_t nextIndex = 0; 3187 bool isJoinControl = gfxFontUtils::IsJoinControl(aCh); 3188 bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh); 3189 bool isVarSelector = gfxFontUtils::IsVarSelector(aCh); 3190 bool nextIsVarSelector = gfxFontUtils::IsVarSelector(aNextCh); 3191 3192 // For Unicode hyphens, if not supported in the font then we'll try for 3193 // the ASCII hyphen-minus as a fallback. 3194 // Similarly, for NBSP we try normal <space> as a fallback. 3195 uint32_t fallbackChar = (aCh == 0x2010 || aCh == 0x2011) ? '-' 3196 : (aCh == 0x00A0) ? ' ' 3197 : 0; 3198 3199 // Whether we've seen a font that is currently loading a resource that may 3200 // provide this character (so we should not start a new load). 3201 bool loading = false; 3202 3203 // Do we need to explicitly look for a font that does or does not provide a 3204 // color glyph for the given character? 3205 // For characters with no `EMOJI` property, we'll use whatever the family 3206 // list calls for; but if it's a potential emoji codepoint, we need to check 3207 // if there's a variation selector specifically asking for Text-style or 3208 // Emoji-style rendering and look for a suitable font. 3209 FontPresentation presentation = FontPresentation::Any; 3210 if (EmojiPresentation emojiPresentation = GetEmojiPresentation(aCh); 3211 emojiPresentation != TextOnly) { 3212 // Default presentation from the font-variant-emoji property. 3213 if (mFontVariantEmoji == StyleFontVariantEmoji::Emoji) { 3214 presentation = FontPresentation::EmojiExplicit; 3215 } else if (mFontVariantEmoji == StyleFontVariantEmoji::Text) { 3216 presentation = FontPresentation::TextExplicit; 3217 } 3218 // If there wasn't an explicit font-variant-emoji setting, default to 3219 // what Unicode prefers for this character. 3220 if (presentation == FontPresentation::Any) { 3221 if (emojiPresentation == EmojiPresentation::TextDefault) { 3222 presentation = FontPresentation::TextDefault; 3223 } else { 3224 presentation = FontPresentation::EmojiDefault; 3225 } 3226 } 3227 // If the prefer-emoji selector is present, or if it's a default-emoji 3228 // char and the prefer-text selector is NOT present, or if there's a 3229 // skin-tone modifier, we specifically look for a font with a color 3230 // glyph. 3231 // If the prefer-text selector is present, we specifically look for a 3232 // font that will provide a monochrome glyph. 3233 if (aNextCh == kVariationSelector16 || IsEmojiSkinToneModifier(aNextCh) || 3234 gfxFontUtils::IsEmojiFlagAndTag(aCh, aNextCh)) { 3235 // Emoji presentation is explicitly requested by a variation selector 3236 // or the presence of a skin-tone codepoint. 3237 presentation = FontPresentation::EmojiExplicit; 3238 } else if (aNextCh == kVariationSelector15) { 3239 // Text presentation is explicitly requested. 3240 presentation = FontPresentation::TextExplicit; 3241 } 3242 } 3243 3244 if (!isJoinControl && !wasJoinCauser && !isVarSelector && 3245 !nextIsVarSelector && presentation == FontPresentation::Any) { 3246 RefPtr<gfxFont> firstFont = GetFontAt(0, aCh, &loading); 3247 if (firstFont) { 3248 if (firstFont->HasCharacter(aCh) || 3249 (fallbackChar && firstFont->HasCharacter(fallbackChar))) { 3250 *aMatchType = {FontMatchType::Kind::kFontGroup, mFonts[0].Generic()}; 3251 return firstFont.forget(); 3252 } 3253 3254 RefPtr<gfxFont> font; 3255 if (mFonts[0].CheckForFallbackFaces()) { 3256 font = FindFallbackFaceForChar(mFonts[0], aCh, aNextCh, presentation); 3257 } else if (!firstFont->GetFontEntry()->IsUserFont()) { 3258 // For platform fonts (but not userfonts), we may need to do 3259 // fallback within the family to handle cases where some faces 3260 // such as Italic or Black have reduced character sets compared 3261 // to the family's Regular face. 3262 font = FindFallbackFaceForChar(mFonts[0], aCh, aNextCh, presentation); 3263 } 3264 if (font) { 3265 *aMatchType = {FontMatchType::Kind::kFontGroup, mFonts[0].Generic()}; 3266 return font.forget(); 3267 } 3268 } else { 3269 if (fontListLength > 0) { 3270 loading = loading || mFonts[0].IsLoadingFor(aCh); 3271 } 3272 } 3273 3274 // we don't need to check the first font again below 3275 ++nextIndex; 3276 } 3277 3278 if (aPrevMatchedFont) { 3279 // Don't switch fonts for control characters, regardless of 3280 // whether they are present in the current font, as they won't 3281 // actually be rendered (see bug 716229) 3282 if (isJoinControl || 3283 GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_CONTROL) { 3284 return do_AddRef(aPrevMatchedFont); 3285 } 3286 3287 // if previous character was a join-causer (ZWJ), 3288 // use the same font as the previous range if we can 3289 if (wasJoinCauser) { 3290 if (aPrevMatchedFont->HasCharacter(aCh)) { 3291 return do_AddRef(aPrevMatchedFont); 3292 } 3293 } 3294 } 3295 3296 // If this character is a variation selector or default-ignorable, use the 3297 // previous font regardless of whether it supports the codepoint or not. 3298 // (We don't want to unnecessarily split glyph runs, and the character will 3299 // not be visibly rendered.) 3300 if (isVarSelector || IsDefaultIgnorable(aCh)) { 3301 return do_AddRef(aPrevMatchedFont); 3302 } 3303 3304 // Used to remember the first "candidate" font that would provide a fallback 3305 // text-style rendering if no color glyph can be found. 3306 RefPtr<gfxFont> candidateFont; 3307 FontMatchType candidateMatchType; 3308 3309 // Handle a candidate font that could support the character, returning true 3310 // if we should go ahead and return |f|, false to continue searching. 3311 auto CheckCandidate = [&](gfxFont* f, FontMatchType t) -> bool { 3312 // If a given character is a Private Use Area Unicode codepoint, user 3313 // agents must only match font families named in the font-family list that 3314 // are not generic families. 3315 if (t.generic != StyleGenericFontFamily::None && IsPUA(aCh)) { 3316 return false; 3317 } 3318 // If no preference, or if it's an explicitly-named family in the fontgroup 3319 // and font-variant-emoji is 'normal', then we accept the font. 3320 if (presentation == FontPresentation::Any || 3321 (!IsExplicitPresentation(presentation) && 3322 t.kind == FontMatchType::Kind::kFontGroup && 3323 t.generic == StyleGenericFontFamily::None && 3324 mFontVariantEmoji == StyleFontVariantEmoji::Normal && 3325 !gfxFontUtils::IsRegionalIndicator(aCh))) { 3326 *aMatchType = t; 3327 return true; 3328 } 3329 // Does the candidate font provide a color glyph for the current character? 3330 bool hasColorGlyph = 3331 f->HasColorGlyphFor(aCh, aNextCh) || 3332 (!nextIsVarSelector && f->HasColorGlyphFor(aCh, kVariationSelector16)); 3333 // If the provided glyph matches the preference, accept the font. 3334 if (hasColorGlyph == PrefersColor(presentation) && 3335 // Exception: if this is an emoji flag+tag letters sequence, and the 3336 // following codepoint (the first tag) is missing from the font, we 3337 // don't want to use this font as it will fail to present the cluster. 3338 (!hasColorGlyph || !gfxFontUtils::IsEmojiFlagAndTag(aCh, aNextCh) || 3339 f->HasCharacter(aNextCh))) { 3340 *aMatchType = t; 3341 return true; 3342 } 3343 // If the character was a TextDefault char, but the next char is VS16, 3344 // and the font is a COLR font that supports both these codepoints, then 3345 // we'll assume it knows what it is doing (eg Twemoji Mozilla keycap 3346 // sequences). 3347 // TODO: reconsider all this as part of any fix for bug 543200. 3348 if (aNextCh == kVariationSelector16 && 3349 GetEmojiPresentation(aCh) == EmojiPresentation::TextDefault && 3350 f->HasCharacter(aNextCh) && f->GetFontEntry()->TryGetColorGlyphs()) { 3351 return true; 3352 } 3353 // Otherwise, remember the first potential fallback, but keep searching. 3354 if (!candidateFont) { 3355 candidateFont = f; 3356 candidateMatchType = t; 3357 } 3358 return false; 3359 }; 3360 3361 // 1. check remaining fonts in the font group 3362 for (uint32_t i = nextIndex; i < fontListLength; i++) { 3363 FamilyFace& ff = mFonts[i]; 3364 if (ff.IsInvalid() || ff.IsLoading()) { 3365 if (ff.IsLoadingFor(aCh)) { 3366 loading = true; 3367 } 3368 continue; 3369 } 3370 3371 RefPtr<gfxFont> font = ff.Font(); 3372 if (font) { 3373 // if available, use already-made gfxFont and check for character 3374 if (font->HasCharacter(aCh) || 3375 (fallbackChar && font->HasCharacter(fallbackChar))) { 3376 if (CheckCandidate(font, 3377 {FontMatchType::Kind::kFontGroup, ff.Generic()})) { 3378 return font.forget(); 3379 } 3380 } 3381 } else { 3382 // don't have a gfxFont yet, test charmap before instantiating 3383 gfxFontEntry* fe = ff.FontEntry(); 3384 if (fe && fe->mIsUserFontContainer) { 3385 // for userfonts, need to test both the unicode range map and 3386 // the cmap of the platform font entry 3387 gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe); 3388 3389 // never match a character outside the defined unicode range 3390 if (!ufe->CharacterInUnicodeRange(aCh)) { 3391 continue; 3392 } 3393 3394 // Load if not already loaded, unless we've already seen an in- 3395 // progress load that is expected to satisfy this request. 3396 if (!loading && 3397 ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED) { 3398 ufe->Load(); 3399 ff.CheckState(mSkipDrawing); 3400 } 3401 3402 if (ff.IsLoading()) { 3403 loading = true; 3404 } 3405 3406 gfxFontEntry* pfe = ufe->GetPlatformFontEntry(); 3407 if (pfe && (pfe->HasCharacter(aCh) || 3408 (fallbackChar && pfe->HasCharacter(fallbackChar)))) { 3409 font = GetFontAt(i, aCh, &loading); 3410 if (font) { 3411 if (CheckCandidate(font, {FontMatchType::Kind::kFontGroup, 3412 mFonts[i].Generic()})) { 3413 return font.forget(); 3414 } 3415 } 3416 } 3417 } else if (fe && (fe->HasCharacter(aCh) || 3418 (fallbackChar && fe->HasCharacter(fallbackChar)))) { 3419 // for normal platform fonts, after checking the cmap 3420 // build the font via GetFontAt 3421 font = GetFontAt(i, aCh, &loading); 3422 if (font) { 3423 if (CheckCandidate(font, {FontMatchType::Kind::kFontGroup, 3424 mFonts[i].Generic()})) { 3425 return font.forget(); 3426 } 3427 } 3428 } 3429 } 3430 3431 // check other family faces if needed 3432 if (ff.CheckForFallbackFaces()) { 3433 #ifdef DEBUG 3434 if (i > 0) { 3435 fontlist::FontList* list = 3436 gfxPlatformFontList::PlatformFontList()->SharedFontList(); 3437 nsCString s1 = mFonts[i - 1].IsSharedFamily() 3438 ? mFonts[i - 1].SharedFamily()->Key().AsString(list) 3439 : mFonts[i - 1].OwnedFamily()->Name(); 3440 nsCString s2 = ff.IsSharedFamily() 3441 ? ff.SharedFamily()->Key().AsString(list) 3442 : ff.OwnedFamily()->Name(); 3443 MOZ_ASSERT(!mFonts[i - 1].CheckForFallbackFaces() || !s1.Equals(s2), 3444 "should only do fallback once per font family"); 3445 } 3446 #endif 3447 font = FindFallbackFaceForChar(ff, aCh, aNextCh, presentation); 3448 if (font) { 3449 if (CheckCandidate(font, 3450 {FontMatchType::Kind::kFontGroup, ff.Generic()})) { 3451 return font.forget(); 3452 } 3453 } 3454 } else { 3455 // For platform fonts, but not user fonts, consider intra-family 3456 // fallback to handle styles with reduced character sets (see 3457 // also above). 3458 gfxFontEntry* fe = ff.FontEntry(); 3459 if (fe && !fe->mIsUserFontContainer && !fe->IsUserFont()) { 3460 font = FindFallbackFaceForChar(ff, aCh, aNextCh, presentation); 3461 if (font) { 3462 if (CheckCandidate(font, 3463 {FontMatchType::Kind::kFontGroup, ff.Generic()})) { 3464 return font.forget(); 3465 } 3466 } 3467 } 3468 } 3469 } 3470 3471 if (fontListLength == 0) { 3472 RefPtr<gfxFont> defaultFont = GetDefaultFont(); 3473 if (defaultFont->HasCharacter(aCh) || 3474 (fallbackChar && defaultFont->HasCharacter(fallbackChar))) { 3475 if (CheckCandidate(defaultFont, FontMatchType::Kind::kFontGroup)) { 3476 return defaultFont.forget(); 3477 } 3478 } 3479 } 3480 3481 // If character is in Private Use Area, or is unassigned in Unicode, don't do 3482 // matching against pref or system fonts. We only support such codepoints 3483 // when used with an explicitly-specified font, as they have no standard/ 3484 // interoperable meaning. 3485 // Also don't attempt any fallback for control characters or noncharacters, 3486 // where we won't be rendering a glyph anyhow, or for codepoints where global 3487 // fallback has already noted a failure. 3488 FontVisibility level = mFontVisibilityProvider 3489 ? mFontVisibilityProvider->GetFontVisibility() 3490 : FontVisibility::User; 3491 auto* pfl = gfxPlatformFontList::PlatformFontList(); 3492 if (pfl->SkipFontFallbackForChar(level, aCh) || 3493 (!StaticPrefs::gfx_font_rendering_fallback_unassigned_chars() && 3494 GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED)) { 3495 if (candidateFont) { 3496 *aMatchType = candidateMatchType; 3497 } 3498 return candidateFont.forget(); 3499 } 3500 3501 // 2. search pref fonts 3502 RefPtr<gfxFont> font = WhichPrefFontSupportsChar(aCh, aNextCh, presentation); 3503 if (font) { 3504 if (PrefersColor(presentation) && pfl->EmojiPrefHasUserValue()) { 3505 // For emoji, always accept the font from preferences if it's explicitly 3506 // user-set, even if it isn't actually a color-emoji font, as some users 3507 // may want to set their emoji font preference to a monochrome font like 3508 // Symbola. 3509 // So a user-provided font.name-list.emoji preference takes precedence 3510 // over the Unicode presentation style here. 3511 RefPtr<gfxFont> autoRefDeref(candidateFont); 3512 *aMatchType = FontMatchType::Kind::kPrefsFallback; 3513 return font.forget(); 3514 } 3515 if (CheckCandidate(font, FontMatchType::Kind::kPrefsFallback)) { 3516 return font.forget(); 3517 } 3518 } 3519 3520 // For fallback searches, we don't want to use a color-emoji font unless 3521 // emoji-style presentation is specifically required, so we map Any to 3522 // Text here. 3523 if (presentation == FontPresentation::Any) { 3524 presentation = FontPresentation::TextDefault; 3525 } 3526 3527 // 3. use fallback fonts 3528 // -- before searching for something else check the font used for the 3529 // previous character 3530 if (aPrevMatchedFont && 3531 (aPrevMatchedFont->HasCharacter(aCh) || 3532 (fallbackChar && aPrevMatchedFont->HasCharacter(fallbackChar)))) { 3533 if (CheckCandidate(aPrevMatchedFont, 3534 FontMatchType::Kind::kSystemFallback)) { 3535 return do_AddRef(aPrevMatchedFont); 3536 } 3537 } 3538 3539 // for known "space" characters, don't do a full system-fallback search; 3540 // we'll synthesize appropriate-width spaces instead of missing-glyph boxes 3541 font = GetFirstValidFont(); 3542 if (GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR && 3543 font->SynthesizeSpaceWidth(aCh) >= 0.0) { 3544 return nullptr; 3545 } 3546 3547 // -- otherwise look for other stuff 3548 font = WhichSystemFontSupportsChar(aCh, aNextCh, aRunScript, presentation); 3549 if (font) { 3550 if (CheckCandidate(font, FontMatchType::Kind::kSystemFallback)) { 3551 return font.forget(); 3552 } 3553 } 3554 if (candidateFont) { 3555 *aMatchType = candidateMatchType; 3556 } 3557 return candidateFont.forget(); 3558 } 3559 3560 template <typename T> 3561 void gfxFontGroup::ComputeRanges(nsTArray<TextRange>& aRanges, const T* aString, 3562 uint32_t aLength, Script aRunScript, 3563 gfx::ShapedTextFlags aOrientation) { 3564 NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty"); 3565 NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text"); 3566 3567 uint32_t prevCh = 0; 3568 uint32_t nextCh = aString[0]; 3569 if constexpr (sizeof(T) == sizeof(char16_t)) { 3570 if (aLength > 1 && NS_IS_SURROGATE_PAIR(nextCh, aString[1])) { 3571 nextCh = SURROGATE_TO_UCS4(nextCh, aString[1]); 3572 } 3573 } 3574 int32_t lastRangeIndex = -1; 3575 3576 // initialize prevFont to the group's primary font, so that this will be 3577 // used for string-initial control chars, etc rather than risk hitting font 3578 // fallback for these (bug 716229) 3579 StyleGenericFontFamily generic = StyleGenericFontFamily::None; 3580 RefPtr<gfxFont> prevFont = GetFirstValidFont(' ', &generic); 3581 3582 // if we use the initial value of prevFont, we treat this as a match from 3583 // the font group; fixes bug 978313 3584 FontMatchType matchType = {FontMatchType::Kind::kFontGroup, generic}; 3585 3586 for (uint32_t i = 0; i < aLength; i++) { 3587 const uint32_t origI = i; // save off in case we increase for surrogate 3588 3589 // set up current ch 3590 uint32_t ch = nextCh; 3591 3592 // Get next char (if any) so that FindFontForChar can look ahead 3593 // for a possible variation selector. 3594 3595 if constexpr (sizeof(T) == sizeof(char16_t)) { 3596 // In 16-bit case only, check for surrogate pairs. 3597 if (ch > 0xffffu) { 3598 i++; 3599 } 3600 if (i < aLength - 1) { 3601 nextCh = aString[i + 1]; 3602 if (i + 2 < aLength && NS_IS_SURROGATE_PAIR(nextCh, aString[i + 2])) { 3603 nextCh = SURROGATE_TO_UCS4(nextCh, aString[i + 2]); 3604 } 3605 } else { 3606 nextCh = 0; 3607 } 3608 } else { 3609 // 8-bit case is trivial. 3610 nextCh = i < aLength - 1 ? aString[i + 1] : 0; 3611 } 3612 3613 RefPtr<gfxFont> font; 3614 3615 // Find the font for this char; but try to avoid calling the expensive 3616 // FindFontForChar method for the most common case, where the first 3617 // font in the list supports the current char, and it is not one of 3618 // the special cases where FindFontForChar will attempt to propagate 3619 // the font selected for an adjacent character, and does not need to 3620 // consider emoji vs text presentation. 3621 if ((font = GetFontAt(0, ch)) != nullptr && font->HasCharacter(ch) && 3622 ( 3623 // In 8-bit text, we can unconditionally accept the first font if 3624 // font-variant-emoji is 'normal', or if the character does not 3625 // have the emoji property; there cannot be adjacent characters 3626 // that would affect it. 3627 (sizeof(T) == sizeof(uint8_t) && 3628 (mFontVariantEmoji == StyleFontVariantEmoji::Normal || 3629 GetEmojiPresentation(ch) == TextOnly)) || 3630 // For 16-bit text, we need to consider cluster extenders etc. 3631 (sizeof(T) == sizeof(char16_t) && 3632 (!IsClusterExtender(ch) && ch != NARROW_NO_BREAK_SPACE && 3633 !gfxFontUtils::IsJoinControl(ch) && 3634 !gfxFontUtils::IsJoinCauser(prevCh) && 3635 !gfxFontUtils::IsVarSelector(ch) && 3636 (GetEmojiPresentation(ch) == TextOnly || 3637 (!(IsEmojiPresentationSelector(nextCh) || 3638 IsEmojiSkinToneModifier(nextCh) || 3639 gfxFontUtils::IsEmojiFlagAndTag(ch, nextCh)) && 3640 mFontVariantEmoji == StyleFontVariantEmoji::Normal && 3641 mFonts[0].Generic() == StyleGenericFontFamily::None)))))) { 3642 matchType = {FontMatchType::Kind::kFontGroup, mFonts[0].Generic()}; 3643 } else { 3644 font = 3645 FindFontForChar(ch, prevCh, nextCh, aRunScript, prevFont, &matchType); 3646 } 3647 3648 #ifndef RELEASE_OR_BETA 3649 if (MOZ_UNLIKELY(mTextPerf)) { 3650 if (matchType.kind == FontMatchType::Kind::kPrefsFallback) { 3651 mTextPerf->current.fallbackPrefs++; 3652 } else if (matchType.kind == FontMatchType::Kind::kSystemFallback) { 3653 mTextPerf->current.fallbackSystem++; 3654 } 3655 } 3656 #endif 3657 3658 prevCh = ch; 3659 3660 ShapedTextFlags orient = aOrientation; 3661 if (aOrientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) { 3662 // For CSS text-orientation:mixed, we need to resolve orientation 3663 // on a per-character basis using the UTR50 orientation property. 3664 switch (GetVerticalOrientation(ch)) { 3665 case VERTICAL_ORIENTATION_U: 3666 case VERTICAL_ORIENTATION_Tu: 3667 orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT; 3668 break; 3669 case VERTICAL_ORIENTATION_Tr: { 3670 // We check for a vertical presentation form first as that's 3671 // likely to be cheaper than inspecting lookups to see if the 3672 // 'vert' feature is going to handle this character, and if the 3673 // presentation form is available then it will be used as 3674 // fallback if needed, so it's OK if the feature is missing. 3675 // 3676 // Because "common" CJK punctuation characters in isolation will be 3677 // resolved to Bopomofo script (as the first script listed in their 3678 // ScriptExtensions property), but this is not always well supported 3679 // by fonts' OpenType tables, we also try Han script; harfbuzz will 3680 // apply a 'vert' feature from any available script (see 3681 // https://github.com/harfbuzz/harfbuzz/issues/63) when shaping, 3682 // so this is OK. It's not quite as general as what harfbuzz does 3683 // (it will find the feature in *any* script), but should be enough 3684 // for likely real-world examples. 3685 uint32_t v = gfxHarfBuzzShaper::GetVerticalPresentationForm(ch); 3686 const uint32_t kVert = HB_TAG('v', 'e', 'r', 't'); 3687 orient = (!font || (v && font->HasCharacter(v)) || 3688 font->FeatureWillHandleChar(aRunScript, kVert, ch) || 3689 (aRunScript == Script::BOPOMOFO && 3690 font->FeatureWillHandleChar(Script::HAN, kVert, ch))) 3691 ? ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT 3692 : ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT; 3693 break; 3694 } 3695 case VERTICAL_ORIENTATION_R: 3696 orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT; 3697 break; 3698 } 3699 } 3700 3701 if (lastRangeIndex == -1) { 3702 // first char ==> make a new range 3703 aRanges.AppendElement(TextRange(0, 1, font, matchType, orient)); 3704 lastRangeIndex++; 3705 prevFont = std::move(font); 3706 } else { 3707 // if font or orientation has changed, make a new range... 3708 // unless ch is a variation selector (bug 1248248) 3709 TextRange& prevRange = aRanges[lastRangeIndex]; 3710 if (prevRange.font != font || 3711 (prevRange.orientation != orient && !IsClusterExtender(ch))) { 3712 // close out the previous range 3713 prevRange.end = origI; 3714 aRanges.AppendElement(TextRange(origI, i + 1, font, matchType, orient)); 3715 lastRangeIndex++; 3716 3717 // update prevFont for the next match, *unless* we switched 3718 // fonts on a ZWJ, in which case propagating the changed font 3719 // is probably not a good idea (see bug 619511) 3720 if (sizeof(T) == sizeof(uint8_t) || !gfxFontUtils::IsJoinCauser(ch)) { 3721 prevFont = std::move(font); 3722 } 3723 } else { 3724 prevRange.matchType |= matchType; 3725 } 3726 } 3727 } 3728 3729 aRanges[lastRangeIndex].end = aLength; 3730 3731 #ifndef RELEASE_OR_BETA 3732 LogModule* log = mStyle.systemFont ? gfxPlatform::GetLog(eGfxLog_textrunui) 3733 : gfxPlatform::GetLog(eGfxLog_textrun); 3734 3735 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) { 3736 nsAutoCString lang; 3737 mLanguage->ToUTF8String(lang); 3738 auto defaultLanguageGeneric = GetDefaultGeneric(mLanguage); 3739 3740 // collect the font matched for each range 3741 nsAutoCString fontMatches; 3742 for (size_t i = 0, i_end = aRanges.Length(); i < i_end; i++) { 3743 const TextRange& r = aRanges[i]; 3744 nsAutoCString matchTypes; 3745 if (r.matchType.kind & FontMatchType::Kind::kFontGroup) { 3746 matchTypes.AppendLiteral("list"); 3747 } 3748 if (r.matchType.kind & FontMatchType::Kind::kPrefsFallback) { 3749 if (!matchTypes.IsEmpty()) { 3750 matchTypes.AppendLiteral(","); 3751 } 3752 matchTypes.AppendLiteral("prefs"); 3753 } 3754 if (r.matchType.kind & FontMatchType::Kind::kSystemFallback) { 3755 if (!matchTypes.IsEmpty()) { 3756 matchTypes.AppendLiteral(","); 3757 } 3758 matchTypes.AppendLiteral("sys"); 3759 } 3760 fontMatches.AppendPrintf( 3761 " [%u:%u] %.200s (%s)", r.start, r.end, 3762 (r.font.get() ? r.font->GetName().get() : "<null>"), 3763 matchTypes.get()); 3764 } 3765 MOZ_LOG(log, LogLevel::Debug, 3766 ("(%s-fontmatching) fontgroup: [%s] default: %s lang: %s script: %d" 3767 "%s\n", 3768 (mStyle.systemFont ? "textrunui" : "textrun"), 3769 FamilyListToString(mFamilyList).get(), 3770 (defaultLanguageGeneric == StyleGenericFontFamily::Serif 3771 ? "serif" 3772 : (defaultLanguageGeneric == StyleGenericFontFamily::SansSerif 3773 ? "sans-serif" 3774 : "none")), 3775 lang.get(), static_cast<int>(aRunScript), fontMatches.get())); 3776 } 3777 #endif 3778 } 3779 3780 gfxUserFontSet* gfxFontGroup::GetUserFontSet() { return mUserFontSet; } 3781 3782 void gfxFontGroup::SetUserFontSet(gfxUserFontSet* aUserFontSet) { 3783 if (aUserFontSet == mUserFontSet) { 3784 return; 3785 } 3786 mUserFontSet = aUserFontSet; 3787 mCurrGeneration = GetGeneration() - 1; 3788 UpdateUserFonts(); 3789 } 3790 3791 uint64_t gfxFontGroup::GetGeneration() { 3792 return mUserFontSet ? mUserFontSet->GetGeneration() : 0; 3793 } 3794 3795 uint64_t gfxFontGroup::GetRebuildGeneration() { 3796 return mUserFontSet ? mUserFontSet->GetRebuildGeneration() : 0; 3797 } 3798 3799 void gfxFontGroup::UpdateUserFonts() { 3800 if (mCurrGeneration < GetRebuildGeneration()) { 3801 // fonts in userfont set changed, need to redo the fontlist 3802 mResolvedFonts = false; 3803 ClearCachedData(); 3804 mCurrGeneration = GetGeneration(); 3805 } else if (mCurrGeneration != GetGeneration()) { 3806 // load state change occurred, verify load state and validity of fonts 3807 ClearCachedData(); 3808 uint32_t len = mFonts.Length(); 3809 for (uint32_t i = 0; i < len; i++) { 3810 FamilyFace& ff = mFonts[i]; 3811 if (ff.Font() || !ff.IsUserFontContainer()) { 3812 continue; 3813 } 3814 ff.CheckState(mSkipDrawing); 3815 } 3816 mCurrGeneration = GetGeneration(); 3817 } 3818 } 3819 3820 bool gfxFontGroup::ContainsUserFont(const gfxUserFontEntry* aUserFont) { 3821 UpdateUserFonts(); 3822 3823 // If we have resolved the font list to concrete font faces, search through 3824 // the list for a specific user font face. 3825 if (mResolvedFonts) { 3826 uint32_t len = mFonts.Length(); 3827 for (uint32_t i = 0; i < len; i++) { 3828 FamilyFace& ff = mFonts[i]; 3829 if (ff.EqualsUserFont(aUserFont)) { 3830 return true; 3831 } 3832 } 3833 return false; 3834 } 3835 3836 // If the font list is currently not resolved, we assume it might use the 3837 // given face. (This method is only called when we have already seen that 3838 // the family name is present in the list.) 3839 return true; 3840 } 3841 3842 already_AddRefed<gfxFont> gfxFontGroup::WhichPrefFontSupportsChar( 3843 uint32_t aCh, uint32_t aNextCh, FontPresentation aPresentation) { 3844 eFontPrefLang charLang; 3845 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList(); 3846 3847 if (PrefersColor(aPresentation)) { 3848 charLang = eFontPrefLang_Emoji; 3849 } else { 3850 // get the pref font list if it hasn't been set up already 3851 charLang = pfl->GetFontPrefLangFor(aCh); 3852 } 3853 3854 // if the last pref font was the first family in the pref list, no need to 3855 // recheck through a list of families 3856 if (mLastPrefFont && charLang == mLastPrefLang && mLastPrefFirstFont && 3857 mLastPrefFont->HasCharacter(aCh)) { 3858 return do_AddRef(mLastPrefFont); 3859 } 3860 3861 // based on char lang and page lang, set up list of pref lang fonts to check 3862 eFontPrefLang prefLangs[kMaxLenPrefLangList]; 3863 uint32_t i, numLangs = 0; 3864 3865 pfl->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang); 3866 3867 for (i = 0; i < numLangs; i++) { 3868 eFontPrefLang currentLang = prefLangs[i]; 3869 StyleGenericFontFamily generic = 3870 mFallbackGeneric != StyleGenericFontFamily::None 3871 ? mFallbackGeneric 3872 : pfl->GetDefaultGeneric(currentLang); 3873 gfxPlatformFontList::PrefFontList* families = pfl->GetPrefFontsLangGroup( 3874 mFontVisibilityProvider, generic, currentLang); 3875 NS_ASSERTION(families, "no pref font families found"); 3876 3877 // find the first pref font that includes the character 3878 uint32_t j, numPrefs; 3879 numPrefs = families->Length(); 3880 for (j = 0; j < numPrefs; j++) { 3881 // look up the appropriate face 3882 FontFamily family = (*families)[j]; 3883 if (family.IsNull()) { 3884 continue; 3885 } 3886 3887 // if a pref font is used, it's likely to be used again in the same text 3888 // run. the style doesn't change so the face lookup can be cached rather 3889 // than calling FindOrMakeFont repeatedly. speeds up FindFontForChar 3890 // lookup times for subsequent pref font lookups 3891 if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) { 3892 return do_AddRef(mLastPrefFont); 3893 } 3894 3895 gfxFontEntry* fe = nullptr; 3896 if (family.mShared) { 3897 fontlist::Family* fam = family.mShared; 3898 if (!fam->IsInitialized()) { 3899 (void)pfl->InitializeFamily(fam); 3900 } 3901 fontlist::Face* face = 3902 fam->FindFaceForStyle(pfl->SharedFontList(), mStyle); 3903 if (face) { 3904 fe = pfl->GetOrCreateFontEntry(face, fam); 3905 } 3906 } else { 3907 fe = family.mUnshared->FindFontForStyle(mStyle); 3908 } 3909 if (!fe) { 3910 continue; 3911 } 3912 3913 // if ch in cmap, create and return a gfxFont 3914 RefPtr<gfxFont> prefFont; 3915 if (fe->HasCharacter(aCh)) { 3916 prefFont = fe->FindOrMakeFont(&mStyle); 3917 if (!prefFont) { 3918 continue; 3919 } 3920 if (aPresentation == FontPresentation::EmojiExplicit && 3921 !prefFont->HasColorGlyphFor(aCh, aNextCh)) { 3922 continue; 3923 } 3924 } 3925 3926 // If the char was not available, see if we can fall back to an 3927 // alternative face in the same family. 3928 if (!prefFont) { 3929 prefFont = family.mShared 3930 ? FindFallbackFaceForChar(family.mShared, aCh, aNextCh, 3931 aPresentation) 3932 : FindFallbackFaceForChar(family.mUnshared, aCh, aNextCh, 3933 aPresentation); 3934 } 3935 if (prefFont) { 3936 mLastPrefFamily = family; 3937 mLastPrefFont = prefFont; 3938 mLastPrefLang = charLang; 3939 mLastPrefFirstFont = (i == 0 && j == 0); 3940 return prefFont.forget(); 3941 } 3942 } 3943 } 3944 3945 return nullptr; 3946 } 3947 3948 already_AddRefed<gfxFont> gfxFontGroup::WhichSystemFontSupportsChar( 3949 uint32_t aCh, uint32_t aNextCh, Script aRunScript, 3950 FontPresentation aPresentation) { 3951 FontVisibility visibility; 3952 return gfxPlatformFontList::PlatformFontList()->SystemFindFontForChar( 3953 mFontVisibilityProvider, aCh, aNextCh, aRunScript, aPresentation, &mStyle, 3954 &visibility); 3955 } 3956 3957 gfxFont::Metrics gfxFontGroup::GetMetricsForCSSUnits( 3958 gfxFont::Orientation aOrientation, StyleQueryFontMetricsFlags aFlags) { 3959 bool isFirst; 3960 RefPtr<gfxFont> font = GetFirstValidFont(0x20, nullptr, &isFirst); 3961 auto metrics = font->GetMetrics(aOrientation); 3962 3963 // If the font we used to get metrics was not the first in the list, 3964 // or if it doesn't support the ZERO character, check for the font that 3965 // does support ZERO and use its metrics for the 'ch' unit. 3966 if ((aFlags & StyleQueryFontMetricsFlags::NEEDS_CH) && 3967 (!isFirst || !font->HasCharacter('0'))) { 3968 RefPtr<gfxFont> zeroFont = GetFirstValidFont('0'); 3969 if (zeroFont != font) { 3970 const auto& zeroMetrics = zeroFont->GetMetrics(aOrientation); 3971 metrics.zeroWidth = zeroMetrics.zeroWidth; 3972 } 3973 } 3974 3975 // Likewise for the WATER ideograph character used as the basis for 'ic'. 3976 if ((aFlags & StyleQueryFontMetricsFlags::NEEDS_IC) && 3977 (!isFirst || !font->HasCharacter(0x6C34))) { 3978 RefPtr<gfxFont> icFont = GetFirstValidFont(0x6C34); 3979 if (icFont != font) { 3980 const auto& icMetrics = icFont->GetMetrics(aOrientation); 3981 metrics.ideographicWidth = icMetrics.ideographicWidth; 3982 } 3983 } 3984 3985 return metrics; 3986 } 3987 3988 class DeferredNotifyMissingFonts final : public nsIRunnable { 3989 public: 3990 NS_DECL_THREADSAFE_ISUPPORTS 3991 3992 explicit DeferredNotifyMissingFonts(nsString&& aScriptList) 3993 : mScriptList(std::move(aScriptList)) {} 3994 3995 protected: 3996 virtual ~DeferredNotifyMissingFonts() {} 3997 3998 NS_IMETHOD Run(void) override { 3999 nsCOMPtr<nsIObserverService> service = GetObserverService(); 4000 service->NotifyObservers(nullptr, "font-needed", mScriptList.get()); 4001 return NS_OK; 4002 } 4003 4004 nsString mScriptList; 4005 }; 4006 4007 NS_IMPL_ISUPPORTS(DeferredNotifyMissingFonts, nsIRunnable) 4008 4009 void gfxMissingFontRecorder::Flush() { 4010 static uint32_t mNotifiedFonts[gfxMissingFontRecorder::kNumScriptBitsWords]; 4011 static StaticMutex sNotifiedFontsMutex; 4012 4013 nsAutoString fontNeeded; 4014 sNotifiedFontsMutex.Lock(); 4015 for (uint32_t i = 0; i < kNumScriptBitsWords; ++i) { 4016 mMissingFonts[i] &= ~mNotifiedFonts[i]; 4017 if (!mMissingFonts[i]) { 4018 continue; 4019 } 4020 for (uint32_t j = 0; j < 32; ++j) { 4021 if (!(mMissingFonts[i] & (1 << j))) { 4022 continue; 4023 } 4024 mNotifiedFonts[i] |= (1 << j); 4025 if (!fontNeeded.IsEmpty()) { 4026 fontNeeded.Append(char16_t(',')); 4027 } 4028 uint32_t sc = i * 32 + j; 4029 MOZ_ASSERT(sc < static_cast<uint32_t>(Script::NUM_SCRIPT_CODES), 4030 "how did we set the bit for an invalid script code?"); 4031 uint32_t tag = GetScriptTagForCode(static_cast<Script>(sc)); 4032 fontNeeded.Append(char16_t(tag >> 24)); 4033 fontNeeded.Append(char16_t((tag >> 16) & 0xff)); 4034 fontNeeded.Append(char16_t((tag >> 8) & 0xff)); 4035 fontNeeded.Append(char16_t(tag & 0xff)); 4036 } 4037 mMissingFonts[i] = 0; 4038 } 4039 sNotifiedFontsMutex.Unlock(); 4040 4041 if (!fontNeeded.IsEmpty()) { 4042 if (NS_IsMainThread()) { 4043 nsCOMPtr<nsIObserverService> service = GetObserverService(); 4044 service->NotifyObservers(nullptr, "font-needed", fontNeeded.get()); 4045 } else { 4046 NS_DispatchToMainThread( 4047 new DeferredNotifyMissingFonts(std::move(fontNeeded))); 4048 } 4049 } 4050 }