gfx/thebes/gfxFT2Fonts.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial