Bug 686225 - Work around buggy AAT fonts for Bengali and Kannada scripts. r=jdaggett
authorJonathan Kew <jkew@mozilla.com>
Wed, 25 Mar 2015 08:25:49 +0000
changeset 265797 23e60e2a05a52f9907a6ff4a05eff71e1d3fd330
parent 265796 a6b9c152a7d1fa6ea97c8bf877a9731b2b539751
child 265798 bfbca6c64c1b5755139ab1f2c37ecb810a1760cc
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett
bugs686225
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 686225 - Work around buggy AAT fonts for Bengali and Kannada scripts. r=jdaggett
gfx/thebes/gfxCoreTextShaper.cpp
gfx/thebes/gfxCoreTextShaper.h
--- a/gfx/thebes/gfxCoreTextShaper.cpp
+++ b/gfx/thebes/gfxCoreTextShaper.cpp
@@ -12,27 +12,29 @@
 
 #include <algorithm>
 
 using namespace mozilla;
 
 // standard font descriptors that we construct the first time they're needed
 CTFontDescriptorRef gfxCoreTextShaper::sDefaultFeaturesDescriptor = nullptr;
 CTFontDescriptorRef gfxCoreTextShaper::sDisableLigaturesDescriptor = nullptr;
+CTFontDescriptorRef gfxCoreTextShaper::sIndicFeaturesDescriptor = nullptr;
+CTFontDescriptorRef gfxCoreTextShaper::sIndicDisableLigaturesDescriptor = nullptr;
 
 gfxCoreTextShaper::gfxCoreTextShaper(gfxMacFont *aFont)
     : gfxFontShaper(aFont)
 {
     // Create our CTFontRef
-    mCTFont = ::CTFontCreateWithGraphicsFont(aFont->GetCGFontRef(),
-                                             aFont->GetAdjustedSize(),
-                                             nullptr,
-                                             GetDefaultFeaturesDescriptor());
+    mCTFont = CreateCTFontWithFeatures(aFont->GetAdjustedSize(),
+                                       GetDefaultFeaturesDescriptor());
 
-    // Set up the default attribute dictionary that we will need each time we create a CFAttributedString
+    // Set up the default attribute dictionary that we will need each time we
+    // create a CFAttributedString (unless we need to use custom features,
+    // in which case a new dictionary will be created on the fly).
     mAttributesDict = ::CFDictionaryCreate(kCFAllocatorDefault,
                                            (const void**) &kCTFontAttributeName,
                                            (const void**) &mCTFont,
                                            1, // count of attributes
                                            &kCFTypeDictionaryKeyCallBacks,
                                            &kCFTypeDictionaryValueCallBacks);
 }
 
@@ -41,16 +43,22 @@ gfxCoreTextShaper::~gfxCoreTextShaper()
     if (mAttributesDict) {
         ::CFRelease(mAttributesDict);
     }
     if (mCTFont) {
         ::CFRelease(mCTFont);
     }
 }
 
+static bool
+IsBuggyIndicScript(int32_t aScript)
+{
+    return aScript == MOZ_SCRIPT_BENGALI || aScript == MOZ_SCRIPT_KANNADA;
+}
+
 bool
 gfxCoreTextShaper::ShapeText(gfxContext      *aContext,
                              const char16_t *aText,
                              uint32_t         aOffset,
                              uint32_t         aLength,
                              int32_t          aScript,
                              bool             aVertical,
                              gfxShapedText   *aShapedText)
@@ -97,32 +105,50 @@ gfxCoreTextShaper::ShapeText(gfxContext 
     } else {
         startOffset = 0;
         stringObj = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
                                                          reinterpret_cast<const UniChar*>(aText),
                                                          length, kCFAllocatorNull);
     }
 
     CFDictionaryRef attrObj;
