Split FontData.mm into platform-specific and cross-platform pieces.
authorhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 18 May 2006 06:09:30 +0000 (06:09 +0000)
committerhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 18 May 2006 06:09:30 +0000 (06:09 +0000)
        Reviewed by andersca

        * WebCore.xcodeproj/project.pbxproj:
        * platform/FontData.h:
        (WebCore::FontData::xHeight):
        * platform/mac/FontData.mm:
        (WebCore::FontData::widthForGlyph):
        (WebCore::m_smallCapsFontData):
        (WebCore::FontData::~FontData):
        (WebCore::extendWidthMap):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@14460 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/platform/FontData.h
WebCore/platform/mac/FontData.mm
WebCore/platform/mac/FontDataMac.mm [new file with mode: 0644]

index 1cf2c2d46f97cc4a637704348d3ba37b494a87a8..32ba039e4c546dc79d60eba9b4813103da850480 100644 (file)
@@ -1,3 +1,18 @@
+2006-05-17  David Hyatt  <hyatt@apple.com>
+
+        Split FontData.mm into platform-specific and cross-platform pieces.
+
+        Reviewed by andersca
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * platform/FontData.h:
+        (WebCore::FontData::xHeight):
+        * platform/mac/FontData.mm:
+        (WebCore::FontData::widthForGlyph):
+        (WebCore::m_smallCapsFontData):
+        (WebCore::FontData::~FontData):
+        (WebCore::extendWidthMap):
+
 2006-05-17  John Sullivan  <sullivan@apple.com>
 
         Reviewed by Darin Adler
index e09305e0d0d160b0d699c0f7df443830ecd7cfd3..649aca5ef1bd60a236da968c309a7cd87e173f98 100644 (file)
                BCB16C2D0979C3BD00467741 /* loader.h in Headers */ = {isa = PBXBuildFile; fileRef = BCB16C140979C3BD00467741 /* loader.h */; };
                BCB16C2E0979C3BD00467741 /* Request.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BCB16C150979C3BD00467741 /* Request.cpp */; };
                BCB16C2F0979C3BD00467741 /* Request.h in Headers */ = {isa = PBXBuildFile; fileRef = BCB16C160979C3BD00467741 /* Request.h */; };
+               BCC088860A1BD78D006189A6 /* FontDataMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = BCC088850A1BD78D006189A6 /* FontDataMac.mm */; };
                BCC47E2609A3D6F100ADB771 /* FontFamily.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BCC47E2409A3D6F100ADB771 /* FontFamily.cpp */; };
                BCC47E2709A3D6F100ADB771 /* FontFamily.h in Headers */ = {isa = PBXBuildFile; fileRef = BCC47E2509A3D6F100ADB771 /* FontFamily.h */; };
                BCC47E6B09A3FE4700ADB771 /* FontDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = BCC47E6A09A3FE4700ADB771 /* FontDescription.h */; };
                BCB16C140979C3BD00467741 /* loader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = loader.h; sourceTree = "<group>"; };
                BCB16C150979C3BD00467741 /* Request.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = Request.cpp; sourceTree = "<group>"; };
                BCB16C160979C3BD00467741 /* Request.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Request.h; sourceTree = "<group>"; };
+               BCC088850A1BD78D006189A6 /* FontDataMac.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = FontDataMac.mm; sourceTree = "<group>"; };
                BCC47E2409A3D6F100ADB771 /* FontFamily.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = FontFamily.cpp; sourceTree = "<group>"; };
                BCC47E2509A3D6F100ADB771 /* FontFamily.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = FontFamily.h; sourceTree = "<group>"; };
                BCC47E6A09A3FE4700ADB771 /* FontDescription.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = FontDescription.h; sourceTree = "<group>"; };
                6582A14809999D6C00BEEB6D /* mac */ = {
                        isa = PBXGroup;
                        children = (
-                               BC6DB4D30A1AFEEF00E5CD14 /* GlyphMapMac.cpp */,
                                65A640F00533BB1F0085E777 /* BlockExceptions.h */,
                                65F80697054D9F86008BF776 /* BlockExceptions.mm */,
                                2D90660B0665D937006B6F1A /* ClipboardMac.h */,
                                6582A14B09999D6C00BEEB6D /* FloatRectMac.mm */,
                                6582A14C09999D6C00BEEB6D /* FloatSizeMac.mm */,
                                BCF7C2330A16B5F80032F75B /* FontCacheMac.mm */,
+                               BCC088850A1BD78D006189A6 /* FontDataMac.mm */,
                                BCEB377209B7BA3900CB38B1 /* FontMac.mm */,
                                BCF7C0980A1683580032F75B /* FontFallbackListMac.mm */,
                                935C476C09AC4D6300A6AAB4 /* FoundationExtras.h */,
+                               BC6DB4D30A1AFEEF00E5CD14 /* GlyphMapMac.cpp */,
                                935367E809AF77EF00D35CD6 /* GraphicsContextMac.mm */,
                                6582A14D09999D6C00BEEB6D /* ImageMac.mm */,
                                6582A14E09999D6D00BEEB6D /* ImageSourceMac.cpp */,
                                BC6DB4740A1A90FB00E5CD14 /* GlyphMap.cpp in Sources */,
                                BC6DB4D40A1AFEEF00E5CD14 /* GlyphMapMac.cpp in Sources */,
                                1A9EF4570A1B957D00332B63 /* JSCanvasRenderingContext2DCustom.cpp in Sources */,
+                               BCC088860A1BD78D006189A6 /* FontDataMac.mm in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index 0b35575b5d611a9a25a5024fdfc4ac59c4743fb4..23b070747b4501924791d4e1901bbdd82b635397 100644 (file)
@@ -58,10 +58,11 @@ public:
     int descent() const { return m_descent; }
     int lineSpacing() const { return m_lineSpacing; }
     int lineGap() const { return m_lineGap; }
-
-    float xHeight() const;
+    float xHeight() const { return m_xHeight; }
 
     float widthForGlyph(Glyph glyph) const;
+    float platformWidthForGlyph(Glyph glyph) const;
+
     bool containsCharacters(const UChar* characters, int length) const;
 
     void determinePitch();
@@ -76,14 +77,16 @@ public:
 #endif
 
 private:
-    bool platformInit();
+    void platformInit();
+    void platformDestroy();
 
 public:
     int m_ascent;
     int m_descent;
     int m_lineSpacing;
     int m_lineGap;
-    
+    float m_xHeight;
+
     void* m_styleGroup;
     
     FontPlatformData m_font;
index 22f30ef0d9732fa70e3906a5dbafe89bfc30998d..6cc25e9dadb8b16017a44902e2b35d89c5690385 100644 (file)
 
 #import "config.h"
 #import "Font.h"
-#import "FontData.h"
-#import "Color.h"
-
-#import <wtf/Assertions.h>
-
-#import <ApplicationServices/ApplicationServices.h>
-#import <Cocoa/Cocoa.h>
-
-#import "FontCache.h"
-
 #import "WebCoreSystemInterface.h"
 
-#import "FloatRect.h"
-#import "FontDescription.h"
-
-#import <float.h>
-
-#import <unicode/uchar.h>
-#import <unicode/unorm.h>
-
-// FIXME: Just temporary for the #defines of constants that we will eventually stop using.
-#import "GlyphBuffer.h"
-
-@interface NSFont (WebAppKitSecretAPI)
-- (BOOL)_isFakeFixedPitch;
-@end
-
 namespace WebCore
 {
 
-#define SMALLCAPS_FONTSIZE_MULTIPLIER 0.7f
-#define SPACE 0x0020
-#define CONTEXT_DPI (72.0)
-#define SCALE_EM_TO_UNITS(X, U_PER_EM) (X * ((1.0 * CONTEXT_DPI) / (CONTEXT_DPI * U_PER_EM)))
-
 typedef float WebGlyphWidth;
 
 struct WidthMap {
@@ -79,10 +49,6 @@ static WidthMap *extendWidthMap(const FontData *, ATSGlyphRef);
 
 static void freeWidthMap(WidthMap *);
 
-#if !ERROR_DISABLED
-static NSString *pathFromFont(NSFont *font);
-#endif
-
 // Map utility functions
 
 float FontData::widthForGlyph(Glyph glyph) const
@@ -97,263 +63,38 @@ float FontData::widthForGlyph(Glyph glyph) const
     float width = map->widths[glyph - map->startRange];
     if (width >= 0)
         return width;
-    NSFont *font = m_font.font;
-    float pointSize = [font pointSize];
-    CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSize);
-    CGSize advance;
-    if (!wkGetGlyphTransformedAdvances(font, &m, &glyph, &advance)) {
-        LOG_ERROR("Unable to cache glyph widths for %@ %f", [font displayName], pointSize);
-        advance.width = 0;
-    }
-    width = advance.width + m_syntheticBoldOffset;
+        
+    width = platformWidthForGlyph(glyph);
     map->widths[glyph - map->startRange] = width;
     return width;
 }
 
-static NSString *webFallbackFontFamily(void)
-{
-    static NSString *webFallbackFontFamily = nil;
-    if (!webFallbackFontFamily)
-       webFallbackFontFamily = [[[NSFont systemFontOfSize:16.0] familyName] retain];
-    return webFallbackFontFamily;
-}
-
 FontData::FontData(const FontPlatformData& f)
-:m_styleGroup(0), m_font(f), m_glyphToWidthMap(0), m_treatAsFixedPitch(false),
- m_smallCapsFontData(0), m_ATSUStyleInitialized(false), m_ATSUMirrors(false)
-{    
+:m_font(f), m_glyphToWidthMap(0), m_treatAsFixedPitch(false),
+ m_smallCapsFontData(0)
+ {    
     m_font = f;
-
-    m_syntheticBoldOffset = f.syntheticBold ? ceilf([f.font pointSize] / 24.0f) : 0.f;
-    
-    bool failedSetup = false;
-    if (!platformInit()) {
-        // Ack! Something very bad happened, like a corrupt font.
-        // Try looking for an alternate 'base' font for this renderer.
-
-        // Special case hack to use "Times New Roman" in place of "Times".
-        // "Times RO" is a common font whose family name is "Times".
-        // It overrides the normal "Times" family font.
-        // It also appears to have a corrupt regular variant.
-        NSString *fallbackFontFamily;
-        if ([[m_font.font familyName] isEqual:@"Times"])
-            fallbackFontFamily = @"Times New Roman";
-        else
-            fallbackFontFamily = webFallbackFontFamily();
-        
-        // Try setting up the alternate font.
-        // This is a last ditch effort to use a substitute font when something has gone wrong.
-#if !ERROR_DISABLED
-        NSFont *initialFont = m_font.font;
-#endif
-        m_font.font = [[NSFontManager sharedFontManager] convertFont:m_font.font toFamily:fallbackFontFamily];
-#if !ERROR_DISABLED
-        NSString *filePath = pathFromFont(initialFont);
-        if (!filePath)
-            filePath = @"not known";
-#endif
-        if (!platformInit()) {
-           if ([fallbackFontFamily isEqual:@"Times New Roman"]) {
-               // OK, couldn't setup Times New Roman as an alternate to Times, fallback
-               // on the system font.  If this fails we have no alternative left.
-               m_font.font = [[NSFontManager sharedFontManager] convertFont:m_font.font toFamily:webFallbackFontFamily()];
-               if (!platformInit()) {
-                   // We tried, Times, Times New Roman, and the system font. No joy. We have to give up.
-                   LOG_ERROR("unable to initialize with font %@ at %@", initialFont, filePath);
-                    failedSetup = true;
-               }
-           } else {
-               // We tried the requested font and the system font. No joy. We have to give up.
-               LOG_ERROR("unable to initialize with font %@ at %@", initialFont, filePath);
-                failedSetup = true;
-           }
-        }
-
-        // Report the problem.
-        LOG_ERROR("Corrupt font detected, using %@ in place of %@ located at \"%@\".",
-            [m_font.font familyName], [initialFont familyName], filePath);
-    }
-
-    // If all else fails, try to set up using the system font.
-    // This is probably because Times and Times New Roman are both unavailable.
-    if (failedSetup) {
-        m_font.font = [NSFont systemFontOfSize:[m_font.font pointSize]];
-        LOG_ERROR("failed to set up font, using system font %s", m_font.font);
-        platformInit();
-    }
-    
-    int iAscent;
-    int iDescent;
-    int iLineGap;
-    unsigned unitsPerEm;
-    wkGetFontMetrics(m_font.font, &iAscent, &iDescent, &iLineGap, &unitsPerEm); 
-    float pointSize = [m_font.font pointSize];
-    float fAscent = SCALE_EM_TO_UNITS(iAscent, unitsPerEm) * pointSize;
-    float fDescent = -SCALE_EM_TO_UNITS(iDescent, unitsPerEm) * pointSize;
-    float fLineGap = SCALE_EM_TO_UNITS(iLineGap, unitsPerEm) * pointSize;
-
-    // We need to adjust Times, Helvetica, and Courier to closely match the
-    // vertical metrics of their Microsoft counterparts that are the de facto
-    // web standard. The AppKit adjustment of 20% is too big and is
-    // incorrectly added to line spacing, so we use a 15% adjustment instead
-    // and add it to the ascent.
-    NSString *familyName = [m_font.font familyName];
-    if ([familyName isEqualToString:@"Times"] || [familyName isEqualToString:@"Helvetica"] || [familyName isEqualToString:@"Courier"])
-        fAscent += floorf(((fAscent + fDescent) * 0.15f) + 0.5f);
-
-    m_ascent = lroundf(fAscent);
-    m_descent = lroundf(fDescent);
-    m_lineGap = lroundf(fLineGap);
-
-    m_lineSpacing = m_ascent + m_descent + m_lineGap;
-
-    [m_font.font retain];
-}
-
-FontData::~FontData()
-{
-    if (m_styleGroup)
-        wkReleaseStyleGroup(m_styleGroup);
-
-    freeWidthMap(m_glyphToWidthMap);
-
-    if (m_ATSUStyleInitialized)
-        ATSUDisposeStyle(m_ATSUStyle);
-        
-    [m_font.font release];
-    
-    // We only get deleted when the cache gets cleared.  Since the smallCapsRenderer is also in that cache,
-    // it will be deleted then, so we don't need to do anything here.
-}
-
-float FontData::xHeight() const
-{
-    // Measure the actual character "x", because AppKit synthesizes X height rather than getting it from the font.
-    // Unfortunately, NSFont will round this for us so we don't quite get the right value.
-    NSGlyph xGlyph = m_characterToGlyphMap.glyphDataForCharacter('x', this).glyph;
-    if (xGlyph) {
-        NSRect xBox = [m_font.font boundingRectForGlyph:xGlyph];
-        // Use the maximum of either width or height because "x" is nearly square
-        // and web pages that foolishly use this metric for width will be laid out
-        // poorly if we return an accurate height. Classic case is Times 13 point,
-        // which has an "x" that is 7x6 pixels.
-        return MAX(NSMaxX(xBox), NSMaxY(xBox));
-    }
-
-    return [m_font.font xHeight];
-}
-
-FontData* FontData::smallCapsFontData(const FontDescription& fontDescription) const
-{
-    if (!m_smallCapsFontData) {
-       NS_DURING
-            float size = [m_font.font pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER;
-            FontPlatformData smallCapsFont([[NSFontManager sharedFontManager] convertFont:m_font.font toSize:size]);
-            
-            // AppKit resets the type information (screen/printer) when you convert a font to a different size.
-            // We have to fix up the font that we're handed back.
-            smallCapsFont.font = fontDescription.usePrinterFont() ? [smallCapsFont.font printerFont] : [smallCapsFont.font screenFont];
-
-           if (smallCapsFont.font) {
-                NSFontManager *fontManager = [NSFontManager sharedFontManager];
-                NSFontTraitMask fontTraits = [fontManager traitsOfFont:m_font.font];
-
-                if (m_font.syntheticBold)
-                    fontTraits |= NSBoldFontMask;
-                if (m_font.syntheticOblique)
-                    fontTraits |= NSItalicFontMask;
-
-                NSFontTraitMask smallCapsFontTraits = [fontManager traitsOfFont:smallCapsFont.font];
-                smallCapsFont.syntheticBold = (fontTraits & NSBoldFontMask) && !(smallCapsFontTraits & NSBoldFontMask);
-                smallCapsFont.syntheticOblique = (fontTraits & NSItalicFontMask) && !(smallCapsFontTraits & NSItalicFontMask);
-
-                m_smallCapsFontData = FontCache::getCachedFontData(&smallCapsFont);
-            }
-       NS_HANDLER
-            NSLog(@"uncaught exception selecting font for small caps: %@", localException);
-       NS_ENDHANDLER
-    }
-    return m_smallCapsFontData;
-}
-
-bool FontData::containsCharacters(const UChar* characters, int length) const
-{
-    NSString *string = [[NSString alloc] initWithCharactersNoCopy:(UniChar*)characters length:length freeWhenDone:NO];
-    NSCharacterSet *set = [[m_font.font coveredCharacterSet] invertedSet];
-    bool result = set && [string rangeOfCharacterFromSet:set].location == NSNotFound;
-    [string release];
-    return result;
-}
-
-bool FontData::platformInit()
-{
-    ATSUStyle fontStyle;
-    if (ATSUCreateStyle(&fontStyle) != noErr)
-        return false;
+    platformInit();
     
-    ATSUFontID fontId = wkGetNSFontATSUFontId(m_font.font);
-    if (!fontId) {
-        ATSUDisposeStyle(fontStyle);
-        return false;
-    }
-
-    ATSUAttributeTag tag = kATSUFontTag;
-    ByteCount size = sizeof(ATSUFontID);
-    ATSUFontID *valueArray[1] = {&fontId};
-    OSStatus status = ATSUSetAttributes(fontStyle, 1, &tag, &size, (void* const*)valueArray);
-    if (status != noErr) {
-        ATSUDisposeStyle(fontStyle);
-        return false;
-    }
-
-    if (wkGetATSStyleGroup(fontStyle, &m_styleGroup) != noErr) {
-        ATSUDisposeStyle(fontStyle);
-        return false;
-    }
-
-    ATSUDisposeStyle(fontStyle);
-
     // Nasty hack to determine if we should round or ceil space widths.
     // If the font is monospace or fake monospace we ceil to ensure that 
     // every character and the space are the same width.  Otherwise we round.
-    m_spaceGlyph = m_characterToGlyphMap.glyphDataForCharacter(SPACE, this).glyph;
-    if (m_spaceGlyph == 0) {
-        wkReleaseStyleGroup(m_styleGroup);
-        m_styleGroup = 0;
-        return false;
-    }
-
+    m_spaceGlyph = m_characterToGlyphMap.glyphDataForCharacter(' ', this).glyph;
     float width = widthForGlyph(m_spaceGlyph);
-
     m_spaceWidth = width;
-
     determinePitch();
     m_adjustedSpaceWidth = m_treatAsFixedPitch ? ceilf(width) : roundf(width);
-    
-    return true;
 }
 
-#if !ERROR_DISABLED
-
-static NSString *pathFromFont(NSFont *font)
+FontData::~FontData()
 {
-    FSSpec oFile;
-    OSStatus status = ATSFontGetFileSpecification(FMGetATSFontRefFromFont((FMFont)wkGetNSFontATSUFontId(font)), &oFile);
-    if (status == noErr) {
-        OSErr err;
-        FSRef fileRef;
-        err = FSpMakeFSRef(&oFile, &fileRef);
-        if (err == noErr) {
-            UInt8 filePathBuffer[PATH_MAX];
-            status = FSRefMakePath(&fileRef, filePathBuffer, PATH_MAX);
-            if (status == noErr)
-                return [NSString stringWithUTF8String:(const char *)filePathBuffer];
-        }
-    }
-    return nil;
-}
+    platformDestroy();
 
-#endif
+    freeWidthMap(m_glyphToWidthMap);
+
+    // We only get deleted when the cache gets cleared.  Since the smallCapsRenderer is also in that cache,
+    // it will be deleted then, so we don't need to do anything here.
+}
 
 static WidthMap *extendWidthMap(const FontData *renderer, ATSGlyphRef glyph)
 {
@@ -364,10 +105,7 @@ static WidthMap *extendWidthMap(const FontData *renderer, ATSGlyphRef glyph)
     unsigned i, count;
     
     NSFont *f = renderer->m_font.font;
-    if (renderer->m_glyphToWidthMap == 0 && [f numberOfGlyphs] < cGlyphPageSize)
-        blockSize = [f numberOfGlyphs];
-    else
-        blockSize = cGlyphPageSize;
+    blockSize = cGlyphPageSize;
     
     if (blockSize == 0)
         start = 0;
@@ -406,21 +144,4 @@ static void freeWidthMap(WidthMap *map)
     }
 }
 
-void FontData::determinePitch()
-{
-    NSFont* f = m_font.font;
-    // Special case Osaka-Mono.
-    // According to <rdar://problem/3999467>, we should treat Osaka-Mono as fixed pitch.
-    // Note that the AppKit does not report Osaka-Mono as fixed pitch.
-
-    // Special case MS-PGothic.
-    // According to <rdar://problem/4032938, we should not treat MS-PGothic as fixed pitch.
-    // Note that AppKit does report MS-PGothic as fixed pitch.
-
-    NSString *name = [f fontName];
-    m_treatAsFixedPitch = ([f isFixedPitch] || [f _isFakeFixedPitch] || 
-           [name caseInsensitiveCompare:@"Osaka-Mono"] == NSOrderedSame) && 
-          ![name caseInsensitiveCompare:@"MS-PGothic"] == NSOrderedSame;
-}
-
 }
diff --git a/WebCore/platform/mac/FontDataMac.mm b/WebCore/platform/mac/FontDataMac.mm
new file mode 100644 (file)
index 0000000..9d1e310
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "Font.h"
+#import "FontData.h"
+#import "Color.h"
+
+#import <wtf/Assertions.h>
+
+#import <ApplicationServices/ApplicationServices.h>
+#import <Cocoa/Cocoa.h>
+
+#import "FontCache.h"
+
+#import "WebCoreSystemInterface.h"
+
+#import "FloatRect.h"
+#import "FontDescription.h"
+
+#import <float.h>
+
+#import <unicode/uchar.h>
+#import <unicode/unorm.h>
+
+// FIXME: Just temporary for the #defines of constants that we will eventually stop using.
+#import "GlyphBuffer.h"
+
+@interface NSFont (WebAppKitSecretAPI)
+- (BOOL)_isFakeFixedPitch;
+@end
+
+namespace WebCore
+{
+
+#define SMALLCAPS_FONTSIZE_MULTIPLIER 0.7f
+#define SPACE 0x0020
+#define CONTEXT_DPI (72.0)
+#define SCALE_EM_TO_UNITS(X, U_PER_EM) (X * ((1.0 * CONTEXT_DPI) / (CONTEXT_DPI * U_PER_EM)))
+
+#if !ERROR_DISABLED
+static NSString *pathFromFont(NSFont *font);
+#endif
+
+bool initFontData(FontData* fontData)
+{
+    ATSUStyle fontStyle;
+    if (ATSUCreateStyle(&fontStyle) != noErr)
+        return false;
+    
+    ATSUFontID fontId = wkGetNSFontATSUFontId(fontData->m_font.font);
+    if (!fontId) {
+        ATSUDisposeStyle(fontStyle);
+        return false;
+    }
+
+    ATSUAttributeTag tag = kATSUFontTag;
+    ByteCount size = sizeof(ATSUFontID);
+    ATSUFontID *valueArray[1] = {&fontId};
+    OSStatus status = ATSUSetAttributes(fontStyle, 1, &tag, &size, (void* const*)valueArray);
+    if (status != noErr) {
+        ATSUDisposeStyle(fontStyle);
+        return false;
+    }
+
+    if (wkGetATSStyleGroup(fontStyle, &fontData->m_styleGroup) != noErr) {
+        ATSUDisposeStyle(fontStyle);
+        return false;
+    }
+
+    ATSUDisposeStyle(fontStyle);
+
+    return true;
+}
+
+static NSString *webFallbackFontFamily(void)
+{
+    static NSString *webFallbackFontFamily = nil;
+    if (!webFallbackFontFamily)
+        webFallbackFontFamily = [[[NSFont systemFontOfSize:16.0] familyName] retain];
+    return webFallbackFontFamily;
+}
+
+void FontData::platformInit()
+{
+    m_styleGroup = 0;
+    m_ATSUStyleInitialized = false;
+    m_ATSUMirrors = false;
+    
+    m_syntheticBoldOffset = m_font.syntheticBold ? ceilf([m_font.font pointSize] / 24.0f) : 0.f;
+    
+    bool failedSetup = false;
+    if (!initFontData(this)) {
+        // Ack! Something very bad happened, like a corrupt font.
+        // Try looking for an alternate 'base' font for this renderer.
+
+        // Special case hack to use "Times New Roman" in place of "Times".
+        // "Times RO" is a common font whose family name is "Times".
+        // It overrides the normal "Times" family font.
+        // It also appears to have a corrupt regular variant.
+        NSString *fallbackFontFamily;
+        if ([[m_font.font familyName] isEqual:@"Times"])
+            fallbackFontFamily = @"Times New Roman";
+        else
+            fallbackFontFamily = webFallbackFontFamily();
+        
+        // Try setting up the alternate font.
+        // This is a last ditch effort to use a substitute font when something has gone wrong.
+#if !ERROR_DISABLED
+        NSFont *initialFont = m_font.font;
+#endif
+        m_font.font = [[NSFontManager sharedFontManager] convertFont:m_font.font toFamily:fallbackFontFamily];
+#if !ERROR_DISABLED
+        NSString *filePath = pathFromFont(initialFont);
+        if (!filePath)
+            filePath = @"not known";
+#endif
+        if (!initFontData(this)) {
+            if ([fallbackFontFamily isEqual:@"Times New Roman"]) {
+                // OK, couldn't setup Times New Roman as an alternate to Times, fallback
+                // on the system font.  If this fails we have no alternative left.
+                m_font.font = [[NSFontManager sharedFontManager] convertFont:m_font.font toFamily:webFallbackFontFamily()];
+                if (!initFontData(this)) {
+                    // We tried, Times, Times New Roman, and the system font. No joy. We have to give up.
+                    LOG_ERROR("unable to initialize with font %@ at %@", initialFont, filePath);
+                    failedSetup = true;
+                }
+            } else {
+                // We tried the requested font and the system font. No joy. We have to give up.
+                LOG_ERROR("unable to initialize with font %@ at %@", initialFont, filePath);
+                failedSetup = true;
+            }
+        }
+
+        // Report the problem.
+        LOG_ERROR("Corrupt font detected, using %@ in place of %@ located at \"%@\".",
+            [m_font.font familyName], [initialFont familyName], filePath);
+    }
+
+    // If all else fails, try to set up using the system font.
+    // This is probably because Times and Times New Roman are both unavailable.
+    if (failedSetup) {
+        m_font.font = [NSFont systemFontOfSize:[m_font.font pointSize]];
+        LOG_ERROR("failed to set up font, using system font %s", m_font.font);
+        initFontData(this);
+    }
+    
+    int iAscent;
+    int iDescent;
+    int iLineGap;
+    unsigned unitsPerEm;
+    wkGetFontMetrics(m_font.font, &iAscent, &iDescent, &iLineGap, &unitsPerEm); 
+    float pointSize = [m_font.font pointSize];
+    float fAscent = SCALE_EM_TO_UNITS(iAscent, unitsPerEm) * pointSize;
+    float fDescent = -SCALE_EM_TO_UNITS(iDescent, unitsPerEm) * pointSize;
+    float fLineGap = SCALE_EM_TO_UNITS(iLineGap, unitsPerEm) * pointSize;
+
+    // We need to adjust Times, Helvetica, and Courier to closely match the
+    // vertical metrics of their Microsoft counterparts that are the de facto
+    // web standard. The AppKit adjustment of 20% is too big and is
+    // incorrectly added to line spacing, so we use a 15% adjustment instead
+    // and add it to the ascent.
+    NSString *familyName = [m_font.font familyName];
+    if ([familyName isEqualToString:@"Times"] || [familyName isEqualToString:@"Helvetica"] || [familyName isEqualToString:@"Courier"])
+        fAscent += floorf(((fAscent + fDescent) * 0.15f) + 0.5f);
+
+    m_ascent = lroundf(fAscent);
+    m_descent = lroundf(fDescent);
+    m_lineGap = lroundf(fLineGap);
+    m_lineSpacing = m_ascent + m_descent + m_lineGap;
+    
+    // Measure the actual character "x", because AppKit synthesizes X height rather than getting it from the font.
+    // Unfortunately, NSFont will round this for us so we don't quite get the right value.
+    NSGlyph xGlyph = m_characterToGlyphMap.glyphDataForCharacter('x', this).glyph;
+    if (xGlyph) {
+        NSRect xBox = [m_font.font boundingRectForGlyph:xGlyph];
+        // Use the maximum of either width or height because "x" is nearly square
+        // and web pages that foolishly use this metric for width will be laid out
+        // poorly if we return an accurate height. Classic case is Times 13 point,
+        // which has an "x" that is 7x6 pixels.
+        m_xHeight = MAX(NSMaxX(xBox), NSMaxY(xBox));
+    } else
+        m_xHeight = [m_font.font xHeight];
+
+    [m_font.font retain];
+}
+
+void FontData::platformDestroy()
+{
+    if (m_styleGroup)
+        wkReleaseStyleGroup(m_styleGroup);
+
+    if (m_ATSUStyleInitialized)
+        ATSUDisposeStyle(m_ATSUStyle);
+        
+    [m_font.font release];
+}
+
+FontData* FontData::smallCapsFontData(const FontDescription& fontDescription) const
+{
+    if (!m_smallCapsFontData) {
+        NS_DURING
+            float size = [m_font.font pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER;
+            FontPlatformData smallCapsFont([[NSFontManager sharedFontManager] convertFont:m_font.font toSize:size]);
+            
+            // AppKit resets the type information (screen/printer) when you convert a font to a different size.
+            // We have to fix up the font that we're handed back.
+            smallCapsFont.font = fontDescription.usePrinterFont() ? [smallCapsFont.font printerFont] : [smallCapsFont.font screenFont];
+
+            if (smallCapsFont.font) {
+                NSFontManager *fontManager = [NSFontManager sharedFontManager];
+                NSFontTraitMask fontTraits = [fontManager traitsOfFont:m_font.font];
+
+                if (m_font.syntheticBold)
+                    fontTraits |= NSBoldFontMask;
+                if (m_font.syntheticOblique)
+                    fontTraits |= NSItalicFontMask;
+
+                NSFontTraitMask smallCapsFontTraits = [fontManager traitsOfFont:smallCapsFont.font];
+                smallCapsFont.syntheticBold = (fontTraits & NSBoldFontMask) && !(smallCapsFontTraits & NSBoldFontMask);
+                smallCapsFont.syntheticOblique = (fontTraits & NSItalicFontMask) && !(smallCapsFontTraits & NSItalicFontMask);
+
+                m_smallCapsFontData = FontCache::getCachedFontData(&smallCapsFont);
+            }
+        NS_HANDLER
+            NSLog(@"uncaught exception selecting font for small caps: %@", localException);
+        NS_ENDHANDLER
+    }
+    return m_smallCapsFontData;
+}
+
+bool FontData::containsCharacters(const UChar* characters, int length) const
+{
+    NSString *string = [[NSString alloc] initWithCharactersNoCopy:(UniChar*)characters length:length freeWhenDone:NO];
+    NSCharacterSet *set = [[m_font.font coveredCharacterSet] invertedSet];
+    bool result = set && [string rangeOfCharacterFromSet:set].location == NSNotFound;
+    [string release];
+    return result;
+}
+
+#if !ERROR_DISABLED
+
+static NSString *pathFromFont(NSFont *font)
+{
+    FSSpec oFile;
+    OSStatus status = ATSFontGetFileSpecification(FMGetATSFontRefFromFont((FMFont)wkGetNSFontATSUFontId(font)), &oFile);
+    if (status == noErr) {
+        OSErr err;
+        FSRef fileRef;
+        err = FSpMakeFSRef(&oFile, &fileRef);
+        if (err == noErr) {
+            UInt8 filePathBuffer[PATH_MAX];
+            status = FSRefMakePath(&fileRef, filePathBuffer, PATH_MAX);
+            if (status == noErr)
+                return [NSString stringWithUTF8String:(const char *)filePathBuffer];
+        }
+    }
+    return nil;
+}
+
+#endif
+
+void FontData::determinePitch()
+{
+    NSFont* f = m_font.font;
+    // Special case Osaka-Mono.
+    // According to <rdar://problem/3999467>, we should treat Osaka-Mono as fixed pitch.
+    // Note that the AppKit does not report Osaka-Mono as fixed pitch.
+
+    // Special case MS-PGothic.
+    // According to <rdar://problem/4032938, we should not treat MS-PGothic as fixed pitch.
+    // Note that AppKit does report MS-PGothic as fixed pitch.
+
+    NSString *name = [f fontName];
+    m_treatAsFixedPitch = ([f isFixedPitch] || [f _isFakeFixedPitch] || 
+           [name caseInsensitiveCompare:@"Osaka-Mono"] == NSOrderedSame) && 
+          ![name caseInsensitiveCompare:@"MS-PGothic"] == NSOrderedSame;
+}
+
+float FontData::platformWidthForGlyph(Glyph glyph) const
+{
+    NSFont *font = m_font.font;
+    float pointSize = [font pointSize];
+    CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSize);
+    CGSize advance;
+    if (!wkGetGlyphTransformedAdvances(font, &m, &glyph, &advance)) {
+        LOG_ERROR("Unable to cache glyph widths for %@ %f", [font displayName], pointSize);
+        advance.width = 0;
+    }
+    return advance.width + m_syntheticBoldOffset;
+}
+
+}