|
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 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(), ¶ms, 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 |