-    if (aShapedText->DisableLigatures()) {
-        // For letterspacing (or maybe other situations) we need to make a copy of the CTFont
-        // with the ligature feature disabled
-        CTFontRef ctFont =
-            CreateCTFontWithDisabledLigatures(::CTFontGetSize(mCTFont));
+    CTFontRef tempCTFont = nullptr;
 
+    if (IsBuggyIndicScript(aScript)) {
+        // To work around buggy Indic AAT fonts shipped with OS X,
+        // we re-enable the Line Initial Smart Swashes feature that is needed
+        // for "split vowels" to work in at least Bengali and Kannada fonts.
+        // Affected fonts include Bangla MN, Bangla Sangam MN, Kannada MN,
+        // Kannada Sangam MN. See bugs 686225, 728557, 953231, 1145515.
+        tempCTFont =
+            CreateCTFontWithFeatures(::CTFontGetSize(mCTFont),
+                                     aShapedText->DisableLigatures()
+                                         ? GetIndicDisableLigaturesDescriptor()
+                                         : GetIndicFeaturesDescriptor());
+    } else if (aShapedText->DisableLigatures()) {
+        // For letterspacing (or maybe other situations) we need to make
+        // a copy of the CTFont with the ligature feature disabled.
+        tempCTFont =
+            CreateCTFontWithFeatures(::CTFontGetSize(mCTFont),
+                                     GetDisableLigaturesDescriptor());
+    }
+
+    if (tempCTFont) {
         attrObj =
             ::CFDictionaryCreate(kCFAllocatorDefault,
                                  (const void**) &kCTFontAttributeName,
-                                 (const void**) &ctFont,
+                                 (const void**) &tempCTFont,
                                  1, // count of attributes
                                  &kCFTypeDictionaryKeyCallBacks,
                                  &kCFTypeDictionaryValueCallBacks);
-        // Having created the dict, we're finished with our ligature-disabled CTFontRef
-        ::CFRelease(ctFont);
+        // Having created the dict, we're finished with our temporary
+        // Indic and/or ligature-disabled CTFontRef.
+        ::CFRelease(tempCTFont);
     } else {
+        // The default case is to use our preallocated attr dict
         attrObj = mAttributesDict;
         ::CFRetain(attrObj);
     }
 
     // Now we can create an attributed string
     CFAttributedStringRef attrStringObj =
         ::CFAttributedStringCreate(kCFAllocatorDefault, stringObj, attrObj);
     ::CFRelease(stringObj);
@@ -509,153 +535,167 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxS
         charStart = charEnd;
     }
 
     return NS_OK;
 }
 
 #undef SMALL_GLYPH_RUN
 
