|
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 #if defined(MOZ_WIDGET_GTK) |
|
7 #include "gfxPlatformGtk.h" |
|
8 #define gfxToolkitPlatform gfxPlatformGtk |
|
9 #elif defined(MOZ_WIDGET_QT) |
|
10 #include <qfontinfo.h> |
|
11 #include "gfxQtPlatform.h" |
|
12 #define gfxToolkitPlatform gfxQtPlatform |
|
13 #elif defined(XP_WIN) |
|
14 #include "gfxWindowsPlatform.h" |
|
15 #define gfxToolkitPlatform gfxWindowsPlatform |
|
16 #elif defined(ANDROID) |
|
17 #include "gfxAndroidPlatform.h" |
|
18 #define gfxToolkitPlatform gfxAndroidPlatform |
|
19 #endif |
|
20 |
|
21 #include "gfxTypes.h" |
|
22 #include "gfxFT2Fonts.h" |
|
23 #include "gfxFT2FontBase.h" |
|
24 #include "gfxFT2Utils.h" |
|
25 #include "gfxFT2FontList.h" |
|
26 #include <locale.h> |
|
27 #include "gfxHarfBuzzShaper.h" |
|
28 #include "gfxGraphiteShaper.h" |
|
29 #include "nsGkAtoms.h" |
|
30 #include "nsTArray.h" |
|
31 #include "nsUnicodeRange.h" |
|
32 #include "nsCRT.h" |
|
33 #include "nsXULAppAPI.h" |
|
34 |
|
35 #include "prlog.h" |
|
36 #include "prinit.h" |
|
37 |
|
38 #include "mozilla/MemoryReporting.h" |
|
39 #include "mozilla/Preferences.h" |
|
40 #include "mozilla/gfx/2D.h" |
|
41 |
|
42 // rounding and truncation functions for a Freetype floating point number |
|
43 // (FT26Dot6) stored in a 32bit integer with high 26 bits for the integer |
|
44 // part and low 6 bits for the fractional part. |
|
45 #define MOZ_FT_ROUND(x) (((x) + 32) & ~63) // 63 = 2^6 - 1 |
|
46 #define MOZ_FT_TRUNC(x) ((x) >> 6) |
|
47 #define CONVERT_DESIGN_UNITS_TO_PIXELS(v, s) \ |
|
48 MOZ_FT_TRUNC(MOZ_FT_ROUND(FT_MulFix((v) , (s)))) |
|
49 |
|
50 #ifndef ANDROID // not needed on Android, we use the generic gfxFontGroup |
|
51 /** |
|
52 * gfxFT2FontGroup |
|
53 */ |
|
54 |
|
55 static PRLogModuleInfo * |
|
56 GetFontLog() |
|
57 { |
|
58 static PRLogModuleInfo *sLog; |
|
59 if (!sLog) |
|
60 sLog = PR_NewLogModule("ft2fonts"); |
|
61 return sLog; |
|
62 } |
|
63 |
|
64 bool |
|
65 gfxFT2FontGroup::FontCallback(const nsAString& fontName, |
|
66 const nsACString& genericName, |
|
67 bool aUseFontSet, |
|
68 void *closure) |
|
69 { |
|
70 nsTArray<nsString> *sa = static_cast<nsTArray<nsString>*>(closure); |
|
71 |
|
72 if (!fontName.IsEmpty() && !sa->Contains(fontName)) { |
|
73 sa->AppendElement(fontName); |
|
74 #ifdef DEBUG_pavlov |
|
75 printf(" - %s\n", NS_ConvertUTF16toUTF8(fontName).get()); |
|
76 #endif |
|
77 } |
|
78 |
|
79 return true; |
|
80 } |
|
81 |
|
82 gfxFT2FontGroup::gfxFT2FontGroup(const nsAString& families, |
|
83 const gfxFontStyle *aStyle, |
|
84 gfxUserFontSet *aUserFontSet) |
|
85 : gfxFontGroup(families, aStyle, aUserFontSet) |
|
86 { |
|
87 #ifdef DEBUG_pavlov |
|
88 printf("Looking for %s\n", NS_ConvertUTF16toUTF8(families).get()); |
|
89 #endif |
|
90 nsTArray<nsString> familyArray; |
|
91 ForEachFont(FontCallback, &familyArray); |
|
92 |
|
93 if (familyArray.Length() == 0) { |
|
94 nsAutoString prefFamilies; |
|
95 gfxToolkitPlatform::GetPlatform()->GetPrefFonts(aStyle->language, prefFamilies, nullptr); |
|
96 if (!prefFamilies.IsEmpty()) { |
|
97 ForEachFont(prefFamilies, aStyle->language, FontCallback, &familyArray); |
|
98 } |
|
99 } |
|
100 if (familyArray.Length() == 0) { |
|
101 #if defined(MOZ_WIDGET_QT) /* FIXME DFB */ |
|
102 printf("failde to find a font. sadface\n"); |
|
103 // We want to get rid of this entirely at some point, but first we need real lists of fonts. |
|
104 QFont defaultFont; |
|
105 QFontInfo fi (defaultFont); |
|
106 familyArray.AppendElement(nsDependentString(static_cast<const char16_t *>(fi.family().utf16()))); |
|
107 #elif defined(MOZ_WIDGET_GTK) |
|
108 FcResult result; |
|
109 FcChar8 *family = nullptr; |
|
110 FcPattern* pat = FcPatternCreate(); |
|
111 FcPattern *match = FcFontMatch(nullptr, pat, &result); |
|
112 if (match) |
|
113 FcPatternGetString(match, FC_FAMILY, 0, &family); |
|
114 if (family) |
|
115 familyArray.AppendElement(NS_ConvertUTF8toUTF16((char*)family)); |
|
116 #elif defined(XP_WIN) |
|
117 HGDIOBJ hGDI = ::GetStockObject(SYSTEM_FONT); |
|
118 LOGFONTW logFont; |
|
119 if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont)) |
|
120 familyArray.AppendElement(nsDependentString(logFont.lfFaceName)); |
|
121 #elif defined(ANDROID) |
|
122 familyArray.AppendElement(NS_LITERAL_STRING("Droid Sans")); |
|
123 familyArray.AppendElement(NS_LITERAL_STRING("Roboto")); |
|
124 #else |
|
125 #error "Platform not supported" |
|
126 #endif |
|
127 } |
|
128 |
|
129 for (uint32_t i = 0; i < familyArray.Length(); i++) { |
|
130 nsRefPtr<gfxFT2Font> font = gfxFT2Font::GetOrMakeFont(familyArray[i], &mStyle); |
|
131 if (font) { |
|
132 mFonts.AppendElement(font); |
|
133 } |
|
134 } |
|
135 NS_ASSERTION(mFonts.Length() > 0, "We need at least one font in a fontgroup"); |
|
136 } |
|
137 |
|
138 gfxFT2FontGroup::~gfxFT2FontGroup() |
|
139 { |
|
140 } |
|
141 |
|
142 gfxFontGroup * |
|
143 gfxFT2FontGroup::Copy(const gfxFontStyle *aStyle) |
|
144 { |
|
145 return new gfxFT2FontGroup(mFamilies, aStyle, nullptr); |
|
146 } |
|
147 |
|
148 // Helper function to return the leading UTF-8 character in a char pointer |
|
149 // as 32bit number. Also sets the length of the current character (i.e. the |
|
150 // offset to the next one) in the second argument |
|
151 uint32_t getUTF8CharAndNext(const uint8_t *aString, uint8_t *aLength) |
|
152 { |
|
153 *aLength = 1; |
|
154 if (aString[0] < 0x80) { // normal 7bit ASCII char |
|
155 return aString[0]; |
|
156 } |
|
157 if ((aString[0] >> 5) == 6) { // two leading ones -> two bytes |
|
158 *aLength = 2; |
|
159 return ((aString[0] & 0x1F) << 6) + (aString[1] & 0x3F); |
|
160 } |
|
161 if ((aString[0] >> 4) == 14) { // three leading ones -> three bytes |
|
162 *aLength = 3; |
|
163 return ((aString[0] & 0x0F) << 12) + ((aString[1] & 0x3F) << 6) + |
|
164 (aString[2] & 0x3F); |
|
165 } |
|
166 if ((aString[0] >> 4) == 15) { // four leading ones -> four bytes |
|
167 *aLength = 4; |
|
168 return ((aString[0] & 0x07) << 18) + ((aString[1] & 0x3F) << 12) + |
|
169 ((aString[2] & 0x3F) << 6) + (aString[3] & 0x3F); |
|
170 } |
|
171 return aString[0]; |
|
172 } |
|
173 |
|
174 |
|
175 static bool |
|
176 AddFontNameToArray(const nsAString& aName, |
|
177 const nsACString& aGenericName, |
|
178 bool aUseFontSet, |
|
179 void *aClosure) |
|
180 { |
|
181 if (!aName.IsEmpty()) { |
|
182 nsTArray<nsString> *list = static_cast<nsTArray<nsString> *>(aClosure); |
|
183 |
|
184 if (list->IndexOf(aName) == list->NoIndex) |
|
185 list->AppendElement(aName); |
|
186 } |
|
187 |
|
188 return true; |
|
189 } |
|
190 |
|
191 void |
|
192 gfxFT2FontGroup::FamilyListToArrayList(const nsString& aFamilies, |
|
193 nsIAtom *aLangGroup, |
|
194 nsTArray<nsRefPtr<gfxFontEntry> > *aFontEntryList) |
|
195 { |
|
196 nsAutoTArray<nsString, 15> fonts; |
|
197 ForEachFont(aFamilies, aLangGroup, AddFontNameToArray, &fonts); |
|
198 |
|
199 uint32_t len = fonts.Length(); |
|
200 for (uint32_t i = 0; i < len; ++i) { |
|
201 const nsString& str = fonts[i]; |
|
202 nsRefPtr<gfxFontEntry> fe = (gfxToolkitPlatform::GetPlatform()->FindFontEntry(str, mStyle)); |
|
203 aFontEntryList->AppendElement(fe); |
|
204 } |
|
205 } |
|
206 |
|
207 void gfxFT2FontGroup::GetPrefFonts(nsIAtom *aLangGroup, nsTArray<nsRefPtr<gfxFontEntry> >& aFontEntryList) |
|
208 { |
|
209 NS_ASSERTION(aLangGroup, "aLangGroup is null"); |
|
210 gfxToolkitPlatform *platform = gfxToolkitPlatform::GetPlatform(); |
|
211 nsAutoTArray<nsRefPtr<gfxFontEntry>, 5> fonts; |
|
212 nsAutoCString key; |
|
213 aLangGroup->ToUTF8String(key); |
|
214 key.Append("-"); |
|
215 key.AppendInt(GetStyle()->style); |
|
216 key.Append("-"); |
|
217 key.AppendInt(GetStyle()->weight); |
|
218 if (!platform->GetPrefFontEntries(key, &fonts)) { |
|
219 nsString fontString; |
|
220 platform->GetPrefFonts(aLangGroup, fontString); |
|
221 if (fontString.IsEmpty()) |
|
222 return; |
|
223 |
|
224 FamilyListToArrayList(fontString, aLangGroup, &fonts); |
|
225 |
|
226 platform->SetPrefFontEntries(key, fonts); |
|
227 } |
|
228 aFontEntryList.AppendElements(fonts); |
|
229 } |
|
230 |
|
231 static int32_t GetCJKLangGroupIndex(const char *aLangGroup) { |
|
232 int32_t i; |
|
233 for (i = 0; i < COUNT_OF_CJK_LANG_GROUP; i++) { |
|
234 if (!PL_strcasecmp(aLangGroup, sCJKLangGroup[i])) |
|
235 return i; |
|
236 } |
|
237 return -1; |
|
238 } |
|
239 |
|
240 // this function assigns to the array passed in. |
|
241 void gfxFT2FontGroup::GetCJKPrefFonts(nsTArray<nsRefPtr<gfxFontEntry> >& aFontEntryList) { |
|
242 gfxToolkitPlatform *platform = gfxToolkitPlatform::GetPlatform(); |
|
243 |
|
244 nsAutoCString key("x-internal-cjk-"); |
|
245 key.AppendInt(mStyle.style); |
|
246 key.Append("-"); |
|
247 key.AppendInt(mStyle.weight); |
|
248 |
|
249 if (!platform->GetPrefFontEntries(key, &aFontEntryList)) { |
|
250 NS_ENSURE_TRUE_VOID(Preferences::GetRootBranch()); |
|
251 // Add the CJK pref fonts from accept languages, the order should be same order |
|
252 nsAdoptingCString list = Preferences::GetLocalizedCString("intl.accept_languages"); |
|
253 if (!list.IsEmpty()) { |
|
254 const char kComma = ','; |
|
255 const char *p, *p_end; |
|
256 list.BeginReading(p); |
|
257 list.EndReading(p_end); |
|
258 while (p < p_end) { |
|
259 while (nsCRT::IsAsciiSpace(*p)) { |
|
260 if (++p == p_end) |
|
261 break; |
|
262 } |
|
263 if (p == p_end) |
|
264 break; |
|
265 const char *start = p; |
|
266 while (++p != p_end && *p != kComma) |
|
267 /* nothing */ ; |
|
268 nsAutoCString lang(Substring(start, p)); |
|
269 lang.CompressWhitespace(false, true); |
|
270 int32_t index = GetCJKLangGroupIndex(lang.get()); |
|
271 if (index >= 0) { |
|
272 nsCOMPtr<nsIAtom> atom = do_GetAtom(sCJKLangGroup[index]); |
|
273 GetPrefFonts(atom, aFontEntryList); |
|
274 } |
|
275 p++; |
|
276 } |
|
277 } |
|
278 |
|
279 // Add the system locale |
|
280 #ifdef XP_WIN |
|
281 switch (::GetACP()) { |
|
282 case 932: GetPrefFonts(nsGkAtoms::Japanese, aFontEntryList); break; |
|
283 case 936: GetPrefFonts(nsGkAtoms::zh_cn, aFontEntryList); break; |
|
284 case 949: GetPrefFonts(nsGkAtoms::ko, aFontEntryList); break; |
|
285 // XXX Don't we need to append nsGkAtoms::zh_hk if the codepage is 950? |
|
286 case 950: GetPrefFonts(nsGkAtoms::zh_tw, aFontEntryList); break; |
|
287 } |
|
288 #else |
|
289 const char *ctype = setlocale(LC_CTYPE, nullptr); |
|
290 if (ctype) { |
|
291 if (!PL_strncasecmp(ctype, "ja", 2)) { |
|
292 GetPrefFonts(nsGkAtoms::Japanese, aFontEntryList); |
|
293 } else if (!PL_strncasecmp(ctype, "zh_cn", 5)) { |
|
294 GetPrefFonts(nsGkAtoms::zh_cn, aFontEntryList); |
|
295 } else if (!PL_strncasecmp(ctype, "zh_hk", 5)) { |
|
296 GetPrefFonts(nsGkAtoms::zh_hk, aFontEntryList); |
|
297 } else if (!PL_strncasecmp(ctype, "zh_tw", 5)) { |
|
298 GetPrefFonts(nsGkAtoms::zh_tw, aFontEntryList); |
|
299 } else if (!PL_strncasecmp(ctype, "ko", 2)) { |
|
300 GetPrefFonts(nsGkAtoms::ko, aFontEntryList); |
|
301 } |
|
302 } |
|
303 #endif |
|
304 |
|
305 // last resort... |
|
306 GetPrefFonts(nsGkAtoms::Japanese, aFontEntryList); |
|
307 GetPrefFonts(nsGkAtoms::ko, aFontEntryList); |
|
308 GetPrefFonts(nsGkAtoms::zh_cn, aFontEntryList); |
|
309 GetPrefFonts(nsGkAtoms::zh_hk, aFontEntryList); |
|
310 GetPrefFonts(nsGkAtoms::zh_tw, aFontEntryList); |
|
311 |
|
312 platform->SetPrefFontEntries(key, aFontEntryList); |
|
313 } |
|
314 } |
|
315 |
|
316 already_AddRefed<gfxFT2Font> |
|
317 gfxFT2FontGroup::WhichFontSupportsChar(const nsTArray<nsRefPtr<gfxFontEntry> >& aFontEntryList, uint32_t aCh) |
|
318 { |
|
319 for (uint32_t i = 0; i < aFontEntryList.Length(); i++) { |
|
320 gfxFontEntry *fe = aFontEntryList[i].get(); |
|
321 if (fe->HasCharacter(aCh)) { |
|
322 nsRefPtr<gfxFT2Font> font = |
|
323 gfxFT2Font::GetOrMakeFont(static_cast<FontEntry*>(fe), &mStyle); |
|
324 return font.forget(); |
|
325 } |
|
326 } |
|
327 return nullptr; |
|
328 } |
|
329 |
|
330 already_AddRefed<gfxFont> |
|
331 gfxFT2FontGroup::WhichPrefFontSupportsChar(uint32_t aCh) |
|
332 { |
|
333 if (aCh > 0xFFFF) |
|
334 return nullptr; |
|
335 |
|
336 nsRefPtr<gfxFT2Font> selectedFont; |
|
337 |
|
338 // check out the style's language |
|
339 nsAutoTArray<nsRefPtr<gfxFontEntry>, 5> fonts; |
|
340 GetPrefFonts(mStyle.language, fonts); |
|
341 selectedFont = WhichFontSupportsChar(fonts, aCh); |
|
342 |
|
343 // otherwise search prefs |
|
344 if (!selectedFont) { |
|
345 uint32_t unicodeRange = FindCharUnicodeRange(aCh); |
|
346 |
|
347 /* special case CJK */ |
|
348 if (unicodeRange == kRangeSetCJK) { |
|
349 if (PR_LOG_TEST(GetFontLog(), PR_LOG_DEBUG)) { |
|
350 PR_LOG(GetFontLog(), PR_LOG_DEBUG, (" - Trying to find fonts for: CJK")); |
|
351 } |
|
352 |
|
353 nsAutoTArray<nsRefPtr<gfxFontEntry>, 15> fonts; |
|
354 GetCJKPrefFonts(fonts); |
|
355 selectedFont = WhichFontSupportsChar(fonts, aCh); |
|
356 } else { |
|
357 nsIAtom *langGroup = LangGroupFromUnicodeRange(unicodeRange); |
|
358 if (langGroup) { |
|
359 PR_LOG(GetFontLog(), PR_LOG_DEBUG, (" - Trying to find fonts for: %s", nsAtomCString(langGroup).get())); |
|
360 |
|
361 nsAutoTArray<nsRefPtr<gfxFontEntry>, 5> fonts; |
|
362 GetPrefFonts(langGroup, fonts); |
|
363 selectedFont = WhichFontSupportsChar(fonts, aCh); |
|
364 } |
|
365 } |
|
366 } |
|
367 |
|
368 if (selectedFont) { |
|
369 nsRefPtr<gfxFont> f = static_cast<gfxFont*>(selectedFont.get()); |
|
370 return f.forget(); |
|
371 } |
|
372 |
|
373 return nullptr; |
|
374 } |
|
375 |
|
376 already_AddRefed<gfxFont> |
|
377 gfxFT2FontGroup::WhichSystemFontSupportsChar(uint32_t aCh, int32_t aRunScript) |
|
378 { |
|
379 #if defined(XP_WIN) || defined(ANDROID) |
|
380 FontEntry *fe = static_cast<FontEntry*> |
|
381 (gfxPlatformFontList::PlatformFontList()-> |
|
382 SystemFindFontForChar(aCh, aRunScript, &mStyle)); |
|
383 if (fe) { |
|
384 nsRefPtr<gfxFT2Font> f = gfxFT2Font::GetOrMakeFont(fe, &mStyle); |
|
385 nsRefPtr<gfxFont> font = f.get(); |
|
386 return font.forget(); |
|
387 } |
|
388 #else |
|
389 nsRefPtr<gfxFont> selectedFont; |
|
390 nsRefPtr<gfxFont> refFont = GetFontAt(0); |
|
391 gfxToolkitPlatform *platform = gfxToolkitPlatform::GetPlatform(); |
|
392 selectedFont = platform->FindFontForChar(aCh, refFont); |
|
393 if (selectedFont) |
|
394 return selectedFont.forget(); |
|
395 #endif |
|
396 return nullptr; |
|
397 } |
|
398 |
|
399 #endif // !ANDROID |
|
400 |
|
401 /** |
|
402 * gfxFT2Font |
|
403 */ |
|
404 |
|
405 bool |
|
406 gfxFT2Font::ShapeText(gfxContext *aContext, |
|
407 const char16_t *aText, |
|
408 uint32_t aOffset, |
|
409 uint32_t aLength, |
|
410 int32_t aScript, |
|
411 gfxShapedText *aShapedText, |
|
412 bool aPreferPlatformShaping) |
|
413 { |
|
414 bool ok = false; |
|
415 |
|
416 if (FontCanSupportGraphite()) { |
|
417 if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) { |
|
418 if (!mGraphiteShaper) { |
|
419 mGraphiteShaper = new gfxGraphiteShaper(this); |
|
420 } |
|
421 ok = mGraphiteShaper->ShapeText(aContext, aText, |
|
422 aOffset, aLength, |
|
423 aScript, aShapedText); |
|
424 } |
|
425 } |
|
426 |
|
427 if (!ok && gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) { |
|
428 if (!mHarfBuzzShaper) { |
|
429 mHarfBuzzShaper = new gfxHarfBuzzShaper(this); |
|
430 } |
|
431 ok = mHarfBuzzShaper->ShapeText(aContext, aText, |
|
432 aOffset, aLength, |
|
433 aScript, aShapedText); |
|
434 } |
|
435 |
|
436 if (!ok) { |
|
437 AddRange(aText, aOffset, aLength, aShapedText); |
|
438 } |
|
439 |
|
440 PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText); |
|
441 |
|
442 return true; |
|
443 } |
|
444 |
|
445 void |
|
446 gfxFT2Font::AddRange(const char16_t *aText, uint32_t aOffset, |
|
447 uint32_t aLength, gfxShapedText *aShapedText) |
|
448 { |
|
449 const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit(); |
|
450 // we'll pass this in/figure it out dynamically, but at this point there can be only one face. |
|
451 gfxFT2LockedFace faceLock(this); |
|
452 FT_Face face = faceLock.get(); |
|
453 |
|
454 gfxShapedText::CompressedGlyph *charGlyphs = |
|
455 aShapedText->GetCharacterGlyphs(); |
|
456 |
|
457 const gfxFT2Font::CachedGlyphData *cgd = nullptr, *cgdNext = nullptr; |
|
458 |
|
459 FT_UInt spaceGlyph = GetSpaceGlyph(); |
|
460 |
|
461 for (uint32_t i = 0; i < aLength; i++, aOffset++) { |
|
462 char16_t ch = aText[i]; |
|
463 |
|
464 if (ch == 0) { |
|
465 // treat this null byte as a missing glyph, don't create a glyph for it |
|
466 aShapedText->SetMissingGlyph(aOffset, 0, this); |
|
467 continue; |
|
468 } |
|
469 |
|
470 NS_ASSERTION(!gfxFontGroup::IsInvalidChar(ch), "Invalid char detected"); |
|
471 |
|
472 if (cgdNext) { |
|
473 cgd = cgdNext; |
|
474 cgdNext = nullptr; |
|
475 } else { |
|
476 cgd = GetGlyphDataForChar(ch); |
|
477 } |
|
478 |
|
479 FT_UInt gid = cgd->glyphIndex; |
|
480 int32_t advance = 0; |
|
481 |
|
482 if (gid == 0) { |
|
483 advance = -1; // trigger the missing glyphs case below |
|
484 } else { |
|
485 // find next character and its glyph -- in case they exist |
|
486 // and exist in the current font face -- to compute kerning |
|
487 char16_t chNext = 0; |
|
488 FT_UInt gidNext = 0; |
|
489 FT_Pos lsbDeltaNext = 0; |
|
490 |
|
491 if (FT_HAS_KERNING(face) && i + 1 < aLength) { |
|
492 chNext = aText[i + 1]; |
|
493 if (chNext != 0) { |
|
494 cgdNext = GetGlyphDataForChar(chNext); |
|
495 gidNext = cgdNext->glyphIndex; |
|
496 if (gidNext && gidNext != spaceGlyph) |
|
497 lsbDeltaNext = cgdNext->lsbDelta; |
|
498 } |
|
499 } |
|
500 |
|
501 advance = cgd->xAdvance; |
|
502 |
|
503 // now add kerning to the current glyph's advance |
|
504 if (chNext && gidNext) { |
|
505 FT_Vector kerning; kerning.x = 0; |
|
506 FT_Get_Kerning(face, gid, gidNext, FT_KERNING_DEFAULT, &kerning); |
|
507 advance += kerning.x; |
|
508 if (cgd->rsbDelta - lsbDeltaNext >= 32) { |
|
509 advance -= 64; |
|
510 } else if (cgd->rsbDelta - lsbDeltaNext < -32) { |
|
511 advance += 64; |
|
512 } |
|
513 } |
|
514 |
|
515 // convert 26.6 fixed point to app units |
|
516 // round rather than truncate to nearest pixel |
|
517 // because these advances are often scaled |
|
518 advance = ((advance * appUnitsPerDevUnit + 32) >> 6); |
|
519 } |
|
520 |
|
521 if (advance >= 0 && |
|
522 gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) && |
|
523 gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gid)) { |
|
524 charGlyphs[aOffset].SetSimpleGlyph(advance, gid); |
|
525 } else if (gid == 0) { |
|
526 // gid = 0 only happens when the glyph is missing from the font |
|
527 aShapedText->SetMissingGlyph(aOffset, ch, this); |
|
528 } else { |
|
529 gfxTextRun::DetailedGlyph details; |
|
530 details.mGlyphID = gid; |
|
531 NS_ASSERTION(details.mGlyphID == gid, |
|
532 "Seriously weird glyph ID detected!"); |
|
533 details.mAdvance = advance; |
|
534 details.mXOffset = 0; |
|
535 details.mYOffset = 0; |
|
536 gfxShapedText::CompressedGlyph g; |
|
537 g.SetComplex(charGlyphs[aOffset].IsClusterStart(), true, 1); |
|
538 aShapedText->SetGlyphs(aOffset, g, &details); |
|
539 } |
|
540 } |
|
541 } |
|
542 |
|
543 gfxFT2Font::gfxFT2Font(cairo_scaled_font_t *aCairoFont, |
|
544 FT2FontEntry *aFontEntry, |
|
545 const gfxFontStyle *aFontStyle, |
|
546 bool aNeedsBold) |
|
547 : gfxFT2FontBase(aCairoFont, aFontEntry, aFontStyle) |
|
548 , mCharGlyphCache(64) |
|
549 { |
|
550 NS_ASSERTION(mFontEntry, "Unable to find font entry for font. Something is whack."); |
|
551 mApplySyntheticBold = aNeedsBold; |
|
552 } |
|
553 |
|
554 gfxFT2Font::~gfxFT2Font() |
|
555 { |
|
556 } |
|
557 |
|
558 /** |
|
559 * Look up the font in the gfxFont cache. If we don't find it, create one. |
|
560 * In either case, add a ref, append it to the aFonts array, and return it --- |
|
561 * except for OOM in which case we do nothing and return null. |
|
562 */ |
|
563 already_AddRefed<gfxFT2Font> |
|
564 gfxFT2Font::GetOrMakeFont(const nsAString& aName, const gfxFontStyle *aStyle, |
|
565 bool aNeedsBold) |
|
566 { |
|
567 #ifdef ANDROID |
|
568 FT2FontEntry *fe = static_cast<FT2FontEntry*> |
|
569 (gfxPlatformFontList::PlatformFontList()-> |
|
570 FindFontForFamily(aName, aStyle, aNeedsBold)); |
|
571 #else |
|
572 FT2FontEntry *fe = static_cast<FT2FontEntry*> |
|
573 (gfxToolkitPlatform::GetPlatform()->FindFontEntry(aName, *aStyle)); |
|
574 #endif |
|
575 if (!fe) { |
|
576 NS_WARNING("Failed to find font entry for font!"); |
|
577 return nullptr; |
|
578 } |
|
579 |
|
580 nsRefPtr<gfxFT2Font> font = GetOrMakeFont(fe, aStyle, aNeedsBold); |
|
581 return font.forget(); |
|
582 } |
|
583 |
|
584 already_AddRefed<gfxFT2Font> |
|
585 gfxFT2Font::GetOrMakeFont(FT2FontEntry *aFontEntry, const gfxFontStyle *aStyle, |
|
586 bool aNeedsBold) |
|
587 { |
|
588 nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(aFontEntry, aStyle); |
|
589 if (!font) { |
|
590 cairo_scaled_font_t *scaledFont = aFontEntry->CreateScaledFont(aStyle); |
|
591 if (!scaledFont) { |
|
592 return nullptr; |
|
593 } |
|
594 font = new gfxFT2Font(scaledFont, aFontEntry, aStyle, aNeedsBold); |
|
595 cairo_scaled_font_destroy(scaledFont); |
|
596 if (!font) { |
|
597 return nullptr; |
|
598 } |
|
599 gfxFontCache::GetCache()->AddNew(font); |
|
600 } |
|
601 return font.forget().downcast<gfxFT2Font>(); |
|
602 } |
|
603 |
|
604 void |
|
605 gfxFT2Font::FillGlyphDataForChar(uint32_t ch, CachedGlyphData *gd) |
|
606 { |
|
607 gfxFT2LockedFace faceLock(this); |
|
608 FT_Face face = faceLock.get(); |
|
609 |
|
610 if (!face->charmap || face->charmap->encoding != FT_ENCODING_UNICODE) { |
|
611 FT_Select_Charmap(face, FT_ENCODING_UNICODE); |
|
612 } |
|
613 FT_UInt gid = FT_Get_Char_Index(face, ch); |
|
614 |
|
615 if (gid == 0) { |
|
616 // this font doesn't support this char! |
|
617 NS_ASSERTION(gid != 0, "We don't have a glyph, but font indicated that it supported this char in tables?"); |
|
618 gd->glyphIndex = 0; |
|
619 return; |
|
620 } |
|
621 |
|
622 FT_Int32 flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ? |
|
623 FT_LOAD_DEFAULT : |
|
624 (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING); |
|
625 FT_Error err = FT_Load_Glyph(face, gid, flags); |
|
626 |
|
627 if (err) { |
|
628 // hmm, this is weird, we failed to load a glyph that we had? |
|
629 NS_WARNING("Failed to load glyph that we got from Get_Char_index"); |
|
630 |
|
631 gd->glyphIndex = 0; |
|
632 return; |
|
633 } |
|
634 |
|
635 gd->glyphIndex = gid; |
|
636 gd->lsbDelta = face->glyph->lsb_delta; |
|
637 gd->rsbDelta = face->glyph->rsb_delta; |
|
638 gd->xAdvance = face->glyph->advance.x; |
|
639 } |
|
640 |
|
641 void |
|
642 gfxFT2Font::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, |
|
643 FontCacheSizes* aSizes) const |
|
644 { |
|
645 gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); |
|
646 aSizes->mFontInstances += |
|
647 mCharGlyphCache.SizeOfExcludingThis(nullptr, aMallocSizeOf); |
|
648 } |
|
649 |
|
650 void |
|
651 gfxFT2Font::AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, |
|
652 FontCacheSizes* aSizes) const |
|
653 { |
|
654 aSizes->mFontInstances += aMallocSizeOf(this); |
|
655 AddSizeOfExcludingThis(aMallocSizeOf, aSizes); |
|
656 } |
|
657 |
|
658 #ifdef USE_SKIA |
|
659 mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions> |
|
660 gfxFT2Font::GetGlyphRenderingOptions() |
|
661 { |
|
662 mozilla::gfx::FontHinting hinting; |
|
663 |
|
664 if (gfxPlatform::GetPlatform()->FontHintingEnabled()) { |
|
665 hinting = mozilla::gfx::FontHinting::NORMAL; |
|
666 } else { |
|
667 hinting = mozilla::gfx::FontHinting::NONE; |
|
668 } |
|
669 |
|
670 // We don't want to force the use of the autohinter over the font's built in hints |
|
671 return mozilla::gfx::Factory::CreateCairoGlyphRenderingOptions(hinting, false); |
|
672 } |
|
673 #endif |
|
674 |