Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
      3  * Copyright (C) 2006 Alexey Proskuryakov
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *
      9  * 1.  Redistributions of source code must retain the above copyright
     10  *     notice, this list of conditions and the following disclaimer.
     11  * 2.  Redistributions in binary form must reproduce the above copyright
     12  *     notice, this list of conditions and the following disclaimer in the
     13  *     documentation and/or other materials provided with the distribution.
     14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     15  *     its contributors may be used to endorse or promote products derived
     16  *     from this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 #import "config.h"
     31 #import "SimpleFontData.h"
     32 
     33 #import "BlockExceptions.h"
     34 #import "Color.h"
     35 #import "FloatRect.h"
     36 #import "Font.h"
     37 #import "FontCache.h"
     38 #import "FontDescription.h"
     39 #import "SharedBuffer.h"
     40 #import "WebCoreSystemInterface.h"
     41 #import <AppKit/AppKit.h>
     42 #import <ApplicationServices/ApplicationServices.h>
     43 #import <float.h>
     44 #import <unicode/uchar.h>
     45 #import <wtf/Assertions.h>
     46 #import <wtf/StdLibExtras.h>
     47 #import <wtf/RetainPtr.h>
     48 
     49 @interface NSFont (WebAppKitSecretAPI)
     50 - (BOOL)_isFakeFixedPitch;
     51 @end
     52 
     53 using namespace std;
     54 
     55 namespace WebCore {
     56 
     57 const float smallCapsFontSizeMultiplier = 0.7f;
     58 static inline float scaleEmToUnits(float x, unsigned unitsPerEm) { return x / unitsPerEm; }
     59 
     60 static bool initFontData(SimpleFontData* fontData)
     61 {
     62     if (!fontData->platformData().cgFont())
     63         return false;
     64 
     65 #ifdef BUILDING_ON_TIGER
     66     ATSUStyle fontStyle;
     67     if (ATSUCreateStyle(&fontStyle) != noErr)
     68         return false;
     69 
     70     ATSUFontID fontId = fontData->platformData().m_atsuFontID;
     71     if (!fontId) {
     72         ATSUDisposeStyle(fontStyle);
     73         return false;
     74     }
     75 
     76     ATSUAttributeTag tag = kATSUFontTag;
     77     ByteCount size = sizeof(ATSUFontID);
     78     ATSUFontID *valueArray[1] = {&fontId};
     79     OSStatus status = ATSUSetAttributes(fontStyle, 1, &tag, &size, (void* const*)valueArray);
     80     if (status != noErr) {
     81         ATSUDisposeStyle(fontStyle);
     82         return false;
     83     }
     84 
     85     if (wkGetATSStyleGroup(fontStyle, &fontData->m_styleGroup) != noErr) {
     86         ATSUDisposeStyle(fontStyle);
     87         return false;
     88     }
     89 
     90     ATSUDisposeStyle(fontStyle);
     91 #endif
     92 
     93     return true;
     94 }
     95 
     96 static NSString *webFallbackFontFamily(void)
     97 {
     98     DEFINE_STATIC_LOCAL(RetainPtr<NSString>, webFallbackFontFamily, ([[NSFont systemFontOfSize:16.0f] familyName]));
     99     return webFallbackFontFamily.get();
    100 }
    101 
    102 #if !ERROR_DISABLED
    103 #ifdef __LP64__
    104 static NSString* pathFromFont(NSFont*)
    105 {
    106     // FMGetATSFontRefFromFont is not available in 64-bit. As pathFromFont is only used for debugging
    107     // purposes, returning nil is acceptable.
    108     return nil;
    109 }
    110 #else
    111 static NSString* pathFromFont(NSFont *font)
    112 {
    113 #ifndef BUILDING_ON_TIGER
    114     ATSFontRef atsFont = FMGetATSFontRefFromFont(CTFontGetPlatformFont(toCTFontRef(font), 0));
    115 #else
    116     ATSFontRef atsFont = FMGetATSFontRefFromFont(wkGetNSFontATSUFontId(font));
    117 #endif
    118     FSRef fileRef;
    119 
    120 #ifndef BUILDING_ON_TIGER
    121     OSStatus status = ATSFontGetFileReference(atsFont, &fileRef);
    122     if (status != noErr)
    123         return nil;
    124 #else
    125     FSSpec oFile;
    126     OSStatus status = ATSFontGetFileSpecification(atsFont, &oFile);
    127     if (status != noErr)
    128         return nil;
    129 
    130     status = FSpMakeFSRef(&oFile, &fileRef);
    131     if (status != noErr)
    132         return nil;
    133 #endif
    134 
    135     UInt8 filePathBuffer[PATH_MAX];
    136     status = FSRefMakePath(&fileRef, filePathBuffer, PATH_MAX);
    137     if (status == noErr)
    138         return [NSString stringWithUTF8String:(const char*)filePathBuffer];
    139 
    140     return nil;
    141 }
    142 #endif // __LP64__
    143 #endif // !ERROR_DISABLED
    144 
    145 void SimpleFontData::platformInit()
    146 {
    147 #ifdef BUILDING_ON_TIGER
    148     m_styleGroup = 0;
    149 #endif
    150 #if USE(ATSUI)
    151     m_ATSUMirrors = false;
    152     m_checkedShapesArabic = false;
    153     m_shapesArabic = false;
    154 #endif
    155 
    156     m_syntheticBoldOffset = m_platformData.m_syntheticBold ? 1.0f : 0.f;
    157 
    158     bool failedSetup = false;
    159     if (!initFontData(this)) {
    160         // Ack! Something very bad happened, like a corrupt font.
    161         // Try looking for an alternate 'base' font for this renderer.
    162 
    163         // Special case hack to use "Times New Roman" in place of "Times".
    164         // "Times RO" is a common font whose family name is "Times".
    165         // It overrides the normal "Times" family font.
    166         // It also appears to have a corrupt regular variant.
    167         NSString *fallbackFontFamily;
    168         if ([[m_platformData.font() familyName] isEqual:@"Times"])
    169             fallbackFontFamily = @"Times New Roman";
    170         else
    171             fallbackFontFamily = webFallbackFontFamily();
    172 
    173         // Try setting up the alternate font.
    174         // This is a last ditch effort to use a substitute font when something has gone wrong.
    175 #if !ERROR_DISABLED
    176         RetainPtr<NSFont> initialFont = m_platformData.font();
    177 #endif
    178         if (m_platformData.font())
    179             m_platformData.setFont([[NSFontManager sharedFontManager] convertFont:m_platformData.font() toFamily:fallbackFontFamily]);
    180         else
    181             m_platformData.setFont([NSFont fontWithName:fallbackFontFamily size:m_platformData.size()]);
    182 #if !ERROR_DISABLED
    183         NSString *filePath = pathFromFont(initialFont.get());
    184         if (!filePath)
    185             filePath = @"not known";
    186 #endif
    187         if (!initFontData(this)) {
    188             if ([fallbackFontFamily isEqual:@"Times New Roman"]) {
    189                 // OK, couldn't setup Times New Roman as an alternate to Times, fallback
    190                 // on the system font.  If this fails we have no alternative left.
    191                 m_platformData.setFont([[NSFontManager sharedFontManager] convertFont:m_platformData.font() toFamily:webFallbackFontFamily()]);
    192                 if (!initFontData(this)) {
    193                     // We tried, Times, Times New Roman, and the system font. No joy. We have to give up.
    194                     LOG_ERROR("unable to initialize with font %@ at %@", initialFont.get(), filePath);
    195                     failedSetup = true;
    196                 }
    197             } else {
    198                 // We tried the requested font and the system font. No joy. We have to give up.
    199                 LOG_ERROR("unable to initialize with font %@ at %@", initialFont.get(), filePath);
    200                 failedSetup = true;
    201             }
    202         }
    203 
    204         // Report the problem.
    205         LOG_ERROR("Corrupt font detected, using %@ in place of %@ located at \"%@\".",
    206             [m_platformData.font() familyName], [initialFont.get() familyName], filePath);
    207     }
    208 
    209     // If all else fails, try to set up using the system font.
    210     // This is probably because Times and Times New Roman are both unavailable.
    211     if (failedSetup) {
    212         m_platformData.setFont([NSFont systemFontOfSize:[m_platformData.font() pointSize]]);
    213         LOG_ERROR("failed to set up font, using system font %s", m_platformData.font());
    214         initFontData(this);
    215     }
    216 
    217     int iAscent;
    218     int iDescent;
    219     int iLineGap;
    220 #ifdef BUILDING_ON_TIGER
    221     wkGetFontMetrics(m_platformData.cgFont(), &iAscent, &iDescent, &iLineGap, &m_unitsPerEm);
    222 #else
    223     iAscent = CGFontGetAscent(m_platformData.cgFont());
    224     iDescent = CGFontGetDescent(m_platformData.cgFont());
    225     iLineGap = CGFontGetLeading(m_platformData.cgFont());
    226     m_unitsPerEm = CGFontGetUnitsPerEm(m_platformData.cgFont());
    227 #endif
    228 
    229     float pointSize = m_platformData.m_size;
    230     float fAscent = scaleEmToUnits(iAscent, m_unitsPerEm) * pointSize;
    231     float fDescent = -scaleEmToUnits(iDescent, m_unitsPerEm) * pointSize;
    232     float fLineGap = scaleEmToUnits(iLineGap, m_unitsPerEm) * pointSize;
    233 
    234     // We need to adjust Times, Helvetica, and Courier to closely match the
    235     // vertical metrics of their Microsoft counterparts that are the de facto
    236     // web standard. The AppKit adjustment of 20% is too big and is
    237     // incorrectly added to line spacing, so we use a 15% adjustment instead
    238     // and add it to the ascent.
    239     NSString *familyName = [m_platformData.font() familyName];
    240     if ([familyName isEqualToString:@"Times"] || [familyName isEqualToString:@"Helvetica"] || [familyName isEqualToString:@"Courier"])
    241         fAscent += floorf(((fAscent + fDescent) * 0.15f) + 0.5f);
    242     else if ([familyName isEqualToString:@"Geeza Pro"]) {
    243         // Geeza Pro has glyphs that draw slightly above the ascent or far below the descent. Adjust
    244         // those vertical metrics to better match reality, so that diacritics at the bottom of one line
    245         // do not overlap diacritics at the top of the next line.
    246         fAscent *= 1.08f;
    247         fDescent *= 2.f;
    248     }
    249 
    250     m_ascent = lroundf(fAscent);
    251     m_descent = lroundf(fDescent);
    252     m_lineGap = lroundf(fLineGap);
    253     m_lineSpacing = m_ascent + m_descent + m_lineGap;
    254 
    255     // Hack Hiragino line metrics to allow room for marked text underlines.
    256     // <rdar://problem/5386183>
    257     if (m_descent < 3 && m_lineGap >= 3 && [familyName hasPrefix:@"Hiragino"]) {
    258         m_lineGap -= 3 - m_descent;
    259         m_descent = 3;
    260     }
    261 
    262     // Measure the actual character "x", because AppKit synthesizes X height rather than getting it from the font.
    263     // Unfortunately, NSFont will round this for us so we don't quite get the right value.
    264     GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page();
    265     NSGlyph xGlyph = glyphPageZero ? glyphPageZero->glyphDataForCharacter('x').glyph : 0;
    266     if (xGlyph) {
    267         NSRect xBox = [m_platformData.font() boundingRectForGlyph:xGlyph];
    268         // Use the maximum of either width or height because "x" is nearly square
    269         // and web pages that foolishly use this metric for width will be laid out
    270         // poorly if we return an accurate height. Classic case is Times 13 point,
    271         // which has an "x" that is 7x6 pixels.
    272         m_xHeight = max(NSMaxX(xBox), NSMaxY(xBox));
    273     } else
    274         m_xHeight = [m_platformData.font() xHeight];
    275 }
    276 
    277 void SimpleFontData::platformCharWidthInit()
    278 {
    279     m_avgCharWidth = 0.f;
    280 
    281     // Calculate avgCharWidth according to http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6OS2.html
    282     // We can try grabbing it out of the OS/2 table or via ATSFontGetHorizontalMetrics, but
    283     // ATSFontGetHorizontalMetrics never seems to return a non-zero value and the OS/2 table
    284     // contains zero for a large number of fonts.
    285     GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page();
    286     if (glyphPageZero) {
    287         static int weights[] = { 64, 14, 27, 35, 100, 20, 14, 42, 63, 3, 6, 35, 20, 56, 56, 17, 4, 49, 56, 71, 31, 10, 18, 3, 18, 2, 166 };
    288         int numGlyphs = 27;
    289         ASSERT(numGlyphs == sizeof(weights) / sizeof(int));
    290         // Compute the weighted sum of the space character and the lowercase letters in the Latin alphabet.
    291         float sum = 0.f;
    292         int totalWeight = 0;
    293         for (int i = 0; i < numGlyphs; i++) {
    294             Glyph glyph = glyphPageZero->glyphDataForCharacter((i < 26 ? i + 'a' : ' ')).glyph;
    295             if (glyph) {
    296                 totalWeight += weights[i];
    297                 sum += widthForGlyph(glyph) * weights[i];
    298             }
    299         }
    300         if (sum > 0.f && totalWeight > 0)
    301             m_avgCharWidth = sum / totalWeight;
    302     }
    303 
    304     m_maxCharWidth = 0.f;
    305     if (m_platformData.font())
    306         m_maxCharWidth = [m_platformData.font() maximumAdvancement].width;
    307 
    308     // Fallback to a cross-platform estimate, which will populate these values if they are non-positive.
    309     initCharWidths();
    310 }
    311 
    312 void SimpleFontData::platformDestroy()
    313 {
    314 #ifdef BUILDING_ON_TIGER
    315     if (m_styleGroup)
    316         wkReleaseStyleGroup(m_styleGroup);
    317 #endif
    318 #if USE(ATSUI)
    319     HashMap<unsigned, ATSUStyle>::iterator end = m_ATSUStyleMap.end();
    320     for (HashMap<unsigned, ATSUStyle>::iterator it = m_ATSUStyleMap.begin(); it != end; ++it)
    321         ATSUDisposeStyle(it->second);
    322 #endif
    323 }
    324 
    325 SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const
    326 {
    327     if (!m_smallCapsFontData) {
    328         if (isCustomFont()) {
    329             FontPlatformData smallCapsFontData(m_platformData);
    330             smallCapsFontData.m_size = smallCapsFontData.m_size * smallCapsFontSizeMultiplier;
    331             m_smallCapsFontData = new SimpleFontData(smallCapsFontData, true, false);
    332         } else {
    333             BEGIN_BLOCK_OBJC_EXCEPTIONS;
    334             float size = [m_platformData.font() pointSize] * smallCapsFontSizeMultiplier;
    335             FontPlatformData smallCapsFont([[NSFontManager sharedFontManager] convertFont:m_platformData.font() toSize:size]);
    336 
    337             // AppKit resets the type information (screen/printer) when you convert a font to a different size.
    338             // We have to fix up the font that we're handed back.
    339             smallCapsFont.setFont(fontDescription.usePrinterFont() ? [smallCapsFont.font() printerFont] : [smallCapsFont.font() screenFont]);
    340 
    341             if (smallCapsFont.font()) {
    342                 NSFontManager *fontManager = [NSFontManager sharedFontManager];
    343                 NSFontTraitMask fontTraits = [fontManager traitsOfFont:m_platformData.font()];
    344 
    345                 if (m_platformData.m_syntheticBold)
    346                     fontTraits |= NSBoldFontMask;
    347                 if (m_platformData.m_syntheticOblique)
    348                     fontTraits |= NSItalicFontMask;
    349 
    350                 NSFontTraitMask smallCapsFontTraits = [fontManager traitsOfFont:smallCapsFont.font()];
    351                 smallCapsFont.m_syntheticBold = (fontTraits & NSBoldFontMask) && !(smallCapsFontTraits & NSBoldFontMask);
    352                 smallCapsFont.m_syntheticOblique = (fontTraits & NSItalicFontMask) && !(smallCapsFontTraits & NSItalicFontMask);
    353 
    354                 m_smallCapsFontData = fontCache()->getCachedFontData(&smallCapsFont);
    355             }
    356             END_BLOCK_OBJC_EXCEPTIONS;
    357         }
    358     }
    359     return m_smallCapsFontData;
    360 }
    361 
    362 bool SimpleFontData::containsCharacters(const UChar* characters, int length) const
    363 {
    364     NSString *string = [[NSString alloc] initWithCharactersNoCopy:const_cast<unichar*>(characters) length:length freeWhenDone:NO];
    365     NSCharacterSet *set = [[m_platformData.font() coveredCharacterSet] invertedSet];
    366     bool result = set && [string rangeOfCharacterFromSet:set].location == NSNotFound;
    367     [string release];
    368     return result;
    369 }
    370 
    371 void SimpleFontData::determinePitch()
    372 {
    373     NSFont* f = m_platformData.font();
    374     // Special case Osaka-Mono.
    375     // According to <rdar://problem/3999467>, we should treat Osaka-Mono as fixed pitch.
    376     // Note that the AppKit does not report Osaka-Mono as fixed pitch.
    377 
    378     // Special case MS-PGothic.
    379     // According to <rdar://problem/4032938>, we should not treat MS-PGothic as fixed pitch.
    380     // Note that AppKit does report MS-PGothic as fixed pitch.
    381 
    382     // Special case MonotypeCorsiva
    383     // According to <rdar://problem/5454704>, we should not treat MonotypeCorsiva as fixed pitch.
    384     // Note that AppKit does report MonotypeCorsiva as fixed pitch.
    385 
    386     NSString *name = [f fontName];
    387     m_treatAsFixedPitch = ([f isFixedPitch] || [f _isFakeFixedPitch] ||
    388            [name caseInsensitiveCompare:@"Osaka-Mono"] == NSOrderedSame) &&
    389            [name caseInsensitiveCompare:@"MS-PGothic"] != NSOrderedSame &&
    390            [name caseInsensitiveCompare:@"MonotypeCorsiva"] != NSOrderedSame;
    391 }
    392 
    393 float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
    394 {
    395     NSFont* font = m_platformData.font();
    396     float pointSize = m_platformData.m_size;
    397     CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSize);
    398     CGSize advance;
    399     if (!wkGetGlyphTransformedAdvances(m_platformData.cgFont(), font, &m, &glyph, &advance)) {
    400         LOG_ERROR("Unable to cache glyph widths for %@ %f", [font displayName], pointSize);
    401         advance.width = 0;
    402     }
    403     return advance.width + m_syntheticBoldOffset;
    404 }
    405 
    406 #if USE(ATSUI)
    407 void SimpleFontData::checkShapesArabic() const
    408 {
    409     ASSERT(!m_checkedShapesArabic);
    410 
    411     m_checkedShapesArabic = true;
    412 
    413     ATSUFontID fontID = m_platformData.m_atsuFontID;
    414     if (!fontID) {
    415         LOG_ERROR("unable to get ATSUFontID for %@", m_platformData.font());
    416         return;
    417     }
    418 
    419     // This function is called only on fonts that contain Arabic glyphs. Our
    420     // heuristic is that if such a font has a glyph metamorphosis table, then
    421     // it includes shaping information for Arabic.
    422     FourCharCode tables[] = { 'morx', 'mort' };
    423     for (unsigned i = 0; i < sizeof(tables) / sizeof(tables[0]); ++i) {
    424         ByteCount tableSize;
    425         OSStatus status = ATSFontGetTable(fontID, tables[i], 0, 0, 0, &tableSize);
    426         if (status == noErr) {
    427             m_shapesArabic = true;
    428             return;
    429         }
    430 
    431         if (status != kATSInvalidFontTableAccess)
    432             LOG_ERROR("ATSFontGetTable failed (%d)", status);
    433     }
    434 }
    435 #endif
    436 
    437 #if USE(CORE_TEXT)
    438 CTFontRef SimpleFontData::getCTFont() const
    439 {
    440     if (getNSFont())
    441         return toCTFontRef(getNSFont());
    442     if (!m_CTFont)
    443         m_CTFont.adoptCF(CTFontCreateWithGraphicsFont(m_platformData.cgFont(), m_platformData.size(), NULL, NULL));
    444     return m_CTFont.get();
    445 }
    446 
    447 CFDictionaryRef SimpleFontData::getCFStringAttributes(TypesettingFeatures typesettingFeatures) const
    448 {
    449     unsigned key = typesettingFeatures + 1;
    450     pair<HashMap<unsigned, RetainPtr<CFDictionaryRef> >::iterator, bool> addResult = m_CFStringAttributes.add(key, RetainPtr<CFDictionaryRef>());
    451     RetainPtr<CFDictionaryRef>& attributesDictionary = addResult.first->second;
    452     if (!addResult.second)
    453         return attributesDictionary.get();
    454 
    455     bool allowLigatures = platformData().allowsLigatures() || (typesettingFeatures & Ligatures);
    456 
    457     static const int ligaturesNotAllowedValue = 0;
    458     static CFNumberRef ligaturesNotAllowed = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ligaturesNotAllowedValue);
    459 
    460     static const int ligaturesAllowedValue = 1;
    461     static CFNumberRef ligaturesAllowed = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ligaturesAllowedValue);
    462 
    463     if (!(typesettingFeatures & Kerning)) {
    464         static const float kerningAdjustmentValue = 0;
    465         static CFNumberRef kerningAdjustment = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &kerningAdjustmentValue);
    466         static const void* keysWithKerningDisabled[] = { kCTFontAttributeName, kCTKernAttributeName, kCTLigatureAttributeName };
    467         const void* valuesWithKerningDisabled[] = { getCTFont(), kerningAdjustment, allowLigatures
    468             ? ligaturesAllowed : ligaturesNotAllowed };
    469         attributesDictionary.adoptCF(CFDictionaryCreate(NULL, keysWithKerningDisabled, valuesWithKerningDisabled,
    470             sizeof(keysWithKerningDisabled) / sizeof(*keysWithKerningDisabled),
    471             &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
    472     } else {
    473         // By omitting the kCTKernAttributeName attribute, we get Core Text's standard kerning.
    474         static const void* keysWithKerningEnabled[] = { kCTFontAttributeName, kCTLigatureAttributeName };
    475         const void* valuesWithKerningEnabled[] = { getCTFont(), allowLigatures ? ligaturesAllowed : ligaturesNotAllowed };
    476         attributesDictionary.adoptCF(CFDictionaryCreate(NULL, keysWithKerningEnabled, valuesWithKerningEnabled,
    477             sizeof(keysWithKerningEnabled) / sizeof(*keysWithKerningEnabled),
    478             &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
    479     }
    480 
    481     return attributesDictionary.get();
    482 }
    483 
    484 #endif
    485 
    486 } // namespace WebCore
    487