/src/mozilla-central/gfx/thebes/gfxFontEntry.cpp
Line | Count | Source (jump to first uncovered line) |
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 "gfxFontEntry.h" |
7 | | |
8 | | #include "mozilla/DebugOnly.h" |
9 | | #include "mozilla/FontPropertyTypes.h" |
10 | | #include "mozilla/MathAlgorithms.h" |
11 | | |
12 | | #include "mozilla/Logging.h" |
13 | | |
14 | | #include "gfxTextRun.h" |
15 | | #include "gfxPlatform.h" |
16 | | #include "nsGkAtoms.h" |
17 | | |
18 | | #include "gfxTypes.h" |
19 | | #include "gfxContext.h" |
20 | | #include "gfxFontConstants.h" |
21 | | #include "gfxHarfBuzzShaper.h" |
22 | | #include "gfxUserFontSet.h" |
23 | | #include "gfxPlatformFontList.h" |
24 | | #include "nsUnicodeProperties.h" |
25 | | #include "nsMathUtils.h" |
26 | | #include "nsBidiUtils.h" |
27 | | #include "nsUnicodeRange.h" |
28 | | #include "nsStyleConsts.h" |
29 | | #include "mozilla/AppUnits.h" |
30 | | #include "mozilla/FloatingPoint.h" |
31 | | #include "mozilla/Likely.h" |
32 | | #include "mozilla/MemoryReporting.h" |
33 | | #include "mozilla/Preferences.h" |
34 | | #include "mozilla/Services.h" |
35 | | #include "mozilla/StaticPrefs.h" |
36 | | #include "mozilla/Telemetry.h" |
37 | | #include "gfxSVGGlyphs.h" |
38 | | #include "gfx2DGlue.h" |
39 | | |
40 | | #include "harfbuzz/hb.h" |
41 | | #include "harfbuzz/hb-ot.h" |
42 | | #include "graphite2/Font.h" |
43 | | |
44 | | #include <algorithm> |
45 | | |
46 | | using namespace mozilla; |
47 | | using namespace mozilla::gfx; |
48 | | using namespace mozilla::unicode; |
49 | | using mozilla::services::GetObserverService; |
50 | | |
51 | | void |
52 | | gfxCharacterMap::NotifyReleased() |
53 | 0 | { |
54 | 0 | gfxPlatformFontList *fontlist = gfxPlatformFontList::PlatformFontList(); |
55 | 0 | if (mShared) { |
56 | 0 | fontlist->RemoveCmap(this); |
57 | 0 | } |
58 | 0 | delete this; |
59 | 0 | } |
60 | | |
61 | | gfxFontEntry::gfxFontEntry() : |
62 | | mFixedPitch(false), |
63 | | mIsBadUnderlineFont(false), |
64 | | mIsUserFontContainer(false), |
65 | | mIsDataUserFont(false), |
66 | | mIsLocalUserFont(false), |
67 | | mStandardFace(false), |
68 | | mIgnoreGDEF(false), |
69 | | mIgnoreGSUB(false), |
70 | | mSVGInitialized(false), |
71 | | mHasSpaceFeaturesInitialized(false), |
72 | | mHasSpaceFeatures(false), |
73 | | mHasSpaceFeaturesKerning(false), |
74 | | mHasSpaceFeaturesNonKerning(false), |
75 | | mSkipDefaultFeatureSpaceCheck(false), |
76 | | mGraphiteSpaceContextualsInitialized(false), |
77 | | mHasGraphiteSpaceContextuals(false), |
78 | | mSpaceGlyphIsInvisible(false), |
79 | | mSpaceGlyphIsInvisibleInitialized(false), |
80 | | mHasGraphiteTables(false), |
81 | | mCheckedForGraphiteTables(false), |
82 | | mHasCmapTable(false), |
83 | | mGrFaceInitialized(false), |
84 | | mCheckedForColorGlyph(false), |
85 | | mCheckedForVariationAxes(false) |
86 | 0 | { |
87 | 0 | memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures)); |
88 | 0 | memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures)); |
89 | 0 | } |
90 | | |
91 | | gfxFontEntry::gfxFontEntry(const nsACString& aName, bool aIsStandardFace) : |
92 | | mName(aName), |
93 | | mFixedPitch(false), |
94 | | mIsBadUnderlineFont(false), |
95 | | mIsUserFontContainer(false), |
96 | | mIsDataUserFont(false), |
97 | | mIsLocalUserFont(false), |
98 | | mStandardFace(aIsStandardFace), |
99 | | mIgnoreGDEF(false), |
100 | | mIgnoreGSUB(false), |
101 | | mSVGInitialized(false), |
102 | | mHasSpaceFeaturesInitialized(false), |
103 | | mHasSpaceFeatures(false), |
104 | | mHasSpaceFeaturesKerning(false), |
105 | | mHasSpaceFeaturesNonKerning(false), |
106 | | mSkipDefaultFeatureSpaceCheck(false), |
107 | | mGraphiteSpaceContextualsInitialized(false), |
108 | | mHasGraphiteSpaceContextuals(false), |
109 | | mSpaceGlyphIsInvisible(false), |
110 | | mSpaceGlyphIsInvisibleInitialized(false), |
111 | | mHasGraphiteTables(false), |
112 | | mCheckedForGraphiteTables(false), |
113 | | mHasCmapTable(false), |
114 | | mGrFaceInitialized(false), |
115 | | mCheckedForColorGlyph(false), |
116 | | mCheckedForVariationAxes(false) |
117 | 0 | { |
118 | 0 | memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures)); |
119 | 0 | memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures)); |
120 | 0 | } |
121 | | |
122 | | gfxFontEntry::~gfxFontEntry() |
123 | 0 | { |
124 | 0 | // Should not be dropped by stylo |
125 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
126 | 0 | if (mCOLR) { |
127 | 0 | hb_blob_destroy(mCOLR); |
128 | 0 | } |
129 | 0 |
|
130 | 0 | if (mCPAL) { |
131 | 0 | hb_blob_destroy(mCPAL); |
132 | 0 | } |
133 | 0 |
|
134 | 0 | // For downloaded fonts, we need to tell the user font cache that this |
135 | 0 | // entry is being deleted. |
136 | 0 | if (mIsDataUserFont) { |
137 | 0 | gfxUserFontSet::UserFontCache::ForgetFont(this); |
138 | 0 | } |
139 | 0 |
|
140 | 0 | if (mFeatureInputs) { |
141 | 0 | for (auto iter = mFeatureInputs->Iter(); !iter.Done(); iter.Next()) { |
142 | 0 | hb_set_t*& set = iter.Data(); |
143 | 0 | hb_set_destroy(set); |
144 | 0 | } |
145 | 0 | } |
146 | 0 |
|
147 | 0 | // By the time the entry is destroyed, all font instances that were |
148 | 0 | // using it should already have been deleted, and so the HB and/or Gr |
149 | 0 | // face objects should have been released. |
150 | 0 | MOZ_ASSERT(!mHBFace); |
151 | 0 | MOZ_ASSERT(!mGrFaceInitialized); |
152 | 0 | } |
153 | | |
154 | | bool gfxFontEntry::TestCharacterMap(uint32_t aCh) |
155 | 0 | { |
156 | 0 | if (!mCharacterMap) { |
157 | 0 | ReadCMAP(); |
158 | 0 | NS_ASSERTION(mCharacterMap, "failed to initialize character map"); |
159 | 0 | } |
160 | 0 | return mCharacterMap->test(aCh); |
161 | 0 | } |
162 | | |
163 | | nsresult gfxFontEntry::InitializeUVSMap() |
164 | 0 | { |
165 | 0 | // mUVSOffset will not be initialized |
166 | 0 | // until cmap is initialized. |
167 | 0 | if (!mCharacterMap) { |
168 | 0 | ReadCMAP(); |
169 | 0 | NS_ASSERTION(mCharacterMap, "failed to initialize character map"); |
170 | 0 | } |
171 | 0 |
|
172 | 0 | if (!mUVSOffset) { |
173 | 0 | return NS_ERROR_FAILURE; |
174 | 0 | } |
175 | 0 | |
176 | 0 | if (!mUVSData) { |
177 | 0 | const uint32_t kCmapTag = TRUETYPE_TAG('c','m','a','p'); |
178 | 0 | AutoTable cmapTable(this, kCmapTag); |
179 | 0 | if (!cmapTable) { |
180 | 0 | mUVSOffset = 0; // don't bother to read the table again |
181 | 0 | return NS_ERROR_FAILURE; |
182 | 0 | } |
183 | 0 | |
184 | 0 | UniquePtr<uint8_t[]> uvsData; |
185 | 0 | unsigned int cmapLen; |
186 | 0 | const char* cmapData = hb_blob_get_data(cmapTable, &cmapLen); |
187 | 0 | nsresult rv = gfxFontUtils::ReadCMAPTableFormat14( |
188 | 0 | (const uint8_t*)cmapData + mUVSOffset, |
189 | 0 | cmapLen - mUVSOffset, uvsData); |
190 | 0 |
|
191 | 0 | if (NS_FAILED(rv)) { |
192 | 0 | mUVSOffset = 0; // don't bother to read the table again |
193 | 0 | return rv; |
194 | 0 | } |
195 | 0 | |
196 | 0 | mUVSData = std::move(uvsData); |
197 | 0 | } |
198 | 0 |
|
199 | 0 | return NS_OK; |
200 | 0 | } |
201 | | |
202 | | uint16_t gfxFontEntry::GetUVSGlyph(uint32_t aCh, uint32_t aVS) |
203 | 0 | { |
204 | 0 | InitializeUVSMap(); |
205 | 0 |
|
206 | 0 | if (mUVSData) { |
207 | 0 | return gfxFontUtils::MapUVSToGlyphFormat14(mUVSData.get(), aCh, aVS); |
208 | 0 | } |
209 | 0 | |
210 | 0 | return 0; |
211 | 0 | } |
212 | | |
213 | | bool gfxFontEntry::SupportsScriptInGSUB(const hb_tag_t* aScriptTags) |
214 | 0 | { |
215 | 0 | hb_face_t *face = GetHBFace(); |
216 | 0 | if (!face) { |
217 | 0 | return false; |
218 | 0 | } |
219 | 0 | |
220 | 0 | unsigned int index; |
221 | 0 | hb_tag_t chosenScript; |
222 | 0 | bool found = |
223 | 0 | hb_ot_layout_table_choose_script(face, TRUETYPE_TAG('G','S','U','B'), |
224 | 0 | aScriptTags, &index, &chosenScript); |
225 | 0 | hb_face_destroy(face); |
226 | 0 |
|
227 | 0 | return found && chosenScript != TRUETYPE_TAG('D','F','L','T'); |
228 | 0 | } |
229 | | |
230 | | nsresult gfxFontEntry::ReadCMAP(FontInfoData *aFontInfoData) |
231 | 0 | { |
232 | 0 | NS_ASSERTION(false, "using default no-op implementation of ReadCMAP"); |
233 | 0 | mCharacterMap = new gfxCharacterMap(); |
234 | 0 | return NS_OK; |
235 | 0 | } |
236 | | |
237 | | nsCString |
238 | | gfxFontEntry::RealFaceName() |
239 | 0 | { |
240 | 0 | AutoTable nameTable(this, TRUETYPE_TAG('n','a','m','e')); |
241 | 0 | if (nameTable) { |
242 | 0 | nsAutoCString name; |
243 | 0 | nsresult rv = gfxFontUtils::GetFullNameFromTable(nameTable, name); |
244 | 0 | if (NS_SUCCEEDED(rv)) { |
245 | 0 | return std::move(name); |
246 | 0 | } |
247 | 0 | } |
248 | 0 | return Name(); |
249 | 0 | } |
250 | | |
251 | | gfxFont* |
252 | | gfxFontEntry::FindOrMakeFont(const gfxFontStyle *aStyle, |
253 | | gfxCharacterMap* aUnicodeRangeMap) |
254 | 0 | { |
255 | 0 | // the font entry name is the psname, not the family name |
256 | 0 | gfxFont* font = |
257 | 0 | gfxFontCache::GetCache()->Lookup(this, aStyle, aUnicodeRangeMap); |
258 | 0 |
|
259 | 0 | if (!font) { |
260 | 0 | gfxFont *newFont = CreateFontInstance(aStyle); |
261 | 0 | if (!newFont) { |
262 | 0 | return nullptr; |
263 | 0 | } |
264 | 0 | if (!newFont->Valid()) { |
265 | 0 | delete newFont; |
266 | 0 | return nullptr; |
267 | 0 | } |
268 | 0 | font = newFont; |
269 | 0 | font->SetUnicodeRangeMap(aUnicodeRangeMap); |
270 | 0 | gfxFontCache::GetCache()->AddNew(font); |
271 | 0 | } |
272 | 0 | return font; |
273 | 0 | } |
274 | | |
275 | | uint16_t |
276 | | gfxFontEntry::UnitsPerEm() |
277 | 0 | { |
278 | 0 | if (!mUnitsPerEm) { |
279 | 0 | AutoTable headTable(this, TRUETYPE_TAG('h','e','a','d')); |
280 | 0 | if (headTable) { |
281 | 0 | uint32_t len; |
282 | 0 | const HeadTable* head = |
283 | 0 | reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable, |
284 | 0 | &len)); |
285 | 0 | if (len >= sizeof(HeadTable)) { |
286 | 0 | mUnitsPerEm = head->unitsPerEm; |
287 | 0 | } |
288 | 0 | } |
289 | 0 |
|
290 | 0 | // if we didn't find a usable 'head' table, or if the value was |
291 | 0 | // outside the valid range, record it as invalid |
292 | 0 | if (mUnitsPerEm < kMinUPEM || mUnitsPerEm > kMaxUPEM) { |
293 | 0 | mUnitsPerEm = kInvalidUPEM; |
294 | 0 | } |
295 | 0 | } |
296 | 0 | return mUnitsPerEm; |
297 | 0 | } |
298 | | |
299 | | bool |
300 | | gfxFontEntry::HasSVGGlyph(uint32_t aGlyphId) |
301 | 0 | { |
302 | 0 | NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first."); |
303 | 0 | return mSVGGlyphs->HasSVGGlyph(aGlyphId); |
304 | 0 | } |
305 | | |
306 | | bool |
307 | | gfxFontEntry::GetSVGGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphId, |
308 | | gfxFloat aSize, gfxRect* aResult) |
309 | 0 | { |
310 | 0 | MOZ_ASSERT(mSVGInitialized, |
311 | 0 | "SVG data has not yet been loaded. TryGetSVGData() first."); |
312 | 0 | MOZ_ASSERT(mUnitsPerEm >= kMinUPEM && mUnitsPerEm <= kMaxUPEM, |
313 | 0 | "font has invalid unitsPerEm"); |
314 | 0 |
|
315 | 0 | gfxMatrix svgToApp(aSize / mUnitsPerEm, 0, 0, aSize / mUnitsPerEm, 0, 0); |
316 | 0 | return mSVGGlyphs->GetGlyphExtents(aGlyphId, svgToApp, aResult); |
317 | 0 | } |
318 | | |
319 | | void |
320 | | gfxFontEntry::RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, |
321 | | SVGContextPaint* aContextPaint) |
322 | 0 | { |
323 | 0 | NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first."); |
324 | 0 | mSVGGlyphs->RenderGlyph(aContext, aGlyphId, aContextPaint); |
325 | 0 | } |
326 | | |
327 | | bool |
328 | | gfxFontEntry::TryGetSVGData(gfxFont* aFont) |
329 | 0 | { |
330 | 0 | if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) { |
331 | 0 | return false; |
332 | 0 | } |
333 | 0 | |
334 | 0 | if (!mSVGInitialized) { |
335 | 0 | mSVGInitialized = true; |
336 | 0 |
|
337 | 0 | // If UnitsPerEm is not known/valid, we can't use SVG glyphs |
338 | 0 | if (UnitsPerEm() == kInvalidUPEM) { |
339 | 0 | return false; |
340 | 0 | } |
341 | 0 | |
342 | 0 | // We don't use AutoTable here because we'll pass ownership of this |
343 | 0 | // blob to the gfxSVGGlyphs, once we've confirmed the table exists |
344 | 0 | hb_blob_t *svgTable = GetFontTable(TRUETYPE_TAG('S','V','G',' ')); |
345 | 0 | if (!svgTable) { |
346 | 0 | return false; |
347 | 0 | } |
348 | 0 | |
349 | 0 | // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished |
350 | 0 | // with it. |
351 | 0 | mSVGGlyphs = MakeUnique<gfxSVGGlyphs>(svgTable, this); |
352 | 0 | } |
353 | 0 |
|
354 | 0 | if (mSVGGlyphs && !mFontsUsingSVGGlyphs.Contains(aFont)) { |
355 | 0 | mFontsUsingSVGGlyphs.AppendElement(aFont); |
356 | 0 | } |
357 | 0 |
|
358 | 0 | return !!mSVGGlyphs; |
359 | 0 | } |
360 | | |
361 | | void |
362 | | gfxFontEntry::NotifyFontDestroyed(gfxFont* aFont) |
363 | 0 | { |
364 | 0 | mFontsUsingSVGGlyphs.RemoveElement(aFont); |
365 | 0 | } |
366 | | |
367 | | void |
368 | | gfxFontEntry::NotifyGlyphsChanged() |
369 | 0 | { |
370 | 0 | for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) { |
371 | 0 | gfxFont* font = mFontsUsingSVGGlyphs[i]; |
372 | 0 | font->NotifyGlyphsChanged(); |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | | bool |
377 | | gfxFontEntry::TryGetColorGlyphs() |
378 | 0 | { |
379 | 0 | if (mCheckedForColorGlyph) { |
380 | 0 | return (mCOLR && mCPAL); |
381 | 0 | } |
382 | 0 |
|
383 | 0 | mCheckedForColorGlyph = true; |
384 | 0 |
|
385 | 0 | mCOLR = GetFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R')); |
386 | 0 | if (!mCOLR) { |
387 | 0 | return false; |
388 | 0 | } |
389 | 0 | |
390 | 0 | mCPAL = GetFontTable(TRUETYPE_TAG('C', 'P', 'A', 'L')); |
391 | 0 | if (!mCPAL) { |
392 | 0 | hb_blob_destroy(mCOLR); |
393 | 0 | mCOLR = nullptr; |
394 | 0 | return false; |
395 | 0 | } |
396 | 0 | |
397 | 0 | // validation COLR and CPAL table |
398 | 0 | if (gfxFontUtils::ValidateColorGlyphs(mCOLR, mCPAL)) { |
399 | 0 | return true; |
400 | 0 | } |
401 | 0 | |
402 | 0 | hb_blob_destroy(mCOLR); |
403 | 0 | hb_blob_destroy(mCPAL); |
404 | 0 | mCOLR = nullptr; |
405 | 0 | mCPAL = nullptr; |
406 | 0 | return false; |
407 | 0 | } |
408 | | |
409 | | /** |
410 | | * FontTableBlobData |
411 | | * |
412 | | * See FontTableHashEntry for the general strategy. |
413 | | */ |
414 | | |
415 | | class gfxFontEntry::FontTableBlobData { |
416 | | public: |
417 | | explicit FontTableBlobData(nsTArray<uint8_t>&& aBuffer) |
418 | | : mTableData(std::move(aBuffer)) |
419 | | , mHashtable(nullptr) |
420 | | , mHashKey(0) |
421 | 0 | { |
422 | 0 | MOZ_COUNT_CTOR(FontTableBlobData); |
423 | 0 | } |
424 | | |
425 | 0 | ~FontTableBlobData() { |
426 | 0 | MOZ_COUNT_DTOR(FontTableBlobData); |
427 | 0 | if (mHashtable && mHashKey) { |
428 | 0 | mHashtable->RemoveEntry(mHashKey); |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | | // Useful for creating blobs |
433 | | const char *GetTable() const |
434 | 0 | { |
435 | 0 | return reinterpret_cast<const char*>(mTableData.Elements()); |
436 | 0 | } |
437 | 0 | uint32_t GetTableLength() const { return mTableData.Length(); } |
438 | | |
439 | | // Tell this FontTableBlobData to remove the HashEntry when this is |
440 | | // destroyed. |
441 | | void ManageHashEntry(nsTHashtable<FontTableHashEntry> *aHashtable, |
442 | | uint32_t aHashKey) |
443 | 0 | { |
444 | 0 | mHashtable = aHashtable; |
445 | 0 | mHashKey = aHashKey; |
446 | 0 | } |
447 | | |
448 | | // Disconnect from the HashEntry (because the blob has already been |
449 | | // removed from the hashtable). |
450 | | void ForgetHashEntry() |
451 | 0 | { |
452 | 0 | mHashtable = nullptr; |
453 | 0 | mHashKey = 0; |
454 | 0 | } |
455 | | |
456 | 0 | size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { |
457 | 0 | return mTableData.ShallowSizeOfExcludingThis(aMallocSizeOf); |
458 | 0 | } |
459 | 0 | size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { |
460 | 0 | return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
461 | 0 | } |
462 | | |
463 | | private: |
464 | | // The font table data block |
465 | | nsTArray<uint8_t> mTableData; |
466 | | |
467 | | // The blob destroy function needs to know the owning hashtable |
468 | | // and the hashtable key, so that it can remove the entry. |
469 | | nsTHashtable<FontTableHashEntry> *mHashtable; |
470 | | uint32_t mHashKey; |
471 | | |
472 | | // not implemented |
473 | | FontTableBlobData(const FontTableBlobData&); |
474 | | }; |
475 | | |
476 | | hb_blob_t * |
477 | | gfxFontEntry::FontTableHashEntry:: |
478 | | ShareTableAndGetBlob(nsTArray<uint8_t>&& aTable, |
479 | | nsTHashtable<FontTableHashEntry> *aHashtable) |
480 | 0 | { |
481 | 0 | Clear(); |
482 | 0 | // adopts elements of aTable |
483 | 0 | mSharedBlobData = new FontTableBlobData(std::move(aTable)); |
484 | 0 |
|
485 | 0 | mBlob = hb_blob_create(mSharedBlobData->GetTable(), |
486 | 0 | mSharedBlobData->GetTableLength(), |
487 | 0 | HB_MEMORY_MODE_READONLY, |
488 | 0 | mSharedBlobData, DeleteFontTableBlobData); |
489 | 0 | if (mBlob == hb_blob_get_empty() ) { |
490 | 0 | // The FontTableBlobData was destroyed during hb_blob_create(). |
491 | 0 | // The (empty) blob is still be held in the hashtable with a strong |
492 | 0 | // reference. |
493 | 0 | return hb_blob_reference(mBlob); |
494 | 0 | } |
495 | 0 | |
496 | 0 | // Tell the FontTableBlobData to remove this hash entry when destroyed. |
497 | 0 | // The hashtable does not keep a strong reference. |
498 | 0 | mSharedBlobData->ManageHashEntry(aHashtable, GetKey()); |
499 | 0 | return mBlob; |
500 | 0 | } |
501 | | |
502 | | void |
503 | | gfxFontEntry::FontTableHashEntry::Clear() |
504 | 0 | { |
505 | 0 | // If the FontTableBlobData is managing the hash entry, then the blob is |
506 | 0 | // not owned by this HashEntry; otherwise there is strong reference to the |
507 | 0 | // blob that must be removed. |
508 | 0 | if (mSharedBlobData) { |
509 | 0 | mSharedBlobData->ForgetHashEntry(); |
510 | 0 | mSharedBlobData = nullptr; |
511 | 0 | } else if (mBlob) { |
512 | 0 | hb_blob_destroy(mBlob); |
513 | 0 | } |
514 | 0 | mBlob = nullptr; |
515 | 0 | } |
516 | | |
517 | | // a hb_destroy_func for hb_blob_create |
518 | | |
519 | | /* static */ void |
520 | | gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(void *aBlobData) |
521 | 0 | { |
522 | 0 | delete static_cast<FontTableBlobData*>(aBlobData); |
523 | 0 | } |
524 | | |
525 | | hb_blob_t * |
526 | | gfxFontEntry::FontTableHashEntry::GetBlob() const |
527 | 0 | { |
528 | 0 | return hb_blob_reference(mBlob); |
529 | 0 | } |
530 | | |
531 | | bool |
532 | | gfxFontEntry::GetExistingFontTable(uint32_t aTag, hb_blob_t **aBlob) |
533 | 0 | { |
534 | 0 | if (!mFontTableCache) { |
535 | 0 | // we do this here rather than on fontEntry construction |
536 | 0 | // because not all shapers will access the table cache at all |
537 | 0 | mFontTableCache = MakeUnique<nsTHashtable<FontTableHashEntry>>(8); |
538 | 0 | } |
539 | 0 |
|
540 | 0 | FontTableHashEntry *entry = mFontTableCache->GetEntry(aTag); |
541 | 0 | if (!entry) { |
542 | 0 | return false; |
543 | 0 | } |
544 | 0 | |
545 | 0 | *aBlob = entry->GetBlob(); |
546 | 0 | return true; |
547 | 0 | } |
548 | | |
549 | | hb_blob_t * |
550 | | gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag, |
551 | | nsTArray<uint8_t>* aBuffer) |
552 | 0 | { |
553 | 0 | if (MOZ_UNLIKELY(!mFontTableCache)) { |
554 | 0 | // we do this here rather than on fontEntry construction |
555 | 0 | // because not all shapers will access the table cache at all |
556 | 0 | mFontTableCache = MakeUnique<nsTHashtable<FontTableHashEntry>>(8); |
557 | 0 | } |
558 | 0 |
|
559 | 0 | FontTableHashEntry *entry = mFontTableCache->PutEntry(aTag); |
560 | 0 | if (MOZ_UNLIKELY(!entry)) { // OOM |
561 | 0 | return nullptr; |
562 | 0 | } |
563 | 0 | |
564 | 0 | if (!aBuffer) { |
565 | 0 | // ensure the entry is null |
566 | 0 | entry->Clear(); |
567 | 0 | return nullptr; |
568 | 0 | } |
569 | 0 | |
570 | 0 | return entry->ShareTableAndGetBlob(std::move(*aBuffer), mFontTableCache.get()); |
571 | 0 | } |
572 | | |
573 | | already_AddRefed<gfxCharacterMap> |
574 | | gfxFontEntry::GetCMAPFromFontInfo(FontInfoData *aFontInfoData, |
575 | | uint32_t& aUVSOffset) |
576 | 0 | { |
577 | 0 | if (!aFontInfoData || !aFontInfoData->mLoadCmaps) { |
578 | 0 | return nullptr; |
579 | 0 | } |
580 | 0 | |
581 | 0 | return aFontInfoData->GetCMAP(mName, aUVSOffset); |
582 | 0 | } |
583 | | |
584 | | hb_blob_t * |
585 | | gfxFontEntry::GetFontTable(uint32_t aTag) |
586 | 0 | { |
587 | 0 | hb_blob_t *blob; |
588 | 0 | if (GetExistingFontTable(aTag, &blob)) { |
589 | 0 | return blob; |
590 | 0 | } |
591 | 0 | |
592 | 0 | nsTArray<uint8_t> buffer; |
593 | 0 | bool haveTable = NS_SUCCEEDED(CopyFontTable(aTag, buffer)); |
594 | 0 |
|
595 | 0 | return ShareFontTableAndGetBlob(aTag, haveTable ? &buffer : nullptr); |
596 | 0 | } |
597 | | |
598 | | // callback for HarfBuzz to get a font table (in hb_blob_t form) |
599 | | // from the font entry (passed as aUserData) |
600 | | /*static*/ hb_blob_t * |
601 | | gfxFontEntry::HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData) |
602 | 0 | { |
603 | 0 | gfxFontEntry *fontEntry = static_cast<gfxFontEntry*>(aUserData); |
604 | 0 |
|
605 | 0 | // bug 589682 - ignore the GDEF table in buggy fonts (applies to |
606 | 0 | // Italic and BoldItalic faces of Times New Roman) |
607 | 0 | if (aTag == TRUETYPE_TAG('G','D','E','F') && |
608 | 0 | fontEntry->IgnoreGDEF()) { |
609 | 0 | return nullptr; |
610 | 0 | } |
611 | 0 | |
612 | 0 | // bug 721719 - ignore the GSUB table in buggy fonts (applies to Roboto, |
613 | 0 | // at least on some Android ICS devices; set in gfxFT2FontList.cpp) |
614 | 0 | if (aTag == TRUETYPE_TAG('G','S','U','B') && |
615 | 0 | fontEntry->IgnoreGSUB()) { |
616 | 0 | return nullptr; |
617 | 0 | } |
618 | 0 | |
619 | 0 | return fontEntry->GetFontTable(aTag); |
620 | 0 | } |
621 | | |
622 | | /*static*/ void |
623 | | gfxFontEntry::HBFaceDeletedCallback(void *aUserData) |
624 | 0 | { |
625 | 0 | gfxFontEntry *fe = static_cast<gfxFontEntry*>(aUserData); |
626 | 0 | fe->ForgetHBFace(); |
627 | 0 | } |
628 | | |
629 | | void |
630 | | gfxFontEntry::ForgetHBFace() |
631 | 0 | { |
632 | 0 | mHBFace = nullptr; |
633 | 0 | } |
634 | | |
635 | | hb_face_t* |
636 | | gfxFontEntry::GetHBFace() |
637 | 0 | { |
638 | 0 | if (!mHBFace) { |
639 | 0 | mHBFace = hb_face_create_for_tables(HBGetTable, this, |
640 | 0 | HBFaceDeletedCallback); |
641 | 0 | return mHBFace; |
642 | 0 | } |
643 | 0 | return hb_face_reference(mHBFace); |
644 | 0 | } |
645 | | |
646 | | /*static*/ const void* |
647 | | gfxFontEntry::GrGetTable(const void *aAppFaceHandle, unsigned int aName, |
648 | | size_t *aLen) |
649 | 0 | { |
650 | 0 | gfxFontEntry *fontEntry = |
651 | 0 | static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle)); |
652 | 0 | hb_blob_t *blob = fontEntry->GetFontTable(aName); |
653 | 0 | if (blob) { |
654 | 0 | unsigned int blobLength; |
655 | 0 | const void *tableData = hb_blob_get_data(blob, &blobLength); |
656 | 0 | fontEntry->mGrTableMap->Put(tableData, blob); |
657 | 0 | *aLen = blobLength; |
658 | 0 | return tableData; |
659 | 0 | } |
660 | 0 | *aLen = 0; |
661 | 0 | return nullptr; |
662 | 0 | } |
663 | | |
664 | | /*static*/ void |
665 | | gfxFontEntry::GrReleaseTable(const void *aAppFaceHandle, |
666 | | const void *aTableBuffer) |
667 | 0 | { |
668 | 0 | gfxFontEntry *fontEntry = |
669 | 0 | static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle)); |
670 | 0 | void* value; |
671 | 0 | if (fontEntry->mGrTableMap->Remove(aTableBuffer, &value)) { |
672 | 0 | hb_blob_destroy(static_cast<hb_blob_t*>(value)); |
673 | 0 | } |
674 | 0 | } |
675 | | |
676 | | gr_face* |
677 | | gfxFontEntry::GetGrFace() |
678 | 0 | { |
679 | 0 | if (!mGrFaceInitialized) { |
680 | 0 | gr_face_ops faceOps = { |
681 | 0 | sizeof(gr_face_ops), |
682 | 0 | GrGetTable, |
683 | 0 | GrReleaseTable |
684 | 0 | }; |
685 | 0 | mGrTableMap = new nsDataHashtable<nsPtrHashKey<const void>,void*>; |
686 | 0 | mGrFace = gr_make_face_with_ops(this, &faceOps, gr_face_default); |
687 | 0 | mGrFaceInitialized = true; |
688 | 0 | } |
689 | 0 | ++mGrFaceRefCnt; |
690 | 0 | return mGrFace; |
691 | 0 | } |
692 | | |
693 | | void |
694 | | gfxFontEntry::ReleaseGrFace(gr_face *aFace) |
695 | 0 | { |
696 | 0 | MOZ_ASSERT(aFace == mGrFace); // sanity-check |
697 | 0 | MOZ_ASSERT(mGrFaceRefCnt > 0); |
698 | 0 | if (--mGrFaceRefCnt == 0) { |
699 | 0 | gr_face_destroy(mGrFace); |
700 | 0 | mGrFace = nullptr; |
701 | 0 | mGrFaceInitialized = false; |
702 | 0 | delete mGrTableMap; |
703 | 0 | mGrTableMap = nullptr; |
704 | 0 | } |
705 | 0 | } |
706 | | |
707 | | void |
708 | | gfxFontEntry::DisconnectSVG() |
709 | 0 | { |
710 | 0 | if (mSVGInitialized && mSVGGlyphs) { |
711 | 0 | mSVGGlyphs = nullptr; |
712 | 0 | mSVGInitialized = false; |
713 | 0 | } |
714 | 0 | } |
715 | | |
716 | | bool |
717 | | gfxFontEntry::HasFontTable(uint32_t aTableTag) |
718 | 0 | { |
719 | 0 | AutoTable table(this, aTableTag); |
720 | 0 | return table && hb_blob_get_length(table) > 0; |
721 | 0 | } |
722 | | |
723 | | void |
724 | | gfxFontEntry::CheckForGraphiteTables() |
725 | 0 | { |
726 | 0 | mHasGraphiteTables = HasFontTable(TRUETYPE_TAG('S','i','l','f')); |
727 | 0 | } |
728 | | |
729 | | bool |
730 | | gfxFontEntry::HasGraphiteSpaceContextuals() |
731 | 0 | { |
732 | 0 | if (!mGraphiteSpaceContextualsInitialized) { |
733 | 0 | gr_face* face = GetGrFace(); |
734 | 0 | if (face) { |
735 | 0 | const gr_faceinfo* faceInfo = gr_face_info(face, 0); |
736 | 0 | mHasGraphiteSpaceContextuals = |
737 | 0 | faceInfo->space_contextuals != gr_faceinfo::gr_space_none; |
738 | 0 | } |
739 | 0 | ReleaseGrFace(face); // always balance GetGrFace, even if face is null |
740 | 0 | mGraphiteSpaceContextualsInitialized = true; |
741 | 0 | } |
742 | 0 | return mHasGraphiteSpaceContextuals; |
743 | 0 | } |
744 | | |
745 | 0 | #define FEATURE_SCRIPT_MASK 0x000000ff // script index replaces low byte of tag |
746 | | |
747 | | static_assert(int(Script::NUM_SCRIPT_CODES) <= FEATURE_SCRIPT_MASK, "Too many script codes"); |
748 | | |
749 | | // high-order three bytes of tag with script in low-order byte |
750 | 0 | #define SCRIPT_FEATURE(s,tag) (((~FEATURE_SCRIPT_MASK) & (tag)) | \ |
751 | 0 | ((FEATURE_SCRIPT_MASK) & static_cast<uint32_t>(s))) |
752 | | |
753 | | bool |
754 | | gfxFontEntry::SupportsOpenTypeFeature(Script aScript, uint32_t aFeatureTag) |
755 | 0 | { |
756 | 0 | if (!mSupportedFeatures) { |
757 | 0 | mSupportedFeatures = MakeUnique<nsDataHashtable<nsUint32HashKey,bool>>(); |
758 | 0 | } |
759 | 0 |
|
760 | 0 | // note: high-order three bytes *must* be unique for each feature |
761 | 0 | // listed below (see SCRIPT_FEATURE macro def'n) |
762 | 0 | NS_ASSERTION(aFeatureTag == HB_TAG('s','m','c','p') || |
763 | 0 | aFeatureTag == HB_TAG('c','2','s','c') || |
764 | 0 | aFeatureTag == HB_TAG('p','c','a','p') || |
765 | 0 | aFeatureTag == HB_TAG('c','2','p','c') || |
766 | 0 | aFeatureTag == HB_TAG('s','u','p','s') || |
767 | 0 | aFeatureTag == HB_TAG('s','u','b','s') || |
768 | 0 | aFeatureTag == HB_TAG('v','e','r','t'), |
769 | 0 | "use of unknown feature tag"); |
770 | 0 |
|
771 | 0 | // note: graphite feature support uses the last script index |
772 | 0 | NS_ASSERTION(int(aScript) < FEATURE_SCRIPT_MASK - 1, |
773 | 0 | "need to bump the size of the feature shift"); |
774 | 0 |
|
775 | 0 | uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag); |
776 | 0 | bool result; |
777 | 0 | if (mSupportedFeatures->Get(scriptFeature, &result)) { |
778 | 0 | return result; |
779 | 0 | } |
780 | 0 | |
781 | 0 | result = false; |
782 | 0 |
|
783 | 0 | hb_face_t *face = GetHBFace(); |
784 | 0 |
|
785 | 0 | if (hb_ot_layout_has_substitution(face)) { |
786 | 0 | hb_script_t hbScript = |
787 | 0 | gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript); |
788 | 0 |
|
789 | 0 | // Get the OpenType tag(s) that match this script code |
790 | 0 | hb_tag_t scriptTags[4] = { |
791 | 0 | HB_TAG_NONE, |
792 | 0 | HB_TAG_NONE, |
793 | 0 | HB_TAG_NONE, |
794 | 0 | HB_TAG_NONE |
795 | 0 | }; |
796 | 0 | hb_ot_tags_from_script(hbScript, &scriptTags[0], &scriptTags[1]); |
797 | 0 |
|
798 | 0 | // Replace the first remaining NONE with DEFAULT |
799 | 0 | hb_tag_t* scriptTag = &scriptTags[0]; |
800 | 0 | while (*scriptTag != HB_TAG_NONE) { |
801 | 0 | ++scriptTag; |
802 | 0 | } |
803 | 0 | *scriptTag = HB_OT_TAG_DEFAULT_SCRIPT; |
804 | 0 |
|
805 | 0 | // Now check for 'smcp' under the first of those scripts that is present |
806 | 0 | const hb_tag_t kGSUB = HB_TAG('G','S','U','B'); |
807 | 0 | scriptTag = &scriptTags[0]; |
808 | 0 | while (*scriptTag != HB_TAG_NONE) { |
809 | 0 | unsigned int scriptIndex; |
810 | 0 | if (hb_ot_layout_table_find_script(face, kGSUB, *scriptTag, |
811 | 0 | &scriptIndex)) { |
812 | 0 | if (hb_ot_layout_language_find_feature(face, kGSUB, |
813 | 0 | scriptIndex, |
814 | 0 | HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, |
815 | 0 | aFeatureTag, nullptr)) { |
816 | 0 | result = true; |
817 | 0 | } |
818 | 0 | break; |
819 | 0 | } |
820 | 0 | ++scriptTag; |
821 | 0 | } |
822 | 0 | } |
823 | 0 |
|
824 | 0 | hb_face_destroy(face); |
825 | 0 |
|
826 | 0 | mSupportedFeatures->Put(scriptFeature, result); |
827 | 0 |
|
828 | 0 | return result; |
829 | 0 | } |
830 | | |
831 | | const hb_set_t* |
832 | | gfxFontEntry::InputsForOpenTypeFeature(Script aScript, uint32_t aFeatureTag) |
833 | 0 | { |
834 | 0 | if (!mFeatureInputs) { |
835 | 0 | mFeatureInputs = MakeUnique<nsDataHashtable<nsUint32HashKey,hb_set_t*>>(); |
836 | 0 | } |
837 | 0 |
|
838 | 0 | NS_ASSERTION(aFeatureTag == HB_TAG('s','u','p','s') || |
839 | 0 | aFeatureTag == HB_TAG('s','u','b','s') || |
840 | 0 | aFeatureTag == HB_TAG('v','e','r','t'), |
841 | 0 | "use of unknown feature tag"); |
842 | 0 |
|
843 | 0 | uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag); |
844 | 0 | hb_set_t *inputGlyphs; |
845 | 0 | if (mFeatureInputs->Get(scriptFeature, &inputGlyphs)) { |
846 | 0 | return inputGlyphs; |
847 | 0 | } |
848 | 0 | |
849 | 0 | inputGlyphs = hb_set_create(); |
850 | 0 |
|
851 | 0 | hb_face_t *face = GetHBFace(); |
852 | 0 |
|
853 | 0 | if (hb_ot_layout_has_substitution(face)) { |
854 | 0 | hb_script_t hbScript = |
855 | 0 | gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript); |
856 | 0 |
|
857 | 0 | // Get the OpenType tag(s) that match this script code |
858 | 0 | hb_tag_t scriptTags[4] = { |
859 | 0 | HB_TAG_NONE, |
860 | 0 | HB_TAG_NONE, |
861 | 0 | HB_TAG_NONE, |
862 | 0 | HB_TAG_NONE |
863 | 0 | }; |
864 | 0 | hb_ot_tags_from_script(hbScript, &scriptTags[0], &scriptTags[1]); |
865 | 0 |
|
866 | 0 | // Replace the first remaining NONE with DEFAULT |
867 | 0 | hb_tag_t* scriptTag = &scriptTags[0]; |
868 | 0 | while (*scriptTag != HB_TAG_NONE) { |
869 | 0 | ++scriptTag; |
870 | 0 | } |
871 | 0 | *scriptTag = HB_OT_TAG_DEFAULT_SCRIPT; |
872 | 0 |
|
873 | 0 | const hb_tag_t kGSUB = HB_TAG('G','S','U','B'); |
874 | 0 | hb_tag_t features[2] = { aFeatureTag, HB_TAG_NONE }; |
875 | 0 | hb_set_t *featurelookups = hb_set_create(); |
876 | 0 | hb_ot_layout_collect_lookups(face, kGSUB, scriptTags, nullptr, |
877 | 0 | features, featurelookups); |
878 | 0 | hb_codepoint_t index = -1; |
879 | 0 | while (hb_set_next(featurelookups, &index)) { |
880 | 0 | hb_ot_layout_lookup_collect_glyphs(face, kGSUB, index, |
881 | 0 | nullptr, inputGlyphs, |
882 | 0 | nullptr, nullptr); |
883 | 0 | } |
884 | 0 | hb_set_destroy(featurelookups); |
885 | 0 | } |
886 | 0 |
|
887 | 0 | hb_face_destroy(face); |
888 | 0 |
|
889 | 0 | mFeatureInputs->Put(scriptFeature, inputGlyphs); |
890 | 0 | return inputGlyphs; |
891 | 0 | } |
892 | | |
893 | | bool |
894 | | gfxFontEntry::SupportsGraphiteFeature(uint32_t aFeatureTag) |
895 | 0 | { |
896 | 0 | if (!mSupportedFeatures) { |
897 | 0 | mSupportedFeatures = MakeUnique<nsDataHashtable<nsUint32HashKey,bool>>(); |
898 | 0 | } |
899 | 0 |
|
900 | 0 | // note: high-order three bytes *must* be unique for each feature |
901 | 0 | // listed below (see SCRIPT_FEATURE macro def'n) |
902 | 0 | NS_ASSERTION(aFeatureTag == HB_TAG('s','m','c','p') || |
903 | 0 | aFeatureTag == HB_TAG('c','2','s','c') || |
904 | 0 | aFeatureTag == HB_TAG('p','c','a','p') || |
905 | 0 | aFeatureTag == HB_TAG('c','2','p','c') || |
906 | 0 | aFeatureTag == HB_TAG('s','u','p','s') || |
907 | 0 | aFeatureTag == HB_TAG('s','u','b','s'), |
908 | 0 | "use of unknown feature tag"); |
909 | 0 |
|
910 | 0 | // graphite feature check uses the last script slot |
911 | 0 | uint32_t scriptFeature = SCRIPT_FEATURE(FEATURE_SCRIPT_MASK, aFeatureTag); |
912 | 0 | bool result; |
913 | 0 | if (mSupportedFeatures->Get(scriptFeature, &result)) { |
914 | 0 | return result; |
915 | 0 | } |
916 | 0 | |
917 | 0 | gr_face* face = GetGrFace(); |
918 | 0 | result = face ? gr_face_find_fref(face, aFeatureTag) != nullptr : false; |
919 | 0 | ReleaseGrFace(face); |
920 | 0 |
|
921 | 0 | mSupportedFeatures->Put(scriptFeature, result); |
922 | 0 |
|
923 | 0 | return result; |
924 | 0 | } |
925 | | |
926 | | void |
927 | | gfxFontEntry::GetFeatureInfo(nsTArray<gfxFontFeatureInfo>& aFeatureInfo) |
928 | 0 | { |
929 | 0 | // TODO: implement alternative code path for graphite fonts |
930 | 0 |
|
931 | 0 | hb_face_t* face = GetHBFace(); |
932 | 0 |
|
933 | 0 | // Get the list of features for a specific <script,langSys> pair and |
934 | 0 | // append them to aFeatureInfo. |
935 | 0 | auto collectForLang = |
936 | 0 | [=,&aFeatureInfo](hb_tag_t aTableTag, |
937 | 0 | unsigned int aScript, hb_tag_t aScriptTag, |
938 | 0 | unsigned int aLang, hb_tag_t aLangTag) { |
939 | 0 | unsigned int featCount = |
940 | 0 | hb_ot_layout_language_get_feature_tags(face, aTableTag, aScript, |
941 | 0 | aLang, 0, nullptr, nullptr); |
942 | 0 | AutoTArray<hb_tag_t,32> featTags; |
943 | 0 | featTags.SetLength(featCount); |
944 | 0 | hb_ot_layout_language_get_feature_tags(face, aTableTag, aScript, |
945 | 0 | aLang, 0, &featCount, |
946 | 0 | featTags.Elements()); |
947 | 0 | MOZ_ASSERT(featCount <= featTags.Length()); |
948 | 0 | // Just in case HB didn't fill featTags (i.e. in case it returned fewer |
949 | 0 | // tags than it promised), we truncate at the length it says it filled: |
950 | 0 | featTags.SetLength(featCount); |
951 | 0 | for (hb_tag_t t : featTags) { |
952 | 0 | aFeatureInfo.AppendElement( |
953 | 0 | gfxFontFeatureInfo{t, aScriptTag, aLangTag}); |
954 | 0 | } |
955 | 0 | }; |
956 | 0 |
|
957 | 0 | // Iterate over the language systems supported by a given script, |
958 | 0 | // and call collectForLang for each of them. |
959 | 0 | auto collectForScript = [=](hb_tag_t aTableTag, |
960 | 0 | unsigned int aScript, hb_tag_t aScriptTag) { |
961 | 0 | collectForLang(aTableTag, aScript, aScriptTag, |
962 | 0 | HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, |
963 | 0 | HB_TAG('d','f','l','t')); |
964 | 0 | unsigned int langCount = |
965 | 0 | hb_ot_layout_script_get_language_tags(face, aTableTag, aScript, 0, |
966 | 0 | nullptr, nullptr); |
967 | 0 | AutoTArray<hb_tag_t,32> langTags; |
968 | 0 | langTags.SetLength(langCount); |
969 | 0 | hb_ot_layout_script_get_language_tags(face, aTableTag, aScript, 0, |
970 | 0 | &langCount, langTags.Elements()); |
971 | 0 | MOZ_ASSERT(langCount <= langTags.Length()); |
972 | 0 | langTags.SetLength(langCount); |
973 | 0 | for (unsigned int lang = 0; lang < langCount; ++lang) { |
974 | 0 | collectForLang(aTableTag, aScript, aScriptTag, lang, langTags[lang]); |
975 | 0 | } |
976 | 0 | }; |
977 | 0 |
|
978 | 0 | // Iterate over the scripts supported by a table (GSUB or GPOS), and call |
979 | 0 | // collectForScript for each of them. |
980 | 0 | auto collectForTable = [=](hb_tag_t aTableTag) { |
981 | 0 | unsigned int scriptCount = |
982 | 0 | hb_ot_layout_table_get_script_tags(face, aTableTag, 0, nullptr, |
983 | 0 | nullptr); |
984 | 0 | AutoTArray<hb_tag_t,32> scriptTags; |
985 | 0 | scriptTags.SetLength(scriptCount); |
986 | 0 | hb_ot_layout_table_get_script_tags(face, aTableTag, 0, &scriptCount, |
987 | 0 | scriptTags.Elements()); |
988 | 0 | MOZ_ASSERT(scriptCount <= scriptTags.Length()); |
989 | 0 | scriptTags.SetLength(scriptCount); |
990 | 0 | for (unsigned int script = 0; script < scriptCount; ++script) { |
991 | 0 | collectForScript(aTableTag, script, scriptTags[script]); |
992 | 0 | } |
993 | 0 | }; |
994 | 0 |
|
995 | 0 | // Collect all OpenType Layout features, both substitution and positioning, |
996 | 0 | // supported by the font resource. |
997 | 0 | collectForTable(HB_TAG('G','S','U','B')); |
998 | 0 | collectForTable(HB_TAG('G','P','O','S')); |
999 | 0 |
|
1000 | 0 | hb_face_destroy(face); |
1001 | 0 | } |
1002 | | |
1003 | | bool |
1004 | | gfxFontEntry::GetColorLayersInfo(uint32_t aGlyphId, |
1005 | | const mozilla::gfx::Color& aDefaultColor, |
1006 | | nsTArray<uint16_t>& aLayerGlyphs, |
1007 | | nsTArray<mozilla::gfx::Color>& aLayerColors) |
1008 | 0 | { |
1009 | 0 | return gfxFontUtils::GetColorGlyphLayers(mCOLR, |
1010 | 0 | mCPAL, |
1011 | 0 | aGlyphId, |
1012 | 0 | aDefaultColor, |
1013 | 0 | aLayerGlyphs, |
1014 | 0 | aLayerColors); |
1015 | 0 | } |
1016 | | |
1017 | | void |
1018 | | gfxFontEntry::SetupVariationRanges() |
1019 | 0 | { |
1020 | 0 | if (!gfxPlatform::GetPlatform()->HasVariationFontSupport() || |
1021 | 0 | !StaticPrefs::layout_css_font_variations_enabled() || |
1022 | 0 | !HasVariations() || IsUserFont()) { |
1023 | 0 | return; |
1024 | 0 | } |
1025 | 0 | AutoTArray<gfxFontVariationAxis,4> axes; |
1026 | 0 | GetVariationAxes(axes); |
1027 | 0 | for (const auto& axis : axes) { |
1028 | 0 | switch (axis.mTag) { |
1029 | 0 | case HB_TAG('w','g','h','t'): |
1030 | 0 | // If the axis range looks like it doesn't fit the CSS font-weight |
1031 | 0 | // scale, we don't hook up the high-level property, and we mark |
1032 | 0 | // the face (in mRangeFlags) as having non-standard weight. This |
1033 | 0 | // means we won't map CSS font-weight to the axis. Setting 'wght' |
1034 | 0 | // with font-variation-settings will still work. |
1035 | 0 | // Strictly speaking, the min value should be checked against 1.0, |
1036 | 0 | // not 0.0, but we'll allow font makers that amount of leeway, as |
1037 | 0 | // in practice a number of fonts seem to use 0..1000. |
1038 | 0 | if (axis.mMinValue >= 0.0f && axis.mMaxValue <= 1000.0f && |
1039 | 0 | // If axis.mMaxValue is less than the default weight we already |
1040 | 0 | // set up, assume the axis has a non-standard range (like Skia) |
1041 | 0 | // and don't try to map it. |
1042 | 0 | Weight().Min() <= FontWeight(axis.mMaxValue)) { |
1043 | 0 | if (FontWeight(axis.mDefaultValue) != Weight().Min()) { |
1044 | 0 | mStandardFace = false; |
1045 | 0 | } |
1046 | 0 | mWeightRange = |
1047 | 0 | WeightRange(FontWeight(std::max(1.0f, axis.mMinValue)), |
1048 | 0 | FontWeight(axis.mMaxValue)); |
1049 | 0 | } else { |
1050 | 0 | mRangeFlags |= RangeFlags::eNonCSSWeight; |
1051 | 0 | } |
1052 | 0 | break; |
1053 | 0 |
|
1054 | 0 | case HB_TAG('w','d','t','h'): |
1055 | 0 | if (axis.mMinValue >= 0.0f && axis.mMaxValue <= 1000.0f && |
1056 | 0 | Stretch().Min() <= FontStretch(axis.mMaxValue)) { |
1057 | 0 | if (FontStretch(axis.mDefaultValue) != Stretch().Min()) { |
1058 | 0 | mStandardFace = false; |
1059 | 0 | } |
1060 | 0 | mStretchRange = |
1061 | 0 | StretchRange(FontStretch(axis.mMinValue), |
1062 | 0 | FontStretch(axis.mMaxValue)); |
1063 | 0 | } else { |
1064 | 0 | mRangeFlags |= RangeFlags::eNonCSSStretch; |
1065 | 0 | } |
1066 | 0 | break; |
1067 | 0 |
|
1068 | 0 | case HB_TAG('s','l','n','t'): |
1069 | 0 | if (axis.mMinValue >= -90.0f && axis.mMaxValue <= 90.0f) { |
1070 | 0 | if (FontSlantStyle::Oblique(axis.mDefaultValue) != SlantStyle().Min()) { |
1071 | 0 | mStandardFace = false; |
1072 | 0 | } |
1073 | 0 | mStyleRange = |
1074 | 0 | SlantStyleRange(FontSlantStyle::Oblique(axis.mMinValue), |
1075 | 0 | FontSlantStyle::Oblique(axis.mMaxValue)); |
1076 | 0 | } |
1077 | 0 | break; |
1078 | 0 |
|
1079 | 0 | case HB_TAG('i','t','a','l'): |
1080 | 0 | if (axis.mMinValue <= 0.0f && axis.mMaxValue >= 1.0f) { |
1081 | 0 | if (axis.mDefaultValue != 0.0f) { |
1082 | 0 | mStandardFace = false; |
1083 | 0 | } |
1084 | 0 | mStyleRange = |
1085 | 0 | SlantStyleRange(FontSlantStyle::Normal(), |
1086 | 0 | FontSlantStyle::Italic()); |
1087 | 0 | } |
1088 | 0 | break; |
1089 | 0 |
|
1090 | 0 | default: |
1091 | 0 | continue; |
1092 | 0 | } |
1093 | 0 | } |
1094 | 0 | } |
1095 | | |
1096 | | void |
1097 | | gfxFontEntry::CheckForVariationAxes() |
1098 | 0 | { |
1099 | 0 | if (HasVariations()) { |
1100 | 0 | AutoTArray<gfxFontVariationAxis,4> axes; |
1101 | 0 | GetVariationAxes(axes); |
1102 | 0 | for (const auto& axis : axes) { |
1103 | 0 | if (axis.mTag == HB_TAG('w','g','h','t') && |
1104 | 0 | axis.mMaxValue >= 600.0f) { |
1105 | 0 | mRangeFlags |= RangeFlags::eBoldVariableWeight; |
1106 | 0 | } else if (axis.mTag == HB_TAG('i','t','a','l') && |
1107 | 0 | axis.mMaxValue >= 1.0f) { |
1108 | 0 | mRangeFlags |= RangeFlags::eItalicVariation; |
1109 | 0 | } |
1110 | 0 | } |
1111 | 0 | } |
1112 | 0 | mCheckedForVariationAxes = true; |
1113 | 0 | } |
1114 | | |
1115 | | bool |
1116 | | gfxFontEntry::HasBoldVariableWeight() |
1117 | 0 | { |
1118 | 0 | MOZ_ASSERT(!mIsUserFontContainer, |
1119 | 0 | "should not be called for user-font containers!"); |
1120 | 0 |
|
1121 | 0 | if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) { |
1122 | 0 | return false; |
1123 | 0 | } |
1124 | 0 | |
1125 | 0 | if (!mCheckedForVariationAxes) { |
1126 | 0 | CheckForVariationAxes(); |
1127 | 0 | } |
1128 | 0 |
|
1129 | 0 | return bool(mRangeFlags & RangeFlags::eBoldVariableWeight); |
1130 | 0 | } |
1131 | | |
1132 | | bool |
1133 | | gfxFontEntry::HasItalicVariation() |
1134 | 0 | { |
1135 | 0 | MOZ_ASSERT(!mIsUserFontContainer, |
1136 | 0 | "should not be called for user-font containers!"); |
1137 | 0 |
|
1138 | 0 | if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) { |
1139 | 0 | return false; |
1140 | 0 | } |
1141 | 0 | |
1142 | 0 | if (!mCheckedForVariationAxes) { |
1143 | 0 | CheckForVariationAxes(); |
1144 | 0 | } |
1145 | 0 |
|
1146 | 0 | return bool(mRangeFlags & RangeFlags::eItalicVariation); |
1147 | 0 | } |
1148 | | |
1149 | | void |
1150 | | gfxFontEntry::GetVariationsForStyle(nsTArray<gfxFontVariation>& aResult, |
1151 | | const gfxFontStyle& aStyle) |
1152 | 0 | { |
1153 | 0 | if (!gfxPlatform::GetPlatform()->HasVariationFontSupport() || |
1154 | 0 | !StaticPrefs::layout_css_font_variations_enabled()) { |
1155 | 0 | return; |
1156 | 0 | } |
1157 | 0 | |
1158 | 0 | if (!HasVariations()) { |
1159 | 0 | return; |
1160 | 0 | } |
1161 | 0 | |
1162 | 0 | // Resolve high-level CSS properties from the requested style |
1163 | 0 | // (font-{style,weight,stretch}) to the appropriate variations. |
1164 | 0 | // The value used is clamped to the range available in the font face, |
1165 | 0 | // unless the face is a user font where no explicit descriptor was |
1166 | 0 | // given, indicated by the corresponding 'auto' range-flag. |
1167 | 0 | |
1168 | 0 | // We don't do these mappings if the font entry has weight and/or stretch |
1169 | 0 | // ranges that do not appear to use the CSS property scale. Some older |
1170 | 0 | // fonts created for QuickDrawGX/AAT may use "normalized" values where the |
1171 | 0 | // standard variation is 1.0 rather than 400.0 (weight) or 100.0 (stretch). |
1172 | 0 | |
1173 | 0 | if (!(mRangeFlags & RangeFlags::eNonCSSWeight)) { |
1174 | 0 | float weight = |
1175 | 0 | (IsUserFont() && (mRangeFlags & RangeFlags::eAutoWeight)) |
1176 | 0 | ? aStyle.weight.ToFloat() |
1177 | 0 | : Weight().Clamp(aStyle.weight).ToFloat(); |
1178 | 0 | aResult.AppendElement(gfxFontVariation{HB_TAG('w','g','h','t'), |
1179 | 0 | weight}); |
1180 | 0 | } |
1181 | 0 |
|
1182 | 0 | if (!(mRangeFlags & RangeFlags::eNonCSSStretch)) { |
1183 | 0 | float stretch = |
1184 | 0 | (IsUserFont() && (mRangeFlags & RangeFlags::eAutoStretch)) |
1185 | 0 | ? aStyle.stretch.Percentage() |
1186 | 0 | : Stretch().Clamp(aStyle.stretch).Percentage(); |
1187 | 0 | aResult.AppendElement(gfxFontVariation{HB_TAG('w','d','t','h'), |
1188 | 0 | stretch}); |
1189 | 0 | } |
1190 | 0 |
|
1191 | 0 | if (aStyle.style.IsItalic() && SupportsItalic()) { |
1192 | 0 | // The 'ital' axis is normally a binary toggle; intermediate values |
1193 | 0 | // can only be set using font-variation-settings. |
1194 | 0 | aResult.AppendElement(gfxFontVariation{HB_TAG('i','t','a','l'), |
1195 | 0 | 1.0f}); |
1196 | 0 | } else if (SlantStyle().Min().IsOblique()) { |
1197 | 0 | // Figure out what slant angle we should try to match from the |
1198 | 0 | // requested style. |
1199 | 0 | float angle = |
1200 | 0 | aStyle.style.IsNormal() |
1201 | 0 | ? 0.0f |
1202 | 0 | : aStyle.style.IsItalic() |
1203 | 0 | ? FontSlantStyle::Oblique().ObliqueAngle() |
1204 | 0 | : aStyle.style.ObliqueAngle(); |
1205 | 0 | // Clamp to the available range, unless the face is a user font |
1206 | 0 | // with no explicit descriptor. |
1207 | 0 | if (!(IsUserFont() && (mRangeFlags & RangeFlags::eAutoSlantStyle))) { |
1208 | 0 | angle = SlantStyle().Clamp( |
1209 | 0 | FontSlantStyle::Oblique(angle)).ObliqueAngle(); |
1210 | 0 | } |
1211 | 0 | aResult.AppendElement(gfxFontVariation{HB_TAG('s','l','n','t'), |
1212 | 0 | angle}); |
1213 | 0 | } |
1214 | 0 |
|
1215 | 0 | auto replaceOrAppend = [&aResult](const gfxFontVariation& aSetting) { |
1216 | 0 | struct TagEquals { |
1217 | 0 | bool Equals(const gfxFontVariation& aIter, uint32_t aTag) const { |
1218 | 0 | return aIter.mTag == aTag; |
1219 | 0 | } |
1220 | 0 | }; |
1221 | 0 | auto index = aResult.IndexOf(aSetting.mTag, 0, TagEquals()); |
1222 | 0 | if (index == aResult.NoIndex) { |
1223 | 0 | aResult.AppendElement(aSetting); |
1224 | 0 | } else { |
1225 | 0 | aResult[index].mValue = aSetting.mValue; |
1226 | 0 | } |
1227 | 0 | }; |
1228 | 0 |
|
1229 | 0 | // The low-level font-variation-settings descriptor from @font-face, |
1230 | 0 | // if present, takes precedence over automatic variation settings |
1231 | 0 | // from high-level properties. |
1232 | 0 | for (const auto& v : mVariationSettings) { |
1233 | 0 | replaceOrAppend(v); |
1234 | 0 | } |
1235 | 0 |
|
1236 | 0 | // And the low-level font-variation-settings property takes precedence |
1237 | 0 | // over the descriptor. |
1238 | 0 | for (const auto& v : aStyle.variationSettings) { |
1239 | 0 | replaceOrAppend(v); |
1240 | 0 | } |
1241 | 0 | } |
1242 | | |
1243 | | size_t |
1244 | | gfxFontEntry::FontTableHashEntry::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
1245 | 0 | { |
1246 | 0 | size_t n = 0; |
1247 | 0 | if (mBlob) { |
1248 | 0 | n += aMallocSizeOf(mBlob); |
1249 | 0 | } |
1250 | 0 | if (mSharedBlobData) { |
1251 | 0 | n += mSharedBlobData->SizeOfIncludingThis(aMallocSizeOf); |
1252 | 0 | } |
1253 | 0 | return n; |
1254 | 0 | } |
1255 | | |
1256 | | void |
1257 | | gfxFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, |
1258 | | FontListSizes* aSizes) const |
1259 | 0 | { |
1260 | 0 | aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
1261 | 0 |
|
1262 | 0 | // cmaps are shared so only non-shared cmaps are included here |
1263 | 0 | if (mCharacterMap && mCharacterMap->mBuildOnTheFly) { |
1264 | 0 | aSizes->mCharMapsSize += |
1265 | 0 | mCharacterMap->SizeOfIncludingThis(aMallocSizeOf); |
1266 | 0 | } |
1267 | 0 | if (mFontTableCache) { |
1268 | 0 | aSizes->mFontTableCacheSize += |
1269 | 0 | mFontTableCache->SizeOfIncludingThis(aMallocSizeOf); |
1270 | 0 | } |
1271 | 0 |
|
1272 | 0 | // If the font has UVS data, we count that as part of the character map. |
1273 | 0 | if (mUVSData) { |
1274 | 0 | aSizes->mCharMapsSize += aMallocSizeOf(mUVSData.get()); |
1275 | 0 | } |
1276 | 0 |
|
1277 | 0 | // The following, if present, are essentially cached forms of font table |
1278 | 0 | // data, so we'll accumulate them together with the basic table cache. |
1279 | 0 | if (mUserFontData) { |
1280 | 0 | aSizes->mFontTableCacheSize += |
1281 | 0 | mUserFontData->SizeOfIncludingThis(aMallocSizeOf); |
1282 | 0 | } |
1283 | 0 | if (mSVGGlyphs) { |
1284 | 0 | aSizes->mFontTableCacheSize += |
1285 | 0 | mSVGGlyphs->SizeOfIncludingThis(aMallocSizeOf); |
1286 | 0 | } |
1287 | 0 | if (mSupportedFeatures) { |
1288 | 0 | aSizes->mFontTableCacheSize += |
1289 | 0 | mSupportedFeatures->ShallowSizeOfIncludingThis(aMallocSizeOf); |
1290 | 0 | } |
1291 | 0 | if (mFeatureInputs) { |
1292 | 0 | aSizes->mFontTableCacheSize += |
1293 | 0 | mFeatureInputs->ShallowSizeOfIncludingThis(aMallocSizeOf); |
1294 | 0 | for (auto iter = mFeatureInputs->ConstIter(); !iter.Done(); |
1295 | 0 | iter.Next()) { |
1296 | 0 | // There's no API to get the real size of an hb_set, so we'll use |
1297 | 0 | // an approximation based on knowledge of the implementation. |
1298 | 0 | aSizes->mFontTableCacheSize += 8192; // vector of 64K bits |
1299 | 0 | } |
1300 | 0 | } |
1301 | 0 | // We don't include the size of mCOLR/mCPAL here, because (depending on the |
1302 | 0 | // font backend implementation) they will either wrap blocks of data owned |
1303 | 0 | // by the system (and potentially shared), or tables that are in our font |
1304 | 0 | // table cache and therefore already counted. |
1305 | 0 | } |
1306 | | |
1307 | | void |
1308 | | gfxFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, |
1309 | | FontListSizes* aSizes) const |
1310 | 0 | { |
1311 | 0 | aSizes->mFontListSize += aMallocSizeOf(this); |
1312 | 0 | AddSizeOfExcludingThis(aMallocSizeOf, aSizes); |
1313 | 0 | } |
1314 | | |
1315 | | // This is used to report the size of an individual downloaded font in the |
1316 | | // user font cache. (Fonts that are part of the platform font list accumulate |
1317 | | // their sizes to the font list's reporter using the AddSizeOf... methods |
1318 | | // above.) |
1319 | | size_t |
1320 | | gfxFontEntry::ComputedSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
1321 | 0 | { |
1322 | 0 | FontListSizes s = { 0 }; |
1323 | 0 | AddSizeOfExcludingThis(aMallocSizeOf, &s); |
1324 | 0 |
|
1325 | 0 | // When reporting memory used for the main platform font list, |
1326 | 0 | // where we're typically summing the totals for a few hundred font faces, |
1327 | 0 | // we report the fields of FontListSizes separately. |
1328 | 0 | // But for downloaded user fonts, the actual resource data (added below) |
1329 | 0 | // will dominate, and the minor overhead of these pieces isn't worth |
1330 | 0 | // splitting out for an individual font. |
1331 | 0 | size_t result = s.mFontListSize + s.mFontTableCacheSize + s.mCharMapsSize; |
1332 | 0 |
|
1333 | 0 | if (mIsDataUserFont) { |
1334 | 0 | MOZ_ASSERT(mComputedSizeOfUserFont > 0, "user font with no data?"); |
1335 | 0 | result += mComputedSizeOfUserFont; |
1336 | 0 | } |
1337 | 0 |
|
1338 | 0 | return result; |
1339 | 0 | } |
1340 | | |
1341 | | ////////////////////////////////////////////////////////////////////////////// |
1342 | | // |
1343 | | // class gfxFontFamily |
1344 | | // |
1345 | | ////////////////////////////////////////////////////////////////////////////// |
1346 | | |
1347 | | // we consider faces with mStandardFace == true to be "less than" those with false, |
1348 | | // because during style matching, earlier entries are tried first |
1349 | | class FontEntryStandardFaceComparator { |
1350 | | public: |
1351 | 0 | bool Equals(const RefPtr<gfxFontEntry>& a, const RefPtr<gfxFontEntry>& b) const { |
1352 | 0 | return a->mStandardFace == b->mStandardFace; |
1353 | 0 | } |
1354 | 0 | bool LessThan(const RefPtr<gfxFontEntry>& a, const RefPtr<gfxFontEntry>& b) const { |
1355 | 0 | return (a->mStandardFace == true && b->mStandardFace == false); |
1356 | 0 | } |
1357 | | }; |
1358 | | |
1359 | | void |
1360 | | gfxFontFamily::SortAvailableFonts() |
1361 | 0 | { |
1362 | 0 | mAvailableFonts.Sort(FontEntryStandardFaceComparator()); |
1363 | 0 | } |
1364 | | |
1365 | | bool |
1366 | | gfxFontFamily::HasOtherFamilyNames() |
1367 | 0 | { |
1368 | 0 | // need to read in other family names to determine this |
1369 | 0 | if (!mOtherFamilyNamesInitialized) { |
1370 | 0 | ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList()); // sets mHasOtherFamilyNames |
1371 | 0 | } |
1372 | 0 | return mHasOtherFamilyNames; |
1373 | 0 | } |
1374 | | |
1375 | | gfxFontEntry* |
1376 | | gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle, |
1377 | | bool aIgnoreSizeTolerance) |
1378 | 0 | { |
1379 | 0 | AutoTArray<gfxFontEntry*,4> matched; |
1380 | 0 | FindAllFontsForStyle(aFontStyle, matched, aIgnoreSizeTolerance); |
1381 | 0 | if (!matched.IsEmpty()) { |
1382 | 0 | return matched[0]; |
1383 | 0 | } |
1384 | 0 | return nullptr; |
1385 | 0 | } |
1386 | | |
1387 | | // style distance ==> [0,500] |
1388 | | static inline double |
1389 | | StyleDistance(const gfxFontEntry* aFontEntry, FontSlantStyle aTargetStyle) |
1390 | 0 | { |
1391 | 0 | const FontSlantStyle minStyle = aFontEntry->SlantStyle().Min(); |
1392 | 0 | if (aTargetStyle == minStyle) { |
1393 | 0 | return 0.0; // styles match exactly ==> 0 |
1394 | 0 | } |
1395 | 0 | |
1396 | 0 | // bias added to angle difference when searching in the non-preferred |
1397 | 0 | // direction from a target angle |
1398 | 0 | const double kReverse = 100.0; |
1399 | 0 |
|
1400 | 0 | // bias added when we've crossed from positive to negative angles or |
1401 | 0 | // vice versa |
1402 | 0 | const double kNegate = 200.0; |
1403 | 0 |
|
1404 | 0 | if (aTargetStyle.IsNormal()) { |
1405 | 0 | if (minStyle.IsOblique()) { |
1406 | 0 | // to distinguish oblique 0deg from normal, we add 1.0 to the angle |
1407 | 0 | const double minAngle = minStyle.ObliqueAngle(); |
1408 | 0 | if (minAngle >= 0.0) { |
1409 | 0 | return 1.0 + minAngle; |
1410 | 0 | } |
1411 | 0 | const FontSlantStyle maxStyle = aFontEntry->SlantStyle().Max(); |
1412 | 0 | const double maxAngle = maxStyle.ObliqueAngle(); |
1413 | 0 | if (maxAngle >= 0.0) { |
1414 | 0 | // [min,max] range includes 0.0, so just return our minimum |
1415 | 0 | return 1.0; |
1416 | 0 | } |
1417 | 0 | // negative oblique is even worse than italic |
1418 | 0 | return kNegate - maxAngle; |
1419 | 0 | } |
1420 | 0 | // must be italic, which is worse than any non-negative oblique; |
1421 | 0 | // treat as a match in the wrong search direction |
1422 | 0 | MOZ_ASSERT(minStyle.IsItalic()); |
1423 | 0 | return kReverse; |
1424 | 0 | } |
1425 | 0 | |
1426 | 0 | const double kDefaultAngle = FontSlantStyle::Oblique().ObliqueAngle(); |
1427 | 0 |
|
1428 | 0 | if (aTargetStyle.IsItalic()) { |
1429 | 0 | if (minStyle.IsOblique()) { |
1430 | 0 | const double minAngle = minStyle.ObliqueAngle(); |
1431 | 0 | if (minAngle >= kDefaultAngle) { |
1432 | 0 | return 1.0 + (minAngle - kDefaultAngle); |
1433 | 0 | } |
1434 | 0 | const FontSlantStyle maxStyle = aFontEntry->SlantStyle().Max(); |
1435 | 0 | const double maxAngle = maxStyle.ObliqueAngle(); |
1436 | 0 | if (maxAngle >= kDefaultAngle) { |
1437 | 0 | return 1.0; |
1438 | 0 | } |
1439 | 0 | if (maxAngle > 0.0) { |
1440 | 0 | // wrong direction but still > 0, add bias of 100 |
1441 | 0 | return kReverse + (kDefaultAngle - maxAngle); |
1442 | 0 | } |
1443 | 0 | // negative oblique angle, add bias of 300 |
1444 | 0 | return kReverse + kNegate + (kDefaultAngle - maxAngle); |
1445 | 0 | } |
1446 | 0 | // normal is worse than oblique > 0, but better than oblique <= 0 |
1447 | 0 | MOZ_ASSERT(minStyle.IsNormal()); |
1448 | 0 | return kNegate; |
1449 | 0 | } |
1450 | 0 | |
1451 | 0 | // target is oblique <angle>: four different cases depending on |
1452 | 0 | // the value of the <angle>, which determines the preferred direction |
1453 | 0 | // of search |
1454 | 0 | const double targetAngle = aTargetStyle.ObliqueAngle(); |
1455 | 0 | if (targetAngle >= kDefaultAngle) { |
1456 | 0 | if (minStyle.IsOblique()) { |
1457 | 0 | const double minAngle = minStyle.ObliqueAngle(); |
1458 | 0 | if (minAngle >= targetAngle) { |
1459 | 0 | return minAngle - targetAngle; |
1460 | 0 | } |
1461 | 0 | const FontSlantStyle maxStyle = aFontEntry->SlantStyle().Max(); |
1462 | 0 | const double maxAngle = maxStyle.ObliqueAngle(); |
1463 | 0 | if (maxAngle >= targetAngle) { |
1464 | 0 | return 0.0; |
1465 | 0 | } |
1466 | 0 | if (maxAngle > 0.0) { |
1467 | 0 | return kReverse + (targetAngle - maxAngle); |
1468 | 0 | } |
1469 | 0 | return kReverse + kNegate + (targetAngle - maxAngle); |
1470 | 0 | } |
1471 | 0 | if (minStyle.IsItalic()) { |
1472 | 0 | return kReverse + kNegate; |
1473 | 0 | } |
1474 | 0 | return kReverse + kNegate + 1.0; |
1475 | 0 | } |
1476 | 0 | |
1477 | 0 | if (targetAngle <= -kDefaultAngle) { |
1478 | 0 | if (minStyle.IsOblique()) { |
1479 | 0 | const FontSlantStyle maxStyle = aFontEntry->SlantStyle().Max(); |
1480 | 0 | const double maxAngle = maxStyle.ObliqueAngle(); |
1481 | 0 | if (maxAngle <= targetAngle) { |
1482 | 0 | return targetAngle - maxAngle; |
1483 | 0 | } |
1484 | 0 | const double minAngle = minStyle.ObliqueAngle(); |
1485 | 0 | if (minAngle <= targetAngle) { |
1486 | 0 | return 0.0; |
1487 | 0 | } |
1488 | 0 | if (minAngle < 0.0) { |
1489 | 0 | return kReverse + (minAngle - targetAngle); |
1490 | 0 | } |
1491 | 0 | return kReverse + kNegate + (minAngle - targetAngle); |
1492 | 0 | } |
1493 | 0 | if (minStyle.IsItalic()) { |
1494 | 0 | return kReverse + kNegate; |
1495 | 0 | } |
1496 | 0 | return kReverse + kNegate + 1.0; |
1497 | 0 | } |
1498 | 0 | |
1499 | 0 | if (targetAngle >= 0.0) { |
1500 | 0 | if (minStyle.IsOblique()) { |
1501 | 0 | const double minAngle = minStyle.ObliqueAngle(); |
1502 | 0 | if (minAngle > targetAngle) { |
1503 | 0 | return kReverse + (minAngle - targetAngle); |
1504 | 0 | } |
1505 | 0 | const FontSlantStyle maxStyle = aFontEntry->SlantStyle().Max(); |
1506 | 0 | const double maxAngle = maxStyle.ObliqueAngle(); |
1507 | 0 | if (maxAngle >= targetAngle) { |
1508 | 0 | return 0.0; |
1509 | 0 | } |
1510 | 0 | if (maxAngle > 0.0) { |
1511 | 0 | return targetAngle - maxAngle; |
1512 | 0 | } |
1513 | 0 | return kReverse + kNegate + (targetAngle - maxAngle); |
1514 | 0 | } |
1515 | 0 | if (minStyle.IsItalic()) { |
1516 | 0 | return kReverse + kNegate - 2.0; |
1517 | 0 | } |
1518 | 0 | return kReverse + kNegate - 1.0; |
1519 | 0 | } |
1520 | 0 | |
1521 | 0 | // last case: (targetAngle < 0.0 && targetAngle > kDefaultAngle) |
1522 | 0 | if (minStyle.IsOblique()) { |
1523 | 0 | const FontSlantStyle maxStyle = aFontEntry->SlantStyle().Max(); |
1524 | 0 | const double maxAngle = maxStyle.ObliqueAngle(); |
1525 | 0 | if (maxAngle < targetAngle) { |
1526 | 0 | return kReverse + (targetAngle - maxAngle); |
1527 | 0 | } |
1528 | 0 | const double minAngle = minStyle.ObliqueAngle(); |
1529 | 0 | if (minAngle <= targetAngle) { |
1530 | 0 | return 0.0; |
1531 | 0 | } |
1532 | 0 | if (minAngle < 0.0) { |
1533 | 0 | return minAngle - targetAngle; |
1534 | 0 | } |
1535 | 0 | return kReverse + kNegate + (minAngle - targetAngle); |
1536 | 0 | } |
1537 | 0 | if (minStyle.IsItalic()) { |
1538 | 0 | return kReverse + kNegate - 2.0; |
1539 | 0 | } |
1540 | 0 | return kReverse + kNegate - 1.0; |
1541 | 0 | } |
1542 | | |
1543 | | // stretch distance ==> [0,2000] |
1544 | | static inline double |
1545 | | StretchDistance(const gfxFontEntry* aFontEntry, FontStretch aTargetStretch) |
1546 | 0 | { |
1547 | 0 | const double kReverseDistance = 1000.0; |
1548 | 0 |
|
1549 | 0 | FontStretch minStretch = aFontEntry->Stretch().Min(); |
1550 | 0 | FontStretch maxStretch = aFontEntry->Stretch().Max(); |
1551 | 0 |
|
1552 | 0 | // The stretch value is a (non-negative) percentage; currently we support |
1553 | 0 | // values in the range 0 .. 1000. (If the upper limit is ever increased, |
1554 | 0 | // the kReverseDistance value used here may need to be adjusted.) |
1555 | 0 | // If aTargetStretch is >100, we prefer larger values if available; |
1556 | 0 | // if <=100, we prefer smaller values if available. |
1557 | 0 | if (aTargetStretch < minStretch) { |
1558 | 0 | if (aTargetStretch > FontStretch::Normal()) { |
1559 | 0 | return minStretch - aTargetStretch; |
1560 | 0 | } |
1561 | 0 | return (minStretch - aTargetStretch) + kReverseDistance; |
1562 | 0 | } |
1563 | 0 | if (aTargetStretch > maxStretch) { |
1564 | 0 | if (aTargetStretch <= FontStretch::Normal()) { |
1565 | 0 | return aTargetStretch - maxStretch; |
1566 | 0 | } |
1567 | 0 | return (aTargetStretch - maxStretch) + kReverseDistance; |
1568 | 0 | } |
1569 | 0 | return 0.0; |
1570 | 0 | } |
1571 | | |
1572 | | // Calculate weight distance with values in the range (0..1000). In general, |
1573 | | // heavier weights match towards even heavier weights while lighter weights |
1574 | | // match towards even lighter weights. Target weight values in the range |
1575 | | // [400..500] are special, since they will first match up to 500, then down |
1576 | | // towards 0, then up again towards 999. |
1577 | | // |
1578 | | // Example: with target 600 and font weight 800, distance will be 200. With |
1579 | | // target 300 and font weight 600, distance will be 900, since heavier |
1580 | | // weights are farther away than lighter weights. If the target is 5 and the |
1581 | | // font weight 995, the distance would be 1590 for the same reason. |
1582 | | |
1583 | | // weight distance ==> [0,1600] |
1584 | | static inline double |
1585 | | WeightDistance(const gfxFontEntry* aFontEntry, FontWeight aTargetWeight) |
1586 | 0 | { |
1587 | 0 | const double kNotWithinCentralRange = 100.0; |
1588 | 0 | const double kReverseDistance = 600.0; |
1589 | 0 |
|
1590 | 0 | FontWeight minWeight = aFontEntry->Weight().Min(); |
1591 | 0 | FontWeight maxWeight = aFontEntry->Weight().Max(); |
1592 | 0 |
|
1593 | 0 | if (aTargetWeight >= minWeight && aTargetWeight <= maxWeight) { |
1594 | 0 | // Target is within the face's range, so it's a perfect match |
1595 | 0 | return 0.0; |
1596 | 0 | } |
1597 | 0 | |
1598 | 0 | if (aTargetWeight < FontWeight(400)) { |
1599 | 0 | // Requested a lighter-than-400 weight |
1600 | 0 | if (maxWeight < aTargetWeight) { |
1601 | 0 | return aTargetWeight - maxWeight; |
1602 | 0 | } |
1603 | 0 | // Add reverse-search penalty for bolder faces |
1604 | 0 | return (minWeight - aTargetWeight) + kReverseDistance; |
1605 | 0 | } |
1606 | 0 | |
1607 | 0 | if (aTargetWeight > FontWeight(500)) { |
1608 | 0 | // Requested a bolder-than-500 weight |
1609 | 0 | if (minWeight > aTargetWeight) { |
1610 | 0 | return minWeight - aTargetWeight; |
1611 | 0 | } |
1612 | 0 | // Add reverse-search penalty for lighter faces |
1613 | 0 | return (aTargetWeight - maxWeight) + kReverseDistance; |
1614 | 0 | } |
1615 | 0 | |
1616 | 0 | // Special case for requested weight in the [400..500] range |
1617 | 0 | if (minWeight > aTargetWeight) { |
1618 | 0 | if (minWeight <= FontWeight(500)) { |
1619 | 0 | // Bolder weight up to 500 is first choice |
1620 | 0 | return minWeight - aTargetWeight; |
1621 | 0 | } |
1622 | 0 | // Other bolder weights get a reverse-search penalty |
1623 | 0 | return (minWeight - aTargetWeight) + kReverseDistance; |
1624 | 0 | } |
1625 | 0 | // Lighter weights are not as good as bolder ones within [400..500] |
1626 | 0 | return (aTargetWeight - maxWeight) + kNotWithinCentralRange; |
1627 | 0 | } |
1628 | | |
1629 | | static inline double |
1630 | | WeightStyleStretchDistance(gfxFontEntry* aFontEntry, |
1631 | | const gfxFontStyle& aTargetStyle) |
1632 | 0 | { |
1633 | 0 | double stretchDist = StretchDistance(aFontEntry, aTargetStyle.stretch); |
1634 | 0 | double styleDist = StyleDistance(aFontEntry, aTargetStyle.style); |
1635 | 0 | double weightDist = WeightDistance(aFontEntry, aTargetStyle.weight); |
1636 | 0 |
|
1637 | 0 | // Sanity-check that the distances are within the expected range |
1638 | 0 | // (update if implementation of the distance functions is changed). |
1639 | 0 | MOZ_ASSERT(stretchDist >= 0.0 && stretchDist <= 2000.0); |
1640 | 0 | MOZ_ASSERT(styleDist >= 0.0 && styleDist <= 500.0); |
1641 | 0 | MOZ_ASSERT(weightDist >= 0.0 && weightDist <= 1600.0); |
1642 | 0 |
|
1643 | 0 | // weight/style/stretch priority: stretch >> style >> weight |
1644 | 0 | // so we multiply the stretch and style values to make them dominate |
1645 | 0 | // the result |
1646 | 0 | return stretchDist * 1.0e8 + styleDist * 1.0e4 + weightDist; |
1647 | 0 | } |
1648 | | |
1649 | | void |
1650 | | gfxFontFamily::FindAllFontsForStyle(const gfxFontStyle& aFontStyle, |
1651 | | nsTArray<gfxFontEntry*>& aFontEntryList, |
1652 | | bool aIgnoreSizeTolerance) |
1653 | 0 | { |
1654 | 0 | if (!mHasStyles) { |
1655 | 0 | FindStyleVariations(); // collect faces for the family, if not already done |
1656 | 0 | } |
1657 | 0 |
|
1658 | 0 | NS_ASSERTION(mAvailableFonts.Length() > 0, "font family with no faces!"); |
1659 | 0 | NS_ASSERTION(aFontEntryList.IsEmpty(), "non-empty fontlist passed in"); |
1660 | 0 |
|
1661 | 0 | gfxFontEntry *fe = nullptr; |
1662 | 0 |
|
1663 | 0 | // If the family has only one face, we simply return it; no further |
1664 | 0 | // checking needed |
1665 | 0 | uint32_t count = mAvailableFonts.Length(); |
1666 | 0 | if (count == 1) { |
1667 | 0 | fe = mAvailableFonts[0]; |
1668 | 0 | aFontEntryList.AppendElement(fe); |
1669 | 0 | return; |
1670 | 0 | } |
1671 | 0 | |
1672 | 0 | // Most families are "simple", having just Regular/Bold/Italic/BoldItalic, |
1673 | 0 | // or some subset of these. In this case, we have exactly 4 entries in mAvailableFonts, |
1674 | 0 | // stored in the above order; note that some of the entries may be nullptr. |
1675 | 0 | // We can then pick the required entry based on whether the request is for |
1676 | 0 | // bold or non-bold, italic or non-italic, without running the more complex |
1677 | 0 | // matching algorithm used for larger families with many weights and/or widths. |
1678 | 0 | |
1679 | 0 | if (mIsSimpleFamily) { |
1680 | 0 | // Family has no more than the "standard" 4 faces, at fixed indexes; |
1681 | 0 | // calculate which one we want. |
1682 | 0 | // Note that we cannot simply return it as not all 4 faces are necessarily present. |
1683 | 0 | bool wantBold = aFontStyle.weight >= FontWeight(600); |
1684 | 0 | bool wantItalic = !aFontStyle.style.IsNormal(); |
1685 | 0 | uint8_t faceIndex = (wantItalic ? kItalicMask : 0) | |
1686 | 0 | (wantBold ? kBoldMask : 0); |
1687 | 0 |
|
1688 | 0 | // if the desired style is available, return it directly |
1689 | 0 | fe = mAvailableFonts[faceIndex]; |
1690 | 0 | if (fe) { |
1691 | 0 | aFontEntryList.AppendElement(fe); |
1692 | 0 | return; |
1693 | 0 | } |
1694 | 0 | |
1695 | 0 | // order to check fallback faces in a simple family, depending on requested style |
1696 | 0 | static const uint8_t simpleFallbacks[4][3] = { |
1697 | 0 | { kBoldFaceIndex, kItalicFaceIndex, kBoldItalicFaceIndex }, // fallbacks for Regular |
1698 | 0 | { kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex },// Bold |
1699 | 0 | { kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex }, // Italic |
1700 | 0 | { kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex } // BoldItalic |
1701 | 0 | }; |
1702 | 0 | const uint8_t *order = simpleFallbacks[faceIndex]; |
1703 | 0 |
|
1704 | 0 | for (uint8_t trial = 0; trial < 3; ++trial) { |
1705 | 0 | // check remaining faces in order of preference to find the first that actually exists |
1706 | 0 | fe = mAvailableFonts[order[trial]]; |
1707 | 0 | if (fe) { |
1708 | 0 | aFontEntryList.AppendElement(fe); |
1709 | 0 | return; |
1710 | 0 | } |
1711 | 0 | } |
1712 | 0 |
|
1713 | 0 | // this can't happen unless we have totally broken the font-list manager! |
1714 | 0 | MOZ_ASSERT_UNREACHABLE("no face found in simple font family!"); |
1715 | 0 | } |
1716 | 0 |
|
1717 | 0 | // Pick the font(s) that are closest to the desired weight, style, and |
1718 | 0 | // stretch. Iterate over all fonts, measuring the weight/style distance. |
1719 | 0 | // Because of unicode-range values, there may be more than one font for a |
1720 | 0 | // given but the 99% use case is only a single font entry per |
1721 | 0 | // weight/style/stretch distance value. To optimize this, only add entries |
1722 | 0 | // to the matched font array when another entry already has the same |
1723 | 0 | // weight/style/stretch distance and add the last matched font entry. For |
1724 | 0 | // normal platform fonts with a single font entry for each |
1725 | 0 | // weight/style/stretch combination, only the last matched font entry will |
1726 | 0 | // be added. |
1727 | 0 |
|
1728 | 0 | double minDistance = INFINITY; |
1729 | 0 | gfxFontEntry* matched = nullptr; |
1730 | 0 | // iterate in forward order so that faces like 'Bold' are matched before |
1731 | 0 | // matching style distance faces such as 'Bold Outline' (see bug 1185812) |
1732 | 0 | for (uint32_t i = 0; i < count; i++) { |
1733 | 0 | fe = mAvailableFonts[i]; |
1734 | 0 | // weight/style/stretch priority: stretch >> style >> weight |
1735 | 0 | double distance = WeightStyleStretchDistance(fe, aFontStyle); |
1736 | 0 | if (distance < minDistance) { |
1737 | 0 | matched = fe; |
1738 | 0 | if (!aFontEntryList.IsEmpty()) { |
1739 | 0 | aFontEntryList.Clear(); |
1740 | 0 | } |
1741 | 0 | minDistance = distance; |
1742 | 0 | } else if (distance == minDistance) { |
1743 | 0 | if (matched) { |
1744 | 0 | aFontEntryList.AppendElement(matched); |
1745 | 0 | } |
1746 | 0 | matched = fe; |
1747 | 0 | } |
1748 | 0 | } |
1749 | 0 |
|
1750 | 0 | NS_ASSERTION(matched, "didn't match a font within a family"); |
1751 | 0 |
|
1752 | 0 | if (matched) { |
1753 | 0 | aFontEntryList.AppendElement(matched); |
1754 | 0 | } |
1755 | 0 | } |
1756 | | |
1757 | | void |
1758 | | gfxFontFamily::CheckForSimpleFamily() |
1759 | 0 | { |
1760 | 0 | // already checked this family |
1761 | 0 | if (mIsSimpleFamily) { |
1762 | 0 | return; |
1763 | 0 | } |
1764 | 0 | |
1765 | 0 | uint32_t count = mAvailableFonts.Length(); |
1766 | 0 | if (count > 4 || count == 0) { |
1767 | 0 | return; // can't be "simple" if there are >4 faces; |
1768 | 0 | // if none then the family is unusable anyway |
1769 | 0 | } |
1770 | 0 | |
1771 | 0 | if (count == 1) { |
1772 | 0 | mIsSimpleFamily = true; |
1773 | 0 | return; |
1774 | 0 | } |
1775 | 0 | |
1776 | 0 | StretchRange firstStretch = mAvailableFonts[0]->Stretch(); |
1777 | 0 | if (!firstStretch.IsSingle()) { |
1778 | 0 | return; // family with variation fonts is not considered "simple" |
1779 | 0 | } |
1780 | 0 | |
1781 | 0 | gfxFontEntry *faces[4] = { 0 }; |
1782 | 0 | for (uint8_t i = 0; i < count; ++i) { |
1783 | 0 | gfxFontEntry *fe = mAvailableFonts[i]; |
1784 | 0 | if (fe->Stretch() != firstStretch || fe->IsOblique()) { |
1785 | 0 | // simple families don't have varying font-stretch or oblique |
1786 | 0 | return; |
1787 | 0 | } |
1788 | 0 | if (!fe->Weight().IsSingle() || !fe->SlantStyle().IsSingle()) { |
1789 | 0 | return; // family with variation fonts is not considered "simple" |
1790 | 0 | } |
1791 | 0 | uint8_t faceIndex = (fe->IsItalic() ? kItalicMask : 0) | |
1792 | 0 | (fe->SupportsBold() ? kBoldMask : 0); |
1793 | 0 | if (faces[faceIndex]) { |
1794 | 0 | return; // two faces resolve to the same slot; family isn't "simple" |
1795 | 0 | } |
1796 | 0 | faces[faceIndex] = fe; |
1797 | 0 | } |
1798 | 0 |
|
1799 | 0 | // we have successfully slotted the available faces into the standard |
1800 | 0 | // 4-face framework |
1801 | 0 | mAvailableFonts.SetLength(4); |
1802 | 0 | for (uint8_t i = 0; i < 4; ++i) { |
1803 | 0 | if (mAvailableFonts[i].get() != faces[i]) { |
1804 | 0 | mAvailableFonts[i].swap(faces[i]); |
1805 | 0 | } |
1806 | 0 | } |
1807 | 0 |
|
1808 | 0 | mIsSimpleFamily = true; |
1809 | 0 | } |
1810 | | |
1811 | | #ifdef DEBUG |
1812 | | bool |
1813 | | gfxFontFamily::ContainsFace(gfxFontEntry* aFontEntry) { |
1814 | | uint32_t i, numFonts = mAvailableFonts.Length(); |
1815 | | for (i = 0; i < numFonts; i++) { |
1816 | | if (mAvailableFonts[i] == aFontEntry) { |
1817 | | return true; |
1818 | | } |
1819 | | // userfonts contain the actual real font entry |
1820 | | if (mAvailableFonts[i] && mAvailableFonts[i]->mIsUserFontContainer) { |
1821 | | gfxUserFontEntry* ufe = |
1822 | | static_cast<gfxUserFontEntry*>(mAvailableFonts[i].get()); |
1823 | | if (ufe->GetPlatformFontEntry() == aFontEntry) { |
1824 | | return true; |
1825 | | } |
1826 | | } |
1827 | | } |
1828 | | return false; |
1829 | | } |
1830 | | #endif |
1831 | | |
1832 | | void gfxFontFamily::LocalizedName(nsACString& aLocalizedName) |
1833 | 0 | { |
1834 | 0 | // just return the primary name; subclasses should override |
1835 | 0 | aLocalizedName = mName; |
1836 | 0 | } |
1837 | | |
1838 | | void |
1839 | | gfxFontFamily::FindFontForChar(GlobalFontMatch* aMatchData) |
1840 | 0 | { |
1841 | 0 | if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) { |
1842 | 0 | // none of the faces in the family support the required char, |
1843 | 0 | // so bail out immediately |
1844 | 0 | return; |
1845 | 0 | } |
1846 | 0 | |
1847 | 0 | gfxFontEntry* fe = |
1848 | 0 | FindFontForStyle(aMatchData->mStyle, /*aIgnoreSizeTolerance*/ true); |
1849 | 0 | if (!fe || fe->SkipDuringSystemFallback()) { |
1850 | 0 | return; |
1851 | 0 | } |
1852 | 0 | |
1853 | 0 | float distance = INFINITY; |
1854 | 0 |
|
1855 | 0 | if (fe->HasCharacter(aMatchData->mCh)) { |
1856 | 0 | aMatchData->mCount++; |
1857 | 0 |
|
1858 | 0 | LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun); |
1859 | 0 |
|
1860 | 0 | if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) { |
1861 | 0 | uint32_t unicodeRange = FindCharUnicodeRange(aMatchData->mCh); |
1862 | 0 | Script script = GetScriptCode(aMatchData->mCh); |
1863 | 0 | MOZ_LOG(log, LogLevel::Debug,\ |
1864 | 0 | ("(textrun-systemfallback-fonts) char: u+%6.6x " |
1865 | 0 | "unicode-range: %d script: %d match: [%s]\n", |
1866 | 0 | aMatchData->mCh, |
1867 | 0 | unicodeRange, int(script), |
1868 | 0 | fe->Name().get())); |
1869 | 0 | } |
1870 | 0 |
|
1871 | 0 | distance = WeightStyleStretchDistance(fe, aMatchData->mStyle); |
1872 | 0 | } else if (!fe->IsNormalStyle()) { |
1873 | 0 | // If style/weight/stretch was not Normal, see if we can |
1874 | 0 | // fall back to a next-best face (e.g. Arial Black -> Bold, |
1875 | 0 | // or Arial Narrow -> Regular). |
1876 | 0 | GlobalFontMatch data(aMatchData->mCh, aMatchData->mStyle); |
1877 | 0 | SearchAllFontsForChar(&data); |
1878 | 0 | if (std::isfinite(data.mMatchDistance)) { |
1879 | 0 | fe = data.mBestMatch; |
1880 | 0 | distance = data.mMatchDistance; |
1881 | 0 | } |
1882 | 0 | } |
1883 | 0 | aMatchData->mCmapsTested++; |
1884 | 0 |
|
1885 | 0 | if (std::isinf(distance)) { |
1886 | 0 | return; |
1887 | 0 | } |
1888 | 0 | |
1889 | 0 | if (distance < aMatchData->mMatchDistance || |
1890 | 0 | (distance == aMatchData->mMatchDistance && |
1891 | 0 | Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) { |
1892 | 0 | aMatchData->mBestMatch = fe; |
1893 | 0 | aMatchData->mMatchedFamily = this; |
1894 | 0 | aMatchData->mMatchDistance = distance; |
1895 | 0 | } |
1896 | 0 | } |
1897 | | |
1898 | | void |
1899 | | gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch* aMatchData) |
1900 | 0 | { |
1901 | 0 | uint32_t i, numFonts = mAvailableFonts.Length(); |
1902 | 0 | for (i = 0; i < numFonts; i++) { |
1903 | 0 | gfxFontEntry *fe = mAvailableFonts[i]; |
1904 | 0 | if (fe && fe->HasCharacter(aMatchData->mCh)) { |
1905 | 0 | float distance = WeightStyleStretchDistance(fe, aMatchData->mStyle); |
1906 | 0 | if (distance < aMatchData->mMatchDistance |
1907 | 0 | || (distance == aMatchData->mMatchDistance && |
1908 | 0 | Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) |
1909 | 0 | { |
1910 | 0 | aMatchData->mBestMatch = fe; |
1911 | 0 | aMatchData->mMatchedFamily = this; |
1912 | 0 | aMatchData->mMatchDistance = distance; |
1913 | 0 | } |
1914 | 0 | } |
1915 | 0 | } |
1916 | 0 | } |
1917 | | |
1918 | | /*virtual*/ |
1919 | | gfxFontFamily::~gfxFontFamily() |
1920 | 0 | { |
1921 | 0 | // Should not be dropped by stylo |
1922 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1923 | 0 | } |
1924 | | |
1925 | | /*static*/ void |
1926 | | gfxFontFamily::ReadOtherFamilyNamesForFace(const nsACString& aFamilyName, |
1927 | | const char *aNameData, |
1928 | | uint32_t aDataLength, |
1929 | | nsTArray<nsCString>& aOtherFamilyNames, |
1930 | | bool useFullName) |
1931 | 0 | { |
1932 | 0 | const gfxFontUtils::NameHeader *nameHeader = |
1933 | 0 | reinterpret_cast<const gfxFontUtils::NameHeader*>(aNameData); |
1934 | 0 |
|
1935 | 0 | uint32_t nameCount = nameHeader->count; |
1936 | 0 | if (nameCount * sizeof(gfxFontUtils::NameRecord) > aDataLength) { |
1937 | 0 | NS_WARNING("invalid font (name records)"); |
1938 | 0 | return; |
1939 | 0 | } |
1940 | 0 | |
1941 | 0 | const gfxFontUtils::NameRecord *nameRecord = |
1942 | 0 | reinterpret_cast<const gfxFontUtils::NameRecord*>(aNameData + sizeof(gfxFontUtils::NameHeader)); |
1943 | 0 | uint32_t stringsBase = uint32_t(nameHeader->stringOffset); |
1944 | 0 |
|
1945 | 0 | for (uint32_t i = 0; i < nameCount; i++, nameRecord++) { |
1946 | 0 | uint32_t nameLen = nameRecord->length; |
1947 | 0 | uint32_t nameOff = nameRecord->offset; // offset from base of string storage |
1948 | 0 |
|
1949 | 0 | if (stringsBase + nameOff + nameLen > aDataLength) { |
1950 | 0 | NS_WARNING("invalid font (name table strings)"); |
1951 | 0 | return; |
1952 | 0 | } |
1953 | 0 |
|
1954 | 0 | uint16_t nameID = nameRecord->nameID; |
1955 | 0 | if ((useFullName && nameID == gfxFontUtils::NAME_ID_FULL) || |
1956 | 0 | (!useFullName && (nameID == gfxFontUtils::NAME_ID_FAMILY || |
1957 | 0 | nameID == gfxFontUtils::NAME_ID_PREFERRED_FAMILY))) { |
1958 | 0 | nsAutoCString otherFamilyName; |
1959 | 0 | bool ok = gfxFontUtils::DecodeFontName(aNameData + stringsBase + nameOff, |
1960 | 0 | nameLen, |
1961 | 0 | uint32_t(nameRecord->platformID), |
1962 | 0 | uint32_t(nameRecord->encodingID), |
1963 | 0 | uint32_t(nameRecord->languageID), |
1964 | 0 | otherFamilyName); |
1965 | 0 | // add if not same as canonical family name |
1966 | 0 | if (ok && otherFamilyName != aFamilyName) { |
1967 | 0 | aOtherFamilyNames.AppendElement(otherFamilyName); |
1968 | 0 | } |
1969 | 0 | } |
1970 | 0 | } |
1971 | 0 | } |
1972 | | |
1973 | | // returns true if other names were found, false otherwise |
1974 | | bool |
1975 | | gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList, |
1976 | | hb_blob_t *aNameTable, |
1977 | | bool useFullName) |
1978 | 0 | { |
1979 | 0 | uint32_t dataLength; |
1980 | 0 | const char *nameData = hb_blob_get_data(aNameTable, &dataLength); |
1981 | 0 | AutoTArray<nsCString,4> otherFamilyNames; |
1982 | 0 |
|
1983 | 0 | ReadOtherFamilyNamesForFace(mName, nameData, dataLength, |
1984 | 0 | otherFamilyNames, useFullName); |
1985 | 0 |
|
1986 | 0 | uint32_t n = otherFamilyNames.Length(); |
1987 | 0 | for (uint32_t i = 0; i < n; i++) { |
1988 | 0 | aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]); |
1989 | 0 | } |
1990 | 0 |
|
1991 | 0 | return n != 0; |
1992 | 0 | } |
1993 | | |
1994 | | void |
1995 | | gfxFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList) |
1996 | 0 | { |
1997 | 0 | if (mOtherFamilyNamesInitialized) |
1998 | 0 | return; |
1999 | 0 | mOtherFamilyNamesInitialized = true; |
2000 | 0 |
|
2001 | 0 | FindStyleVariations(); |
2002 | 0 |
|
2003 | 0 | // read in other family names for the first face in the list |
2004 | 0 | uint32_t i, numFonts = mAvailableFonts.Length(); |
2005 | 0 | const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e'); |
2006 | 0 |
|
2007 | 0 | for (i = 0; i < numFonts; ++i) { |
2008 | 0 | gfxFontEntry *fe = mAvailableFonts[i]; |
2009 | 0 | if (!fe) { |
2010 | 0 | continue; |
2011 | 0 | } |
2012 | 0 | gfxFontEntry::AutoTable nameTable(fe, kNAME); |
2013 | 0 | if (!nameTable) { |
2014 | 0 | continue; |
2015 | 0 | } |
2016 | 0 | mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList, |
2017 | 0 | nameTable); |
2018 | 0 | break; |
2019 | 0 | } |
2020 | 0 |
|
2021 | 0 | // read in other names for the first face in the list with the assumption |
2022 | 0 | // that if extra names don't exist in that face then they don't exist in |
2023 | 0 | // other faces for the same font |
2024 | 0 | if (!mHasOtherFamilyNames) |
2025 | 0 | return; |
2026 | 0 | |
2027 | 0 | // read in names for all faces, needed to catch cases where fonts have |
2028 | 0 | // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6) |
2029 | 0 | for ( ; i < numFonts; i++) { |
2030 | 0 | gfxFontEntry *fe = mAvailableFonts[i]; |
2031 | 0 | if (!fe) { |
2032 | 0 | continue; |
2033 | 0 | } |
2034 | 0 | gfxFontEntry::AutoTable nameTable(fe, kNAME); |
2035 | 0 | if (!nameTable) { |
2036 | 0 | continue; |
2037 | 0 | } |
2038 | 0 | ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable); |
2039 | 0 | } |
2040 | 0 | } |
2041 | | |
2042 | | static bool |
2043 | | LookForLegacyFamilyName(const nsACString& aCanonicalName, |
2044 | | const char* aNameData, |
2045 | | uint32_t aDataLength, |
2046 | | nsACString& aLegacyName /* outparam */) |
2047 | 0 | { |
2048 | 0 | const gfxFontUtils::NameHeader* nameHeader = |
2049 | 0 | reinterpret_cast<const gfxFontUtils::NameHeader*>(aNameData); |
2050 | 0 |
|
2051 | 0 | uint32_t nameCount = nameHeader->count; |
2052 | 0 | if (nameCount * sizeof(gfxFontUtils::NameRecord) > aDataLength) { |
2053 | 0 | NS_WARNING("invalid font (name records)"); |
2054 | 0 | return false; |
2055 | 0 | } |
2056 | 0 |
|
2057 | 0 | const gfxFontUtils::NameRecord* nameRecord = |
2058 | 0 | reinterpret_cast<const gfxFontUtils::NameRecord*> |
2059 | 0 | (aNameData + sizeof(gfxFontUtils::NameHeader)); |
2060 | 0 | uint32_t stringsBase = uint32_t(nameHeader->stringOffset); |
2061 | 0 |
|
2062 | 0 | for (uint32_t i = 0; i < nameCount; i++, nameRecord++) { |
2063 | 0 | uint32_t nameLen = nameRecord->length; |
2064 | 0 | uint32_t nameOff = nameRecord->offset; |
2065 | 0 |
|
2066 | 0 | if (stringsBase + nameOff + nameLen > aDataLength) { |
2067 | 0 | NS_WARNING("invalid font (name table strings)"); |
2068 | 0 | return false; |
2069 | 0 | } |
2070 | 0 |
|
2071 | 0 | if (uint16_t(nameRecord->nameID) == gfxFontUtils::NAME_ID_FAMILY) { |
2072 | 0 | bool ok = |
2073 | 0 | gfxFontUtils::DecodeFontName(aNameData + stringsBase + nameOff, |
2074 | 0 | nameLen, |
2075 | 0 | uint32_t(nameRecord->platformID), |
2076 | 0 | uint32_t(nameRecord->encodingID), |
2077 | 0 | uint32_t(nameRecord->languageID), |
2078 | 0 | aLegacyName); |
2079 | 0 | // it's only a legacy name if it differs from the canonical name |
2080 | 0 | if (ok && aLegacyName != aCanonicalName) { |
2081 | 0 | return true; |
2082 | 0 | } |
2083 | 0 | } |
2084 | 0 | } |
2085 | 0 | return false; |
2086 | 0 | } |
2087 | | |
2088 | | bool |
2089 | | gfxFontFamily::CheckForLegacyFamilyNames(gfxPlatformFontList* aFontList) |
2090 | 0 | { |
2091 | 0 | if (mCheckedForLegacyFamilyNames) { |
2092 | 0 | // we already did this, so there's nothing more to add |
2093 | 0 | return false; |
2094 | 0 | } |
2095 | 0 | mCheckedForLegacyFamilyNames = true; |
2096 | 0 | bool added = false; |
2097 | 0 | const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e'); |
2098 | 0 | // Make a local copy of the array of font faces, in case of changes |
2099 | 0 | // during the iteration. |
2100 | 0 | AutoTArray<RefPtr<gfxFontEntry>,8> faces(mAvailableFonts); |
2101 | 0 | for (auto& fe : faces) { |
2102 | 0 | if (!fe) { |
2103 | 0 | continue; |
2104 | 0 | } |
2105 | 0 | gfxFontEntry::AutoTable nameTable(fe, kNAME); |
2106 | 0 | if (!nameTable) { |
2107 | 0 | continue; |
2108 | 0 | } |
2109 | 0 | nsAutoCString legacyName; |
2110 | 0 | uint32_t dataLength; |
2111 | 0 | const char* nameData = hb_blob_get_data(nameTable, &dataLength); |
2112 | 0 | if (LookForLegacyFamilyName(Name(), nameData, dataLength, |
2113 | 0 | legacyName)) { |
2114 | 0 | if (aFontList->AddWithLegacyFamilyName(legacyName, fe)) { |
2115 | 0 | added = true; |
2116 | 0 | } |
2117 | 0 | } |
2118 | 0 | } |
2119 | 0 | return added; |
2120 | 0 | } |
2121 | | |
2122 | | void |
2123 | | gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList, |
2124 | | bool aNeedFullnamePostscriptNames, |
2125 | | FontInfoData *aFontInfoData) |
2126 | 0 | { |
2127 | 0 | // if all needed names have already been read, skip |
2128 | 0 | if (mOtherFamilyNamesInitialized && |
2129 | 0 | (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) |
2130 | 0 | return; |
2131 | 0 | |
2132 | 0 | bool asyncFontLoaderDisabled = false; |
2133 | 0 |
|
2134 | 0 | if (!mOtherFamilyNamesInitialized && |
2135 | 0 | aFontInfoData && |
2136 | 0 | aFontInfoData->mLoadOtherNames && |
2137 | 0 | !asyncFontLoaderDisabled) |
2138 | 0 | { |
2139 | 0 | AutoTArray<nsCString,4> otherFamilyNames; |
2140 | 0 | bool foundOtherNames = |
2141 | 0 | aFontInfoData->GetOtherFamilyNames(mName, otherFamilyNames); |
2142 | 0 | if (foundOtherNames) { |
2143 | 0 | uint32_t i, n = otherFamilyNames.Length(); |
2144 | 0 | for (i = 0; i < n; i++) { |
2145 | 0 | aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]); |
2146 | 0 | } |
2147 | 0 | } |
2148 | 0 | mOtherFamilyNamesInitialized = true; |
2149 | 0 | } |
2150 | 0 |
|
2151 | 0 | // if all needed data has been initialized, return |
2152 | 0 | if (mOtherFamilyNamesInitialized && |
2153 | 0 | (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) { |
2154 | 0 | return; |
2155 | 0 | } |
2156 | 0 | |
2157 | 0 | FindStyleVariations(aFontInfoData); |
2158 | 0 |
|
2159 | 0 | // check again, as style enumeration code may have loaded names |
2160 | 0 | if (mOtherFamilyNamesInitialized && |
2161 | 0 | (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) { |
2162 | 0 | return; |
2163 | 0 | } |
2164 | 0 | |
2165 | 0 | uint32_t i, numFonts = mAvailableFonts.Length(); |
2166 | 0 | const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e'); |
2167 | 0 |
|
2168 | 0 | bool firstTime = true, readAllFaces = false; |
2169 | 0 | for (i = 0; i < numFonts; ++i) { |
2170 | 0 | gfxFontEntry *fe = mAvailableFonts[i]; |
2171 | 0 | if (!fe) { |
2172 | 0 | continue; |
2173 | 0 | } |
2174 | 0 | |
2175 | 0 | nsAutoCString fullname, psname; |
2176 | 0 | bool foundFaceNames = false; |
2177 | 0 | if (!mFaceNamesInitialized && |
2178 | 0 | aNeedFullnamePostscriptNames && |
2179 | 0 | aFontInfoData && |
2180 | 0 | aFontInfoData->mLoadFaceNames) { |
2181 | 0 | aFontInfoData->GetFaceNames(fe->Name(), fullname, psname); |
2182 | 0 | if (!fullname.IsEmpty()) { |
2183 | 0 | aPlatformFontList->AddFullname(fe, fullname); |
2184 | 0 | } |
2185 | 0 | if (!psname.IsEmpty()) { |
2186 | 0 | aPlatformFontList->AddPostscriptName(fe, psname); |
2187 | 0 | } |
2188 | 0 | foundFaceNames = true; |
2189 | 0 |
|
2190 | 0 | // found everything needed? skip to next font |
2191 | 0 | if (mOtherFamilyNamesInitialized) { |
2192 | 0 | continue; |
2193 | 0 | } |
2194 | 0 | } |
2195 | 0 | |
2196 | 0 | // load directly from the name table |
2197 | 0 | gfxFontEntry::AutoTable nameTable(fe, kNAME); |
2198 | 0 | if (!nameTable) { |
2199 | 0 | continue; |
2200 | 0 | } |
2201 | 0 | |
2202 | 0 | if (aNeedFullnamePostscriptNames && !foundFaceNames) { |
2203 | 0 | if (gfxFontUtils::ReadCanonicalName( |
2204 | 0 | nameTable, gfxFontUtils::NAME_ID_FULL, fullname) == NS_OK) |
2205 | 0 | { |
2206 | 0 | aPlatformFontList->AddFullname(fe, fullname); |
2207 | 0 | } |
2208 | 0 |
|
2209 | 0 | if (gfxFontUtils::ReadCanonicalName( |
2210 | 0 | nameTable, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK) |
2211 | 0 | { |
2212 | 0 | aPlatformFontList->AddPostscriptName(fe, psname); |
2213 | 0 | } |
2214 | 0 | } |
2215 | 0 |
|
2216 | 0 | if (!mOtherFamilyNamesInitialized && (firstTime || readAllFaces)) { |
2217 | 0 | bool foundOtherName = ReadOtherFamilyNamesForFace(aPlatformFontList, |
2218 | 0 | nameTable); |
2219 | 0 |
|
2220 | 0 | // if the first face has a different name, scan all faces, otherwise |
2221 | 0 | // assume the family doesn't have other names |
2222 | 0 | if (firstTime && foundOtherName) { |
2223 | 0 | mHasOtherFamilyNames = true; |
2224 | 0 | readAllFaces = true; |
2225 | 0 | } |
2226 | 0 | firstTime = false; |
2227 | 0 | } |
2228 | 0 |
|
2229 | 0 | // if not reading in any more names, skip other faces |
2230 | 0 | if (!readAllFaces && !aNeedFullnamePostscriptNames) { |
2231 | 0 | break; |
2232 | 0 | } |
2233 | 0 | } |
2234 | 0 |
|
2235 | 0 | mFaceNamesInitialized = true; |
2236 | 0 | mOtherFamilyNamesInitialized = true; |
2237 | 0 | } |
2238 | | |
2239 | | |
2240 | | gfxFontEntry* |
2241 | | gfxFontFamily::FindFont(const nsACString& aPostscriptName) |
2242 | 0 | { |
2243 | 0 | // find the font using a simple linear search |
2244 | 0 | uint32_t numFonts = mAvailableFonts.Length(); |
2245 | 0 | for (uint32_t i = 0; i < numFonts; i++) { |
2246 | 0 | gfxFontEntry *fe = mAvailableFonts[i].get(); |
2247 | 0 | if (fe && fe->Name() == aPostscriptName) |
2248 | 0 | return fe; |
2249 | 0 | } |
2250 | 0 | return nullptr; |
2251 | 0 | } |
2252 | | |
2253 | | void |
2254 | | gfxFontFamily::ReadAllCMAPs(FontInfoData *aFontInfoData) |
2255 | 0 | { |
2256 | 0 | FindStyleVariations(aFontInfoData); |
2257 | 0 |
|
2258 | 0 | uint32_t i, numFonts = mAvailableFonts.Length(); |
2259 | 0 | for (i = 0; i < numFonts; i++) { |
2260 | 0 | gfxFontEntry *fe = mAvailableFonts[i]; |
2261 | 0 | // don't try to load cmaps for downloadable fonts not yet loaded |
2262 | 0 | if (!fe || fe->mIsUserFontContainer) { |
2263 | 0 | continue; |
2264 | 0 | } |
2265 | 0 | fe->ReadCMAP(aFontInfoData); |
2266 | 0 | mFamilyCharacterMap.Union(*(fe->mCharacterMap)); |
2267 | 0 | } |
2268 | 0 | mFamilyCharacterMap.Compact(); |
2269 | 0 | mFamilyCharacterMapInitialized = true; |
2270 | 0 | } |
2271 | | |
2272 | | void |
2273 | | gfxFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, |
2274 | | FontListSizes* aSizes) const |
2275 | 0 | { |
2276 | 0 | aSizes->mFontListSize += |
2277 | 0 | mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
2278 | 0 | aSizes->mCharMapsSize += |
2279 | 0 | mFamilyCharacterMap.SizeOfExcludingThis(aMallocSizeOf); |
2280 | 0 |
|
2281 | 0 | aSizes->mFontListSize += |
2282 | 0 | mAvailableFonts.ShallowSizeOfExcludingThis(aMallocSizeOf); |
2283 | 0 | for (uint32_t i = 0; i < mAvailableFonts.Length(); ++i) { |
2284 | 0 | gfxFontEntry *fe = mAvailableFonts[i]; |
2285 | 0 | if (fe) { |
2286 | 0 | fe->AddSizeOfIncludingThis(aMallocSizeOf, aSizes); |
2287 | 0 | } |
2288 | 0 | } |
2289 | 0 | } |
2290 | | |
2291 | | void |
2292 | | gfxFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, |
2293 | | FontListSizes* aSizes) const |
2294 | 0 | { |
2295 | 0 | aSizes->mFontListSize += aMallocSizeOf(this); |
2296 | 0 | AddSizeOfExcludingThis(aMallocSizeOf, aSizes); |
2297 | 0 | } |