-// Construct the font attribute descriptor that we'll apply by default when creating a CTFontRef.
-// This will turn off line-edge swashes by default, because we don't know the actual line breaks
-// when doing glyph shaping.
-void
-gfxCoreTextShaper::CreateDefaultFeaturesDescriptor()
-{
-    if (sDefaultFeaturesDescriptor != nullptr) {
-        return;
-    }
+// Construct the font attribute descriptor that we'll apply by default when
+// creating a CTFontRef. This will turn off line-edge swashes by default,
+// because we don't know the actual line breaks when doing glyph shaping.
+
+// We also cache feature descriptors for shaping with disabled ligatures, and
+// for buggy Indic AAT font workarounds, created on an as-needed basis.
+
+#define MAX_FEATURES  3 // max used by any of our Get*Descriptor functions
 
-    SInt16 val = kSmartSwashType;
-    CFNumberRef swashesType =
-        ::CFNumberCreate(kCFAllocatorDefault,
-                         kCFNumberSInt16Type,
-                         &val);
-    val = kLineInitialSwashesOffSelector;
-    CFNumberRef lineInitialsOffSelector =
-        ::CFNumberCreate(kCFAllocatorDefault,
-                         kCFNumberSInt16Type,
-                         &val);
+CTFontDescriptorRef
+gfxCoreTextShaper::CreateFontFeaturesDescriptor(
+    const std::pair<SInt16,SInt16> aFeatures[],
+    size_t aCount)
+{
+    MOZ_ASSERT(aCount <= MAX_FEATURES);
+
+    CFDictionaryRef featureSettings[MAX_FEATURES];
 
-    CFTypeRef keys[]   = { kCTFontFeatureTypeIdentifierKey,
-                           kCTFontFeatureSelectorIdentifierKey };
-    CFTypeRef values[] = { swashesType,
-                           lineInitialsOffSelector };
-    CFDictionaryRef featureSettings[2];
-    featureSettings[0] =
-        ::CFDictionaryCreate(kCFAllocatorDefault,
-                             (const void **) keys,
-                             (const void **) values,
-                             ArrayLength(keys),
-                             &kCFTypeDictionaryKeyCallBacks,
-                             &kCFTypeDictionaryValueCallBacks);
-    ::CFRelease(lineInitialsOffSelector);
+    for (size_t i = 0; i < aCount; i++) {
+        CFNumberRef type = ::CFNumberCreate(kCFAllocatorDefault,
+                                            kCFNumberSInt16Type,
+                                            &aFeatures[i].first);
+        CFNumberRef selector = ::CFNumberCreate(kCFAllocatorDefault,
+                                                kCFNumberSInt16Type,
+                                                &aFeatures[i].second);
 
-    val = kLineFinalSwashesOffSelector;
-    CFNumberRef lineFinalsOffSelector =
-        ::CFNumberCreate(kCFAllocatorDefault,
-                         kCFNumberSInt16Type,
-                         &val);
-    values[1] = lineFinalsOffSelector;
-    featureSettings[1] =
-        ::CFDictionaryCreate(kCFAllocatorDefault,
-                             (const void **) keys,
-                             (const void **) values,
-                             ArrayLength(keys),
-                             &kCFTypeDictionaryKeyCallBacks,
-                             &kCFTypeDictionaryValueCallBacks);
-    ::CFRelease(lineFinalsOffSelector);
-    ::CFRelease(swashesType);
+        CFTypeRef keys[]   = { kCTFontFeatureTypeIdentifierKey,
+                               kCTFontFeatureSelectorIdentifierKey };
+        CFTypeRef values[] = { type, selector };
+        featureSettings[i] =
+            ::CFDictionaryCreate(kCFAllocatorDefault,
+                                 (const void **) keys,
+                                 (const void **) values,
+                                 ArrayLength(keys),
+                                 &kCFTypeDictionaryKeyCallBacks,
+                                 &kCFTypeDictionaryValueCallBacks);
+
+        ::CFRelease(selector);
+        ::CFRelease(type);
+    }
 
     CFArrayRef featuresArray =
         ::CFArrayCreate(kCFAllocatorDefault,
                         (const void **) featureSettings,
-                        ArrayLength(featureSettings),
+                        aCount, // not ArrayLength(featureSettings), as we
+                                // may not have used all the allocated slots
                         &kCFTypeArrayCallBacks);
-    ::CFRelease(featureSettings[0]);
-    ::CFRelease(featureSettings[1]);
+
+    for (size_t i = 0; i < aCount; i++) {
+        ::CFRelease(featureSettings[i]);
+    }
 
     const CFTypeRef attrKeys[]   = { kCTFontFeatureSettingsAttribute };
     const CFTypeRef attrValues[] = { featuresArray };
     CFDictionaryRef attributesDict =
         ::CFDictionaryCreate(kCFAllocatorDefault,
                              (const void **) attrKeys,
                              (const void **) attrValues,
                              ArrayLength(attrKeys),
                              &kCFTypeDictionaryKeyCallBacks,
                              &kCFTypeDictionaryValueCallBacks);
     ::CFRelease(featuresArray);
 
-    sDefaultFeaturesDescriptor =
+    CTFontDescriptorRef descriptor =
         ::CTFontDescriptorCreateWithAttributes(attributesDict);
     ::CFRelease(attributesDict);
+
+    return descriptor;
 }
 
-// Create a CTFontRef, with the Common Ligatures feature disabled
-CTFontRef
-gfxCoreTextShaper::CreateCTFontWithDisabledLigatures(CGFloat aSize)
+CTFontDescriptorRef
+gfxCoreTextShaper::GetDefaultFeaturesDescriptor()
+{
+    if (sDefaultFeaturesDescriptor == nullptr) {
+        const std::pair<SInt16,SInt16> kDefaultFeatures[] = {
+            { kSmartSwashType, kLineInitialSwashesOffSelector },
+            { kSmartSwashType, kLineFinalSwashesOffSelector }
+        };
+        sDefaultFeaturesDescriptor =
+            CreateFontFeaturesDescriptor(kDefaultFeatures,
+                                         ArrayLength(kDefaultFeatures));
+    }
+    return sDefaultFeaturesDescriptor;
+}
+
+CTFontDescriptorRef
+gfxCoreTextShaper::GetDisableLigaturesDescriptor()
 {
     if (sDisableLigaturesDescriptor == nullptr) {
-        // initialize cached descriptor to turn off the Common Ligatures feature
-        SInt16 val = kLigaturesType;
-        CFNumberRef ligaturesType =
-            ::CFNumberCreate(kCFAllocatorDefault,
-                             kCFNumberSInt16Type,
-                             &val);
-        val = kCommonLigaturesOffSelector;
-        CFNumberRef commonLigaturesOffSelector =
-            ::CFNumberCreate(kCFAllocatorDefault,
-                             kCFNumberSInt16Type,
-                             &val);
+        const std::pair<SInt16,SInt16> kDisableLigatures[] = {
+            { kSmartSwashType, kLineInitialSwashesOffSelector },
+            { kSmartSwashType, kLineFinalSwashesOffSelector },
+            { kLigaturesType, kCommonLigaturesOffSelector }
+        };
+        sDisableLigaturesDescriptor =
+            CreateFontFeaturesDescriptor(kDisableLigatures,
+                                         ArrayLength(kDisableLigatures));
+    }
+    return sDisableLigaturesDescriptor;
+}
 
-        const CFTypeRef keys[]   = { kCTFontFeatureTypeIdentifierKey,
-                                     kCTFontFeatureSelectorIdentifierKey };
-        const CFTypeRef values[] = { ligaturesType,
-                                     commonLigaturesOffSelector };
-        CFDictionaryRef featureSettingDict =
-            ::CFDictionaryCreate(kCFAllocatorDefault,
-                                 (const void **) keys,
-                                 (const void **) values,
-                                 ArrayLength(keys),
-                                 &kCFTypeDictionaryKeyCallBacks,
-                                 &kCFTypeDictionaryValueCallBacks);
-        ::CFRelease(ligaturesType);
-        ::CFRelease(commonLigaturesOffSelector);
+CTFontDescriptorRef
+gfxCoreTextShaper::GetIndicFeaturesDescriptor()
+{
+    if (sIndicFeaturesDescriptor == nullptr) {
+        const std::pair<SInt16,SInt16> kIndicFeatures[] = {
+            { kSmartSwashType, kLineFinalSwashesOffSelector }
+        };
+        sIndicFeaturesDescriptor =
+            CreateFontFeaturesDescriptor(kIndicFeatures,
+                                         ArrayLength(kIndicFeatures));
+    }
+    return sIndicFeaturesDescriptor;
+}
 
-        CFArrayRef featuresArray =
-            ::CFArrayCreate(kCFAllocatorDefault,
-                            (const void **) &featureSettingDict,
-                            1,
-                            &kCFTypeArrayCallBacks);
-        ::CFRelease(featureSettingDict);
+CTFontDescriptorRef
+gfxCoreTextShaper::GetIndicDisableLigaturesDescriptor()
+{
+    if (sIndicDisableLigaturesDescriptor == nullptr) {
+        const std::pair<SInt16,SInt16> kIndicDisableLigatures[] = {
+            { kSmartSwashType, kLineFinalSwashesOffSelector },
+            { kLigaturesType, kCommonLigaturesOffSelector }
+        };
+        sIndicDisableLigaturesDescriptor =
+            CreateFontFeaturesDescriptor(kIndicDisableLigatures,
+                                         ArrayLength(kIndicDisableLigatures));
+    }
+    return sIndicDisableLigaturesDescriptor;
+}
 
-        CFDictionaryRef attributesDict =
-            ::CFDictionaryCreate(kCFAllocatorDefault,
-                                 (const void **) &kCTFontFeatureSettingsAttribute,
-                                 (const void **) &featuresArray,
-                                 1, // count of keys & values
-                                 &kCFTypeDictionaryKeyCallBacks,
-                                 &kCFTypeDictionaryValueCallBacks);
-        ::CFRelease(featuresArray);
-
-        sDisableLigaturesDescriptor =
-            ::CTFontDescriptorCreateCopyWithAttributes(GetDefaultFeaturesDescriptor(),
-                                                       attributesDict);
-        ::CFRelease(attributesDict);
-    }
-
+CTFontRef
+gfxCoreTextShaper::CreateCTFontWithFeatures(CGFloat aSize,
+                                            CTFontDescriptorRef aDescriptor)
+{
     gfxMacFont *f = static_cast<gfxMacFont*>(mFont);
     return ::CTFontCreateWithGraphicsFont(f->GetCGFontRef(), aSize, nullptr,
-                                          sDisableLigaturesDescriptor);
+                                          aDescriptor);
 }
 
 void
 gfxCoreTextShaper::Shutdown() // [static]
 {
+    if (sIndicDisableLigaturesDescriptor != nullptr) {
+        ::CFRelease(sIndicDisableLigaturesDescriptor);
+        sIndicDisableLigaturesDescriptor = nullptr;
+    }
+    if (sIndicFeaturesDescriptor != nullptr) {
+        ::CFRelease(sIndicFeaturesDescriptor);
+        sIndicFeaturesDescriptor = nullptr;
+    }
     if (sDisableLigaturesDescriptor != nullptr) {
         ::CFRelease(sDisableLigaturesDescriptor);
         sDisableLigaturesDescriptor = nullptr;
-    }        
+    }
     if (sDefaultFeaturesDescriptor != nullptr) {
         ::CFRelease(sDefaultFeaturesDescriptor);
         sDefaultFeaturesDescriptor = nullptr;
     }
 }
