gfx/thebes/gfxFont.cpp

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

mercurial