gfx/thebes/gfxFont.cpp

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

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

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

michael@0 1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "mozilla/DebugOnly.h"
michael@0 7 #include "mozilla/MathAlgorithms.h"
michael@0 8
michael@0 9 #ifdef MOZ_LOGGING
michael@0 10 #define FORCE_PR_LOG /* Allow logging in the release build */
michael@0 11 #endif
michael@0 12 #include "prlog.h"
michael@0 13
michael@0 14 #include "nsServiceManagerUtils.h"
michael@0 15 #include "nsExpirationTracker.h"
michael@0 16 #include "nsILanguageAtomService.h"
michael@0 17 #include "nsITimer.h"
michael@0 18
michael@0 19 #include "gfxFont.h"
michael@0 20 #include "gfxPlatform.h"
michael@0 21 #include "nsGkAtoms.h"
michael@0 22
michael@0 23 #include "gfxTypes.h"
michael@0 24 #include "gfxContext.h"
michael@0 25 #include "gfxFontMissingGlyphs.h"
michael@0 26 #include "gfxHarfBuzzShaper.h"
michael@0 27 #include "gfxUserFontSet.h"
michael@0 28 #include "gfxPlatformFontList.h"
michael@0 29 #include "gfxScriptItemizer.h"
michael@0 30 #include "nsUnicodeProperties.h"
michael@0 31 #include "nsMathUtils.h"
michael@0 32 #include "nsBidiUtils.h"
michael@0 33 #include "nsUnicodeRange.h"
michael@0 34 #include "nsStyleConsts.h"
michael@0 35 #include "mozilla/FloatingPoint.h"
michael@0 36 #include "mozilla/Likely.h"
michael@0 37 #include "mozilla/MemoryReporting.h"
michael@0 38 #include "mozilla/Preferences.h"
michael@0 39 #include "mozilla/Services.h"
michael@0 40 #include "mozilla/Telemetry.h"
michael@0 41 #include "gfxSVGGlyphs.h"
michael@0 42 #include "gfxMathTable.h"
michael@0 43 #include "gfx2DGlue.h"
michael@0 44
michael@0 45 #if defined(XP_MACOSX)
michael@0 46 #include "nsCocoaFeatures.h"
michael@0 47 #endif
michael@0 48
michael@0 49 #include "cairo.h"
michael@0 50 #include "gfxFontTest.h"
michael@0 51
michael@0 52 #include "harfbuzz/hb.h"
michael@0 53 #include "harfbuzz/hb-ot.h"
michael@0 54 #include "graphite2/Font.h"
michael@0 55
michael@0 56 #include "nsCRT.h"
michael@0 57 #include "GeckoProfiler.h"
michael@0 58 #include "gfxFontConstants.h"
michael@0 59
michael@0 60 #include <algorithm>
michael@0 61
michael@0 62 using namespace mozilla;
michael@0 63 using namespace mozilla::gfx;
michael@0 64 using namespace mozilla::unicode;
michael@0 65 using mozilla::services::GetObserverService;
michael@0 66
michael@0 67 gfxFontCache *gfxFontCache::gGlobalCache = nullptr;
michael@0 68
michael@0 69 static const char16_t kEllipsisChar[] = { 0x2026, 0x0 };
michael@0 70 static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
michael@0 71
michael@0 72 #ifdef DEBUG_roc
michael@0 73 #define DEBUG_TEXT_RUN_STORAGE_METRICS
michael@0 74 #endif
michael@0 75
michael@0 76 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
michael@0 77 static uint32_t gTextRunStorageHighWaterMark = 0;
michael@0 78 static uint32_t gTextRunStorage = 0;
michael@0 79 static uint32_t gFontCount = 0;
michael@0 80 static uint32_t gGlyphExtentsCount = 0;
michael@0 81 static uint32_t gGlyphExtentsWidthsTotalSize = 0;
michael@0 82 static uint32_t gGlyphExtentsSetupEagerSimple = 0;
michael@0 83 static uint32_t gGlyphExtentsSetupEagerTight = 0;
michael@0 84 static uint32_t gGlyphExtentsSetupLazyTight = 0;
michael@0 85 static uint32_t gGlyphExtentsSetupFallBackToTight = 0;
michael@0 86 #endif
michael@0 87
michael@0 88 #ifdef PR_LOGGING
michael@0 89 #define LOG_FONTINIT(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
michael@0 90 PR_LOG_DEBUG, args)
michael@0 91 #define LOG_FONTINIT_ENABLED() PR_LOG_TEST( \
michael@0 92 gfxPlatform::GetLog(eGfxLog_fontinit), \
michael@0 93 PR_LOG_DEBUG)
michael@0 94 #endif // PR_LOGGING
michael@0 95
michael@0 96 void
michael@0 97 gfxCharacterMap::NotifyReleased()
michael@0 98 {
michael@0 99 gfxPlatformFontList *fontlist = gfxPlatformFontList::PlatformFontList();
michael@0 100 if (mShared) {
michael@0 101 fontlist->RemoveCmap(this);
michael@0 102 }
michael@0 103 delete this;
michael@0 104 }
michael@0 105
michael@0 106 gfxFontEntry::gfxFontEntry() :
michael@0 107 mItalic(false), mFixedPitch(false),
michael@0 108 mIsProxy(false), mIsValid(true),
michael@0 109 mIsBadUnderlineFont(false),
michael@0 110 mIsUserFont(false),
michael@0 111 mIsLocalUserFont(false),
michael@0 112 mStandardFace(false),
michael@0 113 mSymbolFont(false),
michael@0 114 mIgnoreGDEF(false),
michael@0 115 mIgnoreGSUB(false),
michael@0 116 mSVGInitialized(false),
michael@0 117 mMathInitialized(false),
michael@0 118 mHasSpaceFeaturesInitialized(false),
michael@0 119 mHasSpaceFeatures(false),
michael@0 120 mHasSpaceFeaturesKerning(false),
michael@0 121 mHasSpaceFeaturesNonKerning(false),
michael@0 122 mSkipDefaultFeatureSpaceCheck(false),
michael@0 123 mCheckedForGraphiteTables(false),
michael@0 124 mHasCmapTable(false),
michael@0 125 mGrFaceInitialized(false),
michael@0 126 mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
michael@0 127 mUVSOffset(0), mUVSData(nullptr),
michael@0 128 mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
michael@0 129 mUnitsPerEm(0),
michael@0 130 mHBFace(nullptr),
michael@0 131 mGrFace(nullptr),
michael@0 132 mGrFaceRefCnt(0)
michael@0 133 {
michael@0 134 memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
michael@0 135 memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
michael@0 136 }
michael@0 137
michael@0 138 gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) :
michael@0 139 mName(aName), mItalic(false), mFixedPitch(false),
michael@0 140 mIsProxy(false), mIsValid(true),
michael@0 141 mIsBadUnderlineFont(false), mIsUserFont(false),
michael@0 142 mIsLocalUserFont(false), mStandardFace(aIsStandardFace),
michael@0 143 mSymbolFont(false),
michael@0 144 mIgnoreGDEF(false),
michael@0 145 mIgnoreGSUB(false),
michael@0 146 mSVGInitialized(false),
michael@0 147 mMathInitialized(false),
michael@0 148 mHasSpaceFeaturesInitialized(false),
michael@0 149 mHasSpaceFeatures(false),
michael@0 150 mHasSpaceFeaturesKerning(false),
michael@0 151 mHasSpaceFeaturesNonKerning(false),
michael@0 152 mSkipDefaultFeatureSpaceCheck(false),
michael@0 153 mCheckedForGraphiteTables(false),
michael@0 154 mHasCmapTable(false),
michael@0 155 mGrFaceInitialized(false),
michael@0 156 mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
michael@0 157 mUVSOffset(0), mUVSData(nullptr),
michael@0 158 mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
michael@0 159 mUnitsPerEm(0),
michael@0 160 mHBFace(nullptr),
michael@0 161 mGrFace(nullptr),
michael@0 162 mGrFaceRefCnt(0)
michael@0 163 {
michael@0 164 memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
michael@0 165 memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
michael@0 166 }
michael@0 167
michael@0 168 gfxFontEntry::~gfxFontEntry()
michael@0 169 {
michael@0 170 // For downloaded fonts, we need to tell the user font cache that this
michael@0 171 // entry is being deleted.
michael@0 172 if (!mIsProxy && IsUserFont() && !IsLocalUserFont()) {
michael@0 173 gfxUserFontSet::UserFontCache::ForgetFont(this);
michael@0 174 }
michael@0 175
michael@0 176 // By the time the entry is destroyed, all font instances that were
michael@0 177 // using it should already have been deleted, and so the HB and/or Gr
michael@0 178 // face objects should have been released.
michael@0 179 MOZ_ASSERT(!mHBFace);
michael@0 180 MOZ_ASSERT(!mGrFaceInitialized);
michael@0 181 }
michael@0 182
michael@0 183 bool gfxFontEntry::IsSymbolFont()
michael@0 184 {
michael@0 185 return mSymbolFont;
michael@0 186 }
michael@0 187
michael@0 188 bool gfxFontEntry::TestCharacterMap(uint32_t aCh)
michael@0 189 {
michael@0 190 if (!mCharacterMap) {
michael@0 191 ReadCMAP();
michael@0 192 NS_ASSERTION(mCharacterMap, "failed to initialize character map");
michael@0 193 }
michael@0 194 return mCharacterMap->test(aCh);
michael@0 195 }
michael@0 196
michael@0 197 nsresult gfxFontEntry::InitializeUVSMap()
michael@0 198 {
michael@0 199 // mUVSOffset will not be initialized
michael@0 200 // until cmap is initialized.
michael@0 201 if (!mCharacterMap) {
michael@0 202 ReadCMAP();
michael@0 203 NS_ASSERTION(mCharacterMap, "failed to initialize character map");
michael@0 204 }
michael@0 205
michael@0 206 if (!mUVSOffset) {
michael@0 207 return NS_ERROR_FAILURE;
michael@0 208 }
michael@0 209
michael@0 210 if (!mUVSData) {
michael@0 211 const uint32_t kCmapTag = TRUETYPE_TAG('c','m','a','p');
michael@0 212 AutoTable cmapTable(this, kCmapTag);
michael@0 213 if (!cmapTable) {
michael@0 214 mUVSOffset = 0; // don't bother to read the table again
michael@0 215 return NS_ERROR_FAILURE;
michael@0 216 }
michael@0 217
michael@0 218 uint8_t* uvsData;
michael@0 219 unsigned int cmapLen;
michael@0 220 const char* cmapData = hb_blob_get_data(cmapTable, &cmapLen);
michael@0 221 nsresult rv = gfxFontUtils::ReadCMAPTableFormat14(
michael@0 222 (const uint8_t*)cmapData + mUVSOffset,
michael@0 223 cmapLen - mUVSOffset, uvsData);
michael@0 224
michael@0 225 if (NS_FAILED(rv)) {
michael@0 226 mUVSOffset = 0; // don't bother to read the table again
michael@0 227 return rv;
michael@0 228 }
michael@0 229
michael@0 230 mUVSData = uvsData;
michael@0 231 }
michael@0 232
michael@0 233 return NS_OK;
michael@0 234 }
michael@0 235
michael@0 236 uint16_t gfxFontEntry::GetUVSGlyph(uint32_t aCh, uint32_t aVS)
michael@0 237 {
michael@0 238 InitializeUVSMap();
michael@0 239
michael@0 240 if (mUVSData) {
michael@0 241 return gfxFontUtils::MapUVSToGlyphFormat14(mUVSData, aCh, aVS);
michael@0 242 }
michael@0 243
michael@0 244 return 0;
michael@0 245 }
michael@0 246
michael@0 247 bool gfxFontEntry::SupportsScriptInGSUB(const hb_tag_t* aScriptTags)
michael@0 248 {
michael@0 249 hb_face_t *face = GetHBFace();
michael@0 250 if (!face) {
michael@0 251 return false;
michael@0 252 }
michael@0 253
michael@0 254 unsigned int index;
michael@0 255 hb_tag_t chosenScript;
michael@0 256 bool found =
michael@0 257 hb_ot_layout_table_choose_script(face, TRUETYPE_TAG('G','S','U','B'),
michael@0 258 aScriptTags, &index, &chosenScript);
michael@0 259 hb_face_destroy(face);
michael@0 260
michael@0 261 return found && chosenScript != TRUETYPE_TAG('D','F','L','T');
michael@0 262 }
michael@0 263
michael@0 264 nsresult gfxFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
michael@0 265 {
michael@0 266 NS_ASSERTION(false, "using default no-op implementation of ReadCMAP");
michael@0 267 mCharacterMap = new gfxCharacterMap();
michael@0 268 return NS_OK;
michael@0 269 }
michael@0 270
michael@0 271 nsString
michael@0 272 gfxFontEntry::RealFaceName()
michael@0 273 {
michael@0 274 AutoTable nameTable(this, TRUETYPE_TAG('n','a','m','e'));
michael@0 275 if (nameTable) {
michael@0 276 nsAutoString name;
michael@0 277 nsresult rv = gfxFontUtils::GetFullNameFromTable(nameTable, name);
michael@0 278 if (NS_SUCCEEDED(rv)) {
michael@0 279 return name;
michael@0 280 }
michael@0 281 }
michael@0 282 return Name();
michael@0 283 }
michael@0 284
michael@0 285 already_AddRefed<gfxFont>
michael@0 286 gfxFontEntry::FindOrMakeFont(const gfxFontStyle *aStyle, bool aNeedsBold)
michael@0 287 {
michael@0 288 // the font entry name is the psname, not the family name
michael@0 289 nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(this, aStyle);
michael@0 290
michael@0 291 if (!font) {
michael@0 292 gfxFont *newFont = CreateFontInstance(aStyle, aNeedsBold);
michael@0 293 if (!newFont)
michael@0 294 return nullptr;
michael@0 295 if (!newFont->Valid()) {
michael@0 296 delete newFont;
michael@0 297 return nullptr;
michael@0 298 }
michael@0 299 font = newFont;
michael@0 300 gfxFontCache::GetCache()->AddNew(font);
michael@0 301 }
michael@0 302 return font.forget();
michael@0 303 }
michael@0 304
michael@0 305 uint16_t
michael@0 306 gfxFontEntry::UnitsPerEm()
michael@0 307 {
michael@0 308 if (!mUnitsPerEm) {
michael@0 309 AutoTable headTable(this, TRUETYPE_TAG('h','e','a','d'));
michael@0 310 if (headTable) {
michael@0 311 uint32_t len;
michael@0 312 const HeadTable* head =
michael@0 313 reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable,
michael@0 314 &len));
michael@0 315 if (len >= sizeof(HeadTable)) {
michael@0 316 mUnitsPerEm = head->unitsPerEm;
michael@0 317 }
michael@0 318 }
michael@0 319
michael@0 320 // if we didn't find a usable 'head' table, or if the value was
michael@0 321 // outside the valid range, record it as invalid
michael@0 322 if (mUnitsPerEm < kMinUPEM || mUnitsPerEm > kMaxUPEM) {
michael@0 323 mUnitsPerEm = kInvalidUPEM;
michael@0 324 }
michael@0 325 }
michael@0 326 return mUnitsPerEm;
michael@0 327 }
michael@0 328
michael@0 329 bool
michael@0 330 gfxFontEntry::HasSVGGlyph(uint32_t aGlyphId)
michael@0 331 {
michael@0 332 NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
michael@0 333 return mSVGGlyphs->HasSVGGlyph(aGlyphId);
michael@0 334 }
michael@0 335
michael@0 336 bool
michael@0 337 gfxFontEntry::GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId,
michael@0 338 gfxRect *aResult)
michael@0 339 {
michael@0 340 NS_ABORT_IF_FALSE(mSVGInitialized,
michael@0 341 "SVG data has not yet been loaded. TryGetSVGData() first.");
michael@0 342 NS_ABORT_IF_FALSE(mUnitsPerEm >= kMinUPEM && mUnitsPerEm <= kMaxUPEM,
michael@0 343 "font has invalid unitsPerEm");
michael@0 344
michael@0 345 gfxContextAutoSaveRestore matrixRestore(aContext);
michael@0 346 cairo_matrix_t fontMatrix;
michael@0 347 cairo_get_font_matrix(aContext->GetCairo(), &fontMatrix);
michael@0 348
michael@0 349 gfxMatrix svgToAppSpace = *reinterpret_cast<gfxMatrix*>(&fontMatrix);
michael@0 350 svgToAppSpace.Scale(1.0f / mUnitsPerEm, 1.0f / mUnitsPerEm);
michael@0 351
michael@0 352 return mSVGGlyphs->GetGlyphExtents(aGlyphId, svgToAppSpace, aResult);
michael@0 353 }
michael@0 354
michael@0 355 bool
michael@0 356 gfxFontEntry::RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId,
michael@0 357 int aDrawMode, gfxTextContextPaint *aContextPaint)
michael@0 358 {
michael@0 359 NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
michael@0 360 return mSVGGlyphs->RenderGlyph(aContext, aGlyphId, DrawMode(aDrawMode),
michael@0 361 aContextPaint);
michael@0 362 }
michael@0 363
michael@0 364 bool
michael@0 365 gfxFontEntry::TryGetSVGData(gfxFont* aFont)
michael@0 366 {
michael@0 367 if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) {
michael@0 368 return false;
michael@0 369 }
michael@0 370
michael@0 371 if (!mSVGInitialized) {
michael@0 372 mSVGInitialized = true;
michael@0 373
michael@0 374 // If UnitsPerEm is not known/valid, we can't use SVG glyphs
michael@0 375 if (UnitsPerEm() == kInvalidUPEM) {
michael@0 376 return false;
michael@0 377 }
michael@0 378
michael@0 379 // We don't use AutoTable here because we'll pass ownership of this
michael@0 380 // blob to the gfxSVGGlyphs, once we've confirmed the table exists
michael@0 381 hb_blob_t *svgTable = GetFontTable(TRUETYPE_TAG('S','V','G',' '));
michael@0 382 if (!svgTable) {
michael@0 383 return false;
michael@0 384 }
michael@0 385
michael@0 386 // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished
michael@0 387 // with it.
michael@0 388 mSVGGlyphs = new gfxSVGGlyphs(svgTable, this);
michael@0 389 }
michael@0 390
michael@0 391 if (!mFontsUsingSVGGlyphs.Contains(aFont)) {
michael@0 392 mFontsUsingSVGGlyphs.AppendElement(aFont);
michael@0 393 }
michael@0 394
michael@0 395 return !!mSVGGlyphs;
michael@0 396 }
michael@0 397
michael@0 398 void
michael@0 399 gfxFontEntry::NotifyFontDestroyed(gfxFont* aFont)
michael@0 400 {
michael@0 401 mFontsUsingSVGGlyphs.RemoveElement(aFont);
michael@0 402 }
michael@0 403
michael@0 404 void
michael@0 405 gfxFontEntry::NotifyGlyphsChanged()
michael@0 406 {
michael@0 407 for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) {
michael@0 408 gfxFont* font = mFontsUsingSVGGlyphs[i];
michael@0 409 font->NotifyGlyphsChanged();
michael@0 410 }
michael@0 411 }
michael@0 412
michael@0 413 bool
michael@0 414 gfxFontEntry::TryGetMathTable(gfxFont* aFont)
michael@0 415 {
michael@0 416 if (!mMathInitialized) {
michael@0 417 mMathInitialized = true;
michael@0 418
michael@0 419 // If UnitsPerEm is not known/valid, we can't use MATH table
michael@0 420 if (UnitsPerEm() == kInvalidUPEM) {
michael@0 421 return false;
michael@0 422 }
michael@0 423
michael@0 424 // We don't use AutoTable here because we'll pass ownership of this
michael@0 425 // blob to the gfxMathTable, once we've confirmed the table exists
michael@0 426 hb_blob_t *mathTable = GetFontTable(TRUETYPE_TAG('M','A','T','H'));
michael@0 427 if (!mathTable) {
michael@0 428 return false;
michael@0 429 }
michael@0 430
michael@0 431 // gfxMathTable will hb_blob_destroy() the table when it is finished
michael@0 432 // with it.
michael@0 433 mMathTable = new gfxMathTable(mathTable);
michael@0 434 if (!mMathTable->HasValidHeaders()) {
michael@0 435 mMathTable = nullptr;
michael@0 436 return false;
michael@0 437 }
michael@0 438 }
michael@0 439
michael@0 440 return !!mMathTable;
michael@0 441 }
michael@0 442
michael@0 443 gfxFloat
michael@0 444 gfxFontEntry::GetMathConstant(gfxFontEntry::MathConstant aConstant)
michael@0 445 {
michael@0 446 NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
michael@0 447 gfxFloat value = mMathTable->GetMathConstant(aConstant);
michael@0 448 if (aConstant == gfxFontEntry::ScriptPercentScaleDown ||
michael@0 449 aConstant == gfxFontEntry::ScriptScriptPercentScaleDown ||
michael@0 450 aConstant == gfxFontEntry::RadicalDegreeBottomRaisePercent) {
michael@0 451 return value / 100.0;
michael@0 452 }
michael@0 453 return value / mUnitsPerEm;
michael@0 454 }
michael@0 455
michael@0 456 bool
michael@0 457 gfxFontEntry::GetMathItalicsCorrection(uint32_t aGlyphID,
michael@0 458 gfxFloat* aItalicCorrection)
michael@0 459 {
michael@0 460 NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
michael@0 461 int16_t italicCorrection;
michael@0 462 if (!mMathTable->GetMathItalicsCorrection(aGlyphID, &italicCorrection)) {
michael@0 463 return false;
michael@0 464 }
michael@0 465 *aItalicCorrection = gfxFloat(italicCorrection) / mUnitsPerEm;
michael@0 466 return true;
michael@0 467 }
michael@0 468
michael@0 469 uint32_t
michael@0 470 gfxFontEntry::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
michael@0 471 uint16_t aSize)
michael@0 472 {
michael@0 473 NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
michael@0 474 return mMathTable->GetMathVariantsSize(aGlyphID, aVertical, aSize);
michael@0 475 }
michael@0 476
michael@0 477 bool
michael@0 478 gfxFontEntry::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
michael@0 479 uint32_t aGlyphs[4])
michael@0 480 {
michael@0 481 NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
michael@0 482 return mMathTable->GetMathVariantsParts(aGlyphID, aVertical, aGlyphs);
michael@0 483 }
michael@0 484
michael@0 485 /**
michael@0 486 * FontTableBlobData
michael@0 487 *
michael@0 488 * See FontTableHashEntry for the general strategy.
michael@0 489 */
michael@0 490
michael@0 491 class gfxFontEntry::FontTableBlobData {
michael@0 492 public:
michael@0 493 // Adopts the content of aBuffer.
michael@0 494 FontTableBlobData(FallibleTArray<uint8_t>& aBuffer)
michael@0 495 : mHashtable(nullptr), mHashKey(0)
michael@0 496 {
michael@0 497 MOZ_COUNT_CTOR(FontTableBlobData);
michael@0 498 mTableData.SwapElements(aBuffer);
michael@0 499 }
michael@0 500
michael@0 501 ~FontTableBlobData() {
michael@0 502 MOZ_COUNT_DTOR(FontTableBlobData);
michael@0 503 if (mHashtable && mHashKey) {
michael@0 504 mHashtable->RemoveEntry(mHashKey);
michael@0 505 }
michael@0 506 }
michael@0 507
michael@0 508 // Useful for creating blobs
michael@0 509 const char *GetTable() const
michael@0 510 {
michael@0 511 return reinterpret_cast<const char*>(mTableData.Elements());
michael@0 512 }
michael@0 513 uint32_t GetTableLength() const { return mTableData.Length(); }
michael@0 514
michael@0 515 // Tell this FontTableBlobData to remove the HashEntry when this is
michael@0 516 // destroyed.
michael@0 517 void ManageHashEntry(nsTHashtable<FontTableHashEntry> *aHashtable,
michael@0 518 uint32_t aHashKey)
michael@0 519 {
michael@0 520 mHashtable = aHashtable;
michael@0 521 mHashKey = aHashKey;
michael@0 522 }
michael@0 523
michael@0 524 // Disconnect from the HashEntry (because the blob has already been
michael@0 525 // removed from the hashtable).
michael@0 526 void ForgetHashEntry()
michael@0 527 {
michael@0 528 mHashtable = nullptr;
michael@0 529 mHashKey = 0;
michael@0 530 }
michael@0 531
michael@0 532 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
michael@0 533 return mTableData.SizeOfExcludingThis(aMallocSizeOf);
michael@0 534 }
michael@0 535 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
michael@0 536 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 537 }
michael@0 538
michael@0 539 private:
michael@0 540 // The font table data block, owned (via adoption)
michael@0 541 FallibleTArray<uint8_t> mTableData;
michael@0 542
michael@0 543 // The blob destroy function needs to know the owning hashtable
michael@0 544 // and the hashtable key, so that it can remove the entry.
michael@0 545 nsTHashtable<FontTableHashEntry> *mHashtable;
michael@0 546 uint32_t mHashKey;
michael@0 547
michael@0 548 // not implemented
michael@0 549 FontTableBlobData(const FontTableBlobData&);
michael@0 550 };
michael@0 551
michael@0 552 hb_blob_t *
michael@0 553 gfxFontEntry::FontTableHashEntry::
michael@0 554 ShareTableAndGetBlob(FallibleTArray<uint8_t>& aTable,
michael@0 555 nsTHashtable<FontTableHashEntry> *aHashtable)
michael@0 556 {
michael@0 557 Clear();
michael@0 558 // adopts elements of aTable
michael@0 559 mSharedBlobData = new FontTableBlobData(aTable);
michael@0 560 mBlob = hb_blob_create(mSharedBlobData->GetTable(),
michael@0 561 mSharedBlobData->GetTableLength(),
michael@0 562 HB_MEMORY_MODE_READONLY,
michael@0 563 mSharedBlobData, DeleteFontTableBlobData);
michael@0 564 if (!mSharedBlobData) {
michael@0 565 // The FontTableBlobData was destroyed during hb_blob_create().
michael@0 566 // The (empty) blob is still be held in the hashtable with a strong
michael@0 567 // reference.
michael@0 568 return hb_blob_reference(mBlob);
michael@0 569 }
michael@0 570
michael@0 571 // Tell the FontTableBlobData to remove this hash entry when destroyed.
michael@0 572 // The hashtable does not keep a strong reference.
michael@0 573 mSharedBlobData->ManageHashEntry(aHashtable, GetKey());
michael@0 574 return mBlob;
michael@0 575 }
michael@0 576
michael@0 577 void
michael@0 578 gfxFontEntry::FontTableHashEntry::Clear()
michael@0 579 {
michael@0 580 // If the FontTableBlobData is managing the hash entry, then the blob is
michael@0 581 // not owned by this HashEntry; otherwise there is strong reference to the
michael@0 582 // blob that must be removed.
michael@0 583 if (mSharedBlobData) {
michael@0 584 mSharedBlobData->ForgetHashEntry();
michael@0 585 mSharedBlobData = nullptr;
michael@0 586 } else if (mBlob) {
michael@0 587 hb_blob_destroy(mBlob);
michael@0 588 }
michael@0 589 mBlob = nullptr;
michael@0 590 }
michael@0 591
michael@0 592 // a hb_destroy_func for hb_blob_create
michael@0 593
michael@0 594 /* static */ void
michael@0 595 gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(void *aBlobData)
michael@0 596 {
michael@0 597 delete static_cast<FontTableBlobData*>(aBlobData);
michael@0 598 }
michael@0 599
michael@0 600 hb_blob_t *
michael@0 601 gfxFontEntry::FontTableHashEntry::GetBlob() const
michael@0 602 {
michael@0 603 return hb_blob_reference(mBlob);
michael@0 604 }
michael@0 605
michael@0 606 bool
michael@0 607 gfxFontEntry::GetExistingFontTable(uint32_t aTag, hb_blob_t **aBlob)
michael@0 608 {
michael@0 609 if (!mFontTableCache) {
michael@0 610 // we do this here rather than on fontEntry construction
michael@0 611 // because not all shapers will access the table cache at all
michael@0 612 mFontTableCache = new nsTHashtable<FontTableHashEntry>(10);
michael@0 613 }
michael@0 614
michael@0 615 FontTableHashEntry *entry = mFontTableCache->GetEntry(aTag);
michael@0 616 if (!entry) {
michael@0 617 return false;
michael@0 618 }
michael@0 619
michael@0 620 *aBlob = entry->GetBlob();
michael@0 621 return true;
michael@0 622 }
michael@0 623
michael@0 624 hb_blob_t *
michael@0 625 gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag,
michael@0 626 FallibleTArray<uint8_t>* aBuffer)
michael@0 627 {
michael@0 628 if (MOZ_UNLIKELY(!mFontTableCache)) {
michael@0 629 // we do this here rather than on fontEntry construction
michael@0 630 // because not all shapers will access the table cache at all
michael@0 631 mFontTableCache = new nsTHashtable<FontTableHashEntry>(10);
michael@0 632 }
michael@0 633
michael@0 634 FontTableHashEntry *entry = mFontTableCache->PutEntry(aTag);
michael@0 635 if (MOZ_UNLIKELY(!entry)) { // OOM
michael@0 636 return nullptr;
michael@0 637 }
michael@0 638
michael@0 639 if (!aBuffer) {
michael@0 640 // ensure the entry is null
michael@0 641 entry->Clear();
michael@0 642 return nullptr;
michael@0 643 }
michael@0 644
michael@0 645 return entry->ShareTableAndGetBlob(*aBuffer, mFontTableCache);
michael@0 646 }
michael@0 647
michael@0 648 static int
michael@0 649 DirEntryCmp(const void* aKey, const void* aItem)
michael@0 650 {
michael@0 651 int32_t tag = *static_cast<const int32_t*>(aKey);
michael@0 652 const TableDirEntry* entry = static_cast<const TableDirEntry*>(aItem);
michael@0 653 return tag - int32_t(entry->tag);
michael@0 654 }
michael@0 655
michael@0 656 hb_blob_t*
michael@0 657 gfxFontEntry::GetTableFromFontData(const void* aFontData, uint32_t aTableTag)
michael@0 658 {
michael@0 659 const SFNTHeader* header =
michael@0 660 reinterpret_cast<const SFNTHeader*>(aFontData);
michael@0 661 const TableDirEntry* dir =
michael@0 662 reinterpret_cast<const TableDirEntry*>(header + 1);
michael@0 663 dir = static_cast<const TableDirEntry*>
michael@0 664 (bsearch(&aTableTag, dir, uint16_t(header->numTables),
michael@0 665 sizeof(TableDirEntry), DirEntryCmp));
michael@0 666 if (dir) {
michael@0 667 return hb_blob_create(reinterpret_cast<const char*>(aFontData) +
michael@0 668 dir->offset, dir->length,
michael@0 669 HB_MEMORY_MODE_READONLY, nullptr, nullptr);
michael@0 670
michael@0 671 }
michael@0 672 return nullptr;
michael@0 673 }
michael@0 674
michael@0 675 already_AddRefed<gfxCharacterMap>
michael@0 676 gfxFontEntry::GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
michael@0 677 uint32_t& aUVSOffset,
michael@0 678 bool& aSymbolFont)
michael@0 679 {
michael@0 680 if (!aFontInfoData || !aFontInfoData->mLoadCmaps) {
michael@0 681 return nullptr;
michael@0 682 }
michael@0 683
michael@0 684 return aFontInfoData->GetCMAP(mName, aUVSOffset, aSymbolFont);
michael@0 685 }
michael@0 686
michael@0 687 hb_blob_t *
michael@0 688 gfxFontEntry::GetFontTable(uint32_t aTag)
michael@0 689 {
michael@0 690 hb_blob_t *blob;
michael@0 691 if (GetExistingFontTable(aTag, &blob)) {
michael@0 692 return blob;
michael@0 693 }
michael@0 694
michael@0 695 FallibleTArray<uint8_t> buffer;
michael@0 696 bool haveTable = NS_SUCCEEDED(CopyFontTable(aTag, buffer));
michael@0 697
michael@0 698 return ShareFontTableAndGetBlob(aTag, haveTable ? &buffer : nullptr);
michael@0 699 }
michael@0 700
michael@0 701 // callback for HarfBuzz to get a font table (in hb_blob_t form)
michael@0 702 // from the font entry (passed as aUserData)
michael@0 703 /*static*/ hb_blob_t *
michael@0 704 gfxFontEntry::HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData)
michael@0 705 {
michael@0 706 gfxFontEntry *fontEntry = static_cast<gfxFontEntry*>(aUserData);
michael@0 707
michael@0 708 // bug 589682 - ignore the GDEF table in buggy fonts (applies to
michael@0 709 // Italic and BoldItalic faces of Times New Roman)
michael@0 710 if (aTag == TRUETYPE_TAG('G','D','E','F') &&
michael@0 711 fontEntry->IgnoreGDEF()) {
michael@0 712 return nullptr;
michael@0 713 }
michael@0 714
michael@0 715 // bug 721719 - ignore the GSUB table in buggy fonts (applies to Roboto,
michael@0 716 // at least on some Android ICS devices; set in gfxFT2FontList.cpp)
michael@0 717 if (aTag == TRUETYPE_TAG('G','S','U','B') &&
michael@0 718 fontEntry->IgnoreGSUB()) {
michael@0 719 return nullptr;
michael@0 720 }
michael@0 721
michael@0 722 return fontEntry->GetFontTable(aTag);
michael@0 723 }
michael@0 724
michael@0 725 /*static*/ void
michael@0 726 gfxFontEntry::HBFaceDeletedCallback(void *aUserData)
michael@0 727 {
michael@0 728 gfxFontEntry *fe = static_cast<gfxFontEntry*>(aUserData);
michael@0 729 fe->ForgetHBFace();
michael@0 730 }
michael@0 731
michael@0 732 void
michael@0 733 gfxFontEntry::ForgetHBFace()
michael@0 734 {
michael@0 735 mHBFace = nullptr;
michael@0 736 }
michael@0 737
michael@0 738 hb_face_t*
michael@0 739 gfxFontEntry::GetHBFace()
michael@0 740 {
michael@0 741 if (!mHBFace) {
michael@0 742 mHBFace = hb_face_create_for_tables(HBGetTable, this,
michael@0 743 HBFaceDeletedCallback);
michael@0 744 return mHBFace;
michael@0 745 }
michael@0 746 return hb_face_reference(mHBFace);
michael@0 747 }
michael@0 748
michael@0 749 /*static*/ const void*
michael@0 750 gfxFontEntry::GrGetTable(const void *aAppFaceHandle, unsigned int aName,
michael@0 751 size_t *aLen)
michael@0 752 {
michael@0 753 gfxFontEntry *fontEntry =
michael@0 754 static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle));
michael@0 755 hb_blob_t *blob = fontEntry->GetFontTable(aName);
michael@0 756 if (blob) {
michael@0 757 unsigned int blobLength;
michael@0 758 const void *tableData = hb_blob_get_data(blob, &blobLength);
michael@0 759 fontEntry->mGrTableMap->Put(tableData, blob);
michael@0 760 *aLen = blobLength;
michael@0 761 return tableData;
michael@0 762 }
michael@0 763 *aLen = 0;
michael@0 764 return nullptr;
michael@0 765 }
michael@0 766
michael@0 767 /*static*/ void
michael@0 768 gfxFontEntry::GrReleaseTable(const void *aAppFaceHandle,
michael@0 769 const void *aTableBuffer)
michael@0 770 {
michael@0 771 gfxFontEntry *fontEntry =
michael@0 772 static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle));
michael@0 773 void *data;
michael@0 774 if (fontEntry->mGrTableMap->Get(aTableBuffer, &data)) {
michael@0 775 fontEntry->mGrTableMap->Remove(aTableBuffer);
michael@0 776 hb_blob_destroy(static_cast<hb_blob_t*>(data));
michael@0 777 }
michael@0 778 }
michael@0 779
michael@0 780 gr_face*
michael@0 781 gfxFontEntry::GetGrFace()
michael@0 782 {
michael@0 783 if (!mGrFaceInitialized) {
michael@0 784 gr_face_ops faceOps = {
michael@0 785 sizeof(gr_face_ops),
michael@0 786 GrGetTable,
michael@0 787 GrReleaseTable
michael@0 788 };
michael@0 789 mGrTableMap = new nsDataHashtable<nsPtrHashKey<const void>,void*>;
michael@0 790 mGrFace = gr_make_face_with_ops(this, &faceOps, gr_face_default);
michael@0 791 mGrFaceInitialized = true;
michael@0 792 }
michael@0 793 ++mGrFaceRefCnt;
michael@0 794 return mGrFace;
michael@0 795 }
michael@0 796
michael@0 797 void
michael@0 798 gfxFontEntry::ReleaseGrFace(gr_face *aFace)
michael@0 799 {
michael@0 800 MOZ_ASSERT(aFace == mGrFace); // sanity-check
michael@0 801 MOZ_ASSERT(mGrFaceRefCnt > 0);
michael@0 802 if (--mGrFaceRefCnt == 0) {
michael@0 803 gr_face_destroy(mGrFace);
michael@0 804 mGrFace = nullptr;
michael@0 805 mGrFaceInitialized = false;
michael@0 806 delete mGrTableMap;
michael@0 807 mGrTableMap = nullptr;
michael@0 808 }
michael@0 809 }
michael@0 810
michael@0 811 void
michael@0 812 gfxFontEntry::DisconnectSVG()
michael@0 813 {
michael@0 814 if (mSVGInitialized && mSVGGlyphs) {
michael@0 815 mSVGGlyphs = nullptr;
michael@0 816 mSVGInitialized = false;
michael@0 817 }
michael@0 818 }
michael@0 819
michael@0 820 bool
michael@0 821 gfxFontEntry::HasFontTable(uint32_t aTableTag)
michael@0 822 {
michael@0 823 AutoTable table(this, aTableTag);
michael@0 824 return table && hb_blob_get_length(table) > 0;
michael@0 825 }
michael@0 826
michael@0 827 void
michael@0 828 gfxFontEntry::CheckForGraphiteTables()
michael@0 829 {
michael@0 830 mHasGraphiteTables = HasFontTable(TRUETYPE_TAG('S','i','l','f'));
michael@0 831 }
michael@0 832
michael@0 833 /* static */ size_t
michael@0 834 gfxFontEntry::FontTableHashEntry::SizeOfEntryExcludingThis
michael@0 835 (FontTableHashEntry *aEntry,
michael@0 836 MallocSizeOf aMallocSizeOf,
michael@0 837 void* aUserArg)
michael@0 838 {
michael@0 839 size_t n = 0;
michael@0 840 if (aEntry->mBlob) {
michael@0 841 n += aMallocSizeOf(aEntry->mBlob);
michael@0 842 }
michael@0 843 if (aEntry->mSharedBlobData) {
michael@0 844 n += aEntry->mSharedBlobData->SizeOfIncludingThis(aMallocSizeOf);
michael@0 845 }
michael@0 846 return n;
michael@0 847 }
michael@0 848
michael@0 849 void
michael@0 850 gfxFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
michael@0 851 FontListSizes* aSizes) const
michael@0 852 {
michael@0 853 aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
michael@0 854
michael@0 855 // cmaps are shared so only non-shared cmaps are included here
michael@0 856 if (mCharacterMap && mCharacterMap->mBuildOnTheFly) {
michael@0 857 aSizes->mCharMapsSize +=
michael@0 858 mCharacterMap->SizeOfIncludingThis(aMallocSizeOf);
michael@0 859 }
michael@0 860 if (mFontTableCache) {
michael@0 861 aSizes->mFontTableCacheSize +=
michael@0 862 mFontTableCache->SizeOfExcludingThis(
michael@0 863 FontTableHashEntry::SizeOfEntryExcludingThis,
michael@0 864 aMallocSizeOf);
michael@0 865 }
michael@0 866 }
michael@0 867
michael@0 868 void
michael@0 869 gfxFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
michael@0 870 FontListSizes* aSizes) const
michael@0 871 {
michael@0 872 aSizes->mFontListSize += aMallocSizeOf(this);
michael@0 873 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 874 }
michael@0 875
michael@0 876 //////////////////////////////////////////////////////////////////////////////
michael@0 877 //
michael@0 878 // class gfxFontFamily
michael@0 879 //
michael@0 880 //////////////////////////////////////////////////////////////////////////////
michael@0 881
michael@0 882 // we consider faces with mStandardFace == true to be "greater than" those with false,
michael@0 883 // because during style matching, later entries will replace earlier ones
michael@0 884 class FontEntryStandardFaceComparator {
michael@0 885 public:
michael@0 886 bool Equals(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const {
michael@0 887 return a->mStandardFace == b->mStandardFace;
michael@0 888 }
michael@0 889 bool LessThan(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const {
michael@0 890 return (a->mStandardFace == false && b->mStandardFace == true);
michael@0 891 }
michael@0 892 };
michael@0 893
michael@0 894 void
michael@0 895 gfxFontFamily::SortAvailableFonts()
michael@0 896 {
michael@0 897 mAvailableFonts.Sort(FontEntryStandardFaceComparator());
michael@0 898 }
michael@0 899
michael@0 900 bool
michael@0 901 gfxFontFamily::HasOtherFamilyNames()
michael@0 902 {
michael@0 903 // need to read in other family names to determine this
michael@0 904 if (!mOtherFamilyNamesInitialized) {
michael@0 905 ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList()); // sets mHasOtherFamilyNames
michael@0 906 }
michael@0 907 return mHasOtherFamilyNames;
michael@0 908 }
michael@0 909
michael@0 910 gfxFontEntry*
michael@0 911 gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle,
michael@0 912 bool& aNeedsSyntheticBold)
michael@0 913 {
michael@0 914 if (!mHasStyles)
michael@0 915 FindStyleVariations(); // collect faces for the family, if not already done
michael@0 916
michael@0 917 NS_ASSERTION(mAvailableFonts.Length() > 0, "font family with no faces!");
michael@0 918
michael@0 919 aNeedsSyntheticBold = false;
michael@0 920
michael@0 921 int8_t baseWeight = aFontStyle.ComputeWeight();
michael@0 922 bool wantBold = baseWeight >= 6;
michael@0 923
michael@0 924 // If the family has only one face, we simply return it; no further checking needed
michael@0 925 if (mAvailableFonts.Length() == 1) {
michael@0 926 gfxFontEntry *fe = mAvailableFonts[0];
michael@0 927 aNeedsSyntheticBold = wantBold && !fe->IsBold();
michael@0 928 return fe;
michael@0 929 }
michael@0 930
michael@0 931 bool wantItalic = (aFontStyle.style &
michael@0 932 (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
michael@0 933
michael@0 934 // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
michael@0 935 // or some subset of these. In this case, we have exactly 4 entries in mAvailableFonts,
michael@0 936 // stored in the above order; note that some of the entries may be nullptr.
michael@0 937 // We can then pick the required entry based on whether the request is for
michael@0 938 // bold or non-bold, italic or non-italic, without running the more complex
michael@0 939 // matching algorithm used for larger families with many weights and/or widths.
michael@0 940
michael@0 941 if (mIsSimpleFamily) {
michael@0 942 // Family has no more than the "standard" 4 faces, at fixed indexes;
michael@0 943 // calculate which one we want.
michael@0 944 // Note that we cannot simply return it as not all 4 faces are necessarily present.
michael@0 945 uint8_t faceIndex = (wantItalic ? kItalicMask : 0) |
michael@0 946 (wantBold ? kBoldMask : 0);
michael@0 947
michael@0 948 // if the desired style is available, return it directly
michael@0 949 gfxFontEntry *fe = mAvailableFonts[faceIndex];
michael@0 950 if (fe) {
michael@0 951 // no need to set aNeedsSyntheticBold here as we matched the boldness request
michael@0 952 return fe;
michael@0 953 }
michael@0 954
michael@0 955 // order to check fallback faces in a simple family, depending on requested style
michael@0 956 static const uint8_t simpleFallbacks[4][3] = {
michael@0 957 { kBoldFaceIndex, kItalicFaceIndex, kBoldItalicFaceIndex }, // fallbacks for Regular
michael@0 958 { kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex },// Bold
michael@0 959 { kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex }, // Italic
michael@0 960 { kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex } // BoldItalic
michael@0 961 };
michael@0 962 const uint8_t *order = simpleFallbacks[faceIndex];
michael@0 963
michael@0 964 for (uint8_t trial = 0; trial < 3; ++trial) {
michael@0 965 // check remaining faces in order of preference to find the first that actually exists
michael@0 966 fe = mAvailableFonts[order[trial]];
michael@0 967 if (fe) {
michael@0 968 aNeedsSyntheticBold = wantBold && !fe->IsBold();
michael@0 969 return fe;
michael@0 970 }
michael@0 971 }
michael@0 972
michael@0 973 // this can't happen unless we have totally broken the font-list manager!
michael@0 974 NS_NOTREACHED("no face found in simple font family!");
michael@0 975 return nullptr;
michael@0 976 }
michael@0 977
michael@0 978 // This is a large/rich font family, so we do full style- and weight-matching:
michael@0 979 // first collect a list of weights that are the best match for the requested
michael@0 980 // font-stretch and font-style, then pick the best weight match among those
michael@0 981 // available.
michael@0 982
michael@0 983 gfxFontEntry *weightList[10] = { 0 };
michael@0 984 bool foundWeights = FindWeightsForStyle(weightList, wantItalic, aFontStyle.stretch);
michael@0 985 if (!foundWeights) {
michael@0 986 return nullptr;
michael@0 987 }
michael@0 988
michael@0 989 // First find a match for the best weight
michael@0 990 int8_t matchBaseWeight = 0;
michael@0 991 int8_t i = baseWeight;
michael@0 992
michael@0 993 // Need to special case when normal face doesn't exist but medium does.
michael@0 994 // In that case, use medium otherwise weights < 400
michael@0 995 if (baseWeight == 4 && !weightList[4]) {
michael@0 996 i = 5; // medium
michael@0 997 }
michael@0 998
michael@0 999 // Loop through weights, since one exists loop will terminate
michael@0 1000 int8_t direction = (baseWeight > 5) ? 1 : -1;
michael@0 1001 for (; ; i += direction) {
michael@0 1002 if (weightList[i]) {
michael@0 1003 matchBaseWeight = i;
michael@0 1004 break;
michael@0 1005 }
michael@0 1006
michael@0 1007 // If we've reached one side without finding a font,
michael@0 1008 // start over and go the other direction until we find a match
michael@0 1009 if (i == 1 || i == 9) {
michael@0 1010 i = baseWeight;
michael@0 1011 direction = -direction;
michael@0 1012 }
michael@0 1013 }
michael@0 1014
michael@0 1015 NS_ASSERTION(matchBaseWeight != 0,
michael@0 1016 "weight mapping should always find at least one font in a family");
michael@0 1017
michael@0 1018 gfxFontEntry *matchFE = weightList[matchBaseWeight];
michael@0 1019
michael@0 1020 NS_ASSERTION(matchFE,
michael@0 1021 "weight mapping should always find at least one font in a family");
michael@0 1022
michael@0 1023 if (!matchFE->IsBold() && baseWeight >= 6)
michael@0 1024 {
michael@0 1025 aNeedsSyntheticBold = true;
michael@0 1026 }
michael@0 1027
michael@0 1028 return matchFE;
michael@0 1029 }
michael@0 1030
michael@0 1031 void
michael@0 1032 gfxFontFamily::CheckForSimpleFamily()
michael@0 1033 {
michael@0 1034 // already checked this family
michael@0 1035 if (mIsSimpleFamily) {
michael@0 1036 return;
michael@0 1037 };
michael@0 1038
michael@0 1039 uint32_t count = mAvailableFonts.Length();
michael@0 1040 if (count > 4 || count == 0) {
michael@0 1041 return; // can't be "simple" if there are >4 faces;
michael@0 1042 // if none then the family is unusable anyway
michael@0 1043 }
michael@0 1044
michael@0 1045 if (count == 1) {
michael@0 1046 mIsSimpleFamily = true;
michael@0 1047 return;
michael@0 1048 }
michael@0 1049
michael@0 1050 int16_t firstStretch = mAvailableFonts[0]->Stretch();
michael@0 1051
michael@0 1052 gfxFontEntry *faces[4] = { 0 };
michael@0 1053 for (uint8_t i = 0; i < count; ++i) {
michael@0 1054 gfxFontEntry *fe = mAvailableFonts[i];
michael@0 1055 if (fe->Stretch() != firstStretch) {
michael@0 1056 return; // font-stretch doesn't match, don't treat as simple family
michael@0 1057 }
michael@0 1058 uint8_t faceIndex = (fe->IsItalic() ? kItalicMask : 0) |
michael@0 1059 (fe->Weight() >= 600 ? kBoldMask : 0);
michael@0 1060 if (faces[faceIndex]) {
michael@0 1061 return; // two faces resolve to the same slot; family isn't "simple"
michael@0 1062 }
michael@0 1063 faces[faceIndex] = fe;
michael@0 1064 }
michael@0 1065
michael@0 1066 // we have successfully slotted the available faces into the standard
michael@0 1067 // 4-face framework
michael@0 1068 mAvailableFonts.SetLength(4);
michael@0 1069 for (uint8_t i = 0; i < 4; ++i) {
michael@0 1070 if (mAvailableFonts[i].get() != faces[i]) {
michael@0 1071 mAvailableFonts[i].swap(faces[i]);
michael@0 1072 }
michael@0 1073 }
michael@0 1074
michael@0 1075 mIsSimpleFamily = true;
michael@0 1076 }
michael@0 1077
michael@0 1078 static inline uint32_t
michael@0 1079 StyleDistance(gfxFontEntry *aFontEntry,
michael@0 1080 bool anItalic, int16_t aStretch)
michael@0 1081 {
michael@0 1082 // Compute a measure of the "distance" between the requested style
michael@0 1083 // and the given fontEntry,
michael@0 1084 // considering italicness and font-stretch but not weight.
michael@0 1085
michael@0 1086 int32_t distance = 0;
michael@0 1087 if (aStretch != aFontEntry->mStretch) {
michael@0 1088 // stretch values are in the range -4 .. +4
michael@0 1089 // if aStretch is positive, we prefer more-positive values;
michael@0 1090 // if zero or negative, prefer more-negative
michael@0 1091 if (aStretch > 0) {
michael@0 1092 distance = (aFontEntry->mStretch - aStretch) * 2;
michael@0 1093 } else {
michael@0 1094 distance = (aStretch - aFontEntry->mStretch) * 2;
michael@0 1095 }
michael@0 1096 // if the computed "distance" here is negative, it means that
michael@0 1097 // aFontEntry lies in the "non-preferred" direction from aStretch,
michael@0 1098 // so we treat that as larger than any preferred-direction distance
michael@0 1099 // (max possible is 8) by adding an extra 10 to the absolute value
michael@0 1100 if (distance < 0) {
michael@0 1101 distance = -distance + 10;
michael@0 1102 }
michael@0 1103 }
michael@0 1104 if (aFontEntry->IsItalic() != anItalic) {
michael@0 1105 distance += 1;
michael@0 1106 }
michael@0 1107 return uint32_t(distance);
michael@0 1108 }
michael@0 1109
michael@0 1110 bool
michael@0 1111 gfxFontFamily::FindWeightsForStyle(gfxFontEntry* aFontsForWeights[],
michael@0 1112 bool anItalic, int16_t aStretch)
michael@0 1113 {
michael@0 1114 uint32_t foundWeights = 0;
michael@0 1115 uint32_t bestMatchDistance = 0xffffffff;
michael@0 1116
michael@0 1117 uint32_t count = mAvailableFonts.Length();
michael@0 1118 for (uint32_t i = 0; i < count; i++) {
michael@0 1119 // this is not called for "simple" families, and therefore it does not
michael@0 1120 // need to check the mAvailableFonts entries for nullptr.
michael@0 1121 gfxFontEntry *fe = mAvailableFonts[i];
michael@0 1122 uint32_t distance = StyleDistance(fe, anItalic, aStretch);
michael@0 1123 if (distance <= bestMatchDistance) {
michael@0 1124 int8_t wt = fe->mWeight / 100;
michael@0 1125 NS_ASSERTION(wt >= 1 && wt < 10, "invalid weight in fontEntry");
michael@0 1126 if (!aFontsForWeights[wt]) {
michael@0 1127 // record this as a possible candidate for weight matching
michael@0 1128 aFontsForWeights[wt] = fe;
michael@0 1129 ++foundWeights;
michael@0 1130 } else {
michael@0 1131 uint32_t prevDistance =
michael@0 1132 StyleDistance(aFontsForWeights[wt], anItalic, aStretch);
michael@0 1133 if (prevDistance >= distance) {
michael@0 1134 // replacing a weight we already found,
michael@0 1135 // so don't increment foundWeights
michael@0 1136 aFontsForWeights[wt] = fe;
michael@0 1137 }
michael@0 1138 }
michael@0 1139 bestMatchDistance = distance;
michael@0 1140 }
michael@0 1141 }
michael@0 1142
michael@0 1143 NS_ASSERTION(foundWeights > 0, "Font family containing no faces?");
michael@0 1144
michael@0 1145 if (foundWeights == 1) {
michael@0 1146 // no need to cull entries if we only found one weight
michael@0 1147 return true;
michael@0 1148 }
michael@0 1149
michael@0 1150 // we might have recorded some faces that were a partial style match, but later found
michael@0 1151 // others that were closer; in this case, we need to cull the poorer matches from the
michael@0 1152 // weight list we'll return
michael@0 1153 for (uint32_t i = 0; i < 10; ++i) {
michael@0 1154 if (aFontsForWeights[i] &&
michael@0 1155 StyleDistance(aFontsForWeights[i], anItalic, aStretch) > bestMatchDistance)
michael@0 1156 {
michael@0 1157 aFontsForWeights[i] = 0;
michael@0 1158 }
michael@0 1159 }
michael@0 1160
michael@0 1161 return (foundWeights > 0);
michael@0 1162 }
michael@0 1163
michael@0 1164
michael@0 1165 void gfxFontFamily::LocalizedName(nsAString& aLocalizedName)
michael@0 1166 {
michael@0 1167 // just return the primary name; subclasses should override
michael@0 1168 aLocalizedName = mName;
michael@0 1169 }
michael@0 1170
michael@0 1171 // metric for how close a given font matches a style
michael@0 1172 static int32_t
michael@0 1173 CalcStyleMatch(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle)
michael@0 1174 {
michael@0 1175 int32_t rank = 0;
michael@0 1176 if (aStyle) {
michael@0 1177 // italics
michael@0 1178 bool wantItalic =
michael@0 1179 (aStyle->style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
michael@0 1180 if (aFontEntry->IsItalic() == wantItalic) {
michael@0 1181 rank += 10;
michael@0 1182 }
michael@0 1183
michael@0 1184 // measure of closeness of weight to the desired value
michael@0 1185 rank += 9 - DeprecatedAbs(aFontEntry->Weight() / 100 - aStyle->ComputeWeight());
michael@0 1186 } else {
michael@0 1187 // if no font to match, prefer non-bold, non-italic fonts
michael@0 1188 if (!aFontEntry->IsItalic()) {
michael@0 1189 rank += 3;
michael@0 1190 }
michael@0 1191 if (!aFontEntry->IsBold()) {
michael@0 1192 rank += 2;
michael@0 1193 }
michael@0 1194 }
michael@0 1195
michael@0 1196 return rank;
michael@0 1197 }
michael@0 1198
michael@0 1199 #define RANK_MATCHED_CMAP 20
michael@0 1200
michael@0 1201 void
michael@0 1202 gfxFontFamily::FindFontForChar(GlobalFontMatch *aMatchData)
michael@0 1203 {
michael@0 1204 if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
michael@0 1205 // none of the faces in the family support the required char,
michael@0 1206 // so bail out immediately
michael@0 1207 return;
michael@0 1208 }
michael@0 1209
michael@0 1210 bool needsBold;
michael@0 1211 gfxFontStyle normal;
michael@0 1212 gfxFontEntry *fe = FindFontForStyle(
michael@0 1213 (aMatchData->mStyle == nullptr) ? *aMatchData->mStyle : normal,
michael@0 1214 needsBold);
michael@0 1215
michael@0 1216 if (fe && !fe->SkipDuringSystemFallback()) {
michael@0 1217 int32_t rank = 0;
michael@0 1218
michael@0 1219 if (fe->TestCharacterMap(aMatchData->mCh)) {
michael@0 1220 rank += RANK_MATCHED_CMAP;
michael@0 1221 aMatchData->mCount++;
michael@0 1222 #ifdef PR_LOGGING
michael@0 1223 PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textrun);
michael@0 1224
michael@0 1225 if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_DEBUG))) {
michael@0 1226 uint32_t unicodeRange = FindCharUnicodeRange(aMatchData->mCh);
michael@0 1227 uint32_t script = GetScriptCode(aMatchData->mCh);
michael@0 1228 PR_LOG(log, PR_LOG_DEBUG,\
michael@0 1229 ("(textrun-systemfallback-fonts) char: u+%6.6x "
michael@0 1230 "unicode-range: %d script: %d match: [%s]\n",
michael@0 1231 aMatchData->mCh,
michael@0 1232 unicodeRange, script,
michael@0 1233 NS_ConvertUTF16toUTF8(fe->Name()).get()));
michael@0 1234 }
michael@0 1235 #endif
michael@0 1236 }
michael@0 1237
michael@0 1238 aMatchData->mCmapsTested++;
michael@0 1239 if (rank == 0) {
michael@0 1240 return;
michael@0 1241 }
michael@0 1242
michael@0 1243 // omitting from original windows code -- family name, lang group, pitch
michael@0 1244 // not available in current FontEntry implementation
michael@0 1245 rank += CalcStyleMatch(fe, aMatchData->mStyle);
michael@0 1246
michael@0 1247 // xxx - add whether AAT font with morphing info for specific lang groups
michael@0 1248
michael@0 1249 if (rank > aMatchData->mMatchRank
michael@0 1250 || (rank == aMatchData->mMatchRank &&
michael@0 1251 Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
michael@0 1252 {
michael@0 1253 aMatchData->mBestMatch = fe;
michael@0 1254 aMatchData->mMatchedFamily = this;
michael@0 1255 aMatchData->mMatchRank = rank;
michael@0 1256 }
michael@0 1257 }
michael@0 1258 }
michael@0 1259
michael@0 1260 void
michael@0 1261 gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch *aMatchData)
michael@0 1262 {
michael@0 1263 uint32_t i, numFonts = mAvailableFonts.Length();
michael@0 1264 for (i = 0; i < numFonts; i++) {
michael@0 1265 gfxFontEntry *fe = mAvailableFonts[i];
michael@0 1266 if (fe && fe->TestCharacterMap(aMatchData->mCh)) {
michael@0 1267 int32_t rank = RANK_MATCHED_CMAP;
michael@0 1268 rank += CalcStyleMatch(fe, aMatchData->mStyle);
michael@0 1269 if (rank > aMatchData->mMatchRank
michael@0 1270 || (rank == aMatchData->mMatchRank &&
michael@0 1271 Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
michael@0 1272 {
michael@0 1273 aMatchData->mBestMatch = fe;
michael@0 1274 aMatchData->mMatchedFamily = this;
michael@0 1275 aMatchData->mMatchRank = rank;
michael@0 1276 }
michael@0 1277 }
michael@0 1278 }
michael@0 1279 }
michael@0 1280
michael@0 1281 /*static*/ void
michael@0 1282 gfxFontFamily::ReadOtherFamilyNamesForFace(const nsAString& aFamilyName,
michael@0 1283 const char *aNameData,
michael@0 1284 uint32_t aDataLength,
michael@0 1285 nsTArray<nsString>& aOtherFamilyNames,
michael@0 1286 bool useFullName)
michael@0 1287 {
michael@0 1288 const gfxFontUtils::NameHeader *nameHeader =
michael@0 1289 reinterpret_cast<const gfxFontUtils::NameHeader*>(aNameData);
michael@0 1290
michael@0 1291 uint32_t nameCount = nameHeader->count;
michael@0 1292 if (nameCount * sizeof(gfxFontUtils::NameRecord) > aDataLength) {
michael@0 1293 NS_WARNING("invalid font (name records)");
michael@0 1294 return;
michael@0 1295 }
michael@0 1296
michael@0 1297 const gfxFontUtils::NameRecord *nameRecord =
michael@0 1298 reinterpret_cast<const gfxFontUtils::NameRecord*>(aNameData + sizeof(gfxFontUtils::NameHeader));
michael@0 1299 uint32_t stringsBase = uint32_t(nameHeader->stringOffset);
michael@0 1300
michael@0 1301 for (uint32_t i = 0; i < nameCount; i++, nameRecord++) {
michael@0 1302 uint32_t nameLen = nameRecord->length;
michael@0 1303 uint32_t nameOff = nameRecord->offset; // offset from base of string storage
michael@0 1304
michael@0 1305 if (stringsBase + nameOff + nameLen > aDataLength) {
michael@0 1306 NS_WARNING("invalid font (name table strings)");
michael@0 1307 return;
michael@0 1308 }
michael@0 1309
michael@0 1310 uint16_t nameID = nameRecord->nameID;
michael@0 1311 if ((useFullName && nameID == gfxFontUtils::NAME_ID_FULL) ||
michael@0 1312 (!useFullName && (nameID == gfxFontUtils::NAME_ID_FAMILY ||
michael@0 1313 nameID == gfxFontUtils::NAME_ID_PREFERRED_FAMILY))) {
michael@0 1314 nsAutoString otherFamilyName;
michael@0 1315 bool ok = gfxFontUtils::DecodeFontName(aNameData + stringsBase + nameOff,
michael@0 1316 nameLen,
michael@0 1317 uint32_t(nameRecord->platformID),
michael@0 1318 uint32_t(nameRecord->encodingID),
michael@0 1319 uint32_t(nameRecord->languageID),
michael@0 1320 otherFamilyName);
michael@0 1321 // add if not same as canonical family name
michael@0 1322 if (ok && otherFamilyName != aFamilyName) {
michael@0 1323 aOtherFamilyNames.AppendElement(otherFamilyName);
michael@0 1324 }
michael@0 1325 }
michael@0 1326 }
michael@0 1327 }
michael@0 1328
michael@0 1329 // returns true if other names were found, false otherwise
michael@0 1330 bool
michael@0 1331 gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
michael@0 1332 hb_blob_t *aNameTable,
michael@0 1333 bool useFullName)
michael@0 1334 {
michael@0 1335 uint32_t dataLength;
michael@0 1336 const char *nameData = hb_blob_get_data(aNameTable, &dataLength);
michael@0 1337 nsAutoTArray<nsString,4> otherFamilyNames;
michael@0 1338
michael@0 1339 ReadOtherFamilyNamesForFace(mName, nameData, dataLength,
michael@0 1340 otherFamilyNames, useFullName);
michael@0 1341
michael@0 1342 uint32_t n = otherFamilyNames.Length();
michael@0 1343 for (uint32_t i = 0; i < n; i++) {
michael@0 1344 aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
michael@0 1345 }
michael@0 1346
michael@0 1347 return n != 0;
michael@0 1348 }
michael@0 1349
michael@0 1350 void
michael@0 1351 gfxFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
michael@0 1352 {
michael@0 1353 if (mOtherFamilyNamesInitialized)
michael@0 1354 return;
michael@0 1355 mOtherFamilyNamesInitialized = true;
michael@0 1356
michael@0 1357 FindStyleVariations();
michael@0 1358
michael@0 1359 // read in other family names for the first face in the list
michael@0 1360 uint32_t i, numFonts = mAvailableFonts.Length();
michael@0 1361 const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
michael@0 1362
michael@0 1363 for (i = 0; i < numFonts; ++i) {
michael@0 1364 gfxFontEntry *fe = mAvailableFonts[i];
michael@0 1365 if (!fe) {
michael@0 1366 continue;
michael@0 1367 }
michael@0 1368 gfxFontEntry::AutoTable nameTable(fe, kNAME);
michael@0 1369 if (!nameTable) {
michael@0 1370 continue;
michael@0 1371 }
michael@0 1372 mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
michael@0 1373 nameTable);
michael@0 1374 break;
michael@0 1375 }
michael@0 1376
michael@0 1377 // read in other names for the first face in the list with the assumption
michael@0 1378 // that if extra names don't exist in that face then they don't exist in
michael@0 1379 // other faces for the same font
michael@0 1380 if (!mHasOtherFamilyNames)
michael@0 1381 return;
michael@0 1382
michael@0 1383 // read in names for all faces, needed to catch cases where fonts have
michael@0 1384 // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
michael@0 1385 for ( ; i < numFonts; i++) {
michael@0 1386 gfxFontEntry *fe = mAvailableFonts[i];
michael@0 1387 if (!fe) {
michael@0 1388 continue;
michael@0 1389 }
michael@0 1390 gfxFontEntry::AutoTable nameTable(fe, kNAME);
michael@0 1391 if (!nameTable) {
michael@0 1392 continue;
michael@0 1393 }
michael@0 1394 ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
michael@0 1395 }
michael@0 1396 }
michael@0 1397
michael@0 1398 void
michael@0 1399 gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
michael@0 1400 bool aNeedFullnamePostscriptNames,
michael@0 1401 FontInfoData *aFontInfoData)
michael@0 1402 {
michael@0 1403 // if all needed names have already been read, skip
michael@0 1404 if (mOtherFamilyNamesInitialized &&
michael@0 1405 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames))
michael@0 1406 return;
michael@0 1407
michael@0 1408 bool asyncFontLoaderDisabled = false;
michael@0 1409
michael@0 1410 #if defined(XP_MACOSX)
michael@0 1411 // bug 975460 - async font loader crashes sometimes under 10.6, disable
michael@0 1412 if (!nsCocoaFeatures::OnLionOrLater()) {
michael@0 1413 asyncFontLoaderDisabled = true;
michael@0 1414 }
michael@0 1415 #endif
michael@0 1416
michael@0 1417 if (!mOtherFamilyNamesInitialized &&
michael@0 1418 aFontInfoData &&
michael@0 1419 aFontInfoData->mLoadOtherNames &&
michael@0 1420 !asyncFontLoaderDisabled)
michael@0 1421 {
michael@0 1422 nsAutoTArray<nsString,4> otherFamilyNames;
michael@0 1423 bool foundOtherNames =
michael@0 1424 aFontInfoData->GetOtherFamilyNames(mName, otherFamilyNames);
michael@0 1425 if (foundOtherNames) {
michael@0 1426 uint32_t i, n = otherFamilyNames.Length();
michael@0 1427 for (i = 0; i < n; i++) {
michael@0 1428 aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
michael@0 1429 }
michael@0 1430 }
michael@0 1431 mOtherFamilyNamesInitialized = true;
michael@0 1432 }
michael@0 1433
michael@0 1434 // if all needed data has been initialized, return
michael@0 1435 if (mOtherFamilyNamesInitialized &&
michael@0 1436 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
michael@0 1437 return;
michael@0 1438 }
michael@0 1439
michael@0 1440 FindStyleVariations(aFontInfoData);
michael@0 1441
michael@0 1442 // check again, as style enumeration code may have loaded names
michael@0 1443 if (mOtherFamilyNamesInitialized &&
michael@0 1444 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
michael@0 1445 return;
michael@0 1446 }
michael@0 1447
michael@0 1448 uint32_t i, numFonts = mAvailableFonts.Length();
michael@0 1449 const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
michael@0 1450
michael@0 1451 bool firstTime = true, readAllFaces = false;
michael@0 1452 for (i = 0; i < numFonts; ++i) {
michael@0 1453 gfxFontEntry *fe = mAvailableFonts[i];
michael@0 1454 if (!fe) {
michael@0 1455 continue;
michael@0 1456 }
michael@0 1457
michael@0 1458 nsAutoString fullname, psname;
michael@0 1459 bool foundFaceNames = false;
michael@0 1460 if (!mFaceNamesInitialized &&
michael@0 1461 aNeedFullnamePostscriptNames &&
michael@0 1462 aFontInfoData &&
michael@0 1463 aFontInfoData->mLoadFaceNames) {
michael@0 1464 aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
michael@0 1465 if (!fullname.IsEmpty()) {
michael@0 1466 aPlatformFontList->AddFullname(fe, fullname);
michael@0 1467 }
michael@0 1468 if (!psname.IsEmpty()) {
michael@0 1469 aPlatformFontList->AddPostscriptName(fe, psname);
michael@0 1470 }
michael@0 1471 foundFaceNames = true;
michael@0 1472
michael@0 1473 // found everything needed? skip to next font
michael@0 1474 if (mOtherFamilyNamesInitialized) {
michael@0 1475 continue;
michael@0 1476 }
michael@0 1477 }
michael@0 1478
michael@0 1479 // load directly from the name table
michael@0 1480 gfxFontEntry::AutoTable nameTable(fe, kNAME);
michael@0 1481 if (!nameTable) {
michael@0 1482 continue;
michael@0 1483 }
michael@0 1484
michael@0 1485 if (aNeedFullnamePostscriptNames && !foundFaceNames) {
michael@0 1486 if (gfxFontUtils::ReadCanonicalName(
michael@0 1487 nameTable, gfxFontUtils::NAME_ID_FULL, fullname) == NS_OK)
michael@0 1488 {
michael@0 1489 aPlatformFontList->AddFullname(fe, fullname);
michael@0 1490 }
michael@0 1491
michael@0 1492 if (gfxFontUtils::ReadCanonicalName(
michael@0 1493 nameTable, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK)
michael@0 1494 {
michael@0 1495 aPlatformFontList->AddPostscriptName(fe, psname);
michael@0 1496 }
michael@0 1497 }
michael@0 1498
michael@0 1499 if (!mOtherFamilyNamesInitialized && (firstTime || readAllFaces)) {
michael@0 1500 bool foundOtherName = ReadOtherFamilyNamesForFace(aPlatformFontList,
michael@0 1501 nameTable);
michael@0 1502
michael@0 1503 // if the first face has a different name, scan all faces, otherwise
michael@0 1504 // assume the family doesn't have other names
michael@0 1505 if (firstTime && foundOtherName) {
michael@0 1506 mHasOtherFamilyNames = true;
michael@0 1507 readAllFaces = true;
michael@0 1508 }
michael@0 1509 firstTime = false;
michael@0 1510 }
michael@0 1511
michael@0 1512 // if not reading in any more names, skip other faces
michael@0 1513 if (!readAllFaces && !aNeedFullnamePostscriptNames) {
michael@0 1514 break;
michael@0 1515 }
michael@0 1516 }
michael@0 1517
michael@0 1518 mFaceNamesInitialized = true;
michael@0 1519 mOtherFamilyNamesInitialized = true;
michael@0 1520 }
michael@0 1521
michael@0 1522
michael@0 1523 gfxFontEntry*
michael@0 1524 gfxFontFamily::FindFont(const nsAString& aPostscriptName)
michael@0 1525 {
michael@0 1526 // find the font using a simple linear search
michael@0 1527 uint32_t numFonts = mAvailableFonts.Length();
michael@0 1528 for (uint32_t i = 0; i < numFonts; i++) {
michael@0 1529 gfxFontEntry *fe = mAvailableFonts[i].get();
michael@0 1530 if (fe && fe->Name() == aPostscriptName)
michael@0 1531 return fe;
michael@0 1532 }
michael@0 1533 return nullptr;
michael@0 1534 }
michael@0 1535
michael@0 1536 void
michael@0 1537 gfxFontFamily::ReadAllCMAPs(FontInfoData *aFontInfoData)
michael@0 1538 {
michael@0 1539 FindStyleVariations(aFontInfoData);
michael@0 1540
michael@0 1541 uint32_t i, numFonts = mAvailableFonts.Length();
michael@0 1542 for (i = 0; i < numFonts; i++) {
michael@0 1543 gfxFontEntry *fe = mAvailableFonts[i];
michael@0 1544 // don't try to load cmaps for downloadable fonts not yet loaded
michael@0 1545 if (!fe || fe->mIsProxy) {
michael@0 1546 continue;
michael@0 1547 }
michael@0 1548 fe->ReadCMAP(aFontInfoData);
michael@0 1549 mFamilyCharacterMap.Union(*(fe->mCharacterMap));
michael@0 1550 }
michael@0 1551 mFamilyCharacterMap.Compact();
michael@0 1552 mFamilyCharacterMapInitialized = true;
michael@0 1553 }
michael@0 1554
michael@0 1555 void
michael@0 1556 gfxFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
michael@0 1557 FontListSizes* aSizes) const
michael@0 1558 {
michael@0 1559 aSizes->mFontListSize +=
michael@0 1560 mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
michael@0 1561 aSizes->mCharMapsSize +=
michael@0 1562 mFamilyCharacterMap.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1563
michael@0 1564 aSizes->mFontListSize +=
michael@0 1565 mAvailableFonts.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1566 for (uint32_t i = 0; i < mAvailableFonts.Length(); ++i) {
michael@0 1567 gfxFontEntry *fe = mAvailableFonts[i];
michael@0 1568 if (fe) {
michael@0 1569 fe->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
michael@0 1570 }
michael@0 1571 }
michael@0 1572 }
michael@0 1573
michael@0 1574 void
michael@0 1575 gfxFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
michael@0 1576 FontListSizes* aSizes) const
michael@0 1577 {
michael@0 1578 aSizes->mFontListSize += aMallocSizeOf(this);
michael@0 1579 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 1580 }
michael@0 1581
michael@0 1582 /*
michael@0 1583 * gfxFontCache - global cache of gfxFont instances.
michael@0 1584 * Expires unused fonts after a short interval;
michael@0 1585 * notifies fonts to age their cached shaped-word records;
michael@0 1586 * observes memory-pressure notification and tells fonts to clear their
michael@0 1587 * shaped-word caches to free up memory.
michael@0 1588 */
michael@0 1589
michael@0 1590 MOZ_DEFINE_MALLOC_SIZE_OF(FontCacheMallocSizeOf)
michael@0 1591
michael@0 1592 NS_IMPL_ISUPPORTS(gfxFontCache::MemoryReporter, nsIMemoryReporter)
michael@0 1593
michael@0 1594 NS_IMETHODIMP
michael@0 1595 gfxFontCache::MemoryReporter::CollectReports
michael@0 1596 (nsIMemoryReporterCallback* aCb,
michael@0 1597 nsISupports* aClosure)
michael@0 1598 {
michael@0 1599 FontCacheSizes sizes;
michael@0 1600
michael@0 1601 gfxFontCache::GetCache()->AddSizeOfIncludingThis(&FontCacheMallocSizeOf,
michael@0 1602 &sizes);
michael@0 1603
michael@0 1604 aCb->Callback(EmptyCString(),
michael@0 1605 NS_LITERAL_CSTRING("explicit/gfx/font-cache"),
michael@0 1606 KIND_HEAP, UNITS_BYTES, sizes.mFontInstances,
michael@0 1607 NS_LITERAL_CSTRING("Memory used for active font instances."),
michael@0 1608 aClosure);
michael@0 1609
michael@0 1610 aCb->Callback(EmptyCString(),
michael@0 1611 NS_LITERAL_CSTRING("explicit/gfx/font-shaped-words"),
michael@0 1612 KIND_HEAP, UNITS_BYTES, sizes.mShapedWords,
michael@0 1613 NS_LITERAL_CSTRING("Memory used to cache shaped glyph data."),
michael@0 1614 aClosure);
michael@0 1615
michael@0 1616 return NS_OK;
michael@0 1617 }
michael@0 1618
michael@0 1619 NS_IMPL_ISUPPORTS(gfxFontCache::Observer, nsIObserver)
michael@0 1620
michael@0 1621 NS_IMETHODIMP
michael@0 1622 gfxFontCache::Observer::Observe(nsISupports *aSubject,
michael@0 1623 const char *aTopic,
michael@0 1624 const char16_t *someData)
michael@0 1625 {
michael@0 1626 if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
michael@0 1627 gfxFontCache *fontCache = gfxFontCache::GetCache();
michael@0 1628 if (fontCache) {
michael@0 1629 fontCache->FlushShapedWordCaches();
michael@0 1630 }
michael@0 1631 } else {
michael@0 1632 NS_NOTREACHED("unexpected notification topic");
michael@0 1633 }
michael@0 1634 return NS_OK;
michael@0 1635 }
michael@0 1636
michael@0 1637 nsresult
michael@0 1638 gfxFontCache::Init()
michael@0 1639 {
michael@0 1640 NS_ASSERTION(!gGlobalCache, "Where did this come from?");
michael@0 1641 gGlobalCache = new gfxFontCache();
michael@0 1642 if (!gGlobalCache) {
michael@0 1643 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1644 }
michael@0 1645 RegisterStrongMemoryReporter(new MemoryReporter());
michael@0 1646 return NS_OK;
michael@0 1647 }
michael@0 1648
michael@0 1649 void
michael@0 1650 gfxFontCache::Shutdown()
michael@0 1651 {
michael@0 1652 delete gGlobalCache;
michael@0 1653 gGlobalCache = nullptr;
michael@0 1654
michael@0 1655 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
michael@0 1656 printf("Textrun storage high water mark=%d\n", gTextRunStorageHighWaterMark);
michael@0 1657 printf("Total number of fonts=%d\n", gFontCount);
michael@0 1658 printf("Total glyph extents allocated=%d (size %d)\n", gGlyphExtentsCount,
michael@0 1659 int(gGlyphExtentsCount*sizeof(gfxGlyphExtents)));
michael@0 1660 printf("Total glyph extents width-storage size allocated=%d\n", gGlyphExtentsWidthsTotalSize);
michael@0 1661 printf("Number of simple glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerSimple);
michael@0 1662 printf("Number of tight glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerTight);
michael@0 1663 printf("Number of tight glyph extents lazily requested=%d\n", gGlyphExtentsSetupLazyTight);
michael@0 1664 printf("Number of simple glyph extent setups that fell back to tight=%d\n", gGlyphExtentsSetupFallBackToTight);
michael@0 1665 #endif
michael@0 1666 }
michael@0 1667
michael@0 1668 gfxFontCache::gfxFontCache()
michael@0 1669 : nsExpirationTracker<gfxFont,3>(FONT_TIMEOUT_SECONDS * 1000)
michael@0 1670 {
michael@0 1671 nsCOMPtr<nsIObserverService> obs = GetObserverService();
michael@0 1672 if (obs) {
michael@0 1673 obs->AddObserver(new Observer, "memory-pressure", false);
michael@0 1674 }
michael@0 1675
michael@0 1676 #ifndef RELEASE_BUILD
michael@0 1677 // Currently disabled for release builds, due to unexplained crashes
michael@0 1678 // during expiration; see bug 717175 & 894798.
michael@0 1679 mWordCacheExpirationTimer = do_CreateInstance("@mozilla.org/timer;1");
michael@0 1680 if (mWordCacheExpirationTimer) {
michael@0 1681 mWordCacheExpirationTimer->
michael@0 1682 InitWithFuncCallback(WordCacheExpirationTimerCallback, this,
michael@0 1683 SHAPED_WORD_TIMEOUT_SECONDS * 1000,
michael@0 1684 nsITimer::TYPE_REPEATING_SLACK);
michael@0 1685 }
michael@0 1686 #endif
michael@0 1687 }
michael@0 1688
michael@0 1689 gfxFontCache::~gfxFontCache()
michael@0 1690 {
michael@0 1691 // Ensure the user font cache releases its references to font entries,
michael@0 1692 // so they aren't kept alive after the font instances and font-list
michael@0 1693 // have been shut down.
michael@0 1694 gfxUserFontSet::UserFontCache::Shutdown();
michael@0 1695
michael@0 1696 if (mWordCacheExpirationTimer) {
michael@0 1697 mWordCacheExpirationTimer->Cancel();
michael@0 1698 mWordCacheExpirationTimer = nullptr;
michael@0 1699 }
michael@0 1700
michael@0 1701 // Expire everything that has a zero refcount, so we don't leak them.
michael@0 1702 AgeAllGenerations();
michael@0 1703 // All fonts should be gone.
michael@0 1704 NS_WARN_IF_FALSE(mFonts.Count() == 0,
michael@0 1705 "Fonts still alive while shutting down gfxFontCache");
michael@0 1706 // Note that we have to delete everything through the expiration
michael@0 1707 // tracker, since there might be fonts not in the hashtable but in
michael@0 1708 // the tracker.
michael@0 1709 }
michael@0 1710
michael@0 1711 bool
michael@0 1712 gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const
michael@0 1713 {
michael@0 1714 return aKey->mFontEntry == mFont->GetFontEntry() &&
michael@0 1715 aKey->mStyle->Equals(*mFont->GetStyle());
michael@0 1716 }
michael@0 1717
michael@0 1718 already_AddRefed<gfxFont>
michael@0 1719 gfxFontCache::Lookup(const gfxFontEntry *aFontEntry,
michael@0 1720 const gfxFontStyle *aStyle)
michael@0 1721 {
michael@0 1722 Key key(aFontEntry, aStyle);
michael@0 1723 HashEntry *entry = mFonts.GetEntry(key);
michael@0 1724
michael@0 1725 Telemetry::Accumulate(Telemetry::FONT_CACHE_HIT, entry != nullptr);
michael@0 1726 if (!entry)
michael@0 1727 return nullptr;
michael@0 1728
michael@0 1729 nsRefPtr<gfxFont> font = entry->mFont;
michael@0 1730 return font.forget();
michael@0 1731 }
michael@0 1732
michael@0 1733 void
michael@0 1734 gfxFontCache::AddNew(gfxFont *aFont)
michael@0 1735 {
michael@0 1736 Key key(aFont->GetFontEntry(), aFont->GetStyle());
michael@0 1737 HashEntry *entry = mFonts.PutEntry(key);
michael@0 1738 if (!entry)
michael@0 1739 return;
michael@0 1740 gfxFont *oldFont = entry->mFont;
michael@0 1741 entry->mFont = aFont;
michael@0 1742 // Assert that we can find the entry we just put in (this fails if the key
michael@0 1743 // has a NaN float value in it, e.g. 'sizeAdjust').
michael@0 1744 MOZ_ASSERT(entry == mFonts.GetEntry(key));
michael@0 1745 // If someone's asked us to replace an existing font entry, then that's a
michael@0 1746 // bit weird, but let it happen, and expire the old font if it's not used.
michael@0 1747 if (oldFont && oldFont->GetExpirationState()->IsTracked()) {
michael@0 1748 // if oldFont == aFont, recount should be > 0,
michael@0 1749 // so we shouldn't be here.
michael@0 1750 NS_ASSERTION(aFont != oldFont, "new font is tracked for expiry!");
michael@0 1751 NotifyExpired(oldFont);
michael@0 1752 }
michael@0 1753 }
michael@0 1754
michael@0 1755 void
michael@0 1756 gfxFontCache::NotifyReleased(gfxFont *aFont)
michael@0 1757 {
michael@0 1758 nsresult rv = AddObject(aFont);
michael@0 1759 if (NS_FAILED(rv)) {
michael@0 1760 // We couldn't track it for some reason. Kill it now.
michael@0 1761 DestroyFont(aFont);
michael@0 1762 }
michael@0 1763 // Note that we might have fonts that aren't in the hashtable, perhaps because
michael@0 1764 // of OOM adding to the hashtable or because someone did an AddNew where
michael@0 1765 // we already had a font. These fonts are added to the expiration tracker
michael@0 1766 // anyway, even though Lookup can't resurrect them. Eventually they will
michael@0 1767 // expire and be deleted.
michael@0 1768 }
michael@0 1769
michael@0 1770 void
michael@0 1771 gfxFontCache::NotifyExpired(gfxFont *aFont)
michael@0 1772 {
michael@0 1773 aFont->ClearCachedWords();
michael@0 1774 RemoveObject(aFont);
michael@0 1775 DestroyFont(aFont);
michael@0 1776 }
michael@0 1777
michael@0 1778 void
michael@0 1779 gfxFontCache::DestroyFont(gfxFont *aFont)
michael@0 1780 {
michael@0 1781 Key key(aFont->GetFontEntry(), aFont->GetStyle());
michael@0 1782 HashEntry *entry = mFonts.GetEntry(key);
michael@0 1783 if (entry && entry->mFont == aFont) {
michael@0 1784 mFonts.RemoveEntry(key);
michael@0 1785 }
michael@0 1786 NS_ASSERTION(aFont->GetRefCount() == 0,
michael@0 1787 "Destroying with non-zero ref count!");
michael@0 1788 delete aFont;
michael@0 1789 }
michael@0 1790
michael@0 1791 /*static*/
michael@0 1792 PLDHashOperator
michael@0 1793 gfxFontCache::AgeCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
michael@0 1794 {
michael@0 1795 aHashEntry->mFont->AgeCachedWords();
michael@0 1796 return PL_DHASH_NEXT;
michael@0 1797 }
michael@0 1798
michael@0 1799 /*static*/
michael@0 1800 void
michael@0 1801 gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache)
michael@0 1802 {
michael@0 1803 gfxFontCache* cache = static_cast<gfxFontCache*>(aCache);
michael@0 1804 cache->mFonts.EnumerateEntries(AgeCachedWordsForFont, nullptr);
michael@0 1805 }
michael@0 1806
michael@0 1807 /*static*/
michael@0 1808 PLDHashOperator
michael@0 1809 gfxFontCache::ClearCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
michael@0 1810 {
michael@0 1811 aHashEntry->mFont->ClearCachedWords();
michael@0 1812 return PL_DHASH_NEXT;
michael@0 1813 }
michael@0 1814
michael@0 1815 /*static*/
michael@0 1816 size_t
michael@0 1817 gfxFontCache::AddSizeOfFontEntryExcludingThis(HashEntry* aHashEntry,
michael@0 1818 MallocSizeOf aMallocSizeOf,
michael@0 1819 void* aUserArg)
michael@0 1820 {
michael@0 1821 HashEntry *entry = static_cast<HashEntry*>(aHashEntry);
michael@0 1822 FontCacheSizes *sizes = static_cast<FontCacheSizes*>(aUserArg);
michael@0 1823 entry->mFont->AddSizeOfExcludingThis(aMallocSizeOf, sizes);
michael@0 1824
michael@0 1825 // The entry's size is recorded in the |sizes| parameter, so we return zero
michael@0 1826 // here to the hashtable enumerator.
michael@0 1827 return 0;
michael@0 1828 }
michael@0 1829
michael@0 1830 void
michael@0 1831 gfxFontCache::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
michael@0 1832 FontCacheSizes* aSizes) const
michael@0 1833 {
michael@0 1834 // TODO: add the overhead of the expiration tracker (generation arrays)
michael@0 1835
michael@0 1836 aSizes->mFontInstances +=
michael@0 1837 mFonts.SizeOfExcludingThis(AddSizeOfFontEntryExcludingThis,
michael@0 1838 aMallocSizeOf, aSizes);
michael@0 1839 }
michael@0 1840
michael@0 1841 void
michael@0 1842 gfxFontCache::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
michael@0 1843 FontCacheSizes* aSizes) const
michael@0 1844 {
michael@0 1845 aSizes->mFontInstances += aMallocSizeOf(this);
michael@0 1846 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 1847 }
michael@0 1848
michael@0 1849 #define MAX_SSXX_VALUE 99
michael@0 1850 #define MAX_CVXX_VALUE 99
michael@0 1851
michael@0 1852 static void
michael@0 1853 LookupAlternateValues(gfxFontFeatureValueSet *featureLookup,
michael@0 1854 const nsAString& aFamily,
michael@0 1855 const nsTArray<gfxAlternateValue>& altValue,
michael@0 1856 nsTArray<gfxFontFeature>& aFontFeatures)
michael@0 1857 {
michael@0 1858 uint32_t numAlternates = altValue.Length();
michael@0 1859 for (uint32_t i = 0; i < numAlternates; i++) {
michael@0 1860 const gfxAlternateValue& av = altValue.ElementAt(i);
michael@0 1861 nsAutoTArray<uint32_t,4> values;
michael@0 1862
michael@0 1863 // map <family, name, feature> ==> <values>
michael@0 1864 bool found =
michael@0 1865 featureLookup->GetFontFeatureValuesFor(aFamily, av.alternate,
michael@0 1866 av.value, values);
michael@0 1867 uint32_t numValues = values.Length();
michael@0 1868
michael@0 1869 // nothing defined, skip
michael@0 1870 if (!found || numValues == 0) {
michael@0 1871 continue;
michael@0 1872 }
michael@0 1873
michael@0 1874 gfxFontFeature feature;
michael@0 1875 if (av.alternate == NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT) {
michael@0 1876 NS_ASSERTION(numValues <= 2,
michael@0 1877 "too many values allowed for character-variant");
michael@0 1878 // character-variant(12 3) ==> 'cv12' = 3
michael@0 1879 uint32_t nn = values.ElementAt(0);
michael@0 1880 // ignore values greater than 99
michael@0 1881 if (nn == 0 || nn > MAX_CVXX_VALUE) {
michael@0 1882 continue;
michael@0 1883 }
michael@0 1884 feature.mValue = 1;
michael@0 1885 if (numValues > 1) {
michael@0 1886 feature.mValue = values.ElementAt(1);
michael@0 1887 }
michael@0 1888 feature.mTag = HB_TAG('c','v',('0' + nn / 10), ('0' + nn % 10));
michael@0 1889 aFontFeatures.AppendElement(feature);
michael@0 1890
michael@0 1891 } else if (av.alternate == NS_FONT_VARIANT_ALTERNATES_STYLESET) {
michael@0 1892 // styleset(1 2 7) ==> 'ss01' = 1, 'ss02' = 1, 'ss07' = 1
michael@0 1893 feature.mValue = 1;
michael@0 1894 for (uint32_t v = 0; v < numValues; v++) {
michael@0 1895 uint32_t nn = values.ElementAt(v);
michael@0 1896 if (nn == 0 || nn > MAX_SSXX_VALUE) {
michael@0 1897 continue;
michael@0 1898 }
michael@0 1899 feature.mTag = HB_TAG('s','s',('0' + nn / 10), ('0' + nn % 10));
michael@0 1900 aFontFeatures.AppendElement(feature);
michael@0 1901 }
michael@0 1902
michael@0 1903 } else {
michael@0 1904 NS_ASSERTION(numValues == 1,
michael@0 1905 "too many values for font-specific font-variant-alternates");
michael@0 1906 feature.mValue = values.ElementAt(0);
michael@0 1907
michael@0 1908 switch (av.alternate) {
michael@0 1909 case NS_FONT_VARIANT_ALTERNATES_STYLISTIC: // salt
michael@0 1910 feature.mTag = HB_TAG('s','a','l','t');
michael@0 1911 break;
michael@0 1912 case NS_FONT_VARIANT_ALTERNATES_SWASH: // swsh, cswh
michael@0 1913 feature.mTag = HB_TAG('s','w','s','h');
michael@0 1914 aFontFeatures.AppendElement(feature);
michael@0 1915 feature.mTag = HB_TAG('c','s','w','h');
michael@0 1916 break;
michael@0 1917 case NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: // ornm
michael@0 1918 feature.mTag = HB_TAG('o','r','n','m');
michael@0 1919 break;
michael@0 1920 case NS_FONT_VARIANT_ALTERNATES_ANNOTATION: // nalt
michael@0 1921 feature.mTag = HB_TAG('n','a','l','t');
michael@0 1922 break;
michael@0 1923 default:
michael@0 1924 feature.mTag = 0;
michael@0 1925 break;
michael@0 1926 }
michael@0 1927
michael@0 1928 NS_ASSERTION(feature.mTag, "unsupported alternate type");
michael@0 1929 if (!feature.mTag) {
michael@0 1930 continue;
michael@0 1931 }
michael@0 1932 aFontFeatures.AppendElement(feature);
michael@0 1933 }
michael@0 1934 }
michael@0 1935 }
michael@0 1936
michael@0 1937 /* static */ bool
michael@0 1938 gfxFontShaper::MergeFontFeatures(
michael@0 1939 const gfxFontStyle *aStyle,
michael@0 1940 const nsTArray<gfxFontFeature>& aFontFeatures,
michael@0 1941 bool aDisableLigatures,
michael@0 1942 const nsAString& aFamilyName,
michael@0 1943 nsDataHashtable<nsUint32HashKey,uint32_t>& aMergedFeatures)
michael@0 1944 {
michael@0 1945 uint32_t numAlts = aStyle->alternateValues.Length();
michael@0 1946 const nsTArray<gfxFontFeature>& styleRuleFeatures =
michael@0 1947 aStyle->featureSettings;
michael@0 1948
michael@0 1949 // bail immediately if nothing to do
michael@0 1950 if (styleRuleFeatures.IsEmpty() &&
michael@0 1951 aFontFeatures.IsEmpty() &&
michael@0 1952 !aDisableLigatures &&
michael@0 1953 numAlts == 0) {
michael@0 1954 return false;
michael@0 1955 }
michael@0 1956
michael@0 1957 // Ligature features are enabled by default in the generic shaper,
michael@0 1958 // so we explicitly turn them off if necessary (for letter-spacing)
michael@0 1959 if (aDisableLigatures) {
michael@0 1960 aMergedFeatures.Put(HB_TAG('l','i','g','a'), 0);
michael@0 1961 aMergedFeatures.Put(HB_TAG('c','l','i','g'), 0);
michael@0 1962 }
michael@0 1963
michael@0 1964 // add feature values from font
michael@0 1965 uint32_t i, count;
michael@0 1966
michael@0 1967 count = aFontFeatures.Length();
michael@0 1968 for (i = 0; i < count; i++) {
michael@0 1969 const gfxFontFeature& feature = aFontFeatures.ElementAt(i);
michael@0 1970 aMergedFeatures.Put(feature.mTag, feature.mValue);
michael@0 1971 }
michael@0 1972
michael@0 1973 // add font-specific feature values from style rules
michael@0 1974 if (aStyle->featureValueLookup && numAlts > 0) {
michael@0 1975 nsAutoTArray<gfxFontFeature,4> featureList;
michael@0 1976
michael@0 1977 // insert list of alternate feature settings
michael@0 1978 LookupAlternateValues(aStyle->featureValueLookup, aFamilyName,
michael@0 1979 aStyle->alternateValues, featureList);
michael@0 1980
michael@0 1981 count = featureList.Length();
michael@0 1982 for (i = 0; i < count; i++) {
michael@0 1983 const gfxFontFeature& feature = featureList.ElementAt(i);
michael@0 1984 aMergedFeatures.Put(feature.mTag, feature.mValue);
michael@0 1985 }
michael@0 1986 }
michael@0 1987
michael@0 1988 // add feature values from style rules
michael@0 1989 count = styleRuleFeatures.Length();
michael@0 1990 for (i = 0; i < count; i++) {
michael@0 1991 const gfxFontFeature& feature = styleRuleFeatures.ElementAt(i);
michael@0 1992 aMergedFeatures.Put(feature.mTag, feature.mValue);
michael@0 1993 }
michael@0 1994
michael@0 1995 return aMergedFeatures.Count() != 0;
michael@0 1996 }
michael@0 1997
michael@0 1998 void
michael@0 1999 gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
michael@0 2000 {
michael@0 2001 mAscent = std::max(mAscent, aOther.mAscent);
michael@0 2002 mDescent = std::max(mDescent, aOther.mDescent);
michael@0 2003 if (aOtherIsOnLeft) {
michael@0 2004 mBoundingBox =
michael@0 2005 (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox);
michael@0 2006 } else {
michael@0 2007 mBoundingBox =
michael@0 2008 mBoundingBox.Union(aOther.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
michael@0 2009 }
michael@0 2010 mAdvanceWidth += aOther.mAdvanceWidth;
michael@0 2011 }
michael@0 2012
michael@0 2013 gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
michael@0 2014 AntialiasOption anAAOption, cairo_scaled_font_t *aScaledFont) :
michael@0 2015 mScaledFont(aScaledFont),
michael@0 2016 mFontEntry(aFontEntry), mIsValid(true),
michael@0 2017 mApplySyntheticBold(false),
michael@0 2018 mStyle(*aFontStyle),
michael@0 2019 mAdjustedSize(0.0),
michael@0 2020 mFUnitsConvFactor(0.0f),
michael@0 2021 mAntialiasOption(anAAOption)
michael@0 2022 {
michael@0 2023 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
michael@0 2024 ++gFontCount;
michael@0 2025 #endif
michael@0 2026 mKerningSet = HasFeatureSet(HB_TAG('k','e','r','n'), mKerningEnabled);
michael@0 2027 }
michael@0 2028
michael@0 2029 static PLDHashOperator
michael@0 2030 NotifyFontDestroyed(nsPtrHashKey<gfxFont::GlyphChangeObserver>* aKey,
michael@0 2031 void* aClosure)
michael@0 2032 {
michael@0 2033 aKey->GetKey()->ForgetFont();
michael@0 2034 return PL_DHASH_NEXT;
michael@0 2035 }
michael@0 2036
michael@0 2037 gfxFont::~gfxFont()
michael@0 2038 {
michael@0 2039 uint32_t i, count = mGlyphExtentsArray.Length();
michael@0 2040 // We destroy the contents of mGlyphExtentsArray explicitly instead of
michael@0 2041 // using nsAutoPtr because VC++ can't deal with nsTArrays of nsAutoPtrs
michael@0 2042 // of classes that lack a proper copy constructor
michael@0 2043 for (i = 0; i < count; ++i) {
michael@0 2044 delete mGlyphExtentsArray[i];
michael@0 2045 }
michael@0 2046
michael@0 2047 mFontEntry->NotifyFontDestroyed(this);
michael@0 2048
michael@0 2049 if (mGlyphChangeObservers) {
michael@0 2050 mGlyphChangeObservers->EnumerateEntries(NotifyFontDestroyed, nullptr);
michael@0 2051 }
michael@0 2052 }
michael@0 2053
michael@0 2054 gfxFloat
michael@0 2055 gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID)
michael@0 2056 {
michael@0 2057 if (!SetupCairoFont(aCtx)) {
michael@0 2058 return 0;
michael@0 2059 }
michael@0 2060 if (ProvidesGlyphWidths()) {
michael@0 2061 return GetGlyphWidth(aCtx, aGID) / 65536.0;
michael@0 2062 }
michael@0 2063 if (mFUnitsConvFactor == 0.0f) {
michael@0 2064 GetMetrics();
michael@0 2065 }
michael@0 2066 NS_ASSERTION(mFUnitsConvFactor > 0.0f,
michael@0 2067 "missing font unit conversion factor");
michael@0 2068 if (!mHarfBuzzShaper) {
michael@0 2069 mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
michael@0 2070 }
michael@0 2071 gfxHarfBuzzShaper* shaper =
michael@0 2072 static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
michael@0 2073 if (!shaper->Initialize()) {
michael@0 2074 return 0;
michael@0 2075 }
michael@0 2076 return shaper->GetGlyphHAdvance(aCtx, aGID) / 65536.0;
michael@0 2077 }
michael@0 2078
michael@0 2079 /*static*/
michael@0 2080 PLDHashOperator
michael@0 2081 gfxFont::AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData)
michael@0 2082 {
michael@0 2083 if (!aEntry->mShapedWord) {
michael@0 2084 NS_ASSERTION(aEntry->mShapedWord, "cache entry has no gfxShapedWord!");
michael@0 2085 return PL_DHASH_REMOVE;
michael@0 2086 }
michael@0 2087 if (aEntry->mShapedWord->IncrementAge() == kShapedWordCacheMaxAge) {
michael@0 2088 return PL_DHASH_REMOVE;
michael@0 2089 }
michael@0 2090 return PL_DHASH_NEXT;
michael@0 2091 }
michael@0 2092
michael@0 2093 static void
michael@0 2094 CollectLookupsByFeature(hb_face_t *aFace, hb_tag_t aTableTag,
michael@0 2095 uint32_t aFeatureIndex, hb_set_t *aLookups)
michael@0 2096 {
michael@0 2097 uint32_t lookups[32];
michael@0 2098 uint32_t i, len, offset;
michael@0 2099
michael@0 2100 offset = 0;
michael@0 2101 do {
michael@0 2102 len = ArrayLength(lookups);
michael@0 2103 hb_ot_layout_feature_get_lookups(aFace, aTableTag, aFeatureIndex,
michael@0 2104 offset, &len, lookups);
michael@0 2105 for (i = 0; i < len; i++) {
michael@0 2106 hb_set_add(aLookups, lookups[i]);
michael@0 2107 }
michael@0 2108 offset += len;
michael@0 2109 } while (len == ArrayLength(lookups));
michael@0 2110 }
michael@0 2111
michael@0 2112 static void
michael@0 2113 CollectLookupsByLanguage(hb_face_t *aFace, hb_tag_t aTableTag,
michael@0 2114 const nsTHashtable<nsUint32HashKey>&
michael@0 2115 aSpecificFeatures,
michael@0 2116 hb_set_t *aOtherLookups,
michael@0 2117 hb_set_t *aSpecificFeatureLookups,
michael@0 2118 uint32_t aScriptIndex, uint32_t aLangIndex)
michael@0 2119 {
michael@0 2120 uint32_t reqFeatureIndex;
michael@0 2121 if (hb_ot_layout_language_get_required_feature_index(aFace, aTableTag,
michael@0 2122 aScriptIndex,
michael@0 2123 aLangIndex,
michael@0 2124 &reqFeatureIndex)) {
michael@0 2125 CollectLookupsByFeature(aFace, aTableTag, reqFeatureIndex,
michael@0 2126 aOtherLookups);
michael@0 2127 }
michael@0 2128
michael@0 2129 uint32_t featureIndexes[32];
michael@0 2130 uint32_t i, len, offset;
michael@0 2131
michael@0 2132 offset = 0;
michael@0 2133 do {
michael@0 2134 len = ArrayLength(featureIndexes);
michael@0 2135 hb_ot_layout_language_get_feature_indexes(aFace, aTableTag,
michael@0 2136 aScriptIndex, aLangIndex,
michael@0 2137 offset, &len, featureIndexes);
michael@0 2138
michael@0 2139 for (i = 0; i < len; i++) {
michael@0 2140 uint32_t featureIndex = featureIndexes[i];
michael@0 2141
michael@0 2142 // get the feature tag
michael@0 2143 hb_tag_t featureTag;
michael@0 2144 uint32_t tagLen = 1;
michael@0 2145 hb_ot_layout_language_get_feature_tags(aFace, aTableTag,
michael@0 2146 aScriptIndex, aLangIndex,
michael@0 2147 offset + i, &tagLen,
michael@0 2148 &featureTag);
michael@0 2149
michael@0 2150 // collect lookups
michael@0 2151 hb_set_t *lookups = aSpecificFeatures.GetEntry(featureTag) ?
michael@0 2152 aSpecificFeatureLookups : aOtherLookups;
michael@0 2153 CollectLookupsByFeature(aFace, aTableTag, featureIndex, lookups);
michael@0 2154 }
michael@0 2155 offset += len;
michael@0 2156 } while (len == ArrayLength(featureIndexes));
michael@0 2157 }
michael@0 2158
michael@0 2159 static bool
michael@0 2160 HasLookupRuleWithGlyphByScript(hb_face_t *aFace, hb_tag_t aTableTag,
michael@0 2161 hb_tag_t aScriptTag, uint32_t aScriptIndex,
michael@0 2162 uint16_t aGlyph,
michael@0 2163 const nsTHashtable<nsUint32HashKey>&
michael@0 2164 aDefaultFeatures,
michael@0 2165 bool& aHasDefaultFeatureWithGlyph)
michael@0 2166 {
michael@0 2167 uint32_t numLangs, lang;
michael@0 2168 hb_set_t *defaultFeatureLookups = hb_set_create();
michael@0 2169 hb_set_t *nonDefaultFeatureLookups = hb_set_create();
michael@0 2170
michael@0 2171 // default lang
michael@0 2172 CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
michael@0 2173 nonDefaultFeatureLookups, defaultFeatureLookups,
michael@0 2174 aScriptIndex,
michael@0 2175 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
michael@0 2176
michael@0 2177 // iterate over langs
michael@0 2178 numLangs = hb_ot_layout_script_get_language_tags(aFace, aTableTag,
michael@0 2179 aScriptIndex, 0,
michael@0 2180 nullptr, nullptr);
michael@0 2181 for (lang = 0; lang < numLangs; lang++) {
michael@0 2182 CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
michael@0 2183 nonDefaultFeatureLookups,
michael@0 2184 defaultFeatureLookups,
michael@0 2185 aScriptIndex, lang);
michael@0 2186 }
michael@0 2187
michael@0 2188 // look for the glyph among default feature lookups
michael@0 2189 aHasDefaultFeatureWithGlyph = false;
michael@0 2190 hb_set_t *glyphs = hb_set_create();
michael@0 2191 hb_codepoint_t index = -1;
michael@0 2192 while (hb_set_next(defaultFeatureLookups, &index)) {
michael@0 2193 hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
michael@0 2194 glyphs, glyphs, glyphs,
michael@0 2195 glyphs);
michael@0 2196 if (hb_set_has(glyphs, aGlyph)) {
michael@0 2197 aHasDefaultFeatureWithGlyph = true;
michael@0 2198 break;
michael@0 2199 }
michael@0 2200 }
michael@0 2201
michael@0 2202 // look for the glyph among non-default feature lookups
michael@0 2203 // if no default feature lookups contained spaces
michael@0 2204 bool hasNonDefaultFeatureWithGlyph = false;
michael@0 2205 if (!aHasDefaultFeatureWithGlyph) {
michael@0 2206 hb_set_clear(glyphs);
michael@0 2207 index = -1;
michael@0 2208 while (hb_set_next(nonDefaultFeatureLookups, &index)) {
michael@0 2209 hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
michael@0 2210 glyphs, glyphs, glyphs,
michael@0 2211 glyphs);
michael@0 2212 if (hb_set_has(glyphs, aGlyph)) {
michael@0 2213 hasNonDefaultFeatureWithGlyph = true;
michael@0 2214 break;
michael@0 2215 }
michael@0 2216 }
michael@0 2217 }
michael@0 2218
michael@0 2219 hb_set_destroy(glyphs);
michael@0 2220 hb_set_destroy(defaultFeatureLookups);
michael@0 2221 hb_set_destroy(nonDefaultFeatureLookups);
michael@0 2222
michael@0 2223 return aHasDefaultFeatureWithGlyph || hasNonDefaultFeatureWithGlyph;
michael@0 2224 }
michael@0 2225
michael@0 2226 static void
michael@0 2227 HasLookupRuleWithGlyph(hb_face_t *aFace, hb_tag_t aTableTag, bool& aHasGlyph,
michael@0 2228 hb_tag_t aSpecificFeature, bool& aHasGlyphSpecific,
michael@0 2229 uint16_t aGlyph)
michael@0 2230 {
michael@0 2231 // iterate over the scripts in the font
michael@0 2232 uint32_t numScripts, numLangs, script, lang;
michael@0 2233 hb_set_t *otherLookups = hb_set_create();
michael@0 2234 hb_set_t *specificFeatureLookups = hb_set_create();
michael@0 2235 nsTHashtable<nsUint32HashKey> specificFeature;
michael@0 2236
michael@0 2237 specificFeature.PutEntry(aSpecificFeature);
michael@0 2238
michael@0 2239 numScripts = hb_ot_layout_table_get_script_tags(aFace, aTableTag, 0,
michael@0 2240 nullptr, nullptr);
michael@0 2241
michael@0 2242 for (script = 0; script < numScripts; script++) {
michael@0 2243 // default lang
michael@0 2244 CollectLookupsByLanguage(aFace, aTableTag, specificFeature,
michael@0 2245 otherLookups, specificFeatureLookups,
michael@0 2246 script, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
michael@0 2247
michael@0 2248 // iterate over langs
michael@0 2249 numLangs = hb_ot_layout_script_get_language_tags(aFace, HB_OT_TAG_GPOS,
michael@0 2250 script, 0,
michael@0 2251 nullptr, nullptr);
michael@0 2252 for (lang = 0; lang < numLangs; lang++) {
michael@0 2253 CollectLookupsByLanguage(aFace, aTableTag, specificFeature,
michael@0 2254 otherLookups, specificFeatureLookups,
michael@0 2255 script, lang);
michael@0 2256 }
michael@0 2257 }
michael@0 2258
michael@0 2259 // look for the glyph among non-specific feature lookups
michael@0 2260 hb_set_t *glyphs = hb_set_create();
michael@0 2261 hb_codepoint_t index = -1;
michael@0 2262 while (hb_set_next(otherLookups, &index)) {
michael@0 2263 hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
michael@0 2264 glyphs, glyphs, glyphs,
michael@0 2265 glyphs);
michael@0 2266 if (hb_set_has(glyphs, aGlyph)) {
michael@0 2267 aHasGlyph = true;
michael@0 2268 break;
michael@0 2269 }
michael@0 2270 }
michael@0 2271
michael@0 2272 // look for the glyph among specific feature lookups
michael@0 2273 hb_set_clear(glyphs);
michael@0 2274 index = -1;
michael@0 2275 while (hb_set_next(specificFeatureLookups, &index)) {
michael@0 2276 hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
michael@0 2277 glyphs, glyphs, glyphs,
michael@0 2278 glyphs);
michael@0 2279 if (hb_set_has(glyphs, aGlyph)) {
michael@0 2280 aHasGlyphSpecific = true;
michael@0 2281 break;
michael@0 2282 }
michael@0 2283 }
michael@0 2284
michael@0 2285 hb_set_destroy(glyphs);
michael@0 2286 hb_set_destroy(specificFeatureLookups);
michael@0 2287 hb_set_destroy(otherLookups);
michael@0 2288 }
michael@0 2289
michael@0 2290 nsDataHashtable<nsUint32HashKey, int32_t> *gfxFont::sScriptTagToCode = nullptr;
michael@0 2291 nsTHashtable<nsUint32HashKey> *gfxFont::sDefaultFeatures = nullptr;
michael@0 2292
michael@0 2293 static inline bool
michael@0 2294 HasSubstitution(uint32_t *aBitVector, uint32_t aBit) {
michael@0 2295 return (aBitVector[aBit >> 5] & (1 << (aBit & 0x1f))) != 0;
michael@0 2296 }
michael@0 2297
michael@0 2298 // union of all default substitution features across scripts
michael@0 2299 static const hb_tag_t defaultFeatures[] = {
michael@0 2300 HB_TAG('a','b','v','f'),
michael@0 2301 HB_TAG('a','b','v','s'),
michael@0 2302 HB_TAG('a','k','h','n'),
michael@0 2303 HB_TAG('b','l','w','f'),
michael@0 2304 HB_TAG('b','l','w','s'),
michael@0 2305 HB_TAG('c','a','l','t'),
michael@0 2306 HB_TAG('c','c','m','p'),
michael@0 2307 HB_TAG('c','f','a','r'),
michael@0 2308 HB_TAG('c','j','c','t'),
michael@0 2309 HB_TAG('c','l','i','g'),
michael@0 2310 HB_TAG('f','i','n','2'),
michael@0 2311 HB_TAG('f','i','n','3'),
michael@0 2312 HB_TAG('f','i','n','a'),
michael@0 2313 HB_TAG('h','a','l','f'),
michael@0 2314 HB_TAG('h','a','l','n'),
michael@0 2315 HB_TAG('i','n','i','t'),
michael@0 2316 HB_TAG('i','s','o','l'),
michael@0 2317 HB_TAG('l','i','g','a'),
michael@0 2318 HB_TAG('l','j','m','o'),
michael@0 2319 HB_TAG('l','o','c','l'),
michael@0 2320 HB_TAG('l','t','r','a'),
michael@0 2321 HB_TAG('l','t','r','m'),
michael@0 2322 HB_TAG('m','e','d','2'),
michael@0 2323 HB_TAG('m','e','d','i'),
michael@0 2324 HB_TAG('m','s','e','t'),
michael@0 2325 HB_TAG('n','u','k','t'),
michael@0 2326 HB_TAG('p','r','e','f'),
michael@0 2327 HB_TAG('p','r','e','s'),
michael@0 2328 HB_TAG('p','s','t','f'),
michael@0 2329 HB_TAG('p','s','t','s'),
michael@0 2330 HB_TAG('r','c','l','t'),
michael@0 2331 HB_TAG('r','l','i','g'),
michael@0 2332 HB_TAG('r','k','r','f'),
michael@0 2333 HB_TAG('r','p','h','f'),
michael@0 2334 HB_TAG('r','t','l','a'),
michael@0 2335 HB_TAG('r','t','l','m'),
michael@0 2336 HB_TAG('t','j','m','o'),
michael@0 2337 HB_TAG('v','a','t','u'),
michael@0 2338 HB_TAG('v','e','r','t'),
michael@0 2339 HB_TAG('v','j','m','o')
michael@0 2340 };
michael@0 2341
michael@0 2342 void
michael@0 2343 gfxFont::CheckForFeaturesInvolvingSpace()
michael@0 2344 {
michael@0 2345 mFontEntry->mHasSpaceFeaturesInitialized = true;
michael@0 2346
michael@0 2347 #ifdef PR_LOGGING
michael@0 2348 bool log = LOG_FONTINIT_ENABLED();
michael@0 2349 TimeStamp start;
michael@0 2350 if (MOZ_UNLIKELY(log)) {
michael@0 2351 start = TimeStamp::Now();
michael@0 2352 }
michael@0 2353 #endif
michael@0 2354
michael@0 2355 bool result = false;
michael@0 2356
michael@0 2357 uint32_t spaceGlyph = GetSpaceGlyph();
michael@0 2358 if (!spaceGlyph) {
michael@0 2359 return;
michael@0 2360 }
michael@0 2361
michael@0 2362 hb_face_t *face = GetFontEntry()->GetHBFace();
michael@0 2363
michael@0 2364 // GSUB lookups - examine per script
michael@0 2365 if (hb_ot_layout_has_substitution(face)) {
michael@0 2366
michael@0 2367 // set up the script ==> code hashtable if needed
michael@0 2368 if (!sScriptTagToCode) {
michael@0 2369 sScriptTagToCode =
michael@0 2370 new nsDataHashtable<nsUint32HashKey,
michael@0 2371 int32_t>(MOZ_NUM_SCRIPT_CODES);
michael@0 2372 sScriptTagToCode->Put(HB_TAG('D','F','L','T'), MOZ_SCRIPT_COMMON);
michael@0 2373 for (int32_t s = MOZ_SCRIPT_ARABIC; s < MOZ_NUM_SCRIPT_CODES; s++) {
michael@0 2374 hb_script_t scriptTag = hb_script_t(GetScriptTagForCode(s));
michael@0 2375 hb_tag_t s1, s2;
michael@0 2376 hb_ot_tags_from_script(scriptTag, &s1, &s2);
michael@0 2377 sScriptTagToCode->Put(s1, s);
michael@0 2378 if (s2 != HB_OT_TAG_DEFAULT_SCRIPT) {
michael@0 2379 sScriptTagToCode->Put(s2, s);
michael@0 2380 }
michael@0 2381 }
michael@0 2382
michael@0 2383 uint32_t numDefaultFeatures = ArrayLength(defaultFeatures);
michael@0 2384 sDefaultFeatures =
michael@0 2385 new nsTHashtable<nsUint32HashKey>(numDefaultFeatures);
michael@0 2386 for (uint32_t i = 0; i < numDefaultFeatures; i++) {
michael@0 2387 sDefaultFeatures->PutEntry(defaultFeatures[i]);
michael@0 2388 }
michael@0 2389 }
michael@0 2390
michael@0 2391 // iterate over the scripts in the font
michael@0 2392 hb_tag_t scriptTags[8];
michael@0 2393
michael@0 2394 uint32_t len, offset = 0;
michael@0 2395 do {
michael@0 2396 len = ArrayLength(scriptTags);
michael@0 2397 hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, offset,
michael@0 2398 &len, scriptTags);
michael@0 2399 for (uint32_t i = 0; i < len; i++) {
michael@0 2400 bool isDefaultFeature = false;
michael@0 2401 int32_t s;
michael@0 2402 if (!HasLookupRuleWithGlyphByScript(face, HB_OT_TAG_GSUB,
michael@0 2403 scriptTags[i], offset + i,
michael@0 2404 spaceGlyph,
michael@0 2405 *sDefaultFeatures,
michael@0 2406 isDefaultFeature) ||
michael@0 2407 !sScriptTagToCode->Get(scriptTags[i], &s))
michael@0 2408 {
michael@0 2409 continue;
michael@0 2410 }
michael@0 2411 result = true;
michael@0 2412 uint32_t index = s >> 5;
michael@0 2413 uint32_t bit = s & 0x1f;
michael@0 2414 if (isDefaultFeature) {
michael@0 2415 mFontEntry->mDefaultSubSpaceFeatures[index] |= (1 << bit);
michael@0 2416 } else {
michael@0 2417 mFontEntry->mNonDefaultSubSpaceFeatures[index] |= (1 << bit);
michael@0 2418 }
michael@0 2419 }
michael@0 2420 offset += len;
michael@0 2421 } while (len == ArrayLength(scriptTags));
michael@0 2422 }
michael@0 2423
michael@0 2424 // spaces in default features of default script?
michael@0 2425 // ==> can't use word cache, skip GPOS analysis
michael@0 2426 bool canUseWordCache = true;
michael@0 2427 if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
michael@0 2428 MOZ_SCRIPT_COMMON)) {
michael@0 2429 canUseWordCache = false;
michael@0 2430 }
michael@0 2431
michael@0 2432 // GPOS lookups - distinguish kerning from non-kerning features
michael@0 2433 mFontEntry->mHasSpaceFeaturesKerning = false;
michael@0 2434 mFontEntry->mHasSpaceFeaturesNonKerning = false;
michael@0 2435
michael@0 2436 if (canUseWordCache && hb_ot_layout_has_positioning(face)) {
michael@0 2437 bool hasKerning = false, hasNonKerning = false;
michael@0 2438 HasLookupRuleWithGlyph(face, HB_OT_TAG_GPOS, hasNonKerning,
michael@0 2439 HB_TAG('k','e','r','n'), hasKerning, spaceGlyph);
michael@0 2440 if (hasKerning || hasNonKerning) {
michael@0 2441 result = true;
michael@0 2442 }
michael@0 2443 mFontEntry->mHasSpaceFeaturesKerning = hasKerning;
michael@0 2444 mFontEntry->mHasSpaceFeaturesNonKerning = hasNonKerning;
michael@0 2445 }
michael@0 2446
michael@0 2447 hb_face_destroy(face);
michael@0 2448 mFontEntry->mHasSpaceFeatures = result;
michael@0 2449
michael@0 2450 #ifdef PR_LOGGING
michael@0 2451 if (MOZ_UNLIKELY(log)) {
michael@0 2452 TimeDuration elapsed = TimeStamp::Now() - start;
michael@0 2453 LOG_FONTINIT((
michael@0 2454 "(fontinit-spacelookups) font: %s - "
michael@0 2455 "subst default: %8.8x %8.8x %8.8x %8.8x "
michael@0 2456 "subst non-default: %8.8x %8.8x %8.8x %8.8x "
michael@0 2457 "kerning: %s non-kerning: %s time: %6.3f\n",
michael@0 2458 NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
michael@0 2459 mFontEntry->mDefaultSubSpaceFeatures[3],
michael@0 2460 mFontEntry->mDefaultSubSpaceFeatures[2],
michael@0 2461 mFontEntry->mDefaultSubSpaceFeatures[1],
michael@0 2462 mFontEntry->mDefaultSubSpaceFeatures[0],
michael@0 2463 mFontEntry->mNonDefaultSubSpaceFeatures[3],
michael@0 2464 mFontEntry->mNonDefaultSubSpaceFeatures[2],
michael@0 2465 mFontEntry->mNonDefaultSubSpaceFeatures[1],
michael@0 2466 mFontEntry->mNonDefaultSubSpaceFeatures[0],
michael@0 2467 (mFontEntry->mHasSpaceFeaturesKerning ? "true" : "false"),
michael@0 2468 (mFontEntry->mHasSpaceFeaturesNonKerning ? "true" : "false"),
michael@0 2469 elapsed.ToMilliseconds()
michael@0 2470 ));
michael@0 2471 }
michael@0 2472 #endif
michael@0 2473 }
michael@0 2474
michael@0 2475 bool
michael@0 2476 gfxFont::HasSubstitutionRulesWithSpaceLookups(int32_t aRunScript)
michael@0 2477 {
michael@0 2478 NS_ASSERTION(GetFontEntry()->mHasSpaceFeaturesInitialized,
michael@0 2479 "need to initialize space lookup flags");
michael@0 2480 NS_ASSERTION(aRunScript < MOZ_NUM_SCRIPT_CODES, "weird script code");
michael@0 2481 if (aRunScript == MOZ_SCRIPT_INVALID ||
michael@0 2482 aRunScript >= MOZ_NUM_SCRIPT_CODES) {
michael@0 2483 return false;
michael@0 2484 }
michael@0 2485
michael@0 2486 // default features have space lookups ==> true
michael@0 2487 if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
michael@0 2488 MOZ_SCRIPT_COMMON) ||
michael@0 2489 HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
michael@0 2490 aRunScript))
michael@0 2491 {
michael@0 2492 return true;
michael@0 2493 }
michael@0 2494
michael@0 2495 // non-default features have space lookups and some type of
michael@0 2496 // font feature, in font or style is specified ==> true
michael@0 2497 if ((HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
michael@0 2498 MOZ_SCRIPT_COMMON) ||
michael@0 2499 HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
michael@0 2500 aRunScript)) &&
michael@0 2501 (!mStyle.featureSettings.IsEmpty() ||
michael@0 2502 !mFontEntry->mFeatureSettings.IsEmpty()))
michael@0 2503 {
michael@0 2504 return true;
michael@0 2505 }
michael@0 2506
michael@0 2507 return false;
michael@0 2508 }
michael@0 2509
michael@0 2510 bool
michael@0 2511 gfxFont::SpaceMayParticipateInShaping(int32_t aRunScript)
michael@0 2512 {
michael@0 2513 // avoid checking fonts known not to include default space-dependent features
michael@0 2514 if (MOZ_UNLIKELY(mFontEntry->mSkipDefaultFeatureSpaceCheck)) {
michael@0 2515 if (!mKerningSet && mStyle.featureSettings.IsEmpty() &&
michael@0 2516 mFontEntry->mFeatureSettings.IsEmpty()) {
michael@0 2517 return false;
michael@0 2518 }
michael@0 2519 }
michael@0 2520
michael@0 2521 // We record the presence of space-dependent features in the font entry
michael@0 2522 // so that subsequent instantiations for the same font face won't
michael@0 2523 // require us to re-check the tables; however, the actual check is done
michael@0 2524 // by gfxFont because not all font entry subclasses know how to create
michael@0 2525 // a harfbuzz face for introspection.
michael@0 2526 if (!mFontEntry->mHasSpaceFeaturesInitialized) {
michael@0 2527 CheckForFeaturesInvolvingSpace();
michael@0 2528 }
michael@0 2529
michael@0 2530 if (!mFontEntry->mHasSpaceFeatures) {
michael@0 2531 return false;
michael@0 2532 }
michael@0 2533
michael@0 2534 // if font has substitution rules or non-kerning positioning rules
michael@0 2535 // that involve spaces, bypass
michael@0 2536 if (HasSubstitutionRulesWithSpaceLookups(aRunScript) ||
michael@0 2537 mFontEntry->mHasSpaceFeaturesNonKerning) {
michael@0 2538 return true;
michael@0 2539 }
michael@0 2540
michael@0 2541 // if kerning explicitly enabled/disabled via font-feature-settings or
michael@0 2542 // font-kerning and kerning rules use spaces, only bypass when enabled
michael@0 2543 if (mKerningSet && mFontEntry->mHasSpaceFeaturesKerning) {
michael@0 2544 return mKerningEnabled;
michael@0 2545 }
michael@0 2546
michael@0 2547 return false;
michael@0 2548 }
michael@0 2549
michael@0 2550 bool
michael@0 2551 gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn)
michael@0 2552 {
michael@0 2553 aFeatureOn = false;
michael@0 2554
michael@0 2555 if (mStyle.featureSettings.IsEmpty() &&
michael@0 2556 GetFontEntry()->mFeatureSettings.IsEmpty()) {
michael@0 2557 return false;
michael@0 2558 }
michael@0 2559
michael@0 2560 // add feature values from font
michael@0 2561 bool featureSet = false;
michael@0 2562 uint32_t i, count;
michael@0 2563
michael@0 2564 nsTArray<gfxFontFeature>& fontFeatures = GetFontEntry()->mFeatureSettings;
michael@0 2565 count = fontFeatures.Length();
michael@0 2566 for (i = 0; i < count; i++) {
michael@0 2567 const gfxFontFeature& feature = fontFeatures.ElementAt(i);
michael@0 2568 if (feature.mTag == aFeature) {
michael@0 2569 featureSet = true;
michael@0 2570 aFeatureOn = (feature.mValue != 0);
michael@0 2571 }
michael@0 2572 }
michael@0 2573
michael@0 2574 // add feature values from style rules
michael@0 2575 nsTArray<gfxFontFeature>& styleFeatures = mStyle.featureSettings;
michael@0 2576 count = styleFeatures.Length();
michael@0 2577 for (i = 0; i < count; i++) {
michael@0 2578 const gfxFontFeature& feature = styleFeatures.ElementAt(i);
michael@0 2579 if (feature.mTag == aFeature) {
michael@0 2580 featureSet = true;
michael@0 2581 aFeatureOn = (feature.mValue != 0);
michael@0 2582 }
michael@0 2583 }
michael@0 2584
michael@0 2585 return featureSet;
michael@0 2586 }
michael@0 2587
michael@0 2588 /**
michael@0 2589 * A helper function in case we need to do any rounding or other
michael@0 2590 * processing here.
michael@0 2591 */
michael@0 2592 #define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \
michael@0 2593 (double(aAppUnits)*double(aDevUnitsPerAppUnit))
michael@0 2594
michael@0 2595 struct GlyphBuffer {
michael@0 2596 #define GLYPH_BUFFER_SIZE (2048/sizeof(cairo_glyph_t))
michael@0 2597 cairo_glyph_t mGlyphBuffer[GLYPH_BUFFER_SIZE];
michael@0 2598 unsigned int mNumGlyphs;
michael@0 2599
michael@0 2600 GlyphBuffer()
michael@0 2601 : mNumGlyphs(0) { }
michael@0 2602
michael@0 2603 cairo_glyph_t *AppendGlyph() {
michael@0 2604 return &mGlyphBuffer[mNumGlyphs++];
michael@0 2605 }
michael@0 2606
michael@0 2607 void Flush(cairo_t *aCR, DrawMode aDrawMode, bool aReverse,
michael@0 2608 gfxTextContextPaint *aContextPaint,
michael@0 2609 const gfxMatrix& aGlobalMatrix, bool aFinish = false) {
michael@0 2610 // Ensure there's enough room for a glyph to be added to the buffer
michael@0 2611 // and we actually have glyphs to draw
michael@0 2612 if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) {
michael@0 2613 return;
michael@0 2614 }
michael@0 2615
michael@0 2616 if (aReverse) {
michael@0 2617 for (uint32_t i = 0; i < mNumGlyphs/2; ++i) {
michael@0 2618 cairo_glyph_t tmp = mGlyphBuffer[i];
michael@0 2619 mGlyphBuffer[i] = mGlyphBuffer[mNumGlyphs - 1 - i];
michael@0 2620 mGlyphBuffer[mNumGlyphs - 1 - i] = tmp;
michael@0 2621 }
michael@0 2622 }
michael@0 2623
michael@0 2624 if (aDrawMode == DrawMode::GLYPH_PATH) {
michael@0 2625 cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
michael@0 2626 } else {
michael@0 2627 if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
michael@0 2628 (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) {
michael@0 2629 FlushStroke(aCR, aContextPaint, aGlobalMatrix);
michael@0 2630 }
michael@0 2631 if (int(aDrawMode) & int(DrawMode::GLYPH_FILL)) {
michael@0 2632 PROFILER_LABEL("GlyphBuffer", "cairo_show_glyphs");
michael@0 2633 nsRefPtr<gfxPattern> pattern;
michael@0 2634 if (aContextPaint &&
michael@0 2635 !!(pattern = aContextPaint->GetFillPattern(aGlobalMatrix))) {
michael@0 2636 cairo_save(aCR);
michael@0 2637 cairo_set_source(aCR, pattern->CairoPattern());
michael@0 2638 }
michael@0 2639
michael@0 2640 cairo_show_glyphs(aCR, mGlyphBuffer, mNumGlyphs);
michael@0 2641
michael@0 2642 if (pattern) {
michael@0 2643 cairo_restore(aCR);
michael@0 2644 }
michael@0 2645 }
michael@0 2646 if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
michael@0 2647 int(DrawMode::GLYPH_STROKE)) {
michael@0 2648 FlushStroke(aCR, aContextPaint, aGlobalMatrix);
michael@0 2649 }
michael@0 2650 }
michael@0 2651
michael@0 2652 mNumGlyphs = 0;
michael@0 2653 }
michael@0 2654
michael@0 2655 private:
michael@0 2656 void FlushStroke(cairo_t *aCR, gfxTextContextPaint *aContextPaint,
michael@0 2657 const gfxMatrix& aGlobalMatrix) {
michael@0 2658 nsRefPtr<gfxPattern> pattern;
michael@0 2659 if (aContextPaint &&
michael@0 2660 !!(pattern = aContextPaint->GetStrokePattern(aGlobalMatrix))) {
michael@0 2661 cairo_save(aCR);
michael@0 2662 cairo_set_source(aCR, pattern->CairoPattern());
michael@0 2663 }
michael@0 2664
michael@0 2665 cairo_new_path(aCR);
michael@0 2666 cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
michael@0 2667 cairo_stroke(aCR);
michael@0 2668
michael@0 2669 if (pattern) {
michael@0 2670 cairo_restore(aCR);
michael@0 2671 }
michael@0 2672 }
michael@0 2673
michael@0 2674 #undef GLYPH_BUFFER_SIZE
michael@0 2675 };
michael@0 2676
michael@0 2677 static AntialiasMode Get2DAAMode(gfxFont::AntialiasOption aAAOption) {
michael@0 2678 switch (aAAOption) {
michael@0 2679 case gfxFont::kAntialiasSubpixel:
michael@0 2680 return AntialiasMode::SUBPIXEL;
michael@0 2681 case gfxFont::kAntialiasGrayscale:
michael@0 2682 return AntialiasMode::GRAY;
michael@0 2683 case gfxFont::kAntialiasNone:
michael@0 2684 return AntialiasMode::NONE;
michael@0 2685 default:
michael@0 2686 return AntialiasMode::DEFAULT;
michael@0 2687 }
michael@0 2688 }
michael@0 2689
michael@0 2690 struct GlyphBufferAzure {
michael@0 2691 #define GLYPH_BUFFER_SIZE (2048/sizeof(Glyph))
michael@0 2692 Glyph mGlyphBuffer[GLYPH_BUFFER_SIZE];
michael@0 2693 unsigned int mNumGlyphs;
michael@0 2694
michael@0 2695 GlyphBufferAzure()
michael@0 2696 : mNumGlyphs(0) { }
michael@0 2697
michael@0 2698 Glyph *AppendGlyph() {
michael@0 2699 return &mGlyphBuffer[mNumGlyphs++];
michael@0 2700 }
michael@0 2701
michael@0 2702 void Flush(DrawTarget *aDT, gfxTextContextPaint *aContextPaint, ScaledFont *aFont,
michael@0 2703 DrawMode aDrawMode, bool aReverse, const GlyphRenderingOptions *aOptions,
michael@0 2704 gfxContext *aThebesContext, const Matrix *aInvFontMatrix, const DrawOptions &aDrawOptions,
michael@0 2705 bool aFinish = false)
michael@0 2706 {
michael@0 2707 // Ensure there's enough room for a glyph to be added to the buffer
michael@0 2708 if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) {
michael@0 2709 return;
michael@0 2710 }
michael@0 2711
michael@0 2712 if (aReverse) {
michael@0 2713 Glyph *begin = &mGlyphBuffer[0];
michael@0 2714 Glyph *end = &mGlyphBuffer[mNumGlyphs];
michael@0 2715 std::reverse(begin, end);
michael@0 2716 }
michael@0 2717
michael@0 2718 gfx::GlyphBuffer buf;
michael@0 2719 buf.mGlyphs = mGlyphBuffer;
michael@0 2720 buf.mNumGlyphs = mNumGlyphs;
michael@0 2721
michael@0 2722 gfxContext::AzureState state = aThebesContext->CurrentState();
michael@0 2723 if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
michael@0 2724 (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) {
michael@0 2725 FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state);
michael@0 2726 }
michael@0 2727 if (int(aDrawMode) & int(DrawMode::GLYPH_FILL)) {
michael@0 2728 if (state.pattern || aContextPaint) {
michael@0 2729 Pattern *pat;
michael@0 2730
michael@0 2731 nsRefPtr<gfxPattern> fillPattern;
michael@0 2732 if (!aContextPaint ||
michael@0 2733 !(fillPattern = aContextPaint->GetFillPattern(aThebesContext->CurrentMatrix()))) {
michael@0 2734 if (state.pattern) {
michael@0 2735 pat = state.pattern->GetPattern(aDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
michael@0 2736 } else {
michael@0 2737 pat = nullptr;
michael@0 2738 }
michael@0 2739 } else {
michael@0 2740 pat = fillPattern->GetPattern(aDT);
michael@0 2741 }
michael@0 2742
michael@0 2743 if (pat) {
michael@0 2744 Matrix saved;
michael@0 2745 Matrix *mat = nullptr;
michael@0 2746 if (aInvFontMatrix) {
michael@0 2747 // The brush matrix needs to be multiplied with the inverted matrix
michael@0 2748 // as well, to move the brush into the space of the glyphs. Before
michael@0 2749 // the render target transformation
michael@0 2750
michael@0 2751 // This relies on the returned Pattern not to be reused by
michael@0 2752 // others, but regenerated on GetPattern calls. This is true!
michael@0 2753 if (pat->GetType() == PatternType::LINEAR_GRADIENT) {
michael@0 2754 mat = &static_cast<LinearGradientPattern*>(pat)->mMatrix;
michael@0 2755 } else if (pat->GetType() == PatternType::RADIAL_GRADIENT) {
michael@0 2756 mat = &static_cast<RadialGradientPattern*>(pat)->mMatrix;
michael@0 2757 } else if (pat->GetType() == PatternType::SURFACE) {
michael@0 2758 mat = &static_cast<SurfacePattern*>(pat)->mMatrix;
michael@0 2759 }
michael@0 2760
michael@0 2761 if (mat) {
michael@0 2762 saved = *mat;
michael@0 2763 *mat = (*mat) * (*aInvFontMatrix);
michael@0 2764 }
michael@0 2765 }
michael@0 2766
michael@0 2767 aDT->FillGlyphs(aFont, buf, *pat,
michael@0 2768 aDrawOptions, aOptions);
michael@0 2769
michael@0 2770 if (mat) {
michael@0 2771 *mat = saved;
michael@0 2772 }
michael@0 2773 }
michael@0 2774 } else if (state.sourceSurface) {
michael@0 2775 aDT->FillGlyphs(aFont, buf, SurfacePattern(state.sourceSurface,
michael@0 2776 ExtendMode::CLAMP,
michael@0 2777 state.surfTransform),
michael@0 2778 aDrawOptions, aOptions);
michael@0 2779 } else {
michael@0 2780 aDT->FillGlyphs(aFont, buf, ColorPattern(state.color),
michael@0 2781 aDrawOptions, aOptions);
michael@0 2782 }
michael@0 2783 }
michael@0 2784 if (int(aDrawMode) & int(DrawMode::GLYPH_PATH)) {
michael@0 2785 aThebesContext->EnsurePathBuilder();
michael@0 2786 Matrix mat = aDT->GetTransform();
michael@0 2787 aFont->CopyGlyphsToBuilder(buf, aThebesContext->mPathBuilder, aDT->GetType(), &mat);
michael@0 2788 }
michael@0 2789 if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
michael@0 2790 int(DrawMode::GLYPH_STROKE)) {
michael@0 2791 FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state);
michael@0 2792 }
michael@0 2793
michael@0 2794 mNumGlyphs = 0;
michael@0 2795 }
michael@0 2796
michael@0 2797 private:
michael@0 2798 void FlushStroke(DrawTarget *aDT, gfxTextContextPaint *aContextPaint,
michael@0 2799 ScaledFont *aFont, gfxContext *aThebesContext,
michael@0 2800 gfx::GlyphBuffer& aBuf, gfxContext::AzureState& aState)
michael@0 2801 {
michael@0 2802 RefPtr<Path> path = aFont->GetPathForGlyphs(aBuf, aDT);
michael@0 2803 if (aContextPaint) {
michael@0 2804 nsRefPtr<gfxPattern> strokePattern =
michael@0 2805 aContextPaint->GetStrokePattern(aThebesContext->CurrentMatrix());
michael@0 2806 if (strokePattern) {
michael@0 2807 aDT->Stroke(path, *strokePattern->GetPattern(aDT), aState.strokeOptions);
michael@0 2808 }
michael@0 2809 }
michael@0 2810 }
michael@0 2811
michael@0 2812 #undef GLYPH_BUFFER_SIZE
michael@0 2813 };
michael@0 2814
michael@0 2815 // Bug 674909. When synthetic bolding text by drawing twice, need to
michael@0 2816 // render using a pixel offset in device pixels, otherwise text
michael@0 2817 // doesn't appear bolded, it appears as if a bad text shadow exists
michael@0 2818 // when a non-identity transform exists. Use an offset factor so that
michael@0 2819 // the second draw occurs at a constant offset in device pixels.
michael@0 2820
michael@0 2821 double
michael@0 2822 gfxFont::CalcXScale(gfxContext *aContext)
michael@0 2823 {
michael@0 2824 // determine magnitude of a 1px x offset in device space
michael@0 2825 gfxSize t = aContext->UserToDevice(gfxSize(1.0, 0.0));
michael@0 2826 if (t.width == 1.0 && t.height == 0.0) {
michael@0 2827 // short-circuit the most common case to avoid sqrt() and division
michael@0 2828 return 1.0;
michael@0 2829 }
michael@0 2830
michael@0 2831 double m = sqrt(t.width * t.width + t.height * t.height);
michael@0 2832
michael@0 2833 NS_ASSERTION(m != 0.0, "degenerate transform while synthetic bolding");
michael@0 2834 if (m == 0.0) {
michael@0 2835 return 0.0; // effectively disables offset
michael@0 2836 }
michael@0 2837
michael@0 2838 // scale factor so that offsets are 1px in device pixels
michael@0 2839 return 1.0 / m;
michael@0 2840 }
michael@0 2841
michael@0 2842 static DrawMode
michael@0 2843 ForcePaintingDrawMode(DrawMode aDrawMode)
michael@0 2844 {
michael@0 2845 return aDrawMode == DrawMode::GLYPH_PATH ?
michael@0 2846 DrawMode(int(DrawMode::GLYPH_FILL) | int(DrawMode::GLYPH_STROKE)) :
michael@0 2847 aDrawMode;
michael@0 2848 }
michael@0 2849
michael@0 2850 void
michael@0 2851 gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
michael@0 2852 gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aPt,
michael@0 2853 Spacing *aSpacing, gfxTextContextPaint *aContextPaint,
michael@0 2854 gfxTextRunDrawCallbacks *aCallbacks)
michael@0 2855 {
michael@0 2856 NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)),
michael@0 2857 "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
michael@0 2858
michael@0 2859 if (aStart >= aEnd)
michael@0 2860 return;
michael@0 2861
michael@0 2862 const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
michael@0 2863 const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
michael@0 2864 const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
michael@0 2865 bool isRTL = aTextRun->IsRightToLeft();
michael@0 2866 double direction = aTextRun->GetDirection();
michael@0 2867 gfxMatrix globalMatrix = aContext->CurrentMatrix();
michael@0 2868
michael@0 2869 bool haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
michael@0 2870 nsAutoPtr<gfxTextContextPaint> contextPaint;
michael@0 2871 if (haveSVGGlyphs && !aContextPaint) {
michael@0 2872 // If no pattern is specified for fill, use the current pattern
michael@0 2873 NS_ASSERTION((int(aDrawMode) & int(DrawMode::GLYPH_STROKE)) == 0, "no pattern supplied for stroking text");
michael@0 2874 nsRefPtr<gfxPattern> fillPattern = aContext->GetPattern();
michael@0 2875 contextPaint = new SimpleTextContextPaint(fillPattern, nullptr,
michael@0 2876 aContext->CurrentMatrix());
michael@0 2877 aContextPaint = contextPaint;
michael@0 2878 }
michael@0 2879
michael@0 2880 // synthetic-bold strikes are each offset one device pixel in run direction
michael@0 2881 // (these values are only needed if IsSyntheticBold() is true)
michael@0 2882 double synBoldOnePixelOffset = 0;
michael@0 2883 int32_t strikes = 1;
michael@0 2884 if (IsSyntheticBold()) {
michael@0 2885 double xscale = CalcXScale(aContext);
michael@0 2886 synBoldOnePixelOffset = direction * xscale;
michael@0 2887 if (xscale != 0.0) {
michael@0 2888 // use as many strikes as needed for the the increased advance
michael@0 2889 strikes = NS_lroundf(GetSyntheticBoldOffset() / xscale);
michael@0 2890 }
michael@0 2891 }
michael@0 2892
michael@0 2893 uint32_t i;
michael@0 2894 // Current position in appunits
michael@0 2895 double x = aPt->x;
michael@0 2896 double y = aPt->y;
michael@0 2897
michael@0 2898 cairo_t *cr = aContext->GetCairo();
michael@0 2899 RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
michael@0 2900
michael@0 2901 bool paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs;
michael@0 2902 bool emittedGlyphs = false;
michael@0 2903
michael@0 2904 if (aContext->IsCairo()) {
michael@0 2905 bool success = SetupCairoFont(aContext);
michael@0 2906 if (MOZ_UNLIKELY(!success))
michael@0 2907 return;
michael@0 2908
michael@0 2909 ::GlyphBuffer glyphs;
michael@0 2910 cairo_glyph_t *glyph;
michael@0 2911
michael@0 2912 if (aSpacing) {
michael@0 2913 x += direction*aSpacing[0].mBefore;
michael@0 2914 }
michael@0 2915 for (i = aStart; i < aEnd; ++i) {
michael@0 2916 const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
michael@0 2917 if (glyphData->IsSimpleGlyph()) {
michael@0 2918 double advance = glyphData->GetSimpleAdvance();
michael@0 2919 double glyphX;
michael@0 2920 if (isRTL) {
michael@0 2921 x -= advance;
michael@0 2922 glyphX = x;
michael@0 2923 } else {
michael@0 2924 glyphX = x;
michael@0 2925 x += advance;
michael@0 2926 }
michael@0 2927
michael@0 2928 if (haveSVGGlyphs) {
michael@0 2929 if (!paintSVGGlyphs) {
michael@0 2930 continue;
michael@0 2931 }
michael@0 2932 gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
michael@0 2933 ToDeviceUnits(y, devUnitsPerAppUnit));
michael@0 2934 DrawMode mode = ForcePaintingDrawMode(aDrawMode);
michael@0 2935 if (RenderSVGGlyph(aContext, point, mode,
michael@0 2936 glyphData->GetSimpleGlyph(), aContextPaint,
michael@0 2937 aCallbacks, emittedGlyphs)) {
michael@0 2938 continue;
michael@0 2939 }
michael@0 2940 }
michael@0 2941
michael@0 2942 // Perhaps we should put a scale in the cairo context instead of
michael@0 2943 // doing this scaling here...
michael@0 2944 // Multiplying by the reciprocal may introduce tiny error here,
michael@0 2945 // but we assume cairo is going to round coordinates at some stage
michael@0 2946 // and this is faster
michael@0 2947 glyph = glyphs.AppendGlyph();
michael@0 2948 glyph->index = glyphData->GetSimpleGlyph();
michael@0 2949 glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
michael@0 2950 glyph->y = ToDeviceUnits(y, devUnitsPerAppUnit);
michael@0 2951 glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix);
michael@0 2952
michael@0 2953 // synthetic bolding by multi-striking with 1-pixel offsets
michael@0 2954 // at least once, more if there's room (large font sizes)
michael@0 2955 if (IsSyntheticBold()) {
michael@0 2956 double strikeOffset = synBoldOnePixelOffset;
michael@0 2957 int32_t strikeCount = strikes;
michael@0 2958 do {
michael@0 2959 cairo_glyph_t *doubleglyph;
michael@0 2960 doubleglyph = glyphs.AppendGlyph();
michael@0 2961 doubleglyph->index = glyph->index;
michael@0 2962 doubleglyph->x =
michael@0 2963 ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit,
michael@0 2964 devUnitsPerAppUnit);
michael@0 2965 doubleglyph->y = glyph->y;
michael@0 2966 strikeOffset += synBoldOnePixelOffset;
michael@0 2967 glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix);
michael@0 2968 } while (--strikeCount > 0);
michael@0 2969 }
michael@0 2970 emittedGlyphs = true;
michael@0 2971 } else {
michael@0 2972 uint32_t glyphCount = glyphData->GetGlyphCount();
michael@0 2973 if (glyphCount > 0) {
michael@0 2974 const gfxTextRun::DetailedGlyph *details =
michael@0 2975 aTextRun->GetDetailedGlyphs(i);
michael@0 2976 NS_ASSERTION(details, "detailedGlyph should not be missing!");
michael@0 2977 double advance;
michael@0 2978 for (uint32_t j = 0; j < glyphCount; ++j, ++details, x += direction * advance) {
michael@0 2979 advance = details->mAdvance;
michael@0 2980 if (glyphData->IsMissing()) {
michael@0 2981 // default ignorable characters will have zero advance width.
michael@0 2982 // we don't have to draw the hexbox for them
michael@0 2983 if (aDrawMode != DrawMode::GLYPH_PATH && advance > 0) {
michael@0 2984 double glyphX = x;
michael@0 2985 if (isRTL) {
michael@0 2986 glyphX -= advance;
michael@0 2987 }
michael@0 2988 gfxPoint pt(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
michael@0 2989 ToDeviceUnits(y, devUnitsPerAppUnit));
michael@0 2990 gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit);
michael@0 2991 gfxFloat height = GetMetrics().maxAscent;
michael@0 2992 gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height);
michael@0 2993 gfxFontMissingGlyphs::DrawMissingGlyph(aContext,
michael@0 2994 glyphRect,
michael@0 2995 details->mGlyphID,
michael@0 2996 appUnitsPerDevUnit);
michael@0 2997 }
michael@0 2998 } else {
michael@0 2999 double glyphX = x + details->mXOffset;
michael@0 3000 if (isRTL) {
michael@0 3001 glyphX -= advance;
michael@0 3002 }
michael@0 3003
michael@0 3004 gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
michael@0 3005 ToDeviceUnits(y, devUnitsPerAppUnit));
michael@0 3006
michael@0 3007 if (haveSVGGlyphs) {
michael@0 3008 if (!paintSVGGlyphs) {
michael@0 3009 continue;
michael@0 3010 }
michael@0 3011 DrawMode mode = ForcePaintingDrawMode(aDrawMode);
michael@0 3012 if (RenderSVGGlyph(aContext, point, mode,
michael@0 3013 details->mGlyphID,
michael@0 3014 aContextPaint, aCallbacks,
michael@0 3015 emittedGlyphs)) {
michael@0 3016 continue;
michael@0 3017 }
michael@0 3018 }
michael@0 3019
michael@0 3020 glyph = glyphs.AppendGlyph();
michael@0 3021 glyph->index = details->mGlyphID;
michael@0 3022 glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
michael@0 3023 glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
michael@0 3024 glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix);
michael@0 3025
michael@0 3026 if (IsSyntheticBold()) {
michael@0 3027 double strikeOffset = synBoldOnePixelOffset;
michael@0 3028 int32_t strikeCount = strikes;
michael@0 3029 do {
michael@0 3030 cairo_glyph_t *doubleglyph;
michael@0 3031 doubleglyph = glyphs.AppendGlyph();
michael@0 3032 doubleglyph->index = glyph->index;
michael@0 3033 doubleglyph->x =
michael@0 3034 ToDeviceUnits(glyphX + strikeOffset *
michael@0 3035 appUnitsPerDevUnit,
michael@0 3036 devUnitsPerAppUnit);
michael@0 3037 doubleglyph->y = glyph->y;
michael@0 3038 strikeOffset += synBoldOnePixelOffset;
michael@0 3039 glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix);
michael@0 3040 } while (--strikeCount > 0);
michael@0 3041 }
michael@0 3042 emittedGlyphs = true;
michael@0 3043 }
michael@0 3044 }
michael@0 3045 }
michael@0 3046 }
michael@0 3047
michael@0 3048 if (aSpacing) {
michael@0 3049 double space = aSpacing[i - aStart].mAfter;
michael@0 3050 if (i + 1 < aEnd) {
michael@0 3051 space += aSpacing[i + 1 - aStart].mBefore;
michael@0 3052 }
michael@0 3053 x += direction*space;
michael@0 3054 }
michael@0 3055 }
michael@0 3056
michael@0 3057 if (gfxFontTestStore::CurrentStore()) {
michael@0 3058 /* This assumes that the tests won't have anything that results
michael@0 3059 * in more than GLYPH_BUFFER_SIZE glyphs. Do this before we
michael@0 3060 * flush, since that'll blow away the num_glyphs.
michael@0 3061 */
michael@0 3062 gfxFontTestStore::CurrentStore()->AddItem(GetName(),
michael@0 3063 glyphs.mGlyphBuffer,
michael@0 3064 glyphs.mNumGlyphs);
michael@0 3065 }
michael@0 3066
michael@0 3067 // draw any remaining glyphs
michael@0 3068 glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix, true);
michael@0 3069 if (aCallbacks && emittedGlyphs) {
michael@0 3070 aCallbacks->NotifyGlyphPathEmitted();
michael@0 3071 }
michael@0 3072
michael@0 3073 } else {
michael@0 3074 RefPtr<ScaledFont> scaledFont = GetScaledFont(dt);
michael@0 3075
michael@0 3076 if (!scaledFont) {
michael@0 3077 return;
michael@0 3078 }
michael@0 3079
michael@0 3080 bool oldSubpixelAA = dt->GetPermitSubpixelAA();
michael@0 3081
michael@0 3082 if (!AllowSubpixelAA()) {
michael@0 3083 dt->SetPermitSubpixelAA(false);
michael@0 3084 }
michael@0 3085
michael@0 3086 GlyphBufferAzure glyphs;
michael@0 3087 Glyph *glyph;
michael@0 3088
michael@0 3089 Matrix mat, matInv;
michael@0 3090 Matrix oldMat = dt->GetTransform();
michael@0 3091
michael@0 3092 // This is nullptr when we have inverse-transformed glyphs and we need
michael@0 3093 // to transform the Brush inside flush.
michael@0 3094 Matrix *passedInvMatrix = nullptr;
michael@0 3095
michael@0 3096 RefPtr<GlyphRenderingOptions> renderingOptions =
michael@0 3097 GetGlyphRenderingOptions();
michael@0 3098
michael@0 3099 DrawOptions drawOptions;
michael@0 3100 drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption);
michael@0 3101
michael@0 3102 // The cairo DrawTarget backend uses the cairo_scaled_font directly
michael@0 3103 // and so has the font skew matrix applied already.
michael@0 3104 if (mScaledFont &&
michael@0 3105 dt->GetType() != BackendType::CAIRO) {
michael@0 3106 cairo_matrix_t matrix;
michael@0 3107 cairo_scaled_font_get_font_matrix(mScaledFont, &matrix);
michael@0 3108 if (matrix.xy != 0) {
michael@0 3109 // If this matrix applies a skew, which can happen when drawing
michael@0 3110 // oblique fonts, we will set the DrawTarget matrix to apply the
michael@0 3111 // skew. We'll need to move the glyphs by the inverse of the skew to
michael@0 3112 // get the glyphs positioned correctly in the new device space
michael@0 3113 // though, since the font matrix should only be applied to drawing
michael@0 3114 // the glyphs, and not to their position.
michael@0 3115 mat = ToMatrix(*reinterpret_cast<gfxMatrix*>(&matrix));
michael@0 3116
michael@0 3117 mat._11 = mat._22 = 1.0;
michael@0 3118 float adjustedSize = mAdjustedSize > 0 ? mAdjustedSize : GetStyle()->size;
michael@0 3119 mat._21 /= adjustedSize;
michael@0 3120
michael@0 3121 dt->SetTransform(mat * oldMat);
michael@0 3122
michael@0 3123 matInv = mat;
michael@0 3124 matInv.Invert();
michael@0 3125
michael@0 3126 passedInvMatrix = &matInv;
michael@0 3127 }
michael@0 3128 }
michael@0 3129
michael@0 3130 if (aSpacing) {
michael@0 3131 x += direction*aSpacing[0].mBefore;
michael@0 3132 }
michael@0 3133 for (i = aStart; i < aEnd; ++i) {
michael@0 3134 const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
michael@0 3135 if (glyphData->IsSimpleGlyph()) {
michael@0 3136 double advance = glyphData->GetSimpleAdvance();
michael@0 3137 double glyphX;
michael@0 3138 if (isRTL) {
michael@0 3139 x -= advance;
michael@0 3140 glyphX = x;
michael@0 3141 } else {
michael@0 3142 glyphX = x;
michael@0 3143 x += advance;
michael@0 3144 }
michael@0 3145
michael@0 3146 if (haveSVGGlyphs) {
michael@0 3147 if (!paintSVGGlyphs) {
michael@0 3148 continue;
michael@0 3149 }
michael@0 3150 gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
michael@0 3151 ToDeviceUnits(y, devUnitsPerAppUnit));
michael@0 3152 DrawMode mode = ForcePaintingDrawMode(aDrawMode);
michael@0 3153 if (RenderSVGGlyph(aContext, point, mode,
michael@0 3154 glyphData->GetSimpleGlyph(), aContextPaint,
michael@0 3155 aCallbacks, emittedGlyphs)) {
michael@0 3156 continue;
michael@0 3157 }
michael@0 3158 }
michael@0 3159
michael@0 3160 // Perhaps we should put a scale in the cairo context instead of
michael@0 3161 // doing this scaling here...
michael@0 3162 // Multiplying by the reciprocal may introduce tiny error here,
michael@0 3163 // but we assume cairo is going to round coordinates at some stage
michael@0 3164 // and this is faster
michael@0 3165 glyph = glyphs.AppendGlyph();
michael@0 3166 glyph->mIndex = glyphData->GetSimpleGlyph();
michael@0 3167 glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
michael@0 3168 glyph->mPosition.y = ToDeviceUnits(y, devUnitsPerAppUnit);
michael@0 3169 glyph->mPosition = matInv * glyph->mPosition;
michael@0 3170 glyphs.Flush(dt, aContextPaint, scaledFont,
michael@0 3171 aDrawMode, isRTL, renderingOptions,
michael@0 3172 aContext, passedInvMatrix,
michael@0 3173 drawOptions);
michael@0 3174
michael@0 3175 // synthetic bolding by multi-striking with 1-pixel offsets
michael@0 3176 // at least once, more if there's room (large font sizes)
michael@0 3177 if (IsSyntheticBold()) {
michael@0 3178 double strikeOffset = synBoldOnePixelOffset;
michael@0 3179 int32_t strikeCount = strikes;
michael@0 3180 do {
michael@0 3181 Glyph *doubleglyph;
michael@0 3182 doubleglyph = glyphs.AppendGlyph();
michael@0 3183 doubleglyph->mIndex = glyph->mIndex;
michael@0 3184 doubleglyph->mPosition.x =
michael@0 3185 ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit,
michael@0 3186 devUnitsPerAppUnit);
michael@0 3187 doubleglyph->mPosition.y = glyph->mPosition.y;
michael@0 3188 doubleglyph->mPosition = matInv * doubleglyph->mPosition;
michael@0 3189 strikeOffset += synBoldOnePixelOffset;
michael@0 3190 glyphs.Flush(dt, aContextPaint, scaledFont,
michael@0 3191 aDrawMode, isRTL, renderingOptions,
michael@0 3192 aContext, passedInvMatrix,
michael@0 3193 drawOptions);
michael@0 3194 } while (--strikeCount > 0);
michael@0 3195 }
michael@0 3196 emittedGlyphs = true;
michael@0 3197 } else {
michael@0 3198 uint32_t glyphCount = glyphData->GetGlyphCount();
michael@0 3199 if (glyphCount > 0) {
michael@0 3200 const gfxTextRun::DetailedGlyph *details =
michael@0 3201 aTextRun->GetDetailedGlyphs(i);
michael@0 3202 NS_ASSERTION(details, "detailedGlyph should not be missing!");
michael@0 3203 double advance;
michael@0 3204 for (uint32_t j = 0; j < glyphCount; ++j, ++details, x += direction * advance) {
michael@0 3205 advance = details->mAdvance;
michael@0 3206 if (glyphData->IsMissing()) {
michael@0 3207 // default ignorable characters will have zero advance width.
michael@0 3208 // we don't have to draw the hexbox for them
michael@0 3209 if (aDrawMode != DrawMode::GLYPH_PATH && advance > 0) {
michael@0 3210 double glyphX = x;
michael@0 3211 if (isRTL) {
michael@0 3212 glyphX -= advance;
michael@0 3213 }
michael@0 3214 gfxPoint pt(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
michael@0 3215 ToDeviceUnits(y, devUnitsPerAppUnit));
michael@0 3216 gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit);
michael@0 3217 gfxFloat height = GetMetrics().maxAscent;
michael@0 3218 gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height);
michael@0 3219 gfxFontMissingGlyphs::DrawMissingGlyph(aContext,
michael@0 3220 glyphRect,
michael@0 3221 details->mGlyphID,
michael@0 3222 appUnitsPerDevUnit);
michael@0 3223 }
michael@0 3224 } else {
michael@0 3225 double glyphX = x + details->mXOffset;
michael@0 3226 if (isRTL) {
michael@0 3227 glyphX -= advance;
michael@0 3228 }
michael@0 3229
michael@0 3230 gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
michael@0 3231 ToDeviceUnits(y, devUnitsPerAppUnit));
michael@0 3232
michael@0 3233 if (haveSVGGlyphs) {
michael@0 3234 if (!paintSVGGlyphs) {
michael@0 3235 continue;
michael@0 3236 }
michael@0 3237 DrawMode mode = ForcePaintingDrawMode(aDrawMode);
michael@0 3238 if (RenderSVGGlyph(aContext, point, mode,
michael@0 3239 details->mGlyphID,
michael@0 3240 aContextPaint, aCallbacks,
michael@0 3241 emittedGlyphs)) {
michael@0 3242 continue;
michael@0 3243 }
michael@0 3244 }
michael@0 3245
michael@0 3246 glyph = glyphs.AppendGlyph();
michael@0 3247 glyph->mIndex = details->mGlyphID;
michael@0 3248 glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
michael@0 3249 glyph->mPosition.y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
michael@0 3250 glyph->mPosition = matInv * glyph->mPosition;
michael@0 3251 glyphs.Flush(dt, aContextPaint, scaledFont, aDrawMode,
michael@0 3252 isRTL, renderingOptions, aContext, passedInvMatrix,
michael@0 3253 drawOptions);
michael@0 3254
michael@0 3255 if (IsSyntheticBold()) {
michael@0 3256 double strikeOffset = synBoldOnePixelOffset;
michael@0 3257 int32_t strikeCount = strikes;
michael@0 3258 do {
michael@0 3259 Glyph *doubleglyph;
michael@0 3260 doubleglyph = glyphs.AppendGlyph();
michael@0 3261 doubleglyph->mIndex = glyph->mIndex;
michael@0 3262 doubleglyph->mPosition.x =
michael@0 3263 ToDeviceUnits(glyphX + strikeOffset *
michael@0 3264 appUnitsPerDevUnit,
michael@0 3265 devUnitsPerAppUnit);
michael@0 3266 doubleglyph->mPosition.y = glyph->mPosition.y;
michael@0 3267 strikeOffset += synBoldOnePixelOffset;
michael@0 3268 doubleglyph->mPosition = matInv * doubleglyph->mPosition;
michael@0 3269 glyphs.Flush(dt, aContextPaint, scaledFont,
michael@0 3270 aDrawMode, isRTL, renderingOptions,
michael@0 3271 aContext, passedInvMatrix, drawOptions);
michael@0 3272 } while (--strikeCount > 0);
michael@0 3273 }
michael@0 3274 emittedGlyphs = true;
michael@0 3275 }
michael@0 3276 }
michael@0 3277 }
michael@0 3278 }
michael@0 3279
michael@0 3280 if (aSpacing) {
michael@0 3281 double space = aSpacing[i - aStart].mAfter;
michael@0 3282 if (i + 1 < aEnd) {
michael@0 3283 space += aSpacing[i + 1 - aStart].mBefore;
michael@0 3284 }
michael@0 3285 x += direction*space;
michael@0 3286 }
michael@0 3287 }
michael@0 3288
michael@0 3289 glyphs.Flush(dt, aContextPaint, scaledFont, aDrawMode, isRTL,
michael@0 3290 renderingOptions, aContext, passedInvMatrix,
michael@0 3291 drawOptions, true);
michael@0 3292 if (aCallbacks && emittedGlyphs) {
michael@0 3293 aCallbacks->NotifyGlyphPathEmitted();
michael@0 3294 }
michael@0 3295
michael@0 3296 dt->SetTransform(oldMat);
michael@0 3297
michael@0 3298 dt->SetPermitSubpixelAA(oldSubpixelAA);
michael@0 3299 }
michael@0 3300
michael@0 3301 *aPt = gfxPoint(x, y);
michael@0 3302 }
michael@0 3303
michael@0 3304 bool
michael@0 3305 gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
michael@0 3306 uint32_t aGlyphId, gfxTextContextPaint *aContextPaint)
michael@0 3307 {
michael@0 3308 if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
michael@0 3309 return false;
michael@0 3310 }
michael@0 3311
michael@0 3312 const gfxFloat devUnitsPerSVGUnit =
michael@0 3313 GetAdjustedSize() / GetFontEntry()->UnitsPerEm();
michael@0 3314 gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
michael@0 3315
michael@0 3316 aContext->Translate(gfxPoint(aPoint.x, aPoint.y));
michael@0 3317 aContext->Scale(devUnitsPerSVGUnit, devUnitsPerSVGUnit);
michael@0 3318
michael@0 3319 aContextPaint->InitStrokeGeometry(aContext, devUnitsPerSVGUnit);
michael@0 3320
michael@0 3321 return GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, int(aDrawMode),
michael@0 3322 aContextPaint);
michael@0 3323 }
michael@0 3324
michael@0 3325 bool
michael@0 3326 gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
michael@0 3327 uint32_t aGlyphId, gfxTextContextPaint *aContextPaint,
michael@0 3328 gfxTextRunDrawCallbacks *aCallbacks,
michael@0 3329 bool& aEmittedGlyphs)
michael@0 3330 {
michael@0 3331 if (aCallbacks) {
michael@0 3332 if (aEmittedGlyphs) {
michael@0 3333 aCallbacks->NotifyGlyphPathEmitted();
michael@0 3334 aEmittedGlyphs = false;
michael@0 3335 }
michael@0 3336 aCallbacks->NotifyBeforeSVGGlyphPainted();
michael@0 3337 }
michael@0 3338 bool rendered = RenderSVGGlyph(aContext, aPoint, aDrawMode, aGlyphId,
michael@0 3339 aContextPaint);
michael@0 3340 if (aCallbacks) {
michael@0 3341 aCallbacks->NotifyAfterSVGGlyphPainted();
michael@0 3342 }
michael@0 3343 return rendered;
michael@0 3344 }
michael@0 3345
michael@0 3346 static void
michael@0 3347 UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax)
michael@0 3348 {
michael@0 3349 *aDestMin = std::min(*aDestMin, aX);
michael@0 3350 *aDestMax = std::max(*aDestMax, aX);
michael@0 3351 }
michael@0 3352
michael@0 3353 // We get precise glyph extents if the textrun creator requested them, or
michael@0 3354 // if the font is a user font --- in which case the author may be relying
michael@0 3355 // on overflowing glyphs.
michael@0 3356 static bool
michael@0 3357 NeedsGlyphExtents(gfxFont *aFont, gfxTextRun *aTextRun)
michael@0 3358 {
michael@0 3359 return (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX) ||
michael@0 3360 aFont->GetFontEntry()->IsUserFont();
michael@0 3361 }
michael@0 3362
michael@0 3363 static bool
michael@0 3364 NeedsGlyphExtents(gfxTextRun *aTextRun)
michael@0 3365 {
michael@0 3366 if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX)
michael@0 3367 return true;
michael@0 3368 uint32_t numRuns;
michael@0 3369 const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&numRuns);
michael@0 3370 for (uint32_t i = 0; i < numRuns; ++i) {
michael@0 3371 if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont())
michael@0 3372 return true;
michael@0 3373 }
michael@0 3374 return false;
michael@0 3375 }
michael@0 3376
michael@0 3377 gfxFont::RunMetrics
michael@0 3378 gfxFont::Measure(gfxTextRun *aTextRun,
michael@0 3379 uint32_t aStart, uint32_t aEnd,
michael@0 3380 BoundingBoxType aBoundingBoxType,
michael@0 3381 gfxContext *aRefContext,
michael@0 3382 Spacing *aSpacing)
michael@0 3383 {
michael@0 3384 // If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
michael@0 3385 // and the underlying cairo font may be antialiased,
michael@0 3386 // we need to create a copy in order to avoid getting cached extents.
michael@0 3387 // This is only used by MathML layout at present.
michael@0 3388 if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
michael@0 3389 mAntialiasOption != kAntialiasNone) {
michael@0 3390 if (!mNonAAFont) {
michael@0 3391 mNonAAFont = CopyWithAntialiasOption(kAntialiasNone);
michael@0 3392 }
michael@0 3393 // if font subclass doesn't implement CopyWithAntialiasOption(),
michael@0 3394 // it will return null and we'll proceed to use the existing font
michael@0 3395 if (mNonAAFont) {
michael@0 3396 return mNonAAFont->Measure(aTextRun, aStart, aEnd,
michael@0 3397 TIGHT_HINTED_OUTLINE_EXTENTS,
michael@0 3398 aRefContext, aSpacing);
michael@0 3399 }
michael@0 3400 }
michael@0 3401
michael@0 3402 const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
michael@0 3403 // Current position in appunits
michael@0 3404 const gfxFont::Metrics& fontMetrics = GetMetrics();
michael@0 3405
michael@0 3406 RunMetrics metrics;
michael@0 3407 metrics.mAscent = fontMetrics.maxAscent*appUnitsPerDevUnit;
michael@0 3408 metrics.mDescent = fontMetrics.maxDescent*appUnitsPerDevUnit;
michael@0 3409 if (aStart == aEnd) {
michael@0 3410 // exit now before we look at aSpacing[0], which is undefined
michael@0 3411 metrics.mBoundingBox = gfxRect(0, -metrics.mAscent, 0, metrics.mAscent + metrics.mDescent);
michael@0 3412 return metrics;
michael@0 3413 }
michael@0 3414
michael@0 3415 gfxFloat advanceMin = 0, advanceMax = 0;
michael@0 3416 const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
michael@0 3417 bool isRTL = aTextRun->IsRightToLeft();
michael@0 3418 double direction = aTextRun->GetDirection();
michael@0 3419 bool needsGlyphExtents = NeedsGlyphExtents(this, aTextRun);
michael@0 3420 gfxGlyphExtents *extents =
michael@0 3421 (aBoundingBoxType == LOOSE_INK_EXTENTS &&
michael@0 3422 !needsGlyphExtents &&
michael@0 3423 !aTextRun->HasDetailedGlyphs()) ? nullptr
michael@0 3424 : GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
michael@0 3425 double x = 0;
michael@0 3426 if (aSpacing) {
michael@0 3427 x += direction*aSpacing[0].mBefore;
michael@0 3428 }
michael@0 3429 uint32_t i;
michael@0 3430 for (i = aStart; i < aEnd; ++i) {
michael@0 3431 const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
michael@0 3432 if (glyphData->IsSimpleGlyph()) {
michael@0 3433 double advance = glyphData->GetSimpleAdvance();
michael@0 3434 // Only get the real glyph horizontal extent if we were asked
michael@0 3435 // for the tight bounding box or we're in quality mode
michael@0 3436 if ((aBoundingBoxType != LOOSE_INK_EXTENTS || needsGlyphExtents) &&
michael@0 3437 extents) {
michael@0 3438 uint32_t glyphIndex = glyphData->GetSimpleGlyph();
michael@0 3439 uint16_t extentsWidth = extents->GetContainedGlyphWidthAppUnits(glyphIndex);
michael@0 3440 if (extentsWidth != gfxGlyphExtents::INVALID_WIDTH &&
michael@0 3441 aBoundingBoxType == LOOSE_INK_EXTENTS) {
michael@0 3442 UnionRange(x, &advanceMin, &advanceMax);
michael@0 3443 UnionRange(x + direction*extentsWidth, &advanceMin, &advanceMax);
michael@0 3444 } else {
michael@0 3445 gfxRect glyphRect;
michael@0 3446 if (!extents->GetTightGlyphExtentsAppUnits(this,
michael@0 3447 aRefContext, glyphIndex, &glyphRect)) {
michael@0 3448 glyphRect = gfxRect(0, metrics.mBoundingBox.Y(),
michael@0 3449 advance, metrics.mBoundingBox.Height());
michael@0 3450 }
michael@0 3451 if (isRTL) {
michael@0 3452 glyphRect -= gfxPoint(advance, 0);
michael@0 3453 }
michael@0 3454 glyphRect += gfxPoint(x, 0);
michael@0 3455 metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
michael@0 3456 }
michael@0 3457 }
michael@0 3458 x += direction*advance;
michael@0 3459 } else {
michael@0 3460 uint32_t glyphCount = glyphData->GetGlyphCount();
michael@0 3461 if (glyphCount > 0) {
michael@0 3462 const gfxTextRun::DetailedGlyph *details =
michael@0 3463 aTextRun->GetDetailedGlyphs(i);
michael@0 3464 NS_ASSERTION(details != nullptr,
michael@0 3465 "detaiedGlyph record should not be missing!");
michael@0 3466 uint32_t j;
michael@0 3467 for (j = 0; j < glyphCount; ++j, ++details) {
michael@0 3468 uint32_t glyphIndex = details->mGlyphID;
michael@0 3469 gfxPoint glyphPt(x + details->mXOffset, details->mYOffset);
michael@0 3470 double advance = details->mAdvance;
michael@0 3471 gfxRect glyphRect;
michael@0 3472 if (glyphData->IsMissing() || !extents ||
michael@0 3473 !extents->GetTightGlyphExtentsAppUnits(this,
michael@0 3474 aRefContext, glyphIndex, &glyphRect)) {
michael@0 3475 // We might have failed to get glyph extents due to
michael@0 3476 // OOM or something
michael@0 3477 glyphRect = gfxRect(0, -metrics.mAscent,
michael@0 3478 advance, metrics.mAscent + metrics.mDescent);
michael@0 3479 }
michael@0 3480 if (isRTL) {
michael@0 3481 glyphRect -= gfxPoint(advance, 0);
michael@0 3482 }
michael@0 3483 glyphRect += gfxPoint(x, 0);
michael@0 3484 metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
michael@0 3485 x += direction*advance;
michael@0 3486 }
michael@0 3487 }
michael@0 3488 }
michael@0 3489 // Every other glyph type is ignored
michael@0 3490 if (aSpacing) {
michael@0 3491 double space = aSpacing[i - aStart].mAfter;
michael@0 3492 if (i + 1 < aEnd) {
michael@0 3493 space += aSpacing[i + 1 - aStart].mBefore;
michael@0 3494 }
michael@0 3495 x += direction*space;
michael@0 3496 }
michael@0 3497 }
michael@0 3498
michael@0 3499 if (aBoundingBoxType == LOOSE_INK_EXTENTS) {
michael@0 3500 UnionRange(x, &advanceMin, &advanceMax);
michael@0 3501 gfxRect fontBox(advanceMin, -metrics.mAscent,
michael@0 3502 advanceMax - advanceMin, metrics.mAscent + metrics.mDescent);
michael@0 3503 metrics.mBoundingBox = metrics.mBoundingBox.Union(fontBox);
michael@0 3504 }
michael@0 3505 if (isRTL) {
michael@0 3506 metrics.mBoundingBox -= gfxPoint(x, 0);
michael@0 3507 }
michael@0 3508
michael@0 3509 metrics.mAdvanceWidth = x*direction;
michael@0 3510 return metrics;
michael@0 3511 }
michael@0 3512
michael@0 3513 static PLDHashOperator
michael@0 3514 NotifyGlyphChangeObservers(nsPtrHashKey<gfxFont::GlyphChangeObserver>* aKey,
michael@0 3515 void* aClosure)
michael@0 3516 {
michael@0 3517 aKey->GetKey()->NotifyGlyphsChanged();
michael@0 3518 return PL_DHASH_NEXT;
michael@0 3519 }
michael@0 3520
michael@0 3521 void
michael@0 3522 gfxFont::NotifyGlyphsChanged()
michael@0 3523 {
michael@0 3524 uint32_t i, count = mGlyphExtentsArray.Length();
michael@0 3525 for (i = 0; i < count; ++i) {
michael@0 3526 // Flush cached extents array
michael@0 3527 mGlyphExtentsArray[i]->NotifyGlyphsChanged();
michael@0 3528 }
michael@0 3529
michael@0 3530 if (mGlyphChangeObservers) {
michael@0 3531 mGlyphChangeObservers->EnumerateEntries(NotifyGlyphChangeObservers, nullptr);
michael@0 3532 }
michael@0 3533 }
michael@0 3534
michael@0 3535 static bool
michael@0 3536 IsBoundarySpace(char16_t aChar, char16_t aNextChar)
michael@0 3537 {
michael@0 3538 return (aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar);
michael@0 3539 }
michael@0 3540
michael@0 3541 static inline uint32_t
michael@0 3542 HashMix(uint32_t aHash, char16_t aCh)
michael@0 3543 {
michael@0 3544 return (aHash >> 28) ^ (aHash << 4) ^ aCh;
michael@0 3545 }
michael@0 3546
michael@0 3547 #ifdef __GNUC__
michael@0 3548 #define GFX_MAYBE_UNUSED __attribute__((unused))
michael@0 3549 #else
michael@0 3550 #define GFX_MAYBE_UNUSED
michael@0 3551 #endif
michael@0 3552
michael@0 3553 template<typename T>
michael@0 3554 gfxShapedWord*
michael@0 3555 gfxFont::GetShapedWord(gfxContext *aContext,
michael@0 3556 const T *aText,
michael@0 3557 uint32_t aLength,
michael@0 3558 uint32_t aHash,
michael@0 3559 int32_t aRunScript,
michael@0 3560 int32_t aAppUnitsPerDevUnit,
michael@0 3561 uint32_t aFlags,
michael@0 3562 gfxTextPerfMetrics *aTextPerf GFX_MAYBE_UNUSED)
michael@0 3563 {
michael@0 3564 // if the cache is getting too big, flush it and start over
michael@0 3565 uint32_t wordCacheMaxEntries =
michael@0 3566 gfxPlatform::GetPlatform()->WordCacheMaxEntries();
michael@0 3567 if (mWordCache->Count() > wordCacheMaxEntries) {
michael@0 3568 NS_WARNING("flushing shaped-word cache");
michael@0 3569 ClearCachedWords();
michael@0 3570 }
michael@0 3571
michael@0 3572 // if there's a cached entry for this word, just return it
michael@0 3573 CacheHashKey key(aText, aLength, aHash,
michael@0 3574 aRunScript,
michael@0 3575 aAppUnitsPerDevUnit,
michael@0 3576 aFlags);
michael@0 3577
michael@0 3578 CacheHashEntry *entry = mWordCache->PutEntry(key);
michael@0 3579 if (!entry) {
michael@0 3580 NS_WARNING("failed to create word cache entry - expect missing text");
michael@0 3581 return nullptr;
michael@0 3582 }
michael@0 3583 gfxShapedWord *sw = entry->mShapedWord;
michael@0 3584
michael@0 3585 bool isContent = !mStyle.systemFont;
michael@0 3586
michael@0 3587 if (sw) {
michael@0 3588 sw->ResetAge();
michael@0 3589 Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_HITS_CONTENT :
michael@0 3590 Telemetry::WORD_CACHE_HITS_CHROME),
michael@0 3591 aLength);
michael@0 3592 #ifndef RELEASE_BUILD
michael@0 3593 if (aTextPerf) {
michael@0 3594 aTextPerf->current.wordCacheHit++;
michael@0 3595 }
michael@0 3596 #endif
michael@0 3597 return sw;
michael@0 3598 }
michael@0 3599
michael@0 3600 Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_MISSES_CONTENT :
michael@0 3601 Telemetry::WORD_CACHE_MISSES_CHROME),
michael@0 3602 aLength);
michael@0 3603 #ifndef RELEASE_BUILD
michael@0 3604 if (aTextPerf) {
michael@0 3605 aTextPerf->current.wordCacheMiss++;
michael@0 3606 }
michael@0 3607 #endif
michael@0 3608
michael@0 3609 sw = entry->mShapedWord = gfxShapedWord::Create(aText, aLength,
michael@0 3610 aRunScript,
michael@0 3611 aAppUnitsPerDevUnit,
michael@0 3612 aFlags);
michael@0 3613 if (!sw) {
michael@0 3614 NS_WARNING("failed to create gfxShapedWord - expect missing text");
michael@0 3615 return nullptr;
michael@0 3616 }
michael@0 3617
michael@0 3618 DebugOnly<bool> ok =
michael@0 3619 ShapeText(aContext, aText, 0, aLength, aRunScript, sw);
michael@0 3620
michael@0 3621 NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
michael@0 3622
michael@0 3623 return sw;
michael@0 3624 }
michael@0 3625
michael@0 3626 bool
michael@0 3627 gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
michael@0 3628 {
michael@0 3629 const gfxShapedWord *sw = mShapedWord;
michael@0 3630 if (!sw) {
michael@0 3631 return false;
michael@0 3632 }
michael@0 3633 if (sw->GetLength() != aKey->mLength ||
michael@0 3634 sw->Flags() != aKey->mFlags ||
michael@0 3635 sw->GetAppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
michael@0 3636 sw->Script() != aKey->mScript) {
michael@0 3637 return false;
michael@0 3638 }
michael@0 3639 if (sw->TextIs8Bit()) {
michael@0 3640 if (aKey->mTextIs8Bit) {
michael@0 3641 return (0 == memcmp(sw->Text8Bit(), aKey->mText.mSingle,
michael@0 3642 aKey->mLength * sizeof(uint8_t)));
michael@0 3643 }
michael@0 3644 // The key has 16-bit text, even though all the characters are < 256,
michael@0 3645 // so the TEXT_IS_8BIT flag was set and the cached ShapedWord we're
michael@0 3646 // comparing with will have 8-bit text.
michael@0 3647 const uint8_t *s1 = sw->Text8Bit();
michael@0 3648 const char16_t *s2 = aKey->mText.mDouble;
michael@0 3649 const char16_t *s2end = s2 + aKey->mLength;
michael@0 3650 while (s2 < s2end) {
michael@0 3651 if (*s1++ != *s2++) {
michael@0 3652 return false;
michael@0 3653 }
michael@0 3654 }
michael@0 3655 return true;
michael@0 3656 }
michael@0 3657 NS_ASSERTION((aKey->mFlags & gfxTextRunFactory::TEXT_IS_8BIT) == 0 &&
michael@0 3658 !aKey->mTextIs8Bit, "didn't expect 8-bit text here");
michael@0 3659 return (0 == memcmp(sw->TextUnicode(), aKey->mText.mDouble,
michael@0 3660 aKey->mLength * sizeof(char16_t)));
michael@0 3661 }
michael@0 3662
michael@0 3663 bool
michael@0 3664 gfxFont::ShapeText(gfxContext *aContext,
michael@0 3665 const uint8_t *aText,
michael@0 3666 uint32_t aOffset,
michael@0 3667 uint32_t aLength,
michael@0 3668 int32_t aScript,
michael@0 3669 gfxShapedText *aShapedText,
michael@0 3670 bool aPreferPlatformShaping)
michael@0 3671 {
michael@0 3672 nsDependentCSubstring ascii((const char*)aText, aLength);
michael@0 3673 nsAutoString utf16;
michael@0 3674 AppendASCIItoUTF16(ascii, utf16);
michael@0 3675 if (utf16.Length() != aLength) {
michael@0 3676 return false;
michael@0 3677 }
michael@0 3678 return ShapeText(aContext, utf16.BeginReading(), aOffset, aLength,
michael@0 3679 aScript, aShapedText, aPreferPlatformShaping);
michael@0 3680 }
michael@0 3681
michael@0 3682 bool
michael@0 3683 gfxFont::ShapeText(gfxContext *aContext,
michael@0 3684 const char16_t *aText,
michael@0 3685 uint32_t aOffset,
michael@0 3686 uint32_t aLength,
michael@0 3687 int32_t aScript,
michael@0 3688 gfxShapedText *aShapedText,
michael@0 3689 bool aPreferPlatformShaping)
michael@0 3690 {
michael@0 3691 bool ok = false;
michael@0 3692
michael@0 3693 if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
michael@0 3694 ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
michael@0 3695 aScript, aShapedText);
michael@0 3696 }
michael@0 3697
michael@0 3698 if (!ok && mHarfBuzzShaper && !aPreferPlatformShaping) {
michael@0 3699 if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
michael@0 3700 ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
michael@0 3701 aScript, aShapedText);
michael@0 3702 }
michael@0 3703 }
michael@0 3704
michael@0 3705 if (!ok) {
michael@0 3706 if (!mPlatformShaper) {
michael@0 3707 CreatePlatformShaper();
michael@0 3708 NS_ASSERTION(mPlatformShaper, "no platform shaper available!");
michael@0 3709 }
michael@0 3710 if (mPlatformShaper) {
michael@0 3711 ok = mPlatformShaper->ShapeText(aContext, aText, aOffset, aLength,
michael@0 3712 aScript, aShapedText);
michael@0 3713 }
michael@0 3714 }
michael@0 3715
michael@0 3716 PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
michael@0 3717
michael@0 3718 return ok;
michael@0 3719 }
michael@0 3720
michael@0 3721 void
michael@0 3722 gfxFont::PostShapingFixup(gfxContext *aContext,
michael@0 3723 const char16_t *aText,
michael@0 3724 uint32_t aOffset,
michael@0 3725 uint32_t aLength,
michael@0 3726 gfxShapedText *aShapedText)
michael@0 3727 {
michael@0 3728 if (IsSyntheticBold()) {
michael@0 3729 float synBoldOffset =
michael@0 3730 GetSyntheticBoldOffset() * CalcXScale(aContext);
michael@0 3731 aShapedText->AdjustAdvancesForSyntheticBold(synBoldOffset,
michael@0 3732 aOffset, aLength);
michael@0 3733 }
michael@0 3734 }
michael@0 3735
michael@0 3736 #define MAX_SHAPING_LENGTH 32760 // slightly less than 32K, trying to avoid
michael@0 3737 // over-stressing platform shapers
michael@0 3738 #define BACKTRACK_LIMIT 16 // backtrack this far looking for a good place
michael@0 3739 // to split into fragments for separate shaping
michael@0 3740
michael@0 3741 template<typename T>
michael@0 3742 bool
michael@0 3743 gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext,
michael@0 3744 const T *aText,
michael@0 3745 uint32_t aOffset,
michael@0 3746 uint32_t aLength,
michael@0 3747 int32_t aScript,
michael@0 3748 gfxTextRun *aTextRun)
michael@0 3749 {
michael@0 3750 aTextRun->SetupClusterBoundaries(aOffset, aText, aLength);
michael@0 3751
michael@0 3752 bool ok = true;
michael@0 3753
michael@0 3754 while (ok && aLength > 0) {
michael@0 3755 uint32_t fragLen = aLength;
michael@0 3756
michael@0 3757 // limit the length of text we pass to shapers in a single call
michael@0 3758 if (fragLen > MAX_SHAPING_LENGTH) {
michael@0 3759 fragLen = MAX_SHAPING_LENGTH;
michael@0 3760
michael@0 3761 // in the 8-bit case, there are no multi-char clusters,
michael@0 3762 // so we don't need to do this check
michael@0 3763 if (sizeof(T) == sizeof(char16_t)) {
michael@0 3764 uint32_t i;
michael@0 3765 for (i = 0; i < BACKTRACK_LIMIT; ++i) {
michael@0 3766 if (aTextRun->IsClusterStart(aOffset + fragLen - i)) {
michael@0 3767 fragLen -= i;
michael@0 3768 break;
michael@0 3769 }
michael@0 3770 }
michael@0 3771 if (i == BACKTRACK_LIMIT) {
michael@0 3772 // if we didn't find any cluster start while backtracking,
michael@0 3773 // just check that we're not in the middle of a surrogate
michael@0 3774 // pair; back up by one code unit if we are.
michael@0 3775 if (NS_IS_LOW_SURROGATE(aText[fragLen]) &&
michael@0 3776 NS_IS_HIGH_SURROGATE(aText[fragLen - 1])) {
michael@0 3777 --fragLen;
michael@0 3778 }
michael@0 3779 }
michael@0 3780 }
michael@0 3781 }
michael@0 3782
michael@0 3783 ok = ShapeText(aContext, aText, aOffset, fragLen, aScript, aTextRun);
michael@0 3784
michael@0 3785 aText += fragLen;
michael@0 3786 aOffset += fragLen;
michael@0 3787 aLength -= fragLen;
michael@0 3788 }
michael@0 3789
michael@0 3790 return ok;
michael@0 3791 }
michael@0 3792
michael@0 3793 // Check if aCh is an unhandled control character that should be displayed
michael@0 3794 // as a hexbox rather than rendered by some random font on the system.
michael@0 3795 // We exclude \r as stray &#13;s are rather common (bug 941940).
michael@0 3796 // Note that \n and \t don't come through here, as they have specific
michael@0 3797 // meanings that have already been handled.
michael@0 3798 static bool
michael@0 3799 IsInvalidControlChar(uint32_t aCh)
michael@0 3800 {
michael@0 3801 return aCh != '\r' && ((aCh & 0x7f) < 0x20 || aCh == 0x7f);
michael@0 3802 }
michael@0 3803
michael@0 3804 template<typename T>
michael@0 3805 bool
michael@0 3806 gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
michael@0 3807 const T *aText,
michael@0 3808 uint32_t aOffset,
michael@0 3809 uint32_t aLength,
michael@0 3810 int32_t aScript,
michael@0 3811 gfxTextRun *aTextRun)
michael@0 3812 {
michael@0 3813 uint32_t fragStart = 0;
michael@0 3814 bool ok = true;
michael@0 3815
michael@0 3816 for (uint32_t i = 0; i <= aLength && ok; ++i) {
michael@0 3817 T ch = (i < aLength) ? aText[i] : '\n';
michael@0 3818 bool invalid = gfxFontGroup::IsInvalidChar(ch);
michael@0 3819 uint32_t length = i - fragStart;
michael@0 3820
michael@0 3821 // break into separate fragments when we hit an invalid char
michael@0 3822 if (!invalid) {
michael@0 3823 continue;
michael@0 3824 }
michael@0 3825
michael@0 3826 if (length > 0) {
michael@0 3827 ok = ShapeFragmentWithoutWordCache(aContext, aText + fragStart,
michael@0 3828 aOffset + fragStart, length,
michael@0 3829 aScript, aTextRun);
michael@0 3830 }
michael@0 3831
michael@0 3832 if (i == aLength) {
michael@0 3833 break;
michael@0 3834 }
michael@0 3835
michael@0 3836 // fragment was terminated by an invalid char: skip it,
michael@0 3837 // unless it's a control char that we want to show as a hexbox,
michael@0 3838 // but record where TAB or NEWLINE occur
michael@0 3839 if (ch == '\t') {
michael@0 3840 aTextRun->SetIsTab(aOffset + i);
michael@0 3841 } else if (ch == '\n') {
michael@0 3842 aTextRun->SetIsNewline(aOffset + i);
michael@0 3843 } else if (IsInvalidControlChar(ch) &&
michael@0 3844 !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
michael@0 3845 aTextRun->SetMissingGlyph(aOffset + i, ch, this);
michael@0 3846 }
michael@0 3847 fragStart = i + 1;
michael@0 3848 }
michael@0 3849
michael@0 3850 NS_WARN_IF_FALSE(ok, "failed to shape text - expect garbled text");
michael@0 3851 return ok;
michael@0 3852 }
michael@0 3853
michael@0 3854 #ifndef RELEASE_BUILD
michael@0 3855 #define TEXT_PERF_INCR(tp, m) (tp ? (tp)->current.m++ : 0)
michael@0 3856 #else
michael@0 3857 #define TEXT_PERF_INCR(tp, m)
michael@0 3858 #endif
michael@0 3859
michael@0 3860 inline static bool IsChar8Bit(uint8_t /*aCh*/) { return true; }
michael@0 3861 inline static bool IsChar8Bit(char16_t aCh) { return aCh < 0x100; }
michael@0 3862
michael@0 3863 inline static bool HasSpaces(const uint8_t *aString, uint32_t aLen)
michael@0 3864 {
michael@0 3865 return memchr(aString, 0x20, aLen) != nullptr;
michael@0 3866 }
michael@0 3867
michael@0 3868 inline static bool HasSpaces(const char16_t *aString, uint32_t aLen)
michael@0 3869 {
michael@0 3870 for (const char16_t *ch = aString; ch < aString + aLen; ch++) {
michael@0 3871 if (*ch == 0x20) {
michael@0 3872 return true;
michael@0 3873 }
michael@0 3874 }
michael@0 3875 return false;
michael@0 3876 }
michael@0 3877
michael@0 3878 template<typename T>
michael@0 3879 bool
michael@0 3880 gfxFont::SplitAndInitTextRun(gfxContext *aContext,
michael@0 3881 gfxTextRun *aTextRun,
michael@0 3882 const T *aString,
michael@0 3883 uint32_t aRunStart,
michael@0 3884 uint32_t aRunLength,
michael@0 3885 int32_t aRunScript)
michael@0 3886 {
michael@0 3887 if (aRunLength == 0) {
michael@0 3888 return true;
michael@0 3889 }
michael@0 3890
michael@0 3891 gfxTextPerfMetrics *tp = nullptr;
michael@0 3892
michael@0 3893 #ifndef RELEASE_BUILD
michael@0 3894 tp = aTextRun->GetFontGroup()->GetTextPerfMetrics();
michael@0 3895 if (tp) {
michael@0 3896 if (mStyle.systemFont) {
michael@0 3897 tp->current.numChromeTextRuns++;
michael@0 3898 } else {
michael@0 3899 tp->current.numContentTextRuns++;
michael@0 3900 }
michael@0 3901 tp->current.numChars += aRunLength;
michael@0 3902 if (aRunLength > tp->current.maxTextRunLen) {
michael@0 3903 tp->current.maxTextRunLen = aRunLength;
michael@0 3904 }
michael@0 3905 }
michael@0 3906 #endif
michael@0 3907
michael@0 3908 uint32_t wordCacheCharLimit =
michael@0 3909 gfxPlatform::GetPlatform()->WordCacheCharLimit();
michael@0 3910
michael@0 3911 // If spaces can participate in shaping (e.g. within lookups for automatic
michael@0 3912 // fractions), need to shape without using the word cache which segments
michael@0 3913 // textruns on space boundaries. Word cache can be used if the textrun
michael@0 3914 // is short enough to fit in the word cache and it lacks spaces.
michael@0 3915 if (SpaceMayParticipateInShaping(aRunScript)) {
michael@0 3916 if (aRunLength > wordCacheCharLimit ||
michael@0 3917 HasSpaces(aString + aRunStart, aRunLength)) {
michael@0 3918 TEXT_PERF_INCR(tp, wordCacheSpaceRules);
michael@0 3919 return ShapeTextWithoutWordCache(aContext, aString + aRunStart,
michael@0 3920 aRunStart, aRunLength, aRunScript,
michael@0 3921 aTextRun);
michael@0 3922 }
michael@0 3923 }
michael@0 3924
michael@0 3925 InitWordCache();
michael@0 3926
michael@0 3927 // the only flags we care about for ShapedWord construction/caching
michael@0 3928 uint32_t flags = aTextRun->GetFlags();
michael@0 3929 flags &= (gfxTextRunFactory::TEXT_IS_RTL |
michael@0 3930 gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES |
michael@0 3931 gfxTextRunFactory::TEXT_USE_MATH_SCRIPT);
michael@0 3932 if (sizeof(T) == sizeof(uint8_t)) {
michael@0 3933 flags |= gfxTextRunFactory::TEXT_IS_8BIT;
michael@0 3934 }
michael@0 3935
michael@0 3936 const T *text = aString + aRunStart;
michael@0 3937 uint32_t wordStart = 0;
michael@0 3938 uint32_t hash = 0;
michael@0 3939 bool wordIs8Bit = true;
michael@0 3940 int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
michael@0 3941
michael@0 3942 T nextCh = text[0];
michael@0 3943 for (uint32_t i = 0; i <= aRunLength; ++i) {
michael@0 3944 T ch = nextCh;
michael@0 3945 nextCh = (i < aRunLength - 1) ? text[i + 1] : '\n';
michael@0 3946 bool boundary = IsBoundarySpace(ch, nextCh);
michael@0 3947 bool invalid = !boundary && gfxFontGroup::IsInvalidChar(ch);
michael@0 3948 uint32_t length = i - wordStart;
michael@0 3949
michael@0 3950 // break into separate ShapedWords when we hit an invalid char,
michael@0 3951 // or a boundary space (always handled individually),
michael@0 3952 // or the first non-space after a space
michael@0 3953 if (!boundary && !invalid) {
michael@0 3954 if (!IsChar8Bit(ch)) {
michael@0 3955 wordIs8Bit = false;
michael@0 3956 }
michael@0 3957 // include this character in the hash, and move on to next
michael@0 3958 hash = HashMix(hash, ch);
michael@0 3959 continue;
michael@0 3960 }
michael@0 3961
michael@0 3962 // We've decided to break here (i.e. we're at the end of a "word");
michael@0 3963 // shape the word and add it to the textrun.
michael@0 3964 // For words longer than the limit, we don't use the
michael@0 3965 // font's word cache but just shape directly into the textrun.
michael@0 3966 if (length > wordCacheCharLimit) {
michael@0 3967 TEXT_PERF_INCR(tp, wordCacheLong);
michael@0 3968 bool ok = ShapeFragmentWithoutWordCache(aContext,
michael@0 3969 text + wordStart,
michael@0 3970 aRunStart + wordStart,
michael@0 3971 length,
michael@0 3972 aRunScript,
michael@0 3973 aTextRun);
michael@0 3974 if (!ok) {
michael@0 3975 return false;
michael@0 3976 }
michael@0 3977 } else if (length > 0) {
michael@0 3978 uint32_t wordFlags = flags;
michael@0 3979 // in the 8-bit version of this method, TEXT_IS_8BIT was
michael@0 3980 // already set as part of |flags|, so no need for a per-word
michael@0 3981 // adjustment here
michael@0 3982 if (sizeof(T) == sizeof(char16_t)) {
michael@0 3983 if (wordIs8Bit) {
michael@0 3984 wordFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
michael@0 3985 }
michael@0 3986 }
michael@0 3987 gfxShapedWord *sw = GetShapedWord(aContext,
michael@0 3988 text + wordStart, length,
michael@0 3989 hash, aRunScript,
michael@0 3990 appUnitsPerDevUnit,
michael@0 3991 wordFlags, tp);
michael@0 3992 if (sw) {
michael@0 3993 aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart);
michael@0 3994 } else {
michael@0 3995 return false; // failed, presumably out of memory?
michael@0 3996 }
michael@0 3997 }
michael@0 3998
michael@0 3999 if (boundary) {
michael@0 4000 // word was terminated by a space: add that to the textrun
michael@0 4001 if (!aTextRun->SetSpaceGlyphIfSimple(this, aContext,
michael@0 4002 aRunStart + i, ch))
michael@0 4003 {
michael@0 4004 static const uint8_t space = ' ';
michael@0 4005 gfxShapedWord *sw =
michael@0 4006 GetShapedWord(aContext,
michael@0 4007 &space, 1,
michael@0 4008 HashMix(0, ' '), aRunScript,
michael@0 4009 appUnitsPerDevUnit,
michael@0 4010 flags | gfxTextRunFactory::TEXT_IS_8BIT, tp);
michael@0 4011 if (sw) {
michael@0 4012 aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
michael@0 4013 } else {
michael@0 4014 return false;
michael@0 4015 }
michael@0 4016 }
michael@0 4017 hash = 0;
michael@0 4018 wordStart = i + 1;
michael@0 4019 wordIs8Bit = true;
michael@0 4020 continue;
michael@0 4021 }
michael@0 4022
michael@0 4023 if (i == aRunLength) {
michael@0 4024 break;
michael@0 4025 }
michael@0 4026
michael@0 4027 NS_ASSERTION(invalid,
michael@0 4028 "how did we get here except via an invalid char?");
michael@0 4029
michael@0 4030 // word was terminated by an invalid char: skip it,
michael@0 4031 // unless it's a control char that we want to show as a hexbox,
michael@0 4032 // but record where TAB or NEWLINE occur
michael@0 4033 if (ch == '\t') {
michael@0 4034 aTextRun->SetIsTab(aRunStart + i);
michael@0 4035 } else if (ch == '\n') {
michael@0 4036 aTextRun->SetIsNewline(aRunStart + i);
michael@0 4037 } else if (IsInvalidControlChar(ch) &&
michael@0 4038 !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
michael@0 4039 aTextRun->SetMissingGlyph(aRunStart + i, ch, this);
michael@0 4040 }
michael@0 4041
michael@0 4042 hash = 0;
michael@0 4043 wordStart = i + 1;
michael@0 4044 wordIs8Bit = true;
michael@0 4045 }
michael@0 4046
michael@0 4047 return true;
michael@0 4048 }
michael@0 4049
michael@0 4050 gfxGlyphExtents *
michael@0 4051 gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) {
michael@0 4052 uint32_t i, count = mGlyphExtentsArray.Length();
michael@0 4053 for (i = 0; i < count; ++i) {
michael@0 4054 if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
michael@0 4055 return mGlyphExtentsArray[i];
michael@0 4056 }
michael@0 4057 gfxGlyphExtents *glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit);
michael@0 4058 if (glyphExtents) {
michael@0 4059 mGlyphExtentsArray.AppendElement(glyphExtents);
michael@0 4060 // Initialize the extents of a space glyph, assuming that spaces don't
michael@0 4061 // render anything!
michael@0 4062 glyphExtents->SetContainedGlyphWidthAppUnits(GetSpaceGlyph(), 0);
michael@0 4063 }
michael@0 4064 return glyphExtents;
michael@0 4065 }
michael@0 4066
michael@0 4067 void
michael@0 4068 gfxFont::SetupGlyphExtents(gfxContext *aContext, uint32_t aGlyphID, bool aNeedTight,
michael@0 4069 gfxGlyphExtents *aExtents)
michael@0 4070 {
michael@0 4071 gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
michael@0 4072 aContext->IdentityMatrix();
michael@0 4073
michael@0 4074 gfxRect svgBounds;
michael@0 4075 if (mFontEntry->TryGetSVGData(this) && mFontEntry->HasSVGGlyph(aGlyphID) &&
michael@0 4076 mFontEntry->GetSVGGlyphExtents(aContext, aGlyphID, &svgBounds)) {
michael@0 4077 gfxFloat d2a = aExtents->GetAppUnitsPerDevUnit();
michael@0 4078 aExtents->SetTightGlyphExtents(aGlyphID,
michael@0 4079 gfxRect(svgBounds.x * d2a,
michael@0 4080 svgBounds.y * d2a,
michael@0 4081 svgBounds.width * d2a,
michael@0 4082 svgBounds.height * d2a));
michael@0 4083 return;
michael@0 4084 }
michael@0 4085
michael@0 4086 cairo_glyph_t glyph;
michael@0 4087 glyph.index = aGlyphID;
michael@0 4088 glyph.x = 0;
michael@0 4089 glyph.y = 0;
michael@0 4090 cairo_text_extents_t extents;
michael@0 4091 cairo_glyph_extents(aContext->GetCairo(), &glyph, 1, &extents);
michael@0 4092
michael@0 4093 const Metrics& fontMetrics = GetMetrics();
michael@0 4094 int32_t appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
michael@0 4095 if (!aNeedTight && extents.x_bearing >= 0 &&
michael@0 4096 extents.y_bearing >= -fontMetrics.maxAscent &&
michael@0 4097 extents.height + extents.y_bearing <= fontMetrics.maxDescent) {
michael@0 4098 uint32_t appUnitsWidth =
michael@0 4099 uint32_t(ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit));
michael@0 4100 if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
michael@0 4101 aExtents->SetContainedGlyphWidthAppUnits(aGlyphID, uint16_t(appUnitsWidth));
michael@0 4102 return;
michael@0 4103 }
michael@0 4104 }
michael@0 4105 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
michael@0 4106 if (!aNeedTight) {
michael@0 4107 ++gGlyphExtentsSetupFallBackToTight;
michael@0 4108 }
michael@0 4109 #endif
michael@0 4110
michael@0 4111 gfxFloat d2a = appUnitsPerDevUnit;
michael@0 4112 gfxRect bounds(extents.x_bearing*d2a, extents.y_bearing*d2a,
michael@0 4113 extents.width*d2a, extents.height*d2a);
michael@0 4114 aExtents->SetTightGlyphExtents(aGlyphID, bounds);
michael@0 4115 }
michael@0 4116
michael@0 4117 // Try to initialize font metrics by reading sfnt tables directly;
michael@0 4118 // set mIsValid=TRUE and return TRUE on success.
michael@0 4119 // Return FALSE if the gfxFontEntry subclass does not
michael@0 4120 // implement GetFontTable(), or for non-sfnt fonts where tables are
michael@0 4121 // not available.
michael@0 4122 // If this returns TRUE without setting the mIsValid flag, then we -did-
michael@0 4123 // apparently find an sfnt, but it was too broken to be used.
michael@0 4124 bool
michael@0 4125 gfxFont::InitMetricsFromSfntTables(Metrics& aMetrics)
michael@0 4126 {
michael@0 4127 mIsValid = false; // font is NOT valid in case of early return
michael@0 4128
michael@0 4129 const uint32_t kHheaTableTag = TRUETYPE_TAG('h','h','e','a');
michael@0 4130 const uint32_t kPostTableTag = TRUETYPE_TAG('p','o','s','t');
michael@0 4131 const uint32_t kOS_2TableTag = TRUETYPE_TAG('O','S','/','2');
michael@0 4132
michael@0 4133 uint32_t len;
michael@0 4134
michael@0 4135 if (mFUnitsConvFactor == 0.0) {
michael@0 4136 // If the conversion factor from FUnits is not yet set,
michael@0 4137 // get the unitsPerEm from the 'head' table via the font entry
michael@0 4138 uint16_t unitsPerEm = GetFontEntry()->UnitsPerEm();
michael@0 4139 if (unitsPerEm == gfxFontEntry::kInvalidUPEM) {
michael@0 4140 return false;
michael@0 4141 }
michael@0 4142 mFUnitsConvFactor = mAdjustedSize / unitsPerEm;
michael@0 4143 }
michael@0 4144
michael@0 4145 // 'hhea' table is required to get vertical extents
michael@0 4146 gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
michael@0 4147 if (!hheaTable) {
michael@0 4148 return false; // no 'hhea' table -> not an sfnt
michael@0 4149 }
michael@0 4150 const HheaTable* hhea =
michael@0 4151 reinterpret_cast<const HheaTable*>(hb_blob_get_data(hheaTable, &len));
michael@0 4152 if (len < sizeof(HheaTable)) {
michael@0 4153 return false;
michael@0 4154 }
michael@0 4155
michael@0 4156 #define SET_UNSIGNED(field,src) aMetrics.field = uint16_t(src) * mFUnitsConvFactor
michael@0 4157 #define SET_SIGNED(field,src) aMetrics.field = int16_t(src) * mFUnitsConvFactor
michael@0 4158
michael@0 4159 SET_UNSIGNED(maxAdvance, hhea->advanceWidthMax);
michael@0 4160 SET_SIGNED(maxAscent, hhea->ascender);
michael@0 4161 SET_SIGNED(maxDescent, -int16_t(hhea->descender));
michael@0 4162 SET_SIGNED(externalLeading, hhea->lineGap);
michael@0 4163
michael@0 4164 // 'post' table is required for underline metrics
michael@0 4165 gfxFontEntry::AutoTable postTable(mFontEntry, kPostTableTag);
michael@0 4166 if (!postTable) {
michael@0 4167 return true; // no 'post' table -> sfnt is not valid
michael@0 4168 }
michael@0 4169 const PostTable *post =
michael@0 4170 reinterpret_cast<const PostTable*>(hb_blob_get_data(postTable, &len));
michael@0 4171 if (len < offsetof(PostTable, underlineThickness) + sizeof(uint16_t)) {
michael@0 4172 return true; // bad post table -> sfnt is not valid
michael@0 4173 }
michael@0 4174
michael@0 4175 SET_SIGNED(underlineOffset, post->underlinePosition);
michael@0 4176 SET_UNSIGNED(underlineSize, post->underlineThickness);
michael@0 4177
michael@0 4178 // 'OS/2' table is optional, if not found we'll estimate xHeight
michael@0 4179 // and aveCharWidth by measuring glyphs
michael@0 4180 gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
michael@0 4181 if (os2Table) {
michael@0 4182 const OS2Table *os2 =
michael@0 4183 reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
michael@0 4184 if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
michael@0 4185 uint16_t(os2->version) >= 2) {
michael@0 4186 // version 2 and later includes the x-height field
michael@0 4187 SET_SIGNED(xHeight, os2->sxHeight);
michael@0 4188 // Abs because of negative xHeight seen in Kokonor (Tibetan) font
michael@0 4189 aMetrics.xHeight = Abs(aMetrics.xHeight);
michael@0 4190 }
michael@0 4191 // this should always be present in any valid OS/2 of any version
michael@0 4192 if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
michael@0 4193 SET_SIGNED(aveCharWidth, os2->xAvgCharWidth);
michael@0 4194 SET_SIGNED(subscriptOffset, os2->ySubscriptYOffset);
michael@0 4195 SET_SIGNED(superscriptOffset, os2->ySuperscriptYOffset);
michael@0 4196 SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
michael@0 4197 SET_SIGNED(strikeoutOffset, os2->yStrikeoutPosition);
michael@0 4198
michael@0 4199 // for fonts with USE_TYPO_METRICS set in the fsSelection field,
michael@0 4200 // and for all OpenType math fonts (having a 'MATH' table),
michael@0 4201 // let the OS/2 sTypo* metrics override those from the hhea table
michael@0 4202 // (see http://www.microsoft.com/typography/otspec/os2.htm#fss)
michael@0 4203 const uint16_t kUseTypoMetricsMask = 1 << 7;
michael@0 4204 if ((uint16_t(os2->fsSelection) & kUseTypoMetricsMask) ||
michael@0 4205 mFontEntry->HasFontTable(TRUETYPE_TAG('M','A','T','H'))) {
michael@0 4206 SET_SIGNED(maxAscent, os2->sTypoAscender);
michael@0 4207 SET_SIGNED(maxDescent, - int16_t(os2->sTypoDescender));
michael@0 4208 SET_SIGNED(externalLeading, os2->sTypoLineGap);
michael@0 4209 }
michael@0 4210 }
michael@0 4211 }
michael@0 4212
michael@0 4213 mIsValid = true;
michael@0 4214
michael@0 4215 return true;
michael@0 4216 }
michael@0 4217
michael@0 4218 static double
michael@0 4219 RoundToNearestMultiple(double aValue, double aFraction)
michael@0 4220 {
michael@0 4221 return floor(aValue/aFraction + 0.5) * aFraction;
michael@0 4222 }
michael@0 4223
michael@0 4224 void gfxFont::CalculateDerivedMetrics(Metrics& aMetrics)
michael@0 4225 {
michael@0 4226 aMetrics.maxAscent =
michael@0 4227 ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1/1024.0));
michael@0 4228 aMetrics.maxDescent =
michael@0 4229 ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1/1024.0));
michael@0 4230
michael@0 4231 if (aMetrics.xHeight <= 0) {
michael@0 4232 // only happens if we couldn't find either font metrics
michael@0 4233 // or a char to measure;
michael@0 4234 // pick an arbitrary value that's better than zero
michael@0 4235 aMetrics.xHeight = aMetrics.maxAscent * DEFAULT_XHEIGHT_FACTOR;
michael@0 4236 }
michael@0 4237
michael@0 4238 aMetrics.maxHeight = aMetrics.maxAscent + aMetrics.maxDescent;
michael@0 4239
michael@0 4240 if (aMetrics.maxHeight - aMetrics.emHeight > 0.0) {
michael@0 4241 aMetrics.internalLeading = aMetrics.maxHeight - aMetrics.emHeight;
michael@0 4242 } else {
michael@0 4243 aMetrics.internalLeading = 0.0;
michael@0 4244 }
michael@0 4245
michael@0 4246 aMetrics.emAscent = aMetrics.maxAscent * aMetrics.emHeight
michael@0 4247 / aMetrics.maxHeight;
michael@0 4248 aMetrics.emDescent = aMetrics.emHeight - aMetrics.emAscent;
michael@0 4249
michael@0 4250 if (GetFontEntry()->IsFixedPitch()) {
michael@0 4251 // Some Quartz fonts are fixed pitch, but there's some glyph with a bigger
michael@0 4252 // advance than the average character width... this forces
michael@0 4253 // those fonts to be recognized like fixed pitch fonts by layout.
michael@0 4254 aMetrics.maxAdvance = aMetrics.aveCharWidth;
michael@0 4255 }
michael@0 4256
michael@0 4257 if (!aMetrics.subscriptOffset) {
michael@0 4258 aMetrics.subscriptOffset = aMetrics.xHeight;
michael@0 4259 }
michael@0 4260 if (!aMetrics.superscriptOffset) {
michael@0 4261 aMetrics.superscriptOffset = aMetrics.xHeight;
michael@0 4262 }
michael@0 4263
michael@0 4264 if (!aMetrics.strikeoutOffset) {
michael@0 4265 aMetrics.strikeoutOffset = aMetrics.xHeight * 0.5;
michael@0 4266 }
michael@0 4267 if (!aMetrics.strikeoutSize) {
michael@0 4268 aMetrics.strikeoutSize = aMetrics.underlineSize;
michael@0 4269 }
michael@0 4270 }
michael@0 4271
michael@0 4272 void
michael@0 4273 gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont)
michael@0 4274 {
michael@0 4275 // Even if this font size is zero, this font is created with non-zero size.
michael@0 4276 // However, for layout and others, we should return the metrics of zero size font.
michael@0 4277 if (mStyle.size == 0.0) {
michael@0 4278 memset(aMetrics, 0, sizeof(gfxFont::Metrics));
michael@0 4279 return;
michael@0 4280 }
michael@0 4281
michael@0 4282 // MS (P)Gothic and MS (P)Mincho are not having suitable values in their super script offset.
michael@0 4283 // If the values are not suitable, we should use x-height instead of them.
michael@0 4284 // See https://bugzilla.mozilla.org/show_bug.cgi?id=353632
michael@0 4285 if (aMetrics->superscriptOffset <= 0 ||
michael@0 4286 aMetrics->superscriptOffset >= aMetrics->maxAscent) {
michael@0 4287 aMetrics->superscriptOffset = aMetrics->xHeight;
michael@0 4288 }
michael@0 4289 // And also checking the case of sub script offset. The old gfx for win has checked this too.
michael@0 4290 if (aMetrics->subscriptOffset <= 0 ||
michael@0 4291 aMetrics->subscriptOffset >= aMetrics->maxAscent) {
michael@0 4292 aMetrics->subscriptOffset = aMetrics->xHeight;
michael@0 4293 }
michael@0 4294
michael@0 4295 aMetrics->underlineSize = std::max(1.0, aMetrics->underlineSize);
michael@0 4296 aMetrics->strikeoutSize = std::max(1.0, aMetrics->strikeoutSize);
michael@0 4297
michael@0 4298 aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -1.0);
michael@0 4299
michael@0 4300 if (aMetrics->maxAscent < 1.0) {
michael@0 4301 // We cannot draw strikeout line and overline in the ascent...
michael@0 4302 aMetrics->underlineSize = 0;
michael@0 4303 aMetrics->underlineOffset = 0;
michael@0 4304 aMetrics->strikeoutSize = 0;
michael@0 4305 aMetrics->strikeoutOffset = 0;
michael@0 4306 return;
michael@0 4307 }
michael@0 4308
michael@0 4309 /**
michael@0 4310 * Some CJK fonts have bad underline offset. Therefore, if this is such font,
michael@0 4311 * we need to lower the underline offset to bottom of *em* descent.
michael@0 4312 * However, if this is system font, we should not do this for the rendering compatibility with
michael@0 4313 * another application's UI on the platform.
michael@0 4314 * XXX Should not use this hack if the font size is too small?
michael@0 4315 * Such text cannot be read, this might be used for tight CSS rendering? (E.g., Acid2)
michael@0 4316 */
michael@0 4317 if (!mStyle.systemFont && aIsBadUnderlineFont) {
michael@0 4318 // First, we need 2 pixels between baseline and underline at least. Because many CJK characters
michael@0 4319 // put their glyphs on the baseline, so, 1 pixel is too close for CJK characters.
michael@0 4320 aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -2.0);
michael@0 4321
michael@0 4322 // Next, we put the underline to bottom of below of the descent space.
michael@0 4323 if (aMetrics->internalLeading + aMetrics->externalLeading > aMetrics->underlineSize) {
michael@0 4324 aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -aMetrics->emDescent);
michael@0 4325 } else {
michael@0 4326 aMetrics->underlineOffset = std::min(aMetrics->underlineOffset,
michael@0 4327 aMetrics->underlineSize - aMetrics->emDescent);
michael@0 4328 }
michael@0 4329 }
michael@0 4330 // If underline positioned is too far from the text, descent position is preferred so that underline
michael@0 4331 // will stay within the boundary.
michael@0 4332 else if (aMetrics->underlineSize - aMetrics->underlineOffset > aMetrics->maxDescent) {
michael@0 4333 if (aMetrics->underlineSize > aMetrics->maxDescent)
michael@0 4334 aMetrics->underlineSize = std::max(aMetrics->maxDescent, 1.0);
michael@0 4335 // The max underlineOffset is 1px (the min underlineSize is 1px, and min maxDescent is 0px.)
michael@0 4336 aMetrics->underlineOffset = aMetrics->underlineSize - aMetrics->maxDescent;
michael@0 4337 }
michael@0 4338
michael@0 4339 // If strikeout line is overflowed from the ascent, the line should be resized and moved for
michael@0 4340 // that being in the ascent space.
michael@0 4341 // Note that the strikeoutOffset is *middle* of the strikeout line position.
michael@0 4342 gfxFloat halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
michael@0 4343 if (halfOfStrikeoutSize + aMetrics->strikeoutOffset > aMetrics->maxAscent) {
michael@0 4344 if (aMetrics->strikeoutSize > aMetrics->maxAscent) {
michael@0 4345 aMetrics->strikeoutSize = std::max(aMetrics->maxAscent, 1.0);
michael@0 4346 halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
michael@0 4347 }
michael@0 4348 gfxFloat ascent = floor(aMetrics->maxAscent + 0.5);
michael@0 4349 aMetrics->strikeoutOffset = std::max(halfOfStrikeoutSize, ascent / 2.0);
michael@0 4350 }
michael@0 4351
michael@0 4352 // If overline is larger than the ascent, the line should be resized.
michael@0 4353 if (aMetrics->underlineSize > aMetrics->maxAscent) {
michael@0 4354 aMetrics->underlineSize = aMetrics->maxAscent;
michael@0 4355 }
michael@0 4356 }
michael@0 4357
michael@0 4358 gfxFloat
michael@0 4359 gfxFont::SynthesizeSpaceWidth(uint32_t aCh)
michael@0 4360 {
michael@0 4361 // return an appropriate width for various Unicode space characters
michael@0 4362 // that we "fake" if they're not actually present in the font;
michael@0 4363 // returns negative value if the char is not a known space.
michael@0 4364 switch (aCh) {
michael@0 4365 case 0x2000: // en quad
michael@0 4366 case 0x2002: return GetAdjustedSize() / 2; // en space
michael@0 4367 case 0x2001: // em quad
michael@0 4368 case 0x2003: return GetAdjustedSize(); // em space
michael@0 4369 case 0x2004: return GetAdjustedSize() / 3; // three-per-em space
michael@0 4370 case 0x2005: return GetAdjustedSize() / 4; // four-per-em space
michael@0 4371 case 0x2006: return GetAdjustedSize() / 6; // six-per-em space
michael@0 4372 case 0x2007: return GetMetrics().zeroOrAveCharWidth; // figure space
michael@0 4373 case 0x2008: return GetMetrics().spaceWidth; // punctuation space
michael@0 4374 case 0x2009: return GetAdjustedSize() / 5; // thin space
michael@0 4375 case 0x200a: return GetAdjustedSize() / 10; // hair space
michael@0 4376 case 0x202f: return GetAdjustedSize() / 5; // narrow no-break space
michael@0 4377 default: return -1.0;
michael@0 4378 }
michael@0 4379 }
michael@0 4380
michael@0 4381 /*static*/ size_t
michael@0 4382 gfxFont::WordCacheEntrySizeOfExcludingThis(CacheHashEntry* aHashEntry,
michael@0 4383 MallocSizeOf aMallocSizeOf,
michael@0 4384 void* aUserArg)
michael@0 4385 {
michael@0 4386 return aMallocSizeOf(aHashEntry->mShapedWord.get());
michael@0 4387 }
michael@0 4388
michael@0 4389 void
michael@0 4390 gfxFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
michael@0 4391 FontCacheSizes* aSizes) const
michael@0 4392 {
michael@0 4393 for (uint32_t i = 0; i < mGlyphExtentsArray.Length(); ++i) {
michael@0 4394 aSizes->mFontInstances +=
michael@0 4395 mGlyphExtentsArray[i]->SizeOfIncludingThis(aMallocSizeOf);
michael@0 4396 }
michael@0 4397 if (mWordCache) {
michael@0 4398 aSizes->mShapedWords +=
michael@0 4399 mWordCache->SizeOfExcludingThis(WordCacheEntrySizeOfExcludingThis,
michael@0 4400 aMallocSizeOf);
michael@0 4401 }
michael@0 4402 }
michael@0 4403
michael@0 4404 void
michael@0 4405 gfxFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
michael@0 4406 FontCacheSizes* aSizes) const
michael@0 4407 {
michael@0 4408 aSizes->mFontInstances += aMallocSizeOf(this);
michael@0 4409 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 4410 }
michael@0 4411
michael@0 4412 void
michael@0 4413 gfxFont::AddGlyphChangeObserver(GlyphChangeObserver *aObserver)
michael@0 4414 {
michael@0 4415 if (!mGlyphChangeObservers) {
michael@0 4416 mGlyphChangeObservers = new nsTHashtable<nsPtrHashKey<GlyphChangeObserver> >;
michael@0 4417 }
michael@0 4418 mGlyphChangeObservers->PutEntry(aObserver);
michael@0 4419 }
michael@0 4420
michael@0 4421 void
michael@0 4422 gfxFont::RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver)
michael@0 4423 {
michael@0 4424 NS_ASSERTION(mGlyphChangeObservers, "No observers registered");
michael@0 4425 NS_ASSERTION(mGlyphChangeObservers->Contains(aObserver), "Observer not registered");
michael@0 4426 mGlyphChangeObservers->RemoveEntry(aObserver);
michael@0 4427 }
michael@0 4428
michael@0 4429 gfxGlyphExtents::~gfxGlyphExtents()
michael@0 4430 {
michael@0 4431 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
michael@0 4432 gGlyphExtentsWidthsTotalSize +=
michael@0 4433 mContainedGlyphWidths.SizeOfExcludingThis(&FontCacheMallocSizeOf);
michael@0 4434 gGlyphExtentsCount++;
michael@0 4435 #endif
michael@0 4436 MOZ_COUNT_DTOR(gfxGlyphExtents);
michael@0 4437 }
michael@0 4438
michael@0 4439 bool
michael@0 4440 gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
michael@0 4441 gfxContext *aContext, uint32_t aGlyphID, gfxRect *aExtents)
michael@0 4442 {
michael@0 4443 HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID);
michael@0 4444 if (!entry) {
michael@0 4445 if (!aContext) {
michael@0 4446 NS_WARNING("Could not get glyph extents (no aContext)");
michael@0 4447 return false;
michael@0 4448 }
michael@0 4449
michael@0 4450 if (aFont->SetupCairoFont(aContext)) {
michael@0 4451 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
michael@0 4452 ++gGlyphExtentsSetupLazyTight;
michael@0 4453 #endif
michael@0 4454 aFont->SetupGlyphExtents(aContext, aGlyphID, true, this);
michael@0 4455 entry = mTightGlyphExtents.GetEntry(aGlyphID);
michael@0 4456 }
michael@0 4457 if (!entry) {
michael@0 4458 NS_WARNING("Could not get glyph extents");
michael@0 4459 return false;
michael@0 4460 }
michael@0 4461 }
michael@0 4462
michael@0 4463 *aExtents = gfxRect(entry->x, entry->y, entry->width, entry->height);
michael@0 4464 return true;
michael@0 4465 }
michael@0 4466
michael@0 4467 gfxGlyphExtents::GlyphWidths::~GlyphWidths()
michael@0 4468 {
michael@0 4469 uint32_t i, count = mBlocks.Length();
michael@0 4470 for (i = 0; i < count; ++i) {
michael@0 4471 uintptr_t bits = mBlocks[i];
michael@0 4472 if (bits && !(bits & 0x1)) {
michael@0 4473 delete[] reinterpret_cast<uint16_t *>(bits);
michael@0 4474 }
michael@0 4475 }
michael@0 4476 }
michael@0 4477
michael@0 4478 uint32_t
michael@0 4479 gfxGlyphExtents::GlyphWidths::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 4480 {
michael@0 4481 uint32_t i;
michael@0 4482 uint32_t size = mBlocks.SizeOfExcludingThis(aMallocSizeOf);
michael@0 4483 for (i = 0; i < mBlocks.Length(); ++i) {
michael@0 4484 uintptr_t bits = mBlocks[i];
michael@0 4485 if (bits && !(bits & 0x1)) {
michael@0 4486 size += aMallocSizeOf(reinterpret_cast<void*>(bits));
michael@0 4487 }
michael@0 4488 }
michael@0 4489 return size;
michael@0 4490 }
michael@0 4491
michael@0 4492 void
michael@0 4493 gfxGlyphExtents::GlyphWidths::Set(uint32_t aGlyphID, uint16_t aWidth)
michael@0 4494 {
michael@0 4495 uint32_t block = aGlyphID >> BLOCK_SIZE_BITS;
michael@0 4496 uint32_t len = mBlocks.Length();
michael@0 4497 if (block >= len) {
michael@0 4498 uintptr_t *elems = mBlocks.AppendElements(block + 1 - len);
michael@0 4499 if (!elems)
michael@0 4500 return;
michael@0 4501 memset(elems, 0, sizeof(uintptr_t)*(block + 1 - len));
michael@0 4502 }
michael@0 4503
michael@0 4504 uintptr_t bits = mBlocks[block];
michael@0 4505 uint32_t glyphOffset = aGlyphID & (BLOCK_SIZE - 1);
michael@0 4506 if (!bits) {
michael@0 4507 mBlocks[block] = MakeSingle(glyphOffset, aWidth);
michael@0 4508 return;
michael@0 4509 }
michael@0 4510
michael@0 4511 uint16_t *newBlock;
michael@0 4512 if (bits & 0x1) {
michael@0 4513 // Expand the block to a real block. We could avoid this by checking
michael@0 4514 // glyphOffset == GetGlyphOffset(bits), but that never happens so don't bother
michael@0 4515 newBlock = new uint16_t[BLOCK_SIZE];
michael@0 4516 if (!newBlock)
michael@0 4517 return;
michael@0 4518 uint32_t i;
michael@0 4519 for (i = 0; i < BLOCK_SIZE; ++i) {
michael@0 4520 newBlock[i] = INVALID_WIDTH;
michael@0 4521 }
michael@0 4522 newBlock[GetGlyphOffset(bits)] = GetWidth(bits);
michael@0 4523 mBlocks[block] = reinterpret_cast<uintptr_t>(newBlock);
michael@0 4524 } else {
michael@0 4525 newBlock = reinterpret_cast<uint16_t *>(bits);
michael@0 4526 }
michael@0 4527 newBlock[glyphOffset] = aWidth;
michael@0 4528 }
michael@0 4529
michael@0 4530 void
michael@0 4531 gfxGlyphExtents::SetTightGlyphExtents(uint32_t aGlyphID, const gfxRect& aExtentsAppUnits)
michael@0 4532 {
michael@0 4533 HashEntry *entry = mTightGlyphExtents.PutEntry(aGlyphID);
michael@0 4534 if (!entry)
michael@0 4535 return;
michael@0 4536 entry->x = aExtentsAppUnits.X();
michael@0 4537 entry->y = aExtentsAppUnits.Y();
michael@0 4538 entry->width = aExtentsAppUnits.Width();
michael@0 4539 entry->height = aExtentsAppUnits.Height();
michael@0 4540 }
michael@0 4541
michael@0 4542 size_t
michael@0 4543 gfxGlyphExtents::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 4544 {
michael@0 4545 return mContainedGlyphWidths.SizeOfExcludingThis(aMallocSizeOf) +
michael@0 4546 mTightGlyphExtents.SizeOfExcludingThis(nullptr, aMallocSizeOf);
michael@0 4547 }
michael@0 4548
michael@0 4549 size_t
michael@0 4550 gfxGlyphExtents::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 4551 {
michael@0 4552 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 4553 }
michael@0 4554
michael@0 4555 gfxFontGroup::gfxFontGroup(const nsAString& aFamilies,
michael@0 4556 const gfxFontStyle *aStyle,
michael@0 4557 gfxUserFontSet *aUserFontSet)
michael@0 4558 : mFamilies(aFamilies)
michael@0 4559 , mStyle(*aStyle)
michael@0 4560 , mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
michael@0 4561 , mHyphenWidth(-1)
michael@0 4562 , mUserFontSet(aUserFontSet)
michael@0 4563 , mTextPerf(nullptr)
michael@0 4564 , mPageLang(gfxPlatform::GetFontPrefLangFor(aStyle->language))
michael@0 4565 , mSkipDrawing(false)
michael@0 4566 {
michael@0 4567 // We don't use SetUserFontSet() here, as we want to unconditionally call
michael@0 4568 // BuildFontList() rather than only do UpdateFontList() if it changed.
michael@0 4569 mCurrGeneration = GetGeneration();
michael@0 4570 BuildFontList();
michael@0 4571 }
michael@0 4572
michael@0 4573 void
michael@0 4574 gfxFontGroup::BuildFontList()
michael@0 4575 {
michael@0 4576 // "#if" to be removed once all platforms are moved to gfxPlatformFontList interface
michael@0 4577 // and subclasses of gfxFontGroup eliminated
michael@0 4578 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID)
michael@0 4579 ForEachFont(FindPlatformFont, this);
michael@0 4580
michael@0 4581 if (mFonts.Length() == 0) {
michael@0 4582 bool needsBold;
michael@0 4583 gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
michael@0 4584 gfxFontFamily *defaultFamily = pfl->GetDefaultFont(&mStyle);
michael@0 4585 NS_ASSERTION(defaultFamily,
michael@0 4586 "invalid default font returned by GetDefaultFont");
michael@0 4587
michael@0 4588 if (defaultFamily) {
michael@0 4589 gfxFontEntry *fe = defaultFamily->FindFontForStyle(mStyle,
michael@0 4590 needsBold);
michael@0 4591 if (fe) {
michael@0 4592 nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle,
michael@0 4593 needsBold);
michael@0 4594 if (font) {
michael@0 4595 mFonts.AppendElement(FamilyFace(defaultFamily, font));
michael@0 4596 }
michael@0 4597 }
michael@0 4598 }
michael@0 4599
michael@0 4600 if (mFonts.Length() == 0) {
michael@0 4601 // Try for a "font of last resort...."
michael@0 4602 // Because an empty font list would be Really Bad for later code
michael@0 4603 // that assumes it will be able to get valid metrics for layout,
michael@0 4604 // just look for the first usable font and put in the list.
michael@0 4605 // (see bug 554544)
michael@0 4606 nsAutoTArray<nsRefPtr<gfxFontFamily>,200> families;
michael@0 4607 pfl->GetFontFamilyList(families);
michael@0 4608 uint32_t count = families.Length();
michael@0 4609 for (uint32_t i = 0; i < count; ++i) {
michael@0 4610 gfxFontEntry *fe = families[i]->FindFontForStyle(mStyle,
michael@0 4611 needsBold);
michael@0 4612 if (fe) {
michael@0 4613 nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle,
michael@0 4614 needsBold);
michael@0 4615 if (font) {
michael@0 4616 mFonts.AppendElement(FamilyFace(families[i], font));
michael@0 4617 break;
michael@0 4618 }
michael@0 4619 }
michael@0 4620 }
michael@0 4621 }
michael@0 4622
michael@0 4623 if (mFonts.Length() == 0) {
michael@0 4624 // an empty font list at this point is fatal; we're not going to
michael@0 4625 // be able to do even the most basic layout operations
michael@0 4626 char msg[256]; // CHECK buffer length if revising message below
michael@0 4627 sprintf(msg, "unable to find a usable font (%.220s)",
michael@0 4628 NS_ConvertUTF16toUTF8(mFamilies).get());
michael@0 4629 NS_RUNTIMEABORT(msg);
michael@0 4630 }
michael@0 4631 }
michael@0 4632
michael@0 4633 if (!mStyle.systemFont) {
michael@0 4634 uint32_t count = mFonts.Length();
michael@0 4635 for (uint32_t i = 0; i < count; ++i) {
michael@0 4636 gfxFont* font = mFonts[i].Font();
michael@0 4637 if (font->GetFontEntry()->mIsBadUnderlineFont) {
michael@0 4638 gfxFloat first = mFonts[0].Font()->GetMetrics().underlineOffset;
michael@0 4639 gfxFloat bad = font->GetMetrics().underlineOffset;
michael@0 4640 mUnderlineOffset = std::min(first, bad);
michael@0 4641 break;
michael@0 4642 }
michael@0 4643 }
michael@0 4644 }
michael@0 4645 #endif
michael@0 4646 }
michael@0 4647
michael@0 4648 bool
michael@0 4649 gfxFontGroup::FindPlatformFont(const nsAString& aName,
michael@0 4650 const nsACString& aGenericName,
michael@0 4651 bool aUseFontSet,
michael@0 4652 void *aClosure)
michael@0 4653 {
michael@0 4654 gfxFontGroup *fontGroup = static_cast<gfxFontGroup*>(aClosure);
michael@0 4655 const gfxFontStyle *fontStyle = fontGroup->GetStyle();
michael@0 4656
michael@0 4657 bool needsBold;
michael@0 4658 gfxFontFamily *family = nullptr;
michael@0 4659 gfxFontEntry *fe = nullptr;
michael@0 4660
michael@0 4661 if (aUseFontSet) {
michael@0 4662 // First, look up in the user font set...
michael@0 4663 // If the fontSet matches the family, we must not look for a platform
michael@0 4664 // font of the same name, even if we fail to actually get a fontEntry
michael@0 4665 // here; we'll fall back to the next name in the CSS font-family list.
michael@0 4666 gfxUserFontSet *fs = fontGroup->GetUserFontSet();
michael@0 4667 if (fs) {
michael@0 4668 // If the fontSet matches the family, but the font has not yet finished
michael@0 4669 // loading (nor has its load timeout fired), the fontGroup should wait
michael@0 4670 // for the download, and not actually draw its text yet.
michael@0 4671 family = fs->GetFamily(aName);
michael@0 4672 if (family) {
michael@0 4673 bool waitForUserFont = false;
michael@0 4674 fe = fs->FindFontEntry(family, *fontStyle,
michael@0 4675 needsBold, waitForUserFont);
michael@0 4676 if (!fe && waitForUserFont) {
michael@0 4677 fontGroup->mSkipDrawing = true;
michael@0 4678 }
michael@0 4679 }
michael@0 4680 }
michael@0 4681 }
michael@0 4682
michael@0 4683 // Not known in the user font set ==> check system fonts
michael@0 4684 // XXX: Fallback is bad..
michael@0 4685 if (!family) {
michael@0 4686 gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
michael@0 4687 family = fontList->FindFamily(aName);
michael@0 4688 if (family) {
michael@0 4689 fe = family->FindFontForStyle(*fontStyle, needsBold);
michael@0 4690 }
michael@0 4691 }
michael@0 4692
michael@0 4693 // add to the font group, unless it's already there
michael@0 4694 if (fe && !fontGroup->HasFont(fe)) {
michael@0 4695 nsRefPtr<gfxFont> font = fe->FindOrMakeFont(fontStyle, needsBold);
michael@0 4696 if (font) {
michael@0 4697 fontGroup->mFonts.AppendElement(FamilyFace(family, font));
michael@0 4698 }
michael@0 4699 }
michael@0 4700
michael@0 4701 return true;
michael@0 4702 }
michael@0 4703
michael@0 4704 bool
michael@0 4705 gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry)
michael@0 4706 {
michael@0 4707 uint32_t count = mFonts.Length();
michael@0 4708 for (uint32_t i = 0; i < count; ++i) {
michael@0 4709 if (mFonts[i].Font()->GetFontEntry() == aFontEntry)
michael@0 4710 return true;
michael@0 4711 }
michael@0 4712 return false;
michael@0 4713 }
michael@0 4714
michael@0 4715 gfxFontGroup::~gfxFontGroup()
michael@0 4716 {
michael@0 4717 mFonts.Clear();
michael@0 4718 }
michael@0 4719
michael@0 4720 gfxFontGroup *
michael@0 4721 gfxFontGroup::Copy(const gfxFontStyle *aStyle)
michael@0 4722 {
michael@0 4723 gfxFontGroup *fg = new gfxFontGroup(mFamilies, aStyle, mUserFontSet);
michael@0 4724 fg->SetTextPerfMetrics(mTextPerf);
michael@0 4725 return fg;
michael@0 4726 }
michael@0 4727
michael@0 4728 bool
michael@0 4729 gfxFontGroup::IsInvalidChar(uint8_t ch)
michael@0 4730 {
michael@0 4731 return ((ch & 0x7f) < 0x20 || ch == 0x7f);
michael@0 4732 }
michael@0 4733
michael@0 4734 bool
michael@0 4735 gfxFontGroup::IsInvalidChar(char16_t ch)
michael@0 4736 {
michael@0 4737 // All printable 7-bit ASCII values are OK
michael@0 4738 if (ch >= ' ' && ch < 0x7f) {
michael@0 4739 return false;
michael@0 4740 }
michael@0 4741 // No point in sending non-printing control chars through font shaping
michael@0 4742 if (ch <= 0x9f) {
michael@0 4743 return true;
michael@0 4744 }
michael@0 4745 return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
michael@0 4746 (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/)) ||
michael@0 4747 IsBidiControl(ch));
michael@0 4748 }
michael@0 4749
michael@0 4750 bool
michael@0 4751 gfxFontGroup::ForEachFont(FontCreationCallback fc,
michael@0 4752 void *closure)
michael@0 4753 {
michael@0 4754 return ForEachFontInternal(mFamilies, mStyle.language,
michael@0 4755 true, true, true, fc, closure);
michael@0 4756 }
michael@0 4757
michael@0 4758 bool
michael@0 4759 gfxFontGroup::ForEachFont(const nsAString& aFamilies,
michael@0 4760 nsIAtom *aLanguage,
michael@0 4761 FontCreationCallback fc,
michael@0 4762 void *closure)
michael@0 4763 {
michael@0 4764 return ForEachFontInternal(aFamilies, aLanguage,
michael@0 4765 false, true, true, fc, closure);
michael@0 4766 }
michael@0 4767
michael@0 4768 struct ResolveData {
michael@0 4769 ResolveData(gfxFontGroup::FontCreationCallback aCallback,
michael@0 4770 nsACString& aGenericFamily,
michael@0 4771 bool aUseFontSet,
michael@0 4772 void *aClosure) :
michael@0 4773 mCallback(aCallback),
michael@0 4774 mGenericFamily(aGenericFamily),
michael@0 4775 mUseFontSet(aUseFontSet),
michael@0 4776 mClosure(aClosure) {
michael@0 4777 }
michael@0 4778 gfxFontGroup::FontCreationCallback mCallback;
michael@0 4779 nsCString mGenericFamily;
michael@0 4780 bool mUseFontSet;
michael@0 4781 void *mClosure;
michael@0 4782 };
michael@0 4783
michael@0 4784 bool
michael@0 4785 gfxFontGroup::ForEachFontInternal(const nsAString& aFamilies,
michael@0 4786 nsIAtom *aLanguage,
michael@0 4787 bool aResolveGeneric,
michael@0 4788 bool aResolveFontName,
michael@0 4789 bool aUseFontSet,
michael@0 4790 FontCreationCallback fc,
michael@0 4791 void *closure)
michael@0 4792 {
michael@0 4793 const char16_t kSingleQuote = char16_t('\'');
michael@0 4794 const char16_t kDoubleQuote = char16_t('\"');
michael@0 4795 const char16_t kComma = char16_t(',');
michael@0 4796
michael@0 4797 nsIAtom *groupAtom = nullptr;
michael@0 4798 nsAutoCString groupString;
michael@0 4799 if (aLanguage) {
michael@0 4800 if (!gLangService) {
michael@0 4801 CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
michael@0 4802 }
michael@0 4803 if (gLangService) {
michael@0 4804 nsresult rv;
michael@0 4805 groupAtom = gLangService->GetLanguageGroup(aLanguage, &rv);
michael@0 4806 }
michael@0 4807 }
michael@0 4808 if (!groupAtom) {
michael@0 4809 groupAtom = nsGkAtoms::Unicode;
michael@0 4810 }
michael@0 4811 groupAtom->ToUTF8String(groupString);
michael@0 4812
michael@0 4813 nsPromiseFlatString families(aFamilies);
michael@0 4814 const char16_t *p, *p_end;
michael@0 4815 families.BeginReading(p);
michael@0 4816 families.EndReading(p_end);
michael@0 4817 nsAutoString family;
michael@0 4818 nsAutoCString lcFamily;
michael@0 4819 nsAutoString genericFamily;
michael@0 4820
michael@0 4821 while (p < p_end) {
michael@0 4822 while (nsCRT::IsAsciiSpace(*p) || *p == kComma)
michael@0 4823 if (++p == p_end)
michael@0 4824 return true;
michael@0 4825
michael@0 4826 bool generic;
michael@0 4827 if (*p == kSingleQuote || *p == kDoubleQuote) {
michael@0 4828 // quoted font family
michael@0 4829 char16_t quoteMark = *p;
michael@0 4830 if (++p == p_end)
michael@0 4831 return true;
michael@0 4832 const char16_t *nameStart = p;
michael@0 4833
michael@0 4834 // XXX What about CSS character escapes?
michael@0 4835 while (*p != quoteMark)
michael@0 4836 if (++p == p_end)
michael@0 4837 return true;
michael@0 4838
michael@0 4839 family = Substring(nameStart, p);
michael@0 4840 generic = false;
michael@0 4841 genericFamily.SetIsVoid(true);
michael@0 4842
michael@0 4843 while (++p != p_end && *p != kComma)
michael@0 4844 /* nothing */ ;
michael@0 4845
michael@0 4846 } else {
michael@0 4847 // unquoted font family
michael@0 4848 const char16_t *nameStart = p;
michael@0 4849 while (++p != p_end && *p != kComma)
michael@0 4850 /* nothing */ ;
michael@0 4851
michael@0 4852 family = Substring(nameStart, p);
michael@0 4853 family.CompressWhitespace(false, true);
michael@0 4854
michael@0 4855 if (aResolveGeneric &&
michael@0 4856 (family.LowerCaseEqualsLiteral("serif") ||
michael@0 4857 family.LowerCaseEqualsLiteral("sans-serif") ||
michael@0 4858 family.LowerCaseEqualsLiteral("monospace") ||
michael@0 4859 family.LowerCaseEqualsLiteral("cursive") ||
michael@0 4860 family.LowerCaseEqualsLiteral("fantasy")))
michael@0 4861 {
michael@0 4862 generic = true;
michael@0 4863
michael@0 4864 ToLowerCase(NS_LossyConvertUTF16toASCII(family), lcFamily);
michael@0 4865
michael@0 4866 nsAutoCString prefName("font.name.");
michael@0 4867 prefName.Append(lcFamily);
michael@0 4868 prefName.AppendLiteral(".");
michael@0 4869 prefName.Append(groupString);
michael@0 4870
michael@0 4871 nsAdoptingString value = Preferences::GetString(prefName.get());
michael@0 4872 if (value) {
michael@0 4873 CopyASCIItoUTF16(lcFamily, genericFamily);
michael@0 4874 family = value;
michael@0 4875 }
michael@0 4876 } else {
michael@0 4877 generic = false;
michael@0 4878 genericFamily.SetIsVoid(true);
michael@0 4879 }
michael@0 4880 }
michael@0 4881
michael@0 4882 NS_LossyConvertUTF16toASCII gf(genericFamily);
michael@0 4883 if (generic) {
michael@0 4884 ForEachFontInternal(family, groupAtom, false,
michael@0 4885 aResolveFontName, false,
michael@0 4886 fc, closure);
michael@0 4887 } else if (!family.IsEmpty()) {
michael@0 4888 if (aResolveFontName) {
michael@0 4889 ResolveData data(fc, gf, aUseFontSet, closure);
michael@0 4890 bool aborted = false, needsBold;
michael@0 4891 nsresult rv = NS_OK;
michael@0 4892 bool foundFamily = false;
michael@0 4893 bool waitForUserFont = false;
michael@0 4894 gfxFontEntry *fe = nullptr;
michael@0 4895 if (aUseFontSet && mUserFontSet) {
michael@0 4896 gfxFontFamily *fam = mUserFontSet->GetFamily(family);
michael@0 4897 if (fam) {
michael@0 4898 fe = mUserFontSet->FindFontEntry(fam, mStyle,
michael@0 4899 needsBold,
michael@0 4900 waitForUserFont);
michael@0 4901 }
michael@0 4902 }
michael@0 4903 if (fe) {
michael@0 4904 gfxFontGroup::FontResolverProc(family, &data);
michael@0 4905 } else {
michael@0 4906 if (waitForUserFont) {
michael@0 4907 mSkipDrawing = true;
michael@0 4908 }
michael@0 4909 if (!foundFamily) {
michael@0 4910 gfxPlatform *pf = gfxPlatform::GetPlatform();
michael@0 4911 // XXX: Fallback is bad
michael@0 4912 rv = pf->ResolveFontName(family,
michael@0 4913 gfxFontGroup::FontResolverProc,
michael@0 4914 &data, aborted);
michael@0 4915 }
michael@0 4916 }
michael@0 4917 if (NS_FAILED(rv) || aborted)
michael@0 4918 return false;
michael@0 4919 }
michael@0 4920 else {
michael@0 4921 if (!fc(family, gf, aUseFontSet, closure))
michael@0 4922 return false;
michael@0 4923 }
michael@0 4924 }
michael@0 4925
michael@0 4926 if (generic && aResolveGeneric) {
michael@0 4927 nsAutoCString prefName("font.name-list.");
michael@0 4928 prefName.Append(lcFamily);
michael@0 4929 prefName.AppendLiteral(".");
michael@0 4930 prefName.Append(groupString);
michael@0 4931 nsAdoptingString value = Preferences::GetString(prefName.get());
michael@0 4932 if (value) {
michael@0 4933 ForEachFontInternal(value, groupAtom, false,
michael@0 4934 aResolveFontName, false,
michael@0 4935 fc, closure);
michael@0 4936 }
michael@0 4937 }
michael@0 4938
michael@0 4939 ++p; // may advance past p_end
michael@0 4940 }
michael@0 4941
michael@0 4942 return true;
michael@0 4943 }
michael@0 4944
michael@0 4945 bool
michael@0 4946 gfxFontGroup::FontResolverProc(const nsAString& aName, void *aClosure)
michael@0 4947 {
michael@0 4948 ResolveData *data = reinterpret_cast<ResolveData*>(aClosure);
michael@0 4949 return (data->mCallback)(aName, data->mGenericFamily, data->mUseFontSet,
michael@0 4950 data->mClosure);
michael@0 4951 }
michael@0 4952
michael@0 4953 gfxTextRun *
michael@0 4954 gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags)
michael@0 4955 {
michael@0 4956 aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
michael@0 4957 return gfxTextRun::Create(aParams, 0, this, aFlags);
michael@0 4958 }
michael@0 4959
michael@0 4960 gfxTextRun *
michael@0 4961 gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags)
michael@0 4962 {
michael@0 4963 aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
michael@0 4964
michael@0 4965 gfxTextRun *textRun = gfxTextRun::Create(aParams, 1, this, aFlags);
michael@0 4966 if (!textRun) {
michael@0 4967 return nullptr;
michael@0 4968 }
michael@0 4969
michael@0 4970 gfxFont *font = GetFontAt(0);
michael@0 4971 if (MOZ_UNLIKELY(GetStyle()->size == 0)) {
michael@0 4972 // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
michael@0 4973 // them, and always create at least size 1 fonts, i.e. they still
michael@0 4974 // render something for size 0 fonts.
michael@0 4975 textRun->AddGlyphRun(font, gfxTextRange::kFontGroup, 0, false);
michael@0 4976 }
michael@0 4977 else {
michael@0 4978 if (font->GetSpaceGlyph()) {
michael@0 4979 // Normally, the font has a cached space glyph, so we can avoid
michael@0 4980 // the cost of calling FindFontForChar.
michael@0 4981 textRun->SetSpaceGlyph(font, aParams->mContext, 0);
michael@0 4982 } else {
michael@0 4983 // In case the primary font doesn't have <space> (bug 970891),
michael@0 4984 // find one that does.
michael@0 4985 uint8_t matchType;
michael@0 4986 nsRefPtr<gfxFont> spaceFont =
michael@0 4987 FindFontForChar(' ', 0, MOZ_SCRIPT_LATIN, nullptr, &matchType);
michael@0 4988 if (spaceFont) {
michael@0 4989 textRun->SetSpaceGlyph(spaceFont, aParams->mContext, 0);
michael@0 4990 }
michael@0 4991 }
michael@0 4992 }
michael@0 4993
michael@0 4994 // Note that the gfxGlyphExtents glyph bounds storage for the font will
michael@0 4995 // always contain an entry for the font's space glyph, so we don't have
michael@0 4996 // to call FetchGlyphExtents here.
michael@0 4997 return textRun;
michael@0 4998 }
michael@0 4999
michael@0 5000 gfxTextRun *
michael@0 5001 gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
michael@0 5002 const Parameters *aParams, uint32_t aFlags)
michael@0 5003 {
michael@0 5004 gfxTextRun *textRun =
michael@0 5005 gfxTextRun::Create(aParams, aLength, this, aFlags);
michael@0 5006 if (!textRun) {
michael@0 5007 return nullptr;
michael@0 5008 }
michael@0 5009
michael@0 5010 textRun->AddGlyphRun(GetFontAt(0), gfxTextRange::kFontGroup, 0, false);
michael@0 5011 return textRun;
michael@0 5012 }
michael@0 5013
michael@0 5014 gfxTextRun *
michael@0 5015 gfxFontGroup::MakeHyphenTextRun(gfxContext *aCtx, uint32_t aAppUnitsPerDevUnit)
michael@0 5016 {
michael@0 5017 // only use U+2010 if it is supported by the first font in the group;
michael@0 5018 // it's better to use ASCII '-' from the primary font than to fall back to
michael@0 5019 // U+2010 from some other, possibly poorly-matching face
michael@0 5020 static const char16_t hyphen = 0x2010;
michael@0 5021 gfxFont *font = GetFontAt(0);
michael@0 5022 if (font && font->HasCharacter(hyphen)) {
michael@0 5023 return MakeTextRun(&hyphen, 1, aCtx, aAppUnitsPerDevUnit,
michael@0 5024 gfxFontGroup::TEXT_IS_PERSISTENT);
michael@0 5025 }
michael@0 5026
michael@0 5027 static const uint8_t dash = '-';
michael@0 5028 return MakeTextRun(&dash, 1, aCtx, aAppUnitsPerDevUnit,
michael@0 5029 gfxFontGroup::TEXT_IS_PERSISTENT);
michael@0 5030 }
michael@0 5031
michael@0 5032 gfxFloat
michael@0 5033 gfxFontGroup::GetHyphenWidth(gfxTextRun::PropertyProvider *aProvider)
michael@0 5034 {
michael@0 5035 if (mHyphenWidth < 0) {
michael@0 5036 nsRefPtr<gfxContext> ctx(aProvider->GetContext());
michael@0 5037 if (ctx) {
michael@0 5038 nsAutoPtr<gfxTextRun>
michael@0 5039 hyphRun(MakeHyphenTextRun(ctx,
michael@0 5040 aProvider->GetAppUnitsPerDevUnit()));
michael@0 5041 mHyphenWidth = hyphRun.get() ?
michael@0 5042 hyphRun->GetAdvanceWidth(0, hyphRun->GetLength(), nullptr) : 0;
michael@0 5043 }
michael@0 5044 }
michael@0 5045 return mHyphenWidth;
michael@0 5046 }
michael@0 5047
michael@0 5048 gfxTextRun *
michael@0 5049 gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
michael@0 5050 const Parameters *aParams, uint32_t aFlags)
michael@0 5051 {
michael@0 5052 if (aLength == 0) {
michael@0 5053 return MakeEmptyTextRun(aParams, aFlags);
michael@0 5054 }
michael@0 5055 if (aLength == 1 && aString[0] == ' ') {
michael@0 5056 return MakeSpaceTextRun(aParams, aFlags);
michael@0 5057 }
michael@0 5058
michael@0 5059 aFlags |= TEXT_IS_8BIT;
michael@0 5060
michael@0 5061 if (GetStyle()->size == 0) {
michael@0 5062 // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
michael@0 5063 // them, and always create at least size 1 fonts, i.e. they still
michael@0 5064 // render something for size 0 fonts.
michael@0 5065 return MakeBlankTextRun(aLength, aParams, aFlags);
michael@0 5066 }
michael@0 5067
michael@0 5068 gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength,
michael@0 5069 this, aFlags);
michael@0 5070 if (!textRun) {
michael@0 5071 return nullptr;
michael@0 5072 }
michael@0 5073
michael@0 5074 InitTextRun(aParams->mContext, textRun, aString, aLength);
michael@0 5075
michael@0 5076 textRun->FetchGlyphExtents(aParams->mContext);
michael@0 5077
michael@0 5078 return textRun;
michael@0 5079 }
michael@0 5080
michael@0 5081 gfxTextRun *
michael@0 5082 gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
michael@0 5083 const Parameters *aParams, uint32_t aFlags)
michael@0 5084 {
michael@0 5085 if (aLength == 0) {
michael@0 5086 return MakeEmptyTextRun(aParams, aFlags);
michael@0 5087 }
michael@0 5088 if (aLength == 1 && aString[0] == ' ') {
michael@0 5089 return MakeSpaceTextRun(aParams, aFlags);
michael@0 5090 }
michael@0 5091 if (GetStyle()->size == 0) {
michael@0 5092 return MakeBlankTextRun(aLength, aParams, aFlags);
michael@0 5093 }
michael@0 5094
michael@0 5095 gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength,
michael@0 5096 this, aFlags);
michael@0 5097 if (!textRun) {
michael@0 5098 return nullptr;
michael@0 5099 }
michael@0 5100
michael@0 5101 InitTextRun(aParams->mContext, textRun, aString, aLength);
michael@0 5102
michael@0 5103 textRun->FetchGlyphExtents(aParams->mContext);
michael@0 5104
michael@0 5105 return textRun;
michael@0 5106 }
michael@0 5107
michael@0 5108 template<typename T>
michael@0 5109 void
michael@0 5110 gfxFontGroup::InitTextRun(gfxContext *aContext,
michael@0 5111 gfxTextRun *aTextRun,
michael@0 5112 const T *aString,
michael@0 5113 uint32_t aLength)
michael@0 5114 {
michael@0 5115 NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run");
michael@0 5116
michael@0 5117 // we need to do numeral processing even on 8-bit text,
michael@0 5118 // in case we're converting Western to Hindi/Arabic digits
michael@0 5119 int32_t numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
michael@0 5120 nsAutoArrayPtr<char16_t> transformedString;
michael@0 5121 if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
michael@0 5122 // scan the string for numerals that may need to be transformed;
michael@0 5123 // if we find any, we'll make a local copy here and use that for
michael@0 5124 // font matching and glyph generation/shaping
michael@0 5125 bool prevIsArabic =
michael@0 5126 (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR) != 0;
michael@0 5127 for (uint32_t i = 0; i < aLength; ++i) {
michael@0 5128 char16_t origCh = aString[i];
michael@0 5129 char16_t newCh = HandleNumberInChar(origCh, prevIsArabic, numOption);
michael@0 5130 if (newCh != origCh) {
michael@0 5131 if (!transformedString) {
michael@0 5132 transformedString = new char16_t[aLength];
michael@0 5133 if (sizeof(T) == sizeof(char16_t)) {
michael@0 5134 memcpy(transformedString.get(), aString, i * sizeof(char16_t));
michael@0 5135 } else {
michael@0 5136 for (uint32_t j = 0; j < i; ++j) {
michael@0 5137 transformedString[j] = aString[j];
michael@0 5138 }
michael@0 5139 }
michael@0 5140 }
michael@0 5141 }
michael@0 5142 if (transformedString) {
michael@0 5143 transformedString[i] = newCh;
michael@0 5144 }
michael@0 5145 prevIsArabic = IS_ARABIC_CHAR(newCh);
michael@0 5146 }
michael@0 5147 }
michael@0 5148
michael@0 5149 #ifdef PR_LOGGING
michael@0 5150 PRLogModuleInfo *log = (mStyle.systemFont ?
michael@0 5151 gfxPlatform::GetLog(eGfxLog_textrunui) :
michael@0 5152 gfxPlatform::GetLog(eGfxLog_textrun));
michael@0 5153 #endif
michael@0 5154
michael@0 5155 if (sizeof(T) == sizeof(uint8_t) && !transformedString) {
michael@0 5156
michael@0 5157 #ifdef PR_LOGGING
michael@0 5158 if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_WARNING))) {
michael@0 5159 nsAutoCString lang;
michael@0 5160 mStyle.language->ToUTF8String(lang);
michael@0 5161 nsAutoCString str((const char*)aString, aLength);
michael@0 5162 PR_LOG(log, PR_LOG_WARNING,\
michael@0 5163 ("(%s) fontgroup: [%s] lang: %s script: %d len %d "
michael@0 5164 "weight: %d width: %d style: %s size: %6.2f %d-byte "
michael@0 5165 "TEXTRUN [%s] ENDTEXTRUN\n",
michael@0 5166 (mStyle.systemFont ? "textrunui" : "textrun"),
michael@0 5167 NS_ConvertUTF16toUTF8(mFamilies).get(),
michael@0 5168 lang.get(), MOZ_SCRIPT_LATIN, aLength,
michael@0 5169 uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
michael@0 5170 (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
michael@0 5171 (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
michael@0 5172 "normal")),
michael@0 5173 mStyle.size,
michael@0 5174 sizeof(T),
michael@0 5175 str.get()));
michael@0 5176 }
michael@0 5177 #endif
michael@0 5178
michael@0 5179 // the text is still purely 8-bit; bypass the script-run itemizer
michael@0 5180 // and treat it as a single Latin run
michael@0 5181 InitScriptRun(aContext, aTextRun, aString,
michael@0 5182 0, aLength, MOZ_SCRIPT_LATIN);
michael@0 5183 } else {
michael@0 5184 const char16_t *textPtr;
michael@0 5185 if (transformedString) {
michael@0 5186 textPtr = transformedString.get();
michael@0 5187 } else {
michael@0 5188 // typecast to avoid compilation error for the 8-bit version,
michael@0 5189 // even though this is dead code in that case
michael@0 5190 textPtr = reinterpret_cast<const char16_t*>(aString);
michael@0 5191 }
michael@0 5192
michael@0 5193 // split into script runs so that script can potentially influence
michael@0 5194 // the font matching process below
michael@0 5195 gfxScriptItemizer scriptRuns(textPtr, aLength);
michael@0 5196
michael@0 5197 uint32_t runStart = 0, runLimit = aLength;
michael@0 5198 int32_t runScript = MOZ_SCRIPT_LATIN;
michael@0 5199 while (scriptRuns.Next(runStart, runLimit, runScript)) {
michael@0 5200
michael@0 5201 #ifdef PR_LOGGING
michael@0 5202 if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_WARNING))) {
michael@0 5203 nsAutoCString lang;
michael@0 5204 mStyle.language->ToUTF8String(lang);
michael@0 5205 uint32_t runLen = runLimit - runStart;
michael@0 5206 PR_LOG(log, PR_LOG_WARNING,\
michael@0 5207 ("(%s) fontgroup: [%s] lang: %s script: %d len %d "
michael@0 5208 "weight: %d width: %d style: %s size: %6.2f %d-byte "
michael@0 5209 "TEXTRUN [%s] ENDTEXTRUN\n",
michael@0 5210 (mStyle.systemFont ? "textrunui" : "textrun"),
michael@0 5211 NS_ConvertUTF16toUTF8(mFamilies).get(),
michael@0 5212 lang.get(), runScript, runLen,
michael@0 5213 uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
michael@0 5214 (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
michael@0 5215 (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
michael@0 5216 "normal")),
michael@0 5217 mStyle.size,
michael@0 5218 sizeof(T),
michael@0 5219 NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
michael@0 5220 }
michael@0 5221 #endif
michael@0 5222
michael@0 5223 InitScriptRun(aContext, aTextRun, textPtr,
michael@0 5224 runStart, runLimit, runScript);
michael@0 5225 }
michael@0 5226 }
michael@0 5227
michael@0 5228 if (sizeof(T) == sizeof(char16_t) && aLength > 0) {
michael@0 5229 gfxTextRun::CompressedGlyph *glyph = aTextRun->GetCharacterGlyphs();
michael@0 5230 if (!glyph->IsSimpleGlyph()) {
michael@0 5231 glyph->SetClusterStart(true);
michael@0 5232 }
michael@0 5233 }
michael@0 5234
michael@0 5235 // It's possible for CoreText to omit glyph runs if it decides they contain
michael@0 5236 // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we
michael@0 5237 // need to eliminate them from the glyph run array to avoid drawing "partial
michael@0 5238 // ligatures" with the wrong font.
michael@0 5239 // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because
michael@0 5240 // it will iterate back over all glyphruns in the textrun, which leads to
michael@0 5241 // pathologically-bad perf in the case where a textrun contains many script
michael@0 5242 // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs
michael@0 5243 // every time a new script subrun is processed.
michael@0 5244 aTextRun->SanitizeGlyphRuns();
michael@0 5245
michael@0 5246 aTextRun->SortGlyphRuns();
michael@0 5247 }
michael@0 5248
michael@0 5249 template<typename T>
michael@0 5250 void
michael@0 5251 gfxFontGroup::InitScriptRun(gfxContext *aContext,
michael@0 5252 gfxTextRun *aTextRun,
michael@0 5253 const T *aString,
michael@0 5254 uint32_t aScriptRunStart,
michael@0 5255 uint32_t aScriptRunEnd,
michael@0 5256 int32_t aRunScript)
michael@0 5257 {
michael@0 5258 NS_ASSERTION(aScriptRunEnd > aScriptRunStart,
michael@0 5259 "don't call InitScriptRun for a zero-length run");
michael@0 5260
michael@0 5261 gfxFont *mainFont = GetFontAt(0);
michael@0 5262
michael@0 5263 uint32_t runStart = aScriptRunStart;
michael@0 5264 nsAutoTArray<gfxTextRange,3> fontRanges;
michael@0 5265 ComputeRanges(fontRanges, aString + aScriptRunStart,
michael@0 5266 aScriptRunEnd - aScriptRunStart, aRunScript);
michael@0 5267 uint32_t numRanges = fontRanges.Length();
michael@0 5268
michael@0 5269 for (uint32_t r = 0; r < numRanges; r++) {
michael@0 5270 const gfxTextRange& range = fontRanges[r];
michael@0 5271 uint32_t matchedLength = range.Length();
michael@0 5272 gfxFont *matchedFont = range.font;
michael@0 5273
michael@0 5274 // create the glyph run for this range
michael@0 5275 if (matchedFont) {
michael@0 5276 aTextRun->AddGlyphRun(matchedFont, range.matchType,
michael@0 5277 runStart, (matchedLength > 0));
michael@0 5278 // do glyph layout and record the resulting positioned glyphs
michael@0 5279 if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun, aString,
michael@0 5280 runStart, matchedLength,
michael@0 5281 aRunScript)) {
michael@0 5282 // glyph layout failed! treat as missing glyphs
michael@0 5283 matchedFont = nullptr;
michael@0 5284 }
michael@0 5285 } else {
michael@0 5286 aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
michael@0 5287 runStart, (matchedLength > 0));
michael@0 5288 }
michael@0 5289
michael@0 5290 if (!matchedFont) {
michael@0 5291 // We need to set cluster boundaries (and mark spaces) so that
michael@0 5292 // surrogate pairs, combining characters, etc behave properly,
michael@0 5293 // even if we don't have glyphs for them
michael@0 5294 aTextRun->SetupClusterBoundaries(runStart, aString + runStart,
michael@0 5295 matchedLength);
michael@0 5296
michael@0 5297 // various "missing" characters may need special handling,
michael@0 5298 // so we check for them here
michael@0 5299 uint32_t runLimit = runStart + matchedLength;
michael@0 5300 for (uint32_t index = runStart; index < runLimit; index++) {
michael@0 5301 T ch = aString[index];
michael@0 5302
michael@0 5303 // tab and newline are not to be displayed as hexboxes,
michael@0 5304 // but do need to be recorded in the textrun
michael@0 5305 if (ch == '\n') {
michael@0 5306 aTextRun->SetIsNewline(index);
michael@0 5307 continue;
michael@0 5308 }
michael@0 5309 if (ch == '\t') {
michael@0 5310 aTextRun->SetIsTab(index);
michael@0 5311 continue;
michael@0 5312 }
michael@0 5313
michael@0 5314 // for 16-bit textruns only, check for surrogate pairs and
michael@0 5315 // special Unicode spaces; omit these checks in 8-bit runs
michael@0 5316 if (sizeof(T) == sizeof(char16_t)) {
michael@0 5317 if (NS_IS_HIGH_SURROGATE(ch) &&
michael@0 5318 index + 1 < aScriptRunEnd &&
michael@0 5319 NS_IS_LOW_SURROGATE(aString[index + 1]))
michael@0 5320 {
michael@0 5321 aTextRun->SetMissingGlyph(index,
michael@0 5322 SURROGATE_TO_UCS4(ch,
michael@0 5323 aString[index + 1]),
michael@0 5324 mainFont);
michael@0 5325 index++;
michael@0 5326 continue;
michael@0 5327 }
michael@0 5328
michael@0 5329 // check if this is a known Unicode whitespace character that
michael@0 5330 // we can render using the space glyph with a custom width
michael@0 5331 gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
michael@0 5332 if (wid >= 0.0) {
michael@0 5333 nscoord advance =
michael@0 5334 aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
michael@0 5335 if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) {
michael@0 5336 aTextRun->GetCharacterGlyphs()[index].
michael@0 5337 SetSimpleGlyph(advance,
michael@0 5338 mainFont->GetSpaceGlyph());
michael@0 5339 } else {
michael@0 5340 gfxTextRun::DetailedGlyph detailedGlyph;
michael@0 5341 detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
michael@0 5342 detailedGlyph.mAdvance = advance;
michael@0 5343 detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
michael@0 5344 gfxShapedText::CompressedGlyph g;
michael@0 5345 g.SetComplex(true, true, 1);
michael@0 5346 aTextRun->SetGlyphs(index,
michael@0 5347 g, &detailedGlyph);
michael@0 5348 }
michael@0 5349 continue;
michael@0 5350 }
michael@0 5351 }
michael@0 5352
michael@0 5353 if (IsInvalidChar(ch)) {
michael@0 5354 // invalid chars are left as zero-width/invisible
michael@0 5355 continue;
michael@0 5356 }
michael@0 5357
michael@0 5358 // record char code so we can draw a box with the Unicode value
michael@0 5359 aTextRun->SetMissingGlyph(index, ch, mainFont);
michael@0 5360 }
michael@0 5361 }
michael@0 5362
michael@0 5363 runStart += matchedLength;
michael@0 5364 }
michael@0 5365 }
michael@0 5366
michael@0 5367 gfxTextRun *
michael@0 5368 gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
michael@0 5369 LazyReferenceContextGetter& aRefContextGetter)
michael@0 5370 {
michael@0 5371 if (mCachedEllipsisTextRun &&
michael@0 5372 mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
michael@0 5373 return mCachedEllipsisTextRun;
michael@0 5374 }
michael@0 5375
michael@0 5376 // Use a Unicode ellipsis if the font supports it,
michael@0 5377 // otherwise use three ASCII periods as fallback.
michael@0 5378 gfxFont* firstFont = GetFontAt(0);
michael@0 5379 nsString ellipsis = firstFont->HasCharacter(kEllipsisChar[0])
michael@0 5380 ? nsDependentString(kEllipsisChar,
michael@0 5381 ArrayLength(kEllipsisChar) - 1)
michael@0 5382 : nsDependentString(kASCIIPeriodsChar,
michael@0 5383 ArrayLength(kASCIIPeriodsChar) - 1);
michael@0 5384
michael@0 5385 nsRefPtr<gfxContext> refCtx = aRefContextGetter.GetRefContext();
michael@0 5386 Parameters params = {
michael@0 5387 refCtx, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
michael@0 5388 };
michael@0 5389 gfxTextRun* textRun =
michael@0 5390 MakeTextRun(ellipsis.get(), ellipsis.Length(), &params, TEXT_IS_PERSISTENT);
michael@0 5391 if (!textRun) {
michael@0 5392 return nullptr;
michael@0 5393 }
michael@0 5394 mCachedEllipsisTextRun = textRun;
michael@0 5395 textRun->ReleaseFontGroup(); // don't let the presence of a cached ellipsis
michael@0 5396 // textrun prolong the fontgroup's life
michael@0 5397 return textRun;
michael@0 5398 }
michael@0 5399
michael@0 5400 already_AddRefed<gfxFont>
michael@0 5401 gfxFontGroup::TryAllFamilyMembers(gfxFontFamily* aFamily, uint32_t aCh)
michael@0 5402 {
michael@0 5403 if (!aFamily->TestCharacterMap(aCh)) {
michael@0 5404 return nullptr;
michael@0 5405 }
michael@0 5406
michael@0 5407 // Note that we don't need the actual runScript in matchData for
michael@0 5408 // gfxFontFamily::SearchAllFontsForChar, it's only used for the
michael@0 5409 // system-fallback case. So we can just set it to 0 here.
michael@0 5410 GlobalFontMatch matchData(aCh, 0, &mStyle);
michael@0 5411 aFamily->SearchAllFontsForChar(&matchData);
michael@0 5412 gfxFontEntry *fe = matchData.mBestMatch;
michael@0 5413 if (!fe) {
michael@0 5414 return nullptr;
michael@0 5415 }
michael@0 5416
michael@0 5417 bool needsBold = mStyle.weight >= 600 && !fe->IsBold();
michael@0 5418 nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold);
michael@0 5419 return font.forget();
michael@0 5420 }
michael@0 5421
michael@0 5422 already_AddRefed<gfxFont>
michael@0 5423 gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
michael@0 5424 int32_t aRunScript, gfxFont *aPrevMatchedFont,
michael@0 5425 uint8_t *aMatchType)
michael@0 5426 {
michael@0 5427 // To optimize common cases, try the first font in the font-group
michael@0 5428 // before going into the more detailed checks below
michael@0 5429 uint32_t nextIndex = 0;
michael@0 5430 bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
michael@0 5431 bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
michael@0 5432 bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
michael@0 5433
michael@0 5434 if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
michael@0 5435 nsRefPtr<gfxFont> firstFont = mFonts[0].Font();
michael@0 5436 if (firstFont->HasCharacter(aCh)) {
michael@0 5437 *aMatchType = gfxTextRange::kFontGroup;
michael@0 5438 return firstFont.forget();
michael@0 5439 }
michael@0 5440 // It's possible that another font in the family (e.g. regular face,
michael@0 5441 // where the requested style was italic) will support the character
michael@0 5442 nsRefPtr<gfxFont> font = TryAllFamilyMembers(mFonts[0].Family(), aCh);
michael@0 5443 if (font) {
michael@0 5444 *aMatchType = gfxTextRange::kFontGroup;
michael@0 5445 return font.forget();
michael@0 5446 }
michael@0 5447 // we don't need to check the first font again below
michael@0 5448 ++nextIndex;
michael@0 5449 }
michael@0 5450
michael@0 5451 if (aPrevMatchedFont) {
michael@0 5452 // Don't switch fonts for control characters, regardless of
michael@0 5453 // whether they are present in the current font, as they won't
michael@0 5454 // actually be rendered (see bug 716229)
michael@0 5455 if (isJoinControl ||
michael@0 5456 GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
michael@0 5457 nsRefPtr<gfxFont> ret = aPrevMatchedFont;
michael@0 5458 return ret.forget();
michael@0 5459 }
michael@0 5460
michael@0 5461 // if previous character was a join-causer (ZWJ),
michael@0 5462 // use the same font as the previous range if we can
michael@0 5463 if (wasJoinCauser) {
michael@0 5464 if (aPrevMatchedFont->HasCharacter(aCh)) {
michael@0 5465 nsRefPtr<gfxFont> ret = aPrevMatchedFont;
michael@0 5466 return ret.forget();
michael@0 5467 }
michael@0 5468 }
michael@0 5469 }
michael@0 5470
michael@0 5471 // if this character is a variation selector,
michael@0 5472 // use the previous font regardless of whether it supports VS or not.
michael@0 5473 // otherwise the text run will be divided.
michael@0 5474 if (isVarSelector) {
michael@0 5475 if (aPrevMatchedFont) {
michael@0 5476 nsRefPtr<gfxFont> ret = aPrevMatchedFont;
michael@0 5477 return ret.forget();
michael@0 5478 }
michael@0 5479 // VS alone. it's meaningless to search different fonts
michael@0 5480 return nullptr;
michael@0 5481 }
michael@0 5482
michael@0 5483 // 1. check remaining fonts in the font group
michael@0 5484 uint32_t fontListLength = FontListLength();
michael@0 5485 for (uint32_t i = nextIndex; i < fontListLength; i++) {
michael@0 5486 nsRefPtr<gfxFont> font = mFonts[i].Font();
michael@0 5487 if (font->HasCharacter(aCh)) {
michael@0 5488 *aMatchType = gfxTextRange::kFontGroup;
michael@0 5489 return font.forget();
michael@0 5490 }
michael@0 5491
michael@0 5492 font = TryAllFamilyMembers(mFonts[i].Family(), aCh);
michael@0 5493 if (font) {
michael@0 5494 *aMatchType = gfxTextRange::kFontGroup;
michael@0 5495 return font.forget();
michael@0 5496 }
michael@0 5497 }
michael@0 5498
michael@0 5499 // if character is in Private Use Area, don't do matching against pref or system fonts
michael@0 5500 if ((aCh >= 0xE000 && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
michael@0 5501 return nullptr;
michael@0 5502
michael@0 5503 // 2. search pref fonts
michael@0 5504 nsRefPtr<gfxFont> font = WhichPrefFontSupportsChar(aCh);
michael@0 5505 if (font) {
michael@0 5506 *aMatchType = gfxTextRange::kPrefsFallback;
michael@0 5507 return font.forget();
michael@0 5508 }
michael@0 5509
michael@0 5510 // 3. use fallback fonts
michael@0 5511 // -- before searching for something else check the font used for the previous character
michael@0 5512 if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
michael@0 5513 *aMatchType = gfxTextRange::kSystemFallback;
michael@0 5514 nsRefPtr<gfxFont> ret = aPrevMatchedFont;
michael@0 5515 return ret.forget();
michael@0 5516 }
michael@0 5517
michael@0 5518 // never fall back for characters from unknown scripts
michael@0 5519 if (aRunScript == HB_SCRIPT_UNKNOWN) {
michael@0 5520 return nullptr;
michael@0 5521 }
michael@0 5522
michael@0 5523 // for known "space" characters, don't do a full system-fallback search;
michael@0 5524 // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
michael@0 5525 if (GetGeneralCategory(aCh) ==
michael@0 5526 HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
michael@0 5527 GetFontAt(0)->SynthesizeSpaceWidth(aCh) >= 0.0)
michael@0 5528 {
michael@0 5529 return nullptr;
michael@0 5530 }
michael@0 5531
michael@0 5532 // -- otherwise look for other stuff
michael@0 5533 *aMatchType = gfxTextRange::kSystemFallback;
michael@0 5534 font = WhichSystemFontSupportsChar(aCh, aRunScript);
michael@0 5535 return font.forget();
michael@0 5536 }
michael@0 5537
michael@0 5538 template<typename T>
michael@0 5539 void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
michael@0 5540 const T *aString, uint32_t aLength,
michael@0 5541 int32_t aRunScript)
michael@0 5542 {
michael@0 5543 NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
michael@0 5544 NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
michael@0 5545
michael@0 5546 uint32_t prevCh = 0;
michael@0 5547 int32_t lastRangeIndex = -1;
michael@0 5548
michael@0 5549 // initialize prevFont to the group's primary font, so that this will be
michael@0 5550 // used for string-initial control chars, etc rather than risk hitting font
michael@0 5551 // fallback for these (bug 716229)
michael@0 5552 gfxFont *prevFont = GetFontAt(0);
michael@0 5553
michael@0 5554 // if we use the initial value of prevFont, we treat this as a match from
michael@0 5555 // the font group; fixes bug 978313
michael@0 5556 uint8_t matchType = gfxTextRange::kFontGroup;
michael@0 5557
michael@0 5558 for (uint32_t i = 0; i < aLength; i++) {
michael@0 5559
michael@0 5560 const uint32_t origI = i; // save off in case we increase for surrogate
michael@0 5561
michael@0 5562 // set up current ch
michael@0 5563 uint32_t ch = aString[i];
michael@0 5564
michael@0 5565 // in 16-bit case only, check for surrogate pair
michael@0 5566 if (sizeof(T) == sizeof(char16_t)) {
michael@0 5567 if ((i + 1 < aLength) && NS_IS_HIGH_SURROGATE(ch) &&
michael@0 5568 NS_IS_LOW_SURROGATE(aString[i + 1])) {
michael@0 5569 i++;
michael@0 5570 ch = SURROGATE_TO_UCS4(ch, aString[i]);
michael@0 5571 }
michael@0 5572 }
michael@0 5573
michael@0 5574 if (ch == 0xa0) {
michael@0 5575 ch = ' ';
michael@0 5576 }
michael@0 5577
michael@0 5578 // find the font for this char
michael@0 5579 nsRefPtr<gfxFont> font =
michael@0 5580 FindFontForChar(ch, prevCh, aRunScript, prevFont, &matchType);
michael@0 5581
michael@0 5582 #ifndef RELEASE_BUILD
michael@0 5583 if (MOZ_UNLIKELY(mTextPerf)) {
michael@0 5584 if (matchType == gfxTextRange::kPrefsFallback) {
michael@0 5585 mTextPerf->current.fallbackPrefs++;
michael@0 5586 } else if (matchType == gfxTextRange::kSystemFallback) {
michael@0 5587 mTextPerf->current.fallbackSystem++;
michael@0 5588 }
michael@0 5589 }
michael@0 5590 #endif
michael@0 5591
michael@0 5592 prevCh = ch;
michael@0 5593
michael@0 5594 if (lastRangeIndex == -1) {
michael@0 5595 // first char ==> make a new range
michael@0 5596 aRanges.AppendElement(gfxTextRange(0, 1, font, matchType));
michael@0 5597 lastRangeIndex++;
michael@0 5598 prevFont = font;
michael@0 5599 } else {
michael@0 5600 // if font has changed, make a new range
michael@0 5601 gfxTextRange& prevRange = aRanges[lastRangeIndex];
michael@0 5602 if (prevRange.font != font || prevRange.matchType != matchType) {
michael@0 5603 // close out the previous range
michael@0 5604 prevRange.end = origI;
michael@0 5605 aRanges.AppendElement(gfxTextRange(origI, i + 1,
michael@0 5606 font, matchType));
michael@0 5607 lastRangeIndex++;
michael@0 5608
michael@0 5609 // update prevFont for the next match, *unless* we switched
michael@0 5610 // fonts on a ZWJ, in which case propagating the changed font
michael@0 5611 // is probably not a good idea (see bug 619511)
michael@0 5612 if (sizeof(T) == sizeof(uint8_t) ||
michael@0 5613 !gfxFontUtils::IsJoinCauser(ch))
michael@0 5614 {
michael@0 5615 prevFont = font;
michael@0 5616 }
michael@0 5617 }
michael@0 5618 }
michael@0 5619 }
michael@0 5620
michael@0 5621 aRanges[lastRangeIndex].end = aLength;
michael@0 5622 }
michael@0 5623
michael@0 5624 gfxUserFontSet*
michael@0 5625 gfxFontGroup::GetUserFontSet()
michael@0 5626 {
michael@0 5627 return mUserFontSet;
michael@0 5628 }
michael@0 5629
michael@0 5630 void
michael@0 5631 gfxFontGroup::SetUserFontSet(gfxUserFontSet *aUserFontSet)
michael@0 5632 {
michael@0 5633 if (aUserFontSet == mUserFontSet) {
michael@0 5634 return;
michael@0 5635 }
michael@0 5636 mUserFontSet = aUserFontSet;
michael@0 5637 mCurrGeneration = GetGeneration() - 1;
michael@0 5638 UpdateFontList();
michael@0 5639 }
michael@0 5640
michael@0 5641 uint64_t
michael@0 5642 gfxFontGroup::GetGeneration()
michael@0 5643 {
michael@0 5644 if (!mUserFontSet)
michael@0 5645 return 0;
michael@0 5646 return mUserFontSet->GetGeneration();
michael@0 5647 }
michael@0 5648
michael@0 5649 void
michael@0 5650 gfxFontGroup::UpdateFontList()
michael@0 5651 {
michael@0 5652 if (mCurrGeneration != GetGeneration()) {
michael@0 5653 // xxx - can probably improve this to detect when all fonts were found, so no need to update list
michael@0 5654 mFonts.Clear();
michael@0 5655 mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
michael@0 5656 mSkipDrawing = false;
michael@0 5657
michael@0 5658 // bug 548184 - need to clean up FT2, OS/2 platform code to use BuildFontList
michael@0 5659 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID)
michael@0 5660 BuildFontList();
michael@0 5661 #else
michael@0 5662 ForEachFont(FindPlatformFont, this);
michael@0 5663 #endif
michael@0 5664 mCurrGeneration = GetGeneration();
michael@0 5665 mCachedEllipsisTextRun = nullptr;
michael@0 5666 }
michael@0 5667 }
michael@0 5668
michael@0 5669 struct PrefFontCallbackData {
michael@0 5670 PrefFontCallbackData(nsTArray<nsRefPtr<gfxFontFamily> >& aFamiliesArray)
michael@0 5671 : mPrefFamilies(aFamiliesArray)
michael@0 5672 {}
michael@0 5673
michael@0 5674 nsTArray<nsRefPtr<gfxFontFamily> >& mPrefFamilies;
michael@0 5675
michael@0 5676 static bool AddFontFamilyEntry(eFontPrefLang aLang, const nsAString& aName, void *aClosure)
michael@0 5677 {
michael@0 5678 PrefFontCallbackData *prefFontData = static_cast<PrefFontCallbackData*>(aClosure);
michael@0 5679
michael@0 5680 gfxFontFamily *family = gfxPlatformFontList::PlatformFontList()->FindFamily(aName);
michael@0 5681 if (family) {
michael@0 5682 prefFontData->mPrefFamilies.AppendElement(family);
michael@0 5683 }
michael@0 5684 return true;
michael@0 5685 }
michael@0 5686 };
michael@0 5687
michael@0 5688 already_AddRefed<gfxFont>
michael@0 5689 gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh)
michael@0 5690 {
michael@0 5691 nsRefPtr<gfxFont> font;
michael@0 5692
michael@0 5693 // get the pref font list if it hasn't been set up already
michael@0 5694 uint32_t unicodeRange = FindCharUnicodeRange(aCh);
michael@0 5695 eFontPrefLang charLang = gfxPlatform::GetPlatform()->GetFontPrefLangFor(unicodeRange);
michael@0 5696
michael@0 5697 // if the last pref font was the first family in the pref list, no need to recheck through a list of families
michael@0 5698 if (mLastPrefFont && charLang == mLastPrefLang &&
michael@0 5699 mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) {
michael@0 5700 font = mLastPrefFont;
michael@0 5701 return font.forget();
michael@0 5702 }
michael@0 5703
michael@0 5704 // based on char lang and page lang, set up list of pref lang fonts to check
michael@0 5705 eFontPrefLang prefLangs[kMaxLenPrefLangList];
michael@0 5706 uint32_t i, numLangs = 0;
michael@0 5707
michael@0 5708 gfxPlatform::GetPlatform()->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
michael@0 5709
michael@0 5710 for (i = 0; i < numLangs; i++) {
michael@0 5711 nsAutoTArray<nsRefPtr<gfxFontFamily>, 5> families;
michael@0 5712 eFontPrefLang currentLang = prefLangs[i];
michael@0 5713
michael@0 5714 gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
michael@0 5715
michael@0 5716 // get the pref families for a single pref lang
michael@0 5717 if (!fontList->GetPrefFontFamilyEntries(currentLang, &families)) {
michael@0 5718 eFontPrefLang prefLangsToSearch[1] = { currentLang };
michael@0 5719 PrefFontCallbackData prefFontData(families);
michael@0 5720 gfxPlatform::ForEachPrefFont(prefLangsToSearch, 1, PrefFontCallbackData::AddFontFamilyEntry,
michael@0 5721 &prefFontData);
michael@0 5722 fontList->SetPrefFontFamilyEntries(currentLang, families);
michael@0 5723 }
michael@0 5724
michael@0 5725 // find the first pref font that includes the character
michael@0 5726 uint32_t j, numPrefs;
michael@0 5727 numPrefs = families.Length();
michael@0 5728 for (j = 0; j < numPrefs; j++) {
michael@0 5729 // look up the appropriate face
michael@0 5730 gfxFontFamily *family = families[j];
michael@0 5731 if (!family) continue;
michael@0 5732
michael@0 5733 // if a pref font is used, it's likely to be used again in the same text run.
michael@0 5734 // the style doesn't change so the face lookup can be cached rather than calling
michael@0 5735 // FindOrMakeFont repeatedly. speeds up FindFontForChar lookup times for subsequent
michael@0 5736 // pref font lookups
michael@0 5737 if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) {
michael@0 5738 font = mLastPrefFont;
michael@0 5739 return font.forget();
michael@0 5740 }
michael@0 5741
michael@0 5742 bool needsBold;
michael@0 5743 gfxFontEntry *fe = family->FindFontForStyle(mStyle, needsBold);
michael@0 5744 // if ch in cmap, create and return a gfxFont
michael@0 5745 if (fe && fe->TestCharacterMap(aCh)) {
michael@0 5746 nsRefPtr<gfxFont> prefFont = fe->FindOrMakeFont(&mStyle, needsBold);
michael@0 5747 if (!prefFont) continue;
michael@0 5748 mLastPrefFamily = family;
michael@0 5749 mLastPrefFont = prefFont;
michael@0 5750 mLastPrefLang = charLang;
michael@0 5751 mLastPrefFirstFont = (i == 0 && j == 0);
michael@0 5752 return prefFont.forget();
michael@0 5753 }
michael@0 5754
michael@0 5755 }
michael@0 5756 }
michael@0 5757
michael@0 5758 return nullptr;
michael@0 5759 }
michael@0 5760
michael@0 5761 already_AddRefed<gfxFont>
michael@0 5762 gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh, int32_t aRunScript)
michael@0 5763 {
michael@0 5764 gfxFontEntry *fe =
michael@0 5765 gfxPlatformFontList::PlatformFontList()->
michael@0 5766 SystemFindFontForChar(aCh, aRunScript, &mStyle);
michael@0 5767 if (fe) {
michael@0 5768 bool wantBold = mStyle.ComputeWeight() >= 6;
michael@0 5769 nsRefPtr<gfxFont> font =
michael@0 5770 fe->FindOrMakeFont(&mStyle, wantBold && !fe->IsBold());
michael@0 5771 return font.forget();
michael@0 5772 }
michael@0 5773
michael@0 5774 return nullptr;
michael@0 5775 }
michael@0 5776
michael@0 5777 /*static*/ void
michael@0 5778 gfxFontGroup::Shutdown()
michael@0 5779 {
michael@0 5780 NS_IF_RELEASE(gLangService);
michael@0 5781 }
michael@0 5782
michael@0 5783 nsILanguageAtomService* gfxFontGroup::gLangService = nullptr;
michael@0 5784
michael@0 5785
michael@0 5786 #define DEFAULT_PIXEL_FONT_SIZE 16.0f
michael@0 5787
michael@0 5788 /*static*/ uint32_t
michael@0 5789 gfxFontStyle::ParseFontLanguageOverride(const nsString& aLangTag)
michael@0 5790 {
michael@0 5791 if (!aLangTag.Length() || aLangTag.Length() > 4) {
michael@0 5792 return NO_FONT_LANGUAGE_OVERRIDE;
michael@0 5793 }
michael@0 5794 uint32_t index, result = 0;
michael@0 5795 for (index = 0; index < aLangTag.Length(); ++index) {
michael@0 5796 char16_t ch = aLangTag[index];
michael@0 5797 if (!nsCRT::IsAscii(ch)) { // valid tags are pure ASCII
michael@0 5798 return NO_FONT_LANGUAGE_OVERRIDE;
michael@0 5799 }
michael@0 5800 result = (result << 8) + ch;
michael@0 5801 }
michael@0 5802 while (index++ < 4) {
michael@0 5803 result = (result << 8) + 0x20;
michael@0 5804 }
michael@0 5805 return result;
michael@0 5806 }
michael@0 5807
michael@0 5808 gfxFontStyle::gfxFontStyle() :
michael@0 5809 language(nsGkAtoms::x_western),
michael@0 5810 size(DEFAULT_PIXEL_FONT_SIZE), sizeAdjust(0.0f),
michael@0 5811 languageOverride(NO_FONT_LANGUAGE_OVERRIDE),
michael@0 5812 weight(NS_FONT_WEIGHT_NORMAL), stretch(NS_FONT_STRETCH_NORMAL),
michael@0 5813 systemFont(true), printerFont(false), useGrayscaleAntialiasing(false),
michael@0 5814 style(NS_FONT_STYLE_NORMAL)
michael@0 5815 {
michael@0 5816 }
michael@0 5817
michael@0 5818 gfxFontStyle::gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
michael@0 5819 gfxFloat aSize, nsIAtom *aLanguage,
michael@0 5820 float aSizeAdjust, bool aSystemFont,
michael@0 5821 bool aPrinterFont,
michael@0 5822 const nsString& aLanguageOverride):
michael@0 5823 language(aLanguage),
michael@0 5824 size(aSize), sizeAdjust(aSizeAdjust),
michael@0 5825 languageOverride(ParseFontLanguageOverride(aLanguageOverride)),
michael@0 5826 weight(aWeight), stretch(aStretch),
michael@0 5827 systemFont(aSystemFont), printerFont(aPrinterFont),
michael@0 5828 useGrayscaleAntialiasing(false), style(aStyle)
michael@0 5829 {
michael@0 5830 MOZ_ASSERT(!mozilla::IsNaN(size));
michael@0 5831 MOZ_ASSERT(!mozilla::IsNaN(sizeAdjust));
michael@0 5832
michael@0 5833 if (weight > 900)
michael@0 5834 weight = 900;
michael@0 5835 if (weight < 100)
michael@0 5836 weight = 100;
michael@0 5837
michael@0 5838 if (size >= FONT_MAX_SIZE) {
michael@0 5839 size = FONT_MAX_SIZE;
michael@0 5840 sizeAdjust = 0.0;
michael@0 5841 } else if (size < 0.0) {
michael@0 5842 NS_WARNING("negative font size");
michael@0 5843 size = 0.0;
michael@0 5844 }
michael@0 5845
michael@0 5846 if (!language) {
michael@0 5847 NS_WARNING("null language");
michael@0 5848 language = nsGkAtoms::x_western;
michael@0 5849 }
michael@0 5850 }
michael@0 5851
michael@0 5852 gfxFontStyle::gfxFontStyle(const gfxFontStyle& aStyle) :
michael@0 5853 language(aStyle.language),
michael@0 5854 featureValueLookup(aStyle.featureValueLookup),
michael@0 5855 size(aStyle.size), sizeAdjust(aStyle.sizeAdjust),
michael@0 5856 languageOverride(aStyle.languageOverride),
michael@0 5857 weight(aStyle.weight), stretch(aStyle.stretch),
michael@0 5858 systemFont(aStyle.systemFont), printerFont(aStyle.printerFont),
michael@0 5859 useGrayscaleAntialiasing(aStyle.useGrayscaleAntialiasing),
michael@0 5860 style(aStyle.style)
michael@0 5861 {
michael@0 5862 featureSettings.AppendElements(aStyle.featureSettings);
michael@0 5863 alternateValues.AppendElements(aStyle.alternateValues);
michael@0 5864 }
michael@0 5865
michael@0 5866 int8_t
michael@0 5867 gfxFontStyle::ComputeWeight() const
michael@0 5868 {
michael@0 5869 int8_t baseWeight = (weight + 50) / 100;
michael@0 5870
michael@0 5871 if (baseWeight < 0)
michael@0 5872 baseWeight = 0;
michael@0 5873 if (baseWeight > 9)
michael@0 5874 baseWeight = 9;
michael@0 5875
michael@0 5876 return baseWeight;
michael@0 5877 }
michael@0 5878
michael@0 5879 void
michael@0 5880 gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
michael@0 5881 const char16_t *aString,
michael@0 5882 uint32_t aLength)
michael@0 5883 {
michael@0 5884 CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
michael@0 5885
michael@0 5886 gfxTextRun::CompressedGlyph extendCluster;
michael@0 5887 extendCluster.SetComplex(false, true, 0);
michael@0 5888
michael@0 5889 ClusterIterator iter(aString, aLength);
michael@0 5890
michael@0 5891 // the ClusterIterator won't be able to tell us if the string
michael@0 5892 // _begins_ with a cluster-extender, so we handle that here
michael@0 5893 if (aLength && IsClusterExtender(*aString)) {
michael@0 5894 *glyphs = extendCluster;
michael@0 5895 }
michael@0 5896
michael@0 5897 while (!iter.AtEnd()) {
michael@0 5898 if (*iter == char16_t(' ')) {
michael@0 5899 glyphs->SetIsSpace();
michael@0 5900 }
michael@0 5901 // advance iter to the next cluster-start (or end of text)
michael@0 5902 iter.Next();
michael@0 5903 // step past the first char of the cluster
michael@0 5904 aString++;
michael@0 5905 glyphs++;
michael@0 5906 // mark all the rest as cluster-continuations
michael@0 5907 while (aString < iter) {
michael@0 5908 *glyphs = extendCluster;
michael@0 5909 if (NS_IS_LOW_SURROGATE(*aString)) {
michael@0 5910 glyphs->SetIsLowSurrogate();
michael@0 5911 }
michael@0 5912 glyphs++;
michael@0 5913 aString++;
michael@0 5914 }
michael@0 5915 }
michael@0 5916 }
michael@0 5917
michael@0 5918 void
michael@0 5919 gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
michael@0 5920 const uint8_t *aString,
michael@0 5921 uint32_t aLength)
michael@0 5922 {
michael@0 5923 CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
michael@0 5924 const uint8_t *limit = aString + aLength;
michael@0 5925
michael@0 5926 while (aString < limit) {
michael@0 5927 if (*aString == uint8_t(' ')) {
michael@0 5928 glyphs->SetIsSpace();
michael@0 5929 }
michael@0 5930 aString++;
michael@0 5931 glyphs++;
michael@0 5932 }
michael@0 5933 }
michael@0 5934
michael@0 5935 gfxShapedText::DetailedGlyph *
michael@0 5936 gfxShapedText::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
michael@0 5937 {
michael@0 5938 NS_ASSERTION(aIndex < GetLength(), "Index out of range");
michael@0 5939
michael@0 5940 if (!mDetailedGlyphs) {
michael@0 5941 mDetailedGlyphs = new DetailedGlyphStore();
michael@0 5942 }
michael@0 5943
michael@0 5944 DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
michael@0 5945 if (!details) {
michael@0 5946 GetCharacterGlyphs()[aIndex].SetMissing(0);
michael@0 5947 return nullptr;
michael@0 5948 }
michael@0 5949
michael@0 5950 return details;
michael@0 5951 }
michael@0 5952
michael@0 5953 void
michael@0 5954 gfxShapedText::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
michael@0 5955 const DetailedGlyph *aGlyphs)
michael@0 5956 {
michael@0 5957 NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
michael@0 5958 NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
michael@0 5959 "First character can't be a ligature continuation!");
michael@0 5960
michael@0 5961 uint32_t glyphCount = aGlyph.GetGlyphCount();
michael@0 5962 if (glyphCount > 0) {
michael@0 5963 DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
michael@0 5964 if (!details) {
michael@0 5965 return;
michael@0 5966 }
michael@0 5967 memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
michael@0 5968 }
michael@0 5969 GetCharacterGlyphs()[aIndex] = aGlyph;
michael@0 5970 }
michael@0 5971
michael@0 5972 #define ZWNJ 0x200C
michael@0 5973 #define ZWJ 0x200D
michael@0 5974 // U+061C ARABIC LETTER MARK is expected to be added to XIDMOD_DEFAULT_IGNORABLE
michael@0 5975 // in a future Unicode update. Add it manually for now
michael@0 5976 #define ALM 0x061C
michael@0 5977 static inline bool
michael@0 5978 IsDefaultIgnorable(uint32_t aChar)
michael@0 5979 {
michael@0 5980 return GetIdentifierModification(aChar) == XIDMOD_DEFAULT_IGNORABLE ||
michael@0 5981 aChar == ZWNJ || aChar == ZWJ || aChar == ALM;
michael@0 5982 }
michael@0 5983
michael@0 5984 void
michael@0 5985 gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
michael@0 5986 {
michael@0 5987 uint8_t category = GetGeneralCategory(aChar);
michael@0 5988 if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
michael@0 5989 category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
michael@0 5990 {
michael@0 5991 GetCharacterGlyphs()[aIndex].SetComplex(false, true, 0);
michael@0 5992 }
michael@0 5993
michael@0 5994 DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
michael@0 5995 if (!details) {
michael@0 5996 return;
michael@0 5997 }
michael@0 5998
michael@0 5999 details->mGlyphID = aChar;
michael@0 6000 if (IsDefaultIgnorable(aChar)) {
michael@0 6001 // Setting advance width to zero will prevent drawing the hexbox
michael@0 6002 details->mAdvance = 0;
michael@0 6003 } else {
michael@0 6004 gfxFloat width =
michael@0 6005 std::max(aFont->GetMetrics().aveCharWidth,
michael@0 6006 gfxFontMissingGlyphs::GetDesiredMinWidth(aChar,
michael@0 6007 mAppUnitsPerDevUnit));
michael@0 6008 details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit);
michael@0 6009 }
michael@0 6010 details->mXOffset = 0;
michael@0 6011 details->mYOffset = 0;
michael@0 6012 GetCharacterGlyphs()[aIndex].SetMissing(1);
michael@0 6013 }
michael@0 6014
michael@0 6015 bool
michael@0 6016 gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh)
michael@0 6017 {
michael@0 6018 if (IsDefaultIgnorable(aCh)) {
michael@0 6019 DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
michael@0 6020 if (details) {
michael@0 6021 details->mGlyphID = aCh;
michael@0 6022 details->mAdvance = 0;
michael@0 6023 details->mXOffset = 0;
michael@0 6024 details->mYOffset = 0;
michael@0 6025 GetCharacterGlyphs()[aIndex].SetMissing(1);
michael@0 6026 return true;
michael@0 6027 }
michael@0 6028 }
michael@0 6029 return false;
michael@0 6030 }
michael@0 6031
michael@0 6032 void
michael@0 6033 gfxShapedText::AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
michael@0 6034 uint32_t aOffset,
michael@0 6035 uint32_t aLength)
michael@0 6036 {
michael@0 6037 uint32_t synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
michael@0 6038 CompressedGlyph *charGlyphs = GetCharacterGlyphs();
michael@0 6039 for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
michael@0 6040 CompressedGlyph *glyphData = charGlyphs + i;
michael@0 6041 if (glyphData->IsSimpleGlyph()) {
michael@0 6042 // simple glyphs ==> just add the advance
michael@0 6043 int32_t advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
michael@0 6044 if (CompressedGlyph::IsSimpleAdvance(advance)) {
michael@0 6045 glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
michael@0 6046 } else {
michael@0 6047 // rare case, tested by making this the default
michael@0 6048 uint32_t glyphIndex = glyphData->GetSimpleGlyph();
michael@0 6049 glyphData->SetComplex(true, true, 1);
michael@0 6050 DetailedGlyph detail = {glyphIndex, advance, 0, 0};
michael@0 6051 SetGlyphs(i, *glyphData, &detail);
michael@0 6052 }
michael@0 6053 } else {
michael@0 6054 // complex glyphs ==> add offset at cluster/ligature boundaries
michael@0 6055 uint32_t detailedLength = glyphData->GetGlyphCount();
michael@0 6056 if (detailedLength) {
michael@0 6057 DetailedGlyph *details = GetDetailedGlyphs(i);
michael@0 6058 if (!details) {
michael@0 6059 continue;
michael@0 6060 }
michael@0 6061 if (IsRightToLeft()) {
michael@0 6062 details[0].mAdvance += synAppUnitOffset;
michael@0 6063 } else {
michael@0 6064 details[detailedLength - 1].mAdvance += synAppUnitOffset;
michael@0 6065 }
michael@0 6066 }
michael@0 6067 }
michael@0 6068 }
michael@0 6069 }
michael@0 6070
michael@0 6071 bool
michael@0 6072 gfxTextRun::GlyphRunIterator::NextRun() {
michael@0 6073 if (mNextIndex >= mTextRun->mGlyphRuns.Length())
michael@0 6074 return false;
michael@0 6075 mGlyphRun = &mTextRun->mGlyphRuns[mNextIndex];
michael@0 6076 if (mGlyphRun->mCharacterOffset >= mEndOffset)
michael@0 6077 return false;
michael@0 6078
michael@0 6079 mStringStart = std::max(mStartOffset, mGlyphRun->mCharacterOffset);
michael@0 6080 uint32_t last = mNextIndex + 1 < mTextRun->mGlyphRuns.Length()
michael@0 6081 ? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->GetLength();
michael@0 6082 mStringEnd = std::min(mEndOffset, last);
michael@0 6083
michael@0 6084 ++mNextIndex;
michael@0 6085 return true;
michael@0 6086 }
michael@0 6087
michael@0 6088 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
michael@0 6089 static void
michael@0 6090 AccountStorageForTextRun(gfxTextRun *aTextRun, int32_t aSign)
michael@0 6091 {
michael@0 6092 // Ignores detailed glyphs... we don't know when those have been constructed
michael@0 6093 // Also ignores gfxSkipChars dynamic storage (which won't be anything
michael@0 6094 // for preformatted text)
michael@0 6095 // Also ignores GlyphRun array, again because it hasn't been constructed
michael@0 6096 // by the time this gets called. If there's only one glyphrun that's stored
michael@0 6097 // directly in the textrun anyway so no additional overhead.
michael@0 6098 uint32_t length = aTextRun->GetLength();
michael@0 6099 int32_t bytes = length * sizeof(gfxTextRun::CompressedGlyph);
michael@0 6100 bytes += sizeof(gfxTextRun);
michael@0 6101 gTextRunStorage += bytes*aSign;
michael@0 6102 gTextRunStorageHighWaterMark = std::max(gTextRunStorageHighWaterMark, gTextRunStorage);
michael@0 6103 }
michael@0 6104 #endif
michael@0 6105
michael@0 6106 // Helper for textRun creation to preallocate storage for glyph records;
michael@0 6107 // this function returns a pointer to the newly-allocated glyph storage.
michael@0 6108 // Returns nullptr if allocation fails.
michael@0 6109 void *
michael@0 6110 gfxTextRun::AllocateStorageForTextRun(size_t aSize, uint32_t aLength)
michael@0 6111 {
michael@0 6112 // Allocate the storage we need, returning nullptr on failure rather than
michael@0 6113 // throwing an exception (because web content can create huge runs).
michael@0 6114 void *storage = moz_malloc(aSize + aLength * sizeof(CompressedGlyph));
michael@0 6115 if (!storage) {
michael@0 6116 NS_WARNING("failed to allocate storage for text run!");
michael@0 6117 return nullptr;
michael@0 6118 }
michael@0 6119
michael@0 6120 // Initialize the glyph storage (beyond aSize) to zero
michael@0 6121 memset(reinterpret_cast<char*>(storage) + aSize, 0,
michael@0 6122 aLength * sizeof(CompressedGlyph));
michael@0 6123
michael@0 6124 return storage;
michael@0 6125 }
michael@0 6126
michael@0 6127 gfxTextRun *
michael@0 6128 gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
michael@0 6129 uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
michael@0 6130 {
michael@0 6131 void *storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
michael@0 6132 if (!storage) {
michael@0 6133 return nullptr;
michael@0 6134 }
michael@0 6135
michael@0 6136 return new (storage) gfxTextRun(aParams, aLength, aFontGroup, aFlags);
michael@0 6137 }
michael@0 6138
michael@0 6139 gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
michael@0 6140 uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
michael@0 6141 : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
michael@0 6142 , mUserData(aParams->mUserData)
michael@0 6143 , mFontGroup(aFontGroup)
michael@0 6144 , mReleasedFontGroup(false)
michael@0 6145 {
michael@0 6146 NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
michael@0 6147 MOZ_COUNT_CTOR(gfxTextRun);
michael@0 6148 NS_ADDREF(mFontGroup);
michael@0 6149
michael@0 6150 #ifndef RELEASE_BUILD
michael@0 6151 gfxTextPerfMetrics *tp = aFontGroup->GetTextPerfMetrics();
michael@0 6152 if (tp) {
michael@0 6153 tp->current.textrunConst++;
michael@0 6154 }
michael@0 6155 #endif
michael@0 6156
michael@0 6157 mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
michael@0 6158
michael@0 6159 if (aParams->mSkipChars) {
michael@0 6160 mSkipChars.TakeFrom(aParams->mSkipChars);
michael@0 6161 }
michael@0 6162
michael@0 6163 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
michael@0 6164 AccountStorageForTextRun(this, 1);
michael@0 6165 #endif
michael@0 6166
michael@0 6167 mSkipDrawing = mFontGroup->ShouldSkipDrawing();
michael@0 6168 }
michael@0 6169
michael@0 6170 gfxTextRun::~gfxTextRun()
michael@0 6171 {
michael@0 6172 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
michael@0 6173 AccountStorageForTextRun(this, -1);
michael@0 6174 #endif
michael@0 6175 #ifdef DEBUG
michael@0 6176 // Make it easy to detect a dead text run
michael@0 6177 mFlags = 0xFFFFFFFF;
michael@0 6178 #endif
michael@0 6179
michael@0 6180 // The cached ellipsis textrun (if any) in a fontgroup will have already
michael@0 6181 // been told to release its reference to the group, so we mustn't do that
michael@0 6182 // again here.
michael@0 6183 if (!mReleasedFontGroup) {
michael@0 6184 #ifndef RELEASE_BUILD
michael@0 6185 gfxTextPerfMetrics *tp = mFontGroup->GetTextPerfMetrics();
michael@0 6186 if (tp) {
michael@0 6187 tp->current.textrunDestr++;
michael@0 6188 }
michael@0 6189 #endif
michael@0 6190 NS_RELEASE(mFontGroup);
michael@0 6191 }
michael@0 6192
michael@0 6193 MOZ_COUNT_DTOR(gfxTextRun);
michael@0 6194 }
michael@0 6195
michael@0 6196 void
michael@0 6197 gfxTextRun::ReleaseFontGroup()
michael@0 6198 {
michael@0 6199 NS_ASSERTION(!mReleasedFontGroup, "doubly released!");
michael@0 6200 NS_RELEASE(mFontGroup);
michael@0 6201 mReleasedFontGroup = true;
michael@0 6202 }
michael@0 6203
michael@0 6204 bool
michael@0 6205 gfxTextRun::SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength,
michael@0 6206 uint8_t *aBreakBefore,
michael@0 6207 gfxContext *aRefContext)
michael@0 6208 {
michael@0 6209 NS_ASSERTION(aStart + aLength <= GetLength(), "Overflow");
michael@0 6210
michael@0 6211 uint32_t changed = 0;
michael@0 6212 uint32_t i;
michael@0 6213 CompressedGlyph *charGlyphs = mCharacterGlyphs + aStart;
michael@0 6214 for (i = 0; i < aLength; ++i) {
michael@0 6215 uint8_t canBreak = aBreakBefore[i];
michael@0 6216 if (canBreak && !charGlyphs[i].IsClusterStart()) {
michael@0 6217 // This can happen ... there is no guarantee that our linebreaking rules
michael@0 6218 // align with the platform's idea of what constitutes a cluster.
michael@0 6219 NS_WARNING("Break suggested inside cluster!");
michael@0 6220 canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE;
michael@0 6221 }
michael@0 6222 changed |= charGlyphs[i].SetCanBreakBefore(canBreak);
michael@0 6223 }
michael@0 6224 return changed != 0;
michael@0 6225 }
michael@0 6226
michael@0 6227 gfxTextRun::LigatureData
michael@0 6228 gfxTextRun::ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd,
michael@0 6229 PropertyProvider *aProvider)
michael@0 6230 {
michael@0 6231 NS_ASSERTION(aPartStart < aPartEnd, "Computing ligature data for empty range");
michael@0 6232 NS_ASSERTION(aPartEnd <= GetLength(), "Character length overflow");
michael@0 6233
michael@0 6234 LigatureData result;
michael@0 6235 CompressedGlyph *charGlyphs = mCharacterGlyphs;
michael@0 6236
michael@0 6237 uint32_t i;
michael@0 6238 for (i = aPartStart; !charGlyphs[i].IsLigatureGroupStart(); --i) {
michael@0 6239 NS_ASSERTION(i > 0, "Ligature at the start of the run??");
michael@0 6240 }
michael@0 6241 result.mLigatureStart = i;
michael@0 6242 for (i = aPartStart + 1; i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
michael@0 6243 }
michael@0 6244 result.mLigatureEnd = i;
michael@0 6245
michael@0 6246 int32_t ligatureWidth =
michael@0 6247 GetAdvanceForGlyphs(result.mLigatureStart, result.mLigatureEnd);
michael@0 6248 // Count the number of started clusters we have seen
michael@0 6249 uint32_t totalClusterCount = 0;
michael@0 6250 uint32_t partClusterIndex = 0;
michael@0 6251 uint32_t partClusterCount = 0;
michael@0 6252 for (i = result.mLigatureStart; i < result.mLigatureEnd; ++i) {
michael@0 6253 // Treat the first character of the ligature as the start of a
michael@0 6254 // cluster for our purposes of allocating ligature width to its
michael@0 6255 // characters.
michael@0 6256 if (i == result.mLigatureStart || charGlyphs[i].IsClusterStart()) {
michael@0 6257 ++totalClusterCount;
michael@0 6258 if (i < aPartStart) {
michael@0 6259 ++partClusterIndex;
michael@0 6260 } else if (i < aPartEnd) {
michael@0 6261 ++partClusterCount;
michael@0 6262 }
michael@0 6263 }
michael@0 6264 }
michael@0 6265 NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??");
michael@0 6266 result.mPartAdvance = partClusterIndex * (ligatureWidth / totalClusterCount);
michael@0 6267 result.mPartWidth = partClusterCount * (ligatureWidth / totalClusterCount);
michael@0 6268
michael@0 6269 // Any rounding errors are apportioned to the final part of the ligature,
michael@0 6270 // so that measuring all parts of a ligature and summing them is equal to
michael@0 6271 // the ligature width.
michael@0 6272 if (aPartEnd == result.mLigatureEnd) {
michael@0 6273 gfxFloat allParts = totalClusterCount * (ligatureWidth / totalClusterCount);
michael@0 6274 result.mPartWidth += ligatureWidth - allParts;
michael@0 6275 }
michael@0 6276
michael@0 6277 if (partClusterCount == 0) {
michael@0 6278 // nothing to draw
michael@0 6279 result.mClipBeforePart = result.mClipAfterPart = true;
michael@0 6280 } else {
michael@0 6281 // Determine whether we should clip before or after this part when
michael@0 6282 // drawing its slice of the ligature.
michael@0 6283 // We need to clip before the part if any cluster is drawn before
michael@0 6284 // this part.
michael@0 6285 result.mClipBeforePart = partClusterIndex > 0;
michael@0 6286 // We need to clip after the part if any cluster is drawn after
michael@0 6287 // this part.
michael@0 6288 result.mClipAfterPart = partClusterIndex + partClusterCount < totalClusterCount;
michael@0 6289 }
michael@0 6290
michael@0 6291 if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
michael@0 6292 gfxFont::Spacing spacing;
michael@0 6293 if (aPartStart == result.mLigatureStart) {
michael@0 6294 aProvider->GetSpacing(aPartStart, 1, &spacing);
michael@0 6295 result.mPartWidth += spacing.mBefore;
michael@0 6296 }
michael@0 6297 if (aPartEnd == result.mLigatureEnd) {
michael@0 6298 aProvider->GetSpacing(aPartEnd - 1, 1, &spacing);
michael@0 6299 result.mPartWidth += spacing.mAfter;
michael@0 6300 }
michael@0 6301 }
michael@0 6302
michael@0 6303 return result;
michael@0 6304 }
michael@0 6305
michael@0 6306 gfxFloat
michael@0 6307 gfxTextRun::ComputePartialLigatureWidth(uint32_t aPartStart, uint32_t aPartEnd,
michael@0 6308 PropertyProvider *aProvider)
michael@0 6309 {
michael@0 6310 if (aPartStart >= aPartEnd)
michael@0 6311 return 0;
michael@0 6312 LigatureData data = ComputeLigatureData(aPartStart, aPartEnd, aProvider);
michael@0 6313 return data.mPartWidth;
michael@0 6314 }
michael@0 6315
michael@0 6316 int32_t
michael@0 6317 gfxTextRun::GetAdvanceForGlyphs(uint32_t aStart, uint32_t aEnd)
michael@0 6318 {
michael@0 6319 const CompressedGlyph *glyphData = mCharacterGlyphs + aStart;
michael@0 6320 int32_t advance = 0;
michael@0 6321 uint32_t i;
michael@0 6322 for (i = aStart; i < aEnd; ++i, ++glyphData) {
michael@0 6323 if (glyphData->IsSimpleGlyph()) {
michael@0 6324 advance += glyphData->GetSimpleAdvance();
michael@0 6325 } else {
michael@0 6326 uint32_t glyphCount = glyphData->GetGlyphCount();
michael@0 6327 if (glyphCount == 0) {
michael@0 6328 continue;
michael@0 6329 }
michael@0 6330 const DetailedGlyph *details = GetDetailedGlyphs(i);
michael@0 6331 if (details) {
michael@0 6332 uint32_t j;
michael@0 6333 for (j = 0; j < glyphCount; ++j, ++details) {
michael@0 6334 advance += details->mAdvance;
michael@0 6335 }
michael@0 6336 }
michael@0 6337 }
michael@0 6338 }
michael@0 6339 return advance;
michael@0 6340 }
michael@0 6341
michael@0 6342 static void
michael@0 6343 GetAdjustedSpacing(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
michael@0 6344 gfxTextRun::PropertyProvider *aProvider,
michael@0 6345 gfxTextRun::PropertyProvider::Spacing *aSpacing)
michael@0 6346 {
michael@0 6347 if (aStart >= aEnd)
michael@0 6348 return;
michael@0 6349
michael@0 6350 aProvider->GetSpacing(aStart, aEnd - aStart, aSpacing);
michael@0 6351
michael@0 6352 #ifdef DEBUG
michael@0 6353 // Check to see if we have spacing inside ligatures
michael@0 6354
michael@0 6355 const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
michael@0 6356 uint32_t i;
michael@0 6357
michael@0 6358 for (i = aStart; i < aEnd; ++i) {
michael@0 6359 if (!charGlyphs[i].IsLigatureGroupStart()) {
michael@0 6360 NS_ASSERTION(i == aStart || aSpacing[i - aStart].mBefore == 0,
michael@0 6361 "Before-spacing inside a ligature!");
michael@0 6362 NS_ASSERTION(i - 1 <= aStart || aSpacing[i - 1 - aStart].mAfter == 0,
michael@0 6363 "After-spacing inside a ligature!");
michael@0 6364 }
michael@0 6365 }
michael@0 6366 #endif
michael@0 6367 }
michael@0 6368
michael@0 6369 bool
michael@0 6370 gfxTextRun::GetAdjustedSpacingArray(uint32_t aStart, uint32_t aEnd,
michael@0 6371 PropertyProvider *aProvider,
michael@0 6372 uint32_t aSpacingStart, uint32_t aSpacingEnd,
michael@0 6373 nsTArray<PropertyProvider::Spacing> *aSpacing)
michael@0 6374 {
michael@0 6375 if (!aProvider || !(mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING))
michael@0 6376 return false;
michael@0 6377 if (!aSpacing->AppendElements(aEnd - aStart))
michael@0 6378 return false;
michael@0 6379 memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing)*(aSpacingStart - aStart));
michael@0 6380 GetAdjustedSpacing(this, aSpacingStart, aSpacingEnd, aProvider,
michael@0 6381 aSpacing->Elements() + aSpacingStart - aStart);
michael@0 6382 memset(aSpacing->Elements() + aSpacingEnd - aStart, 0, sizeof(gfxFont::Spacing)*(aEnd - aSpacingEnd));
michael@0 6383 return true;
michael@0 6384 }
michael@0 6385
michael@0 6386 void
michael@0 6387 gfxTextRun::ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd)
michael@0 6388 {
michael@0 6389 if (*aStart >= *aEnd)
michael@0 6390 return;
michael@0 6391
michael@0 6392 CompressedGlyph *charGlyphs = mCharacterGlyphs;
michael@0 6393
michael@0 6394 while (*aStart < *aEnd && !charGlyphs[*aStart].IsLigatureGroupStart()) {
michael@0 6395 ++(*aStart);
michael@0 6396 }
michael@0 6397 if (*aEnd < GetLength()) {
michael@0 6398 while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) {
michael@0 6399 --(*aEnd);
michael@0 6400 }
michael@0 6401 }
michael@0 6402 }
michael@0 6403
michael@0 6404 void
michael@0 6405 gfxTextRun::DrawGlyphs(gfxFont *aFont, gfxContext *aContext,
michael@0 6406 DrawMode aDrawMode, gfxPoint *aPt,
michael@0 6407 gfxTextContextPaint *aContextPaint,
michael@0 6408 uint32_t aStart, uint32_t aEnd,
michael@0 6409 PropertyProvider *aProvider,
michael@0 6410 uint32_t aSpacingStart, uint32_t aSpacingEnd,
michael@0 6411 gfxTextRunDrawCallbacks *aCallbacks)
michael@0 6412 {
michael@0 6413 nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
michael@0 6414 bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
michael@0 6415 aSpacingStart, aSpacingEnd, &spacingBuffer);
michael@0 6416 aFont->Draw(this, aStart, aEnd, aContext, aDrawMode, aPt,
michael@0 6417 haveSpacing ? spacingBuffer.Elements() : nullptr, aContextPaint,
michael@0 6418 aCallbacks);
michael@0 6419 }
michael@0 6420
michael@0 6421 static void
michael@0 6422 ClipPartialLigature(gfxTextRun *aTextRun, gfxFloat *aLeft, gfxFloat *aRight,
michael@0 6423 gfxFloat aXOrigin, gfxTextRun::LigatureData *aLigature)
michael@0 6424 {
michael@0 6425 if (aLigature->mClipBeforePart) {
michael@0 6426 if (aTextRun->IsRightToLeft()) {
michael@0 6427 *aRight = std::min(*aRight, aXOrigin);
michael@0 6428 } else {
michael@0 6429 *aLeft = std::max(*aLeft, aXOrigin);
michael@0 6430 }
michael@0 6431 }
michael@0 6432 if (aLigature->mClipAfterPart) {
michael@0 6433 gfxFloat endEdge = aXOrigin + aTextRun->GetDirection()*aLigature->mPartWidth;
michael@0 6434 if (aTextRun->IsRightToLeft()) {
michael@0 6435 *aLeft = std::max(*aLeft, endEdge);
michael@0 6436 } else {
michael@0 6437 *aRight = std::min(*aRight, endEdge);
michael@0 6438 }
michael@0 6439 }
michael@0 6440 }
michael@0 6441
michael@0 6442 void
michael@0 6443 gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx,
michael@0 6444 uint32_t aStart, uint32_t aEnd,
michael@0 6445 gfxPoint *aPt,
michael@0 6446 PropertyProvider *aProvider,
michael@0 6447 gfxTextRunDrawCallbacks *aCallbacks)
michael@0 6448 {
michael@0 6449 if (aStart >= aEnd)
michael@0 6450 return;
michael@0 6451
michael@0 6452 // Draw partial ligature. We hack this by clipping the ligature.
michael@0 6453 LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
michael@0 6454 gfxRect clipExtents = aCtx->GetClipExtents();
michael@0 6455 gfxFloat left = clipExtents.X()*mAppUnitsPerDevUnit;
michael@0 6456 gfxFloat right = clipExtents.XMost()*mAppUnitsPerDevUnit;
michael@0 6457 ClipPartialLigature(this, &left, &right, aPt->x, &data);
michael@0 6458
michael@0 6459 {
michael@0 6460 // Need to preserve the path, otherwise this can break canvas text-on-path;
michael@0 6461 // in general it seems like a good thing, as naive callers probably won't
michael@0 6462 // expect gfxTextRun::Draw to implicitly destroy the current path.
michael@0 6463 gfxContextPathAutoSaveRestore savePath(aCtx);
michael@0 6464
michael@0 6465 // use division here to ensure that when the rect is aligned on multiples
michael@0 6466 // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
michael@0 6467 // Also, make sure we snap the rectangle to device pixels.
michael@0 6468 aCtx->Save();
michael@0 6469 aCtx->NewPath();
michael@0 6470 aCtx->Rectangle(gfxRect(left / mAppUnitsPerDevUnit,
michael@0 6471 clipExtents.Y(),
michael@0 6472 (right - left) / mAppUnitsPerDevUnit,
michael@0 6473 clipExtents.Height()), true);
michael@0 6474 aCtx->Clip();
michael@0 6475 }
michael@0 6476
michael@0 6477 gfxFloat direction = GetDirection();
michael@0 6478 gfxPoint pt(aPt->x - direction*data.mPartAdvance, aPt->y);
michael@0 6479 DrawGlyphs(aFont, aCtx,
michael@0 6480 aCallbacks ? DrawMode::GLYPH_PATH : DrawMode::GLYPH_FILL, &pt,
michael@0 6481 nullptr, data.mLigatureStart, data.mLigatureEnd, aProvider,
michael@0 6482 aStart, aEnd, aCallbacks);
michael@0 6483 aCtx->Restore();
michael@0 6484
michael@0 6485 aPt->x += direction*data.mPartWidth;
michael@0 6486 }
michael@0 6487
michael@0 6488 // returns true if a glyph run is using a font with synthetic bolding enabled, false otherwise
michael@0 6489 static bool
michael@0 6490 HasSyntheticBold(gfxTextRun *aRun, uint32_t aStart, uint32_t aLength)
michael@0 6491 {
michael@0 6492 gfxTextRun::GlyphRunIterator iter(aRun, aStart, aLength);
michael@0 6493 while (iter.NextRun()) {
michael@0 6494 gfxFont *font = iter.GetGlyphRun()->mFont;
michael@0 6495 if (font && font->IsSyntheticBold()) {
michael@0 6496 return true;
michael@0 6497 }
michael@0 6498 }
michael@0 6499
michael@0 6500 return false;
michael@0 6501 }
michael@0 6502
michael@0 6503 // returns true if color is non-opaque (i.e. alpha != 1.0) or completely transparent, false otherwise
michael@0 6504 // if true, color is set on output
michael@0 6505 static bool
michael@0 6506 HasNonOpaqueColor(gfxContext *aContext, gfxRGBA& aCurrentColor)
michael@0 6507 {
michael@0 6508 if (aContext->GetDeviceColor(aCurrentColor)) {
michael@0 6509 if (aCurrentColor.a < 1.0 && aCurrentColor.a > 0.0) {
michael@0 6510 return true;
michael@0 6511 }
michael@0 6512 }
michael@0 6513
michael@0 6514 return false;
michael@0 6515 }
michael@0 6516
michael@0 6517 // helper class for double-buffering drawing with non-opaque color
michael@0 6518 struct BufferAlphaColor {
michael@0 6519 BufferAlphaColor(gfxContext *aContext)
michael@0 6520 : mContext(aContext)
michael@0 6521 {
michael@0 6522
michael@0 6523 }
michael@0 6524
michael@0 6525 ~BufferAlphaColor() {}
michael@0 6526
michael@0 6527 void PushSolidColor(const gfxRect& aBounds, const gfxRGBA& aAlphaColor, uint32_t appsPerDevUnit)
michael@0 6528 {
michael@0 6529 mContext->Save();
michael@0 6530 mContext->NewPath();
michael@0 6531 mContext->Rectangle(gfxRect(aBounds.X() / appsPerDevUnit,
michael@0 6532 aBounds.Y() / appsPerDevUnit,
michael@0 6533 aBounds.Width() / appsPerDevUnit,
michael@0 6534 aBounds.Height() / appsPerDevUnit), true);
michael@0 6535 mContext->Clip();
michael@0 6536 mContext->SetColor(gfxRGBA(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b));
michael@0 6537 mContext->PushGroup(gfxContentType::COLOR_ALPHA);
michael@0 6538 mAlpha = aAlphaColor.a;
michael@0 6539 }
michael@0 6540
michael@0 6541 void PopAlpha()
michael@0 6542 {
michael@0 6543 // pop the text, using the color alpha as the opacity
michael@0 6544 mContext->PopGroupToSource();
michael@0 6545 mContext->SetOperator(gfxContext::OPERATOR_OVER);
michael@0 6546 mContext->Paint(mAlpha);
michael@0 6547 mContext->Restore();
michael@0 6548 }
michael@0 6549
michael@0 6550 gfxContext *mContext;
michael@0 6551 gfxFloat mAlpha;
michael@0 6552 };
michael@0 6553
michael@0 6554 void
michael@0 6555 gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, DrawMode aDrawMode,
michael@0 6556 uint32_t aStart, uint32_t aLength,
michael@0 6557 PropertyProvider *aProvider, gfxFloat *aAdvanceWidth,
michael@0 6558 gfxTextContextPaint *aContextPaint,
michael@0 6559 gfxTextRunDrawCallbacks *aCallbacks)
michael@0 6560 {
michael@0 6561 NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
michael@0 6562 NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)),
michael@0 6563 "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
michael@0 6564 NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !aCallbacks, "callback must not be specified unless using GLYPH_PATH");
michael@0 6565
michael@0 6566 bool skipDrawing = mSkipDrawing;
michael@0 6567 if (aDrawMode == DrawMode::GLYPH_FILL) {
michael@0 6568 gfxRGBA currentColor;
michael@0 6569 if (aContext->GetDeviceColor(currentColor) && currentColor.a == 0) {
michael@0 6570 skipDrawing = true;
michael@0 6571 }
michael@0 6572 }
michael@0 6573
michael@0 6574 gfxFloat direction = GetDirection();
michael@0 6575
michael@0 6576 if (skipDrawing) {
michael@0 6577 // We don't need to draw anything;
michael@0 6578 // but if the caller wants advance width, we need to compute it here
michael@0 6579 if (aAdvanceWidth) {
michael@0 6580 gfxTextRun::Metrics metrics = MeasureText(aStart, aLength,
michael@0 6581 gfxFont::LOOSE_INK_EXTENTS,
michael@0 6582 aContext, aProvider);
michael@0 6583 *aAdvanceWidth = metrics.mAdvanceWidth * direction;
michael@0 6584 }
michael@0 6585
michael@0 6586 // return without drawing
michael@0 6587 return;
michael@0 6588 }
michael@0 6589
michael@0 6590 gfxPoint pt = aPt;
michael@0 6591
michael@0 6592 // synthetic bolding draws glyphs twice ==> colors with opacity won't draw correctly unless first drawn without alpha
michael@0 6593 BufferAlphaColor syntheticBoldBuffer(aContext);
michael@0 6594 gfxRGBA currentColor;
michael@0 6595 bool needToRestore = false;
michael@0 6596
michael@0 6597 if (aDrawMode == DrawMode::GLYPH_FILL && HasNonOpaqueColor(aContext, currentColor)
michael@0 6598 && HasSyntheticBold(this, aStart, aLength)) {
michael@0 6599 needToRestore = true;
michael@0 6600 // measure text, use the bounding box
michael@0 6601 gfxTextRun::Metrics metrics = MeasureText(aStart, aLength, gfxFont::LOOSE_INK_EXTENTS,
michael@0 6602 aContext, aProvider);
michael@0 6603 metrics.mBoundingBox.MoveBy(aPt);
michael@0 6604 syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor, GetAppUnitsPerDevUnit());
michael@0 6605 }
michael@0 6606
michael@0 6607 GlyphRunIterator iter(this, aStart, aLength);
michael@0 6608 while (iter.NextRun()) {
michael@0 6609 gfxFont *font = iter.GetGlyphRun()->mFont;
michael@0 6610 uint32_t start = iter.GetStringStart();
michael@0 6611 uint32_t end = iter.GetStringEnd();
michael@0 6612 uint32_t ligatureRunStart = start;
michael@0 6613 uint32_t ligatureRunEnd = end;
michael@0 6614 ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
michael@0 6615
michael@0 6616 bool drawPartial = aDrawMode == DrawMode::GLYPH_FILL ||
michael@0 6617 (aDrawMode == DrawMode::GLYPH_PATH && aCallbacks);
michael@0 6618
michael@0 6619 if (drawPartial) {
michael@0 6620 DrawPartialLigature(font, aContext, start, ligatureRunStart, &pt,
michael@0 6621 aProvider, aCallbacks);
michael@0 6622 }
michael@0 6623
michael@0 6624 DrawGlyphs(font, aContext, aDrawMode, &pt, aContextPaint, ligatureRunStart,
michael@0 6625 ligatureRunEnd, aProvider, ligatureRunStart, ligatureRunEnd,
michael@0 6626 aCallbacks);
michael@0 6627
michael@0 6628 if (drawPartial) {
michael@0 6629 DrawPartialLigature(font, aContext, ligatureRunEnd, end, &pt,
michael@0 6630 aProvider, aCallbacks);
michael@0 6631 }
michael@0 6632 }
michael@0 6633
michael@0 6634 // composite result when synthetic bolding used
michael@0 6635 if (needToRestore) {
michael@0 6636 syntheticBoldBuffer.PopAlpha();
michael@0 6637 }
michael@0 6638
michael@0 6639 if (aAdvanceWidth) {
michael@0 6640 *aAdvanceWidth = (pt.x - aPt.x)*direction;
michael@0 6641 }
michael@0 6642 }
michael@0 6643
michael@0 6644 void
michael@0 6645 gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
michael@0 6646 uint32_t aStart, uint32_t aEnd,
michael@0 6647 gfxFont::BoundingBoxType aBoundingBoxType,
michael@0 6648 gfxContext *aRefContext,
michael@0 6649 PropertyProvider *aProvider,
michael@0 6650 uint32_t aSpacingStart, uint32_t aSpacingEnd,
michael@0 6651 Metrics *aMetrics)
michael@0 6652 {
michael@0 6653 nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
michael@0 6654 bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
michael@0 6655 aSpacingStart, aSpacingEnd, &spacingBuffer);
michael@0 6656 Metrics metrics = aFont->Measure(this, aStart, aEnd, aBoundingBoxType, aRefContext,
michael@0 6657 haveSpacing ? spacingBuffer.Elements() : nullptr);
michael@0 6658 aMetrics->CombineWith(metrics, IsRightToLeft());
michael@0 6659 }
michael@0 6660
michael@0 6661 void
michael@0 6662 gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont,
michael@0 6663 uint32_t aStart, uint32_t aEnd,
michael@0 6664 gfxFont::BoundingBoxType aBoundingBoxType, gfxContext *aRefContext,
michael@0 6665 PropertyProvider *aProvider, Metrics *aMetrics)
michael@0 6666 {
michael@0 6667 if (aStart >= aEnd)
michael@0 6668 return;
michael@0 6669
michael@0 6670 // Measure partial ligature. We hack this by clipping the metrics in the
michael@0 6671 // same way we clip the drawing.
michael@0 6672 LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
michael@0 6673
michael@0 6674 // First measure the complete ligature
michael@0 6675 Metrics metrics;
michael@0 6676 AccumulateMetricsForRun(aFont, data.mLigatureStart, data.mLigatureEnd,
michael@0 6677 aBoundingBoxType, aRefContext,
michael@0 6678 aProvider, aStart, aEnd, &metrics);
michael@0 6679
michael@0 6680 // Clip the bounding box to the ligature part
michael@0 6681 gfxFloat bboxLeft = metrics.mBoundingBox.X();
michael@0 6682 gfxFloat bboxRight = metrics.mBoundingBox.XMost();
michael@0 6683 // Where we are going to start "drawing" relative to our left baseline origin
michael@0 6684 gfxFloat origin = IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0;
michael@0 6685 ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data);
michael@0 6686 metrics.mBoundingBox.x = bboxLeft;
michael@0 6687 metrics.mBoundingBox.width = bboxRight - bboxLeft;
michael@0 6688
michael@0 6689 // mBoundingBox is now relative to the left baseline origin for the entire
michael@0 6690 // ligature. Shift it left.
michael@0 6691 metrics.mBoundingBox.x -=
michael@0 6692 IsRightToLeft() ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth)
michael@0 6693 : data.mPartAdvance;
michael@0 6694 metrics.mAdvanceWidth = data.mPartWidth;
michael@0 6695
michael@0 6696 aMetrics->CombineWith(metrics, IsRightToLeft());
michael@0 6697 }
michael@0 6698
michael@0 6699 gfxTextRun::Metrics
michael@0 6700 gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength,
michael@0 6701 gfxFont::BoundingBoxType aBoundingBoxType,
michael@0 6702 gfxContext *aRefContext,
michael@0 6703 PropertyProvider *aProvider)
michael@0 6704 {
michael@0 6705 NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
michael@0 6706
michael@0 6707 Metrics accumulatedMetrics;
michael@0 6708 GlyphRunIterator iter(this, aStart, aLength);
michael@0 6709 while (iter.NextRun()) {
michael@0 6710 gfxFont *font = iter.GetGlyphRun()->mFont;
michael@0 6711 uint32_t start = iter.GetStringStart();
michael@0 6712 uint32_t end = iter.GetStringEnd();
michael@0 6713 uint32_t ligatureRunStart = start;
michael@0 6714 uint32_t ligatureRunEnd = end;
michael@0 6715 ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
michael@0 6716
michael@0 6717 AccumulatePartialLigatureMetrics(font, start, ligatureRunStart,
michael@0 6718 aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
michael@0 6719
michael@0 6720 // XXX This sucks. We have to get glyph extents just so we can detect
michael@0 6721 // glyphs outside the font box, even when aBoundingBoxType is LOOSE,
michael@0 6722 // even though in almost all cases we could get correct results just
michael@0 6723 // by getting some ascent/descent from the font and using our stored
michael@0 6724 // advance widths.
michael@0 6725 AccumulateMetricsForRun(font,
michael@0 6726 ligatureRunStart, ligatureRunEnd, aBoundingBoxType,
michael@0 6727 aRefContext, aProvider, ligatureRunStart, ligatureRunEnd,
michael@0 6728 &accumulatedMetrics);
michael@0 6729
michael@0 6730 AccumulatePartialLigatureMetrics(font, ligatureRunEnd, end,
michael@0 6731 aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
michael@0 6732 }
michael@0 6733
michael@0 6734 return accumulatedMetrics;
michael@0 6735 }
michael@0 6736
michael@0 6737 #define MEASUREMENT_BUFFER_SIZE 100
michael@0 6738
michael@0 6739 uint32_t
michael@0 6740 gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
michael@0 6741 bool aLineBreakBefore, gfxFloat aWidth,
michael@0 6742 PropertyProvider *aProvider,
michael@0 6743 bool aSuppressInitialBreak,
michael@0 6744 gfxFloat *aTrimWhitespace,
michael@0 6745 Metrics *aMetrics,
michael@0 6746 gfxFont::BoundingBoxType aBoundingBoxType,
michael@0 6747 gfxContext *aRefContext,
michael@0 6748 bool *aUsedHyphenation,
michael@0 6749 uint32_t *aLastBreak,
michael@0 6750 bool aCanWordWrap,
michael@0 6751 gfxBreakPriority *aBreakPriority)
michael@0 6752 {
michael@0 6753 aMaxLength = std::min(aMaxLength, GetLength() - aStart);
michael@0 6754
michael@0 6755 NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range");
michael@0 6756
michael@0 6757 uint32_t bufferStart = aStart;
michael@0 6758 uint32_t bufferLength = std::min<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE);
michael@0 6759 PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
michael@0 6760 bool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0;
michael@0 6761 if (haveSpacing) {
michael@0 6762 GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
michael@0 6763 spacingBuffer);
michael@0 6764 }
michael@0 6765 bool hyphenBuffer[MEASUREMENT_BUFFER_SIZE];
michael@0 6766 bool haveHyphenation = aProvider &&
michael@0 6767 (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_AUTO ||
michael@0 6768 (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_MANUAL &&
michael@0 6769 (mFlags & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0));
michael@0 6770 if (haveHyphenation) {
michael@0 6771 aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
michael@0 6772 hyphenBuffer);
michael@0 6773 }
michael@0 6774
michael@0 6775 gfxFloat width = 0;
michael@0 6776 gfxFloat advance = 0;
michael@0 6777 // The number of space characters that can be trimmed
michael@0 6778 uint32_t trimmableChars = 0;
michael@0 6779 // The amount of space removed by ignoring trimmableChars
michael@0 6780 gfxFloat trimmableAdvance = 0;
michael@0 6781 int32_t lastBreak = -1;
michael@0 6782 int32_t lastBreakTrimmableChars = -1;
michael@0 6783 gfxFloat lastBreakTrimmableAdvance = -1;
michael@0 6784 bool aborted = false;
michael@0 6785 uint32_t end = aStart + aMaxLength;
michael@0 6786 bool lastBreakUsedHyphenation = false;
michael@0 6787
michael@0 6788 uint32_t ligatureRunStart = aStart;
michael@0 6789 uint32_t ligatureRunEnd = end;
michael@0 6790 ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
michael@0 6791
michael@0 6792 uint32_t i;
michael@0 6793 for (i = aStart; i < end; ++i) {
michael@0 6794 if (i >= bufferStart + bufferLength) {
michael@0 6795 // Fetch more spacing and hyphenation data
michael@0 6796 bufferStart = i;
michael@0 6797 bufferLength = std::min(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE) - i;
michael@0 6798 if (haveSpacing) {
michael@0 6799 GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
michael@0 6800 spacingBuffer);
michael@0 6801 }
michael@0 6802 if (haveHyphenation) {
michael@0 6803 aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
michael@0 6804 hyphenBuffer);
michael@0 6805 }
michael@0 6806 }
michael@0 6807
michael@0 6808 // There can't be a word-wrap break opportunity at the beginning of the
michael@0 6809 // line: if the width is too small for even one character to fit, it
michael@0 6810 // could be the first and last break opportunity on the line, and that
michael@0 6811 // would trigger an infinite loop.
michael@0 6812 if (!aSuppressInitialBreak || i > aStart) {
michael@0 6813 bool atNaturalBreak = mCharacterGlyphs[i].CanBreakBefore() == 1;
michael@0 6814 bool atHyphenationBreak =
michael@0 6815 !atNaturalBreak && haveHyphenation && hyphenBuffer[i - bufferStart];
michael@0 6816 bool atBreak = atNaturalBreak || atHyphenationBreak;
michael@0 6817 bool wordWrapping =
michael@0 6818 aCanWordWrap && mCharacterGlyphs[i].IsClusterStart() &&
michael@0 6819 *aBreakPriority <= gfxBreakPriority::eWordWrapBreak;
michael@0 6820
michael@0 6821 if (atBreak || wordWrapping) {
michael@0 6822 gfxFloat hyphenatedAdvance = advance;
michael@0 6823 if (atHyphenationBreak) {
michael@0 6824 hyphenatedAdvance += aProvider->GetHyphenWidth();
michael@0 6825 }
michael@0 6826
michael@0 6827 if (lastBreak < 0 || width + hyphenatedAdvance - trimmableAdvance <= aWidth) {
michael@0 6828 // We can break here.
michael@0 6829 lastBreak = i;
michael@0 6830 lastBreakTrimmableChars = trimmableChars;
michael@0 6831 lastBreakTrimmableAdvance = trimmableAdvance;
michael@0 6832 lastBreakUsedHyphenation = atHyphenationBreak;
michael@0 6833 *aBreakPriority = atBreak ? gfxBreakPriority::eNormalBreak
michael@0 6834 : gfxBreakPriority::eWordWrapBreak;
michael@0 6835 }
michael@0 6836
michael@0 6837 width += advance;
michael@0 6838 advance = 0;
michael@0 6839 if (width - trimmableAdvance > aWidth) {
michael@0 6840 // No more text fits. Abort
michael@0 6841 aborted = true;
michael@0 6842 break;
michael@0 6843 }
michael@0 6844 }
michael@0 6845 }
michael@0 6846
michael@0 6847 gfxFloat charAdvance;
michael@0 6848 if (i >= ligatureRunStart && i < ligatureRunEnd) {
michael@0 6849 charAdvance = GetAdvanceForGlyphs(i, i + 1);
michael@0 6850 if (haveSpacing) {
michael@0 6851 PropertyProvider::Spacing *space = &spacingBuffer[i - bufferStart];
michael@0 6852 charAdvance += space->mBefore + space->mAfter;
michael@0 6853 }
michael@0 6854 } else {
michael@0 6855 charAdvance = ComputePartialLigatureWidth(i, i + 1, aProvider);
michael@0 6856 }
michael@0 6857
michael@0 6858 advance += charAdvance;
michael@0 6859 if (aTrimWhitespace) {
michael@0 6860 if (mCharacterGlyphs[i].CharIsSpace()) {
michael@0 6861 ++trimmableChars;
michael@0 6862 trimmableAdvance += charAdvance;
michael@0 6863 } else {
michael@0 6864 trimmableAdvance = 0;
michael@0 6865 trimmableChars = 0;
michael@0 6866 }
michael@0 6867 }
michael@0 6868 }
michael@0 6869
michael@0 6870 if (!aborted) {
michael@0 6871 width += advance;
michael@0 6872 }
michael@0 6873
michael@0 6874 // There are three possibilities:
michael@0 6875 // 1) all the text fit (width <= aWidth)
michael@0 6876 // 2) some of the text fit up to a break opportunity (width > aWidth && lastBreak >= 0)
michael@0 6877 // 3) none of the text fits before a break opportunity (width > aWidth && lastBreak < 0)
michael@0 6878 uint32_t charsFit;
michael@0 6879 bool usedHyphenation = false;
michael@0 6880 if (width - trimmableAdvance <= aWidth) {
michael@0 6881 charsFit = aMaxLength;
michael@0 6882 } else if (lastBreak >= 0) {
michael@0 6883 charsFit = lastBreak - aStart;
michael@0 6884 trimmableChars = lastBreakTrimmableChars;
michael@0 6885 trimmableAdvance = lastBreakTrimmableAdvance;
michael@0 6886 usedHyphenation = lastBreakUsedHyphenation;
michael@0 6887 } else {
michael@0 6888 charsFit = aMaxLength;
michael@0 6889 }
michael@0 6890
michael@0 6891 if (aMetrics) {
michael@0 6892 *aMetrics = MeasureText(aStart, charsFit - trimmableChars,
michael@0 6893 aBoundingBoxType, aRefContext, aProvider);
michael@0 6894 }
michael@0 6895 if (aTrimWhitespace) {
michael@0 6896 *aTrimWhitespace = trimmableAdvance;
michael@0 6897 }
michael@0 6898 if (aUsedHyphenation) {
michael@0 6899 *aUsedHyphenation = usedHyphenation;
michael@0 6900 }
michael@0 6901 if (aLastBreak && charsFit == aMaxLength) {
michael@0 6902 if (lastBreak < 0) {
michael@0 6903 *aLastBreak = UINT32_MAX;
michael@0 6904 } else {
michael@0 6905 *aLastBreak = lastBreak - aStart;
michael@0 6906 }
michael@0 6907 }
michael@0 6908
michael@0 6909 return charsFit;
michael@0 6910 }
michael@0 6911
michael@0 6912 gfxFloat
michael@0 6913 gfxTextRun::GetAdvanceWidth(uint32_t aStart, uint32_t aLength,
michael@0 6914 PropertyProvider *aProvider)
michael@0 6915 {
michael@0 6916 NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
michael@0 6917
michael@0 6918 uint32_t ligatureRunStart = aStart;
michael@0 6919 uint32_t ligatureRunEnd = aStart + aLength;
michael@0 6920 ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
michael@0 6921
michael@0 6922 gfxFloat result = ComputePartialLigatureWidth(aStart, ligatureRunStart, aProvider) +
michael@0 6923 ComputePartialLigatureWidth(ligatureRunEnd, aStart + aLength, aProvider);
michael@0 6924
michael@0 6925 // Account for all remaining spacing here. This is more efficient than
michael@0 6926 // processing it along with the glyphs.
michael@0 6927 if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
michael@0 6928 uint32_t i;
michael@0 6929 nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
michael@0 6930 if (spacingBuffer.AppendElements(aLength)) {
michael@0 6931 GetAdjustedSpacing(this, ligatureRunStart, ligatureRunEnd, aProvider,
michael@0 6932 spacingBuffer.Elements());
michael@0 6933 for (i = 0; i < ligatureRunEnd - ligatureRunStart; ++i) {
michael@0 6934 PropertyProvider::Spacing *space = &spacingBuffer[i];
michael@0 6935 result += space->mBefore + space->mAfter;
michael@0 6936 }
michael@0 6937 }
michael@0 6938 }
michael@0 6939
michael@0 6940 return result + GetAdvanceForGlyphs(ligatureRunStart, ligatureRunEnd);
michael@0 6941 }
michael@0 6942
michael@0 6943 bool
michael@0 6944 gfxTextRun::SetLineBreaks(uint32_t aStart, uint32_t aLength,
michael@0 6945 bool aLineBreakBefore, bool aLineBreakAfter,
michael@0 6946 gfxFloat *aAdvanceWidthDelta,
michael@0 6947 gfxContext *aRefContext)
michael@0 6948 {
michael@0 6949 // Do nothing because our shaping does not currently take linebreaks into
michael@0 6950 // account. There is no change in advance width.
michael@0 6951 if (aAdvanceWidthDelta) {
michael@0 6952 *aAdvanceWidthDelta = 0;
michael@0 6953 }
michael@0 6954 return false;
michael@0 6955 }
michael@0 6956
michael@0 6957 uint32_t
michael@0 6958 gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset)
michael@0 6959 {
michael@0 6960 NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun");
michael@0 6961 NS_ASSERTION(GetLength() == 0 || mGlyphRuns.Length() > 0,
michael@0 6962 "non-empty text but no glyph runs present!");
michael@0 6963 if (aOffset == GetLength())
michael@0 6964 return mGlyphRuns.Length();
michael@0 6965 uint32_t start = 0;
michael@0 6966 uint32_t end = mGlyphRuns.Length();
michael@0 6967 while (end - start > 1) {
michael@0 6968 uint32_t mid = (start + end)/2;
michael@0 6969 if (mGlyphRuns[mid].mCharacterOffset <= aOffset) {
michael@0 6970 start = mid;
michael@0 6971 } else {
michael@0 6972 end = mid;
michael@0 6973 }
michael@0 6974 }
michael@0 6975 NS_ASSERTION(mGlyphRuns[start].mCharacterOffset <= aOffset,
michael@0 6976 "Hmm, something went wrong, aOffset should have been found");
michael@0 6977 return start;
michael@0 6978 }
michael@0 6979
michael@0 6980 nsresult
michael@0 6981 gfxTextRun::AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
michael@0 6982 uint32_t aUTF16Offset, bool aForceNewRun)
michael@0 6983 {
michael@0 6984 NS_ASSERTION(aFont, "adding glyph run for null font!");
michael@0 6985 if (!aFont) {
michael@0 6986 return NS_OK;
michael@0 6987 }
michael@0 6988 uint32_t numGlyphRuns = mGlyphRuns.Length();
michael@0 6989 if (!aForceNewRun && numGlyphRuns > 0) {
michael@0 6990 GlyphRun *lastGlyphRun = &mGlyphRuns[numGlyphRuns - 1];
michael@0 6991
michael@0 6992 NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
michael@0 6993 "Glyph runs out of order (and run not forced)");
michael@0 6994
michael@0 6995 // Don't append a run if the font is already the one we want
michael@0 6996 if (lastGlyphRun->mFont == aFont &&
michael@0 6997 lastGlyphRun->mMatchType == aMatchType)
michael@0 6998 {
michael@0 6999 return NS_OK;
michael@0 7000 }
michael@0 7001
michael@0 7002 // If the offset has not changed, avoid leaving a zero-length run
michael@0 7003 // by overwriting the last entry instead of appending...
michael@0 7004 if (lastGlyphRun->mCharacterOffset == aUTF16Offset) {
michael@0 7005
michael@0 7006 // ...except that if the run before the last entry had the same
michael@0 7007 // font as the new one wants, merge with it instead of creating
michael@0 7008 // adjacent runs with the same font
michael@0 7009 if (numGlyphRuns > 1 &&
michael@0 7010 mGlyphRuns[numGlyphRuns - 2].mFont == aFont &&
michael@0 7011 mGlyphRuns[numGlyphRuns - 2].mMatchType == aMatchType)
michael@0 7012 {
michael@0 7013 mGlyphRuns.TruncateLength(numGlyphRuns - 1);
michael@0 7014 return NS_OK;
michael@0 7015 }
michael@0 7016
michael@0 7017 lastGlyphRun->mFont = aFont;
michael@0 7018 lastGlyphRun->mMatchType = aMatchType;
michael@0 7019 return NS_OK;
michael@0 7020 }
michael@0 7021 }
michael@0 7022
michael@0 7023 NS_ASSERTION(aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0,
michael@0 7024 "First run doesn't cover the first character (and run not forced)?");
michael@0 7025
michael@0 7026 GlyphRun *glyphRun = mGlyphRuns.AppendElement();
michael@0 7027 if (!glyphRun)
michael@0 7028 return NS_ERROR_OUT_OF_MEMORY;
michael@0 7029 glyphRun->mFont = aFont;
michael@0 7030 glyphRun->mCharacterOffset = aUTF16Offset;
michael@0 7031 glyphRun->mMatchType = aMatchType;
michael@0 7032 return NS_OK;
michael@0 7033 }
michael@0 7034
michael@0 7035 void
michael@0 7036 gfxTextRun::SortGlyphRuns()
michael@0 7037 {
michael@0 7038 if (mGlyphRuns.Length() <= 1)
michael@0 7039 return;
michael@0 7040
michael@0 7041 nsTArray<GlyphRun> runs(mGlyphRuns);
michael@0 7042 GlyphRunOffsetComparator comp;
michael@0 7043 runs.Sort(comp);
michael@0 7044
michael@0 7045 // Now copy back, coalescing adjacent glyph runs that have the same font
michael@0 7046 mGlyphRuns.Clear();
michael@0 7047 uint32_t i, count = runs.Length();
michael@0 7048 for (i = 0; i < count; ++i) {
michael@0 7049 // a GlyphRun with the same font as the previous GlyphRun can just
michael@0 7050 // be skipped; the last GlyphRun will cover its character range.
michael@0 7051 if (i == 0 || runs[i].mFont != runs[i - 1].mFont) {
michael@0 7052 mGlyphRuns.AppendElement(runs[i]);
michael@0 7053 // If two fonts have the same character offset, Sort() will have
michael@0 7054 // randomized the order.
michael@0 7055 NS_ASSERTION(i == 0 ||
michael@0 7056 runs[i].mCharacterOffset !=
michael@0 7057 runs[i - 1].mCharacterOffset,
michael@0 7058 "Two fonts for the same run, glyph indices may not match the font");
michael@0 7059 }
michael@0 7060 }
michael@0 7061 }
michael@0 7062
michael@0 7063 // Note that SanitizeGlyphRuns scans all glyph runs in the textrun;
michael@0 7064 // therefore we only call it once, at the end of textrun construction,
michael@0 7065 // NOT incrementally as each glyph run is added (bug 680402).
michael@0 7066 void
michael@0 7067 gfxTextRun::SanitizeGlyphRuns()
michael@0 7068 {
michael@0 7069 if (mGlyphRuns.Length() <= 1)
michael@0 7070 return;
michael@0 7071
michael@0 7072 // If any glyph run starts with ligature-continuation characters, we need to advance it
michael@0 7073 // to the first "real" character to avoid drawing partial ligature glyphs from wrong font
michael@0 7074 // (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes
michael@0 7075 // it appear as if a ligature has been formed)
michael@0 7076 int32_t i, lastRunIndex = mGlyphRuns.Length() - 1;
michael@0 7077 const CompressedGlyph *charGlyphs = mCharacterGlyphs;
michael@0 7078 for (i = lastRunIndex; i >= 0; --i) {
michael@0 7079 GlyphRun& run = mGlyphRuns[i];
michael@0 7080 while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
michael@0 7081 run.mCharacterOffset < GetLength()) {
michael@0 7082 run.mCharacterOffset++;
michael@0 7083 }
michael@0 7084 // if the run has become empty, eliminate it
michael@0 7085 if ((i < lastRunIndex &&
michael@0 7086 run.mCharacterOffset >= mGlyphRuns[i+1].mCharacterOffset) ||
michael@0 7087 (i == lastRunIndex && run.mCharacterOffset == GetLength())) {
michael@0 7088 mGlyphRuns.RemoveElementAt(i);
michael@0 7089 --lastRunIndex;
michael@0 7090 }
michael@0 7091 }
michael@0 7092 }
michael@0 7093
michael@0 7094 uint32_t
michael@0 7095 gfxTextRun::CountMissingGlyphs()
michael@0 7096 {
michael@0 7097 uint32_t i;
michael@0 7098 uint32_t count = 0;
michael@0 7099 for (i = 0; i < GetLength(); ++i) {
michael@0 7100 if (mCharacterGlyphs[i].IsMissing()) {
michael@0 7101 ++count;
michael@0 7102 }
michael@0 7103 }
michael@0 7104 return count;
michael@0 7105 }
michael@0 7106
michael@0 7107 gfxTextRun::DetailedGlyph *
michael@0 7108 gfxTextRun::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
michael@0 7109 {
michael@0 7110 NS_ASSERTION(aIndex < GetLength(), "Index out of range");
michael@0 7111
michael@0 7112 if (!mDetailedGlyphs) {
michael@0 7113 mDetailedGlyphs = new DetailedGlyphStore();
michael@0 7114 }
michael@0 7115
michael@0 7116 DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
michael@0 7117 if (!details) {
michael@0 7118 mCharacterGlyphs[aIndex].SetMissing(0);
michael@0 7119 return nullptr;
michael@0 7120 }
michael@0 7121
michael@0 7122 return details;
michael@0 7123 }
michael@0 7124
michael@0 7125 void
michael@0 7126 gfxTextRun::CopyGlyphDataFrom(gfxShapedWord *aShapedWord, uint32_t aOffset)
michael@0 7127 {
michael@0 7128 uint32_t wordLen = aShapedWord->GetLength();
michael@0 7129 NS_ASSERTION(aOffset + wordLen <= GetLength(),
michael@0 7130 "word overruns end of textrun!");
michael@0 7131
michael@0 7132 CompressedGlyph *charGlyphs = GetCharacterGlyphs();
michael@0 7133 const CompressedGlyph *wordGlyphs = aShapedWord->GetCharacterGlyphs();
michael@0 7134 if (aShapedWord->HasDetailedGlyphs()) {
michael@0 7135 for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) {
michael@0 7136 const CompressedGlyph& g = wordGlyphs[i];
michael@0 7137 if (g.IsSimpleGlyph()) {
michael@0 7138 charGlyphs[aOffset] = g;
michael@0 7139 } else {
michael@0 7140 const DetailedGlyph *details =
michael@0 7141 g.GetGlyphCount() > 0 ?
michael@0 7142 aShapedWord->GetDetailedGlyphs(i) : nullptr;
michael@0 7143 SetGlyphs(aOffset, g, details);
michael@0 7144 }
michael@0 7145 }
michael@0 7146 } else {
michael@0 7147 memcpy(charGlyphs + aOffset, wordGlyphs,
michael@0 7148 wordLen * sizeof(CompressedGlyph));
michael@0 7149 }
michael@0 7150 }
michael@0 7151
michael@0 7152 void
michael@0 7153 gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, uint32_t aStart,
michael@0 7154 uint32_t aLength, uint32_t aDest)
michael@0 7155 {
michael@0 7156 NS_ASSERTION(aStart + aLength <= aSource->GetLength(),
michael@0 7157 "Source substring out of range");
michael@0 7158 NS_ASSERTION(aDest + aLength <= GetLength(),
michael@0 7159 "Destination substring out of range");
michael@0 7160
michael@0 7161 if (aSource->mSkipDrawing) {
michael@0 7162 mSkipDrawing = true;
michael@0 7163 }
michael@0 7164
michael@0 7165 // Copy base glyph data, and DetailedGlyph data where present
michael@0 7166 const CompressedGlyph *srcGlyphs = aSource->mCharacterGlyphs + aStart;
michael@0 7167 CompressedGlyph *dstGlyphs = mCharacterGlyphs + aDest;
michael@0 7168 for (uint32_t i = 0; i < aLength; ++i) {
michael@0 7169 CompressedGlyph g = srcGlyphs[i];
michael@0 7170 g.SetCanBreakBefore(!g.IsClusterStart() ?
michael@0 7171 CompressedGlyph::FLAG_BREAK_TYPE_NONE :
michael@0 7172 dstGlyphs[i].CanBreakBefore());
michael@0 7173 if (!g.IsSimpleGlyph()) {
michael@0 7174 uint32_t count = g.GetGlyphCount();
michael@0 7175 if (count > 0) {
michael@0 7176 DetailedGlyph *dst = AllocateDetailedGlyphs(i + aDest, count);
michael@0 7177 if (dst) {
michael@0 7178 DetailedGlyph *src = aSource->GetDetailedGlyphs(i + aStart);
michael@0 7179 if (src) {
michael@0 7180 ::memcpy(dst, src, count * sizeof(DetailedGlyph));
michael@0 7181 } else {
michael@0 7182 g.SetMissing(0);
michael@0 7183 }
michael@0 7184 } else {
michael@0 7185 g.SetMissing(0);
michael@0 7186 }
michael@0 7187 }
michael@0 7188 }
michael@0 7189 dstGlyphs[i] = g;
michael@0 7190 }
michael@0 7191
michael@0 7192 // Copy glyph runs
michael@0 7193 GlyphRunIterator iter(aSource, aStart, aLength);
michael@0 7194 #ifdef DEBUG
michael@0 7195 gfxFont *lastFont = nullptr;
michael@0 7196 #endif
michael@0 7197 while (iter.NextRun()) {
michael@0 7198 gfxFont *font = iter.GetGlyphRun()->mFont;
michael@0 7199 NS_ASSERTION(font != lastFont, "Glyphruns not coalesced?");
michael@0 7200 #ifdef DEBUG
michael@0 7201 lastFont = font;
michael@0 7202 uint32_t end = iter.GetStringEnd();
michael@0 7203 #endif
michael@0 7204 uint32_t start = iter.GetStringStart();
michael@0 7205
michael@0 7206 // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
michael@0 7207 // Although it's unusual (and not desirable), it's possible for us to assign
michael@0 7208 // different fonts to a base character and a following diacritic.
michael@0 7209 // Example on OSX 10.5/10.6 with default fonts installed:
michael@0 7210 // data:text/html,<p style="font-family:helvetica, arial, sans-serif;">
michael@0 7211 // &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486;
michael@0 7212 // This means the rendering of the cluster will probably not be very good,
michael@0 7213 // but it's the best we can do for now if the specified font only covered the
michael@0 7214 // initial base character and not its applied marks.
michael@0 7215 NS_WARN_IF_FALSE(aSource->IsClusterStart(start),
michael@0 7216 "Started font run in the middle of a cluster");
michael@0 7217 NS_WARN_IF_FALSE(end == aSource->GetLength() || aSource->IsClusterStart(end),
michael@0 7218 "Ended font run in the middle of a cluster");
michael@0 7219
michael@0 7220 nsresult rv = AddGlyphRun(font, iter.GetGlyphRun()->mMatchType,
michael@0 7221 start - aStart + aDest, false);
michael@0 7222 if (NS_FAILED(rv))
michael@0 7223 return;
michael@0 7224 }
michael@0 7225 }
michael@0 7226
michael@0 7227 void
michael@0 7228 gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext,
michael@0 7229 uint32_t aCharIndex)
michael@0 7230 {
michael@0 7231 if (SetSpaceGlyphIfSimple(aFont, aContext, aCharIndex, ' ')) {
michael@0 7232 return;
michael@0 7233 }
michael@0 7234
michael@0 7235 aFont->InitWordCache();
michael@0 7236 static const uint8_t space = ' ';
michael@0 7237 gfxShapedWord *sw = aFont->GetShapedWord(aContext,
michael@0 7238 &space, 1,
michael@0 7239 HashMix(0, ' '),
michael@0 7240 MOZ_SCRIPT_LATIN,
michael@0 7241 mAppUnitsPerDevUnit,
michael@0 7242 gfxTextRunFactory::TEXT_IS_8BIT |
michael@0 7243 gfxTextRunFactory::TEXT_IS_ASCII |
michael@0 7244 gfxTextRunFactory::TEXT_IS_PERSISTENT,
michael@0 7245 nullptr);
michael@0 7246 if (sw) {
michael@0 7247 AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
michael@0 7248 CopyGlyphDataFrom(sw, aCharIndex);
michael@0 7249 }
michael@0 7250 }
michael@0 7251
michael@0 7252 bool
michael@0 7253 gfxTextRun::SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext,
michael@0 7254 uint32_t aCharIndex, char16_t aSpaceChar)
michael@0 7255 {
michael@0 7256 uint32_t spaceGlyph = aFont->GetSpaceGlyph();
michael@0 7257 if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
michael@0 7258 return false;
michael@0 7259 }
michael@0 7260
michael@0 7261 uint32_t spaceWidthAppUnits =
michael@0 7262 NS_lroundf(aFont->GetMetrics().spaceWidth * mAppUnitsPerDevUnit);
michael@0 7263 if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
michael@0 7264 return false;
michael@0 7265 }
michael@0 7266
michael@0 7267 AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
michael@0 7268 CompressedGlyph g;
michael@0 7269 g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
michael@0 7270 if (aSpaceChar == ' ') {
michael@0 7271 g.SetIsSpace();
michael@0 7272 }
michael@0 7273 GetCharacterGlyphs()[aCharIndex] = g;
michael@0 7274 return true;
michael@0 7275 }
michael@0 7276
michael@0 7277 void
michael@0 7278 gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
michael@0 7279 {
michael@0 7280 bool needsGlyphExtents = NeedsGlyphExtents(this);
michael@0 7281 if (!needsGlyphExtents && !mDetailedGlyphs)
michael@0 7282 return;
michael@0 7283
michael@0 7284 uint32_t i, runCount = mGlyphRuns.Length();
michael@0 7285 CompressedGlyph *charGlyphs = mCharacterGlyphs;
michael@0 7286 for (i = 0; i < runCount; ++i) {
michael@0 7287 const GlyphRun& run = mGlyphRuns[i];
michael@0 7288 gfxFont *font = run.mFont;
michael@0 7289 uint32_t start = run.mCharacterOffset;
michael@0 7290 uint32_t end = i + 1 < runCount ?
michael@0 7291 mGlyphRuns[i + 1].mCharacterOffset : GetLength();
michael@0 7292 bool fontIsSetup = false;
michael@0 7293 uint32_t j;
michael@0 7294 gfxGlyphExtents *extents = font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
michael@0 7295
michael@0 7296 for (j = start; j < end; ++j) {
michael@0 7297 const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[j];
michael@0 7298 if (glyphData->IsSimpleGlyph()) {
michael@0 7299 // If we're in speed mode, don't set up glyph extents here; we'll
michael@0 7300 // just return "optimistic" glyph bounds later
michael@0 7301 if (needsGlyphExtents) {
michael@0 7302 uint32_t glyphIndex = glyphData->GetSimpleGlyph();
michael@0 7303 if (!extents->IsGlyphKnown(glyphIndex)) {
michael@0 7304 if (!fontIsSetup) {
michael@0 7305 if (!font->SetupCairoFont(aRefContext)) {
michael@0 7306 NS_WARNING("failed to set up font for glyph extents");
michael@0 7307 break;
michael@0 7308 }
michael@0 7309 fontIsSetup = true;
michael@0 7310 }
michael@0 7311 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
michael@0 7312 ++gGlyphExtentsSetupEagerSimple;
michael@0 7313 #endif
michael@0 7314 font->SetupGlyphExtents(aRefContext, glyphIndex, false, extents);
michael@0 7315 }
michael@0 7316 }
michael@0 7317 } else if (!glyphData->IsMissing()) {
michael@0 7318 uint32_t glyphCount = glyphData->GetGlyphCount();
michael@0 7319 if (glyphCount == 0) {
michael@0 7320 continue;
michael@0 7321 }
michael@0 7322 const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j);
michael@0 7323 if (!details) {
michael@0 7324 continue;
michael@0 7325 }
michael@0 7326 for (uint32_t k = 0; k < glyphCount; ++k, ++details) {
michael@0 7327 uint32_t glyphIndex = details->mGlyphID;
michael@0 7328 if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
michael@0 7329 if (!fontIsSetup) {
michael@0 7330 if (!font->SetupCairoFont(aRefContext)) {
michael@0 7331 NS_WARNING("failed to set up font for glyph extents");
michael@0 7332 break;
michael@0 7333 }
michael@0 7334 fontIsSetup = true;
michael@0 7335 }
michael@0 7336 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
michael@0 7337 ++gGlyphExtentsSetupEagerTight;
michael@0 7338 #endif
michael@0 7339 font->SetupGlyphExtents(aRefContext, glyphIndex, true, extents);
michael@0 7340 }
michael@0 7341 }
michael@0 7342 }
michael@0 7343 }
michael@0 7344 }
michael@0 7345 }
michael@0 7346
michael@0 7347
michael@0 7348 gfxTextRun::ClusterIterator::ClusterIterator(gfxTextRun *aTextRun)
michael@0 7349 : mTextRun(aTextRun), mCurrentChar(uint32_t(-1))
michael@0 7350 {
michael@0 7351 }
michael@0 7352
michael@0 7353 void
michael@0 7354 gfxTextRun::ClusterIterator::Reset()
michael@0 7355 {
michael@0 7356 mCurrentChar = uint32_t(-1);
michael@0 7357 }
michael@0 7358
michael@0 7359 bool
michael@0 7360 gfxTextRun::ClusterIterator::NextCluster()
michael@0 7361 {
michael@0 7362 uint32_t len = mTextRun->GetLength();
michael@0 7363 while (++mCurrentChar < len) {
michael@0 7364 if (mTextRun->IsClusterStart(mCurrentChar)) {
michael@0 7365 return true;
michael@0 7366 }
michael@0 7367 }
michael@0 7368
michael@0 7369 mCurrentChar = uint32_t(-1);
michael@0 7370 return false;
michael@0 7371 }
michael@0 7372
michael@0 7373 uint32_t
michael@0 7374 gfxTextRun::ClusterIterator::ClusterLength() const
michael@0 7375 {
michael@0 7376 if (mCurrentChar == uint32_t(-1)) {
michael@0 7377 return 0;
michael@0 7378 }
michael@0 7379
michael@0 7380 uint32_t i = mCurrentChar,
michael@0 7381 len = mTextRun->GetLength();
michael@0 7382 while (++i < len) {
michael@0 7383 if (mTextRun->IsClusterStart(i)) {
michael@0 7384 break;
michael@0 7385 }
michael@0 7386 }
michael@0 7387
michael@0 7388 return i - mCurrentChar;
michael@0 7389 }
michael@0 7390
michael@0 7391 gfxFloat
michael@0 7392 gfxTextRun::ClusterIterator::ClusterAdvance(PropertyProvider *aProvider) const
michael@0 7393 {
michael@0 7394 if (mCurrentChar == uint32_t(-1)) {
michael@0 7395 return 0;
michael@0 7396 }
michael@0 7397
michael@0 7398 return mTextRun->GetAdvanceWidth(mCurrentChar, ClusterLength(), aProvider);
michael@0 7399 }
michael@0 7400
michael@0 7401 size_t
michael@0 7402 gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
michael@0 7403 {
michael@0 7404 // The second arg is how much gfxTextRun::AllocateStorage would have
michael@0 7405 // allocated.
michael@0 7406 size_t total = mGlyphRuns.SizeOfExcludingThis(aMallocSizeOf);
michael@0 7407
michael@0 7408 if (mDetailedGlyphs) {
michael@0 7409 total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
michael@0 7410 }
michael@0 7411
michael@0 7412 return total;
michael@0 7413 }
michael@0 7414
michael@0 7415 size_t
michael@0 7416 gfxTextRun::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
michael@0 7417 {
michael@0 7418 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 7419 }
michael@0 7420
michael@0 7421
michael@0 7422 #ifdef DEBUG
michael@0 7423 void
michael@0 7424 gfxTextRun::Dump(FILE* aOutput) {
michael@0 7425 if (!aOutput) {
michael@0 7426 aOutput = stdout;
michael@0 7427 }
michael@0 7428
michael@0 7429 uint32_t i;
michael@0 7430 fputc('[', aOutput);
michael@0 7431 for (i = 0; i < mGlyphRuns.Length(); ++i) {
michael@0 7432 if (i > 0) {
michael@0 7433 fputc(',', aOutput);
michael@0 7434 }
michael@0 7435 gfxFont* font = mGlyphRuns[i].mFont;
michael@0 7436 const gfxFontStyle* style = font->GetStyle();
michael@0 7437 NS_ConvertUTF16toUTF8 fontName(font->GetName());
michael@0 7438 nsAutoCString lang;
michael@0 7439 style->language->ToUTF8String(lang);
michael@0 7440 fprintf(aOutput, "%d: %s %f/%d/%d/%s", mGlyphRuns[i].mCharacterOffset,
michael@0 7441 fontName.get(), style->size,
michael@0 7442 style->weight, style->style, lang.get());
michael@0 7443 }
michael@0 7444 fputc(']', aOutput);
michael@0 7445 }
michael@0 7446 #endif

mercurial