--- a/gfx/thebes/gfxCoreTextShaper.h
+++ b/gfx/thebes/gfxCoreTextShaper.h
@@ -34,27 +34,32 @@ protected:
     CFDictionaryRef mAttributesDict;
 
     nsresult SetGlyphsFromRun(gfxShapedText *aShapedText,
                               uint32_t       aOffset,
                               uint32_t       aLength,
                               CTRunRef       aCTRun,
                               int32_t        aStringOffset);
 
-    CTFontRef CreateCTFontWithDisabledLigatures(CGFloat aSize);
-
-    static void CreateDefaultFeaturesDescriptor();
+    CTFontRef CreateCTFontWithFeatures(CGFloat aSize,
+                                       CTFontDescriptorRef aDescriptor);
 
-    static CTFontDescriptorRef GetDefaultFeaturesDescriptor() {
-        if (sDefaultFeaturesDescriptor == nullptr) {
-            CreateDefaultFeaturesDescriptor();
-        }
-        return sDefaultFeaturesDescriptor;
-    }
+    static CTFontDescriptorRef
+    CreateFontFeaturesDescriptor(const std::pair<SInt16,SInt16> aFeatures[],
+                                 size_t aCount);
+
+    static CTFontDescriptorRef GetDefaultFeaturesDescriptor();
+    static CTFontDescriptorRef GetDisableLigaturesDescriptor();
+    static CTFontDescriptorRef GetIndicFeaturesDescriptor();
+    static CTFontDescriptorRef GetIndicDisableLigaturesDescriptor();
 
     // cached font descriptor, created the first time it's needed
     static CTFontDescriptorRef    sDefaultFeaturesDescriptor;
 
     // cached descriptor for adding disable-ligatures setting to a font
     static CTFontDescriptorRef    sDisableLigaturesDescriptor;
+
+    // feature descriptors for buggy Indic AAT font workaround
+    static CTFontDescriptorRef    sIndicFeaturesDescriptor;
+    static CTFontDescriptorRef    sIndicDisableLigaturesDescriptor;
 };
 
 #endif /* GFX_CORETEXTSHAPER_H */