Fix for bug 8809, lift the Mac-specific WebTextRenderer code for
authorhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 May 2006 08:49:41 +0000 (08:49 +0000)
committerhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 May 2006 08:49:41 +0000 (08:49 +0000)
        drawing and measuring of runs up into the Font class.  The fast code
        path is in Font.cpp and is designed to be mostly cross-platform.  The
        slow code path is in FontMac.cpp and is Mac-specific.

        Reviewed by darin

        * WebCore.xcodeproj/project.pbxproj:
        * platform/Font.cpp:
        (WebCore::isSpace):
        (WebCore::):
        (WebCore::isRoundingHackCharacter):
        (WebCore::m_finalRoundingWidth):
        (WebCore::WidthIterator::advance):
        (WebCore::WidthIterator::normalizeVoicingMarks):
        (WebCore::Font::primaryFont):
        (WebCore::Font::setAlwaysUseComplexPath):
        (WebCore::Font::canUseGlyphCache):
        (WebCore::Font::drawSimpleText):
        (WebCore::Font::drawText):
        (WebCore::Font::floatWidth):
        (WebCore::Font::floatWidthForSimpleText):
        * platform/Font.h:
        * platform/FontData.h:
        (WebCore::FontData::platformData):
        * platform/GlyphBuffer.h: Added.
        (WebCore::GlyphBuffer::GlyphBuffer):
        (WebCore::GlyphBuffer::isEmpty):
        (WebCore::GlyphBuffer::size):
        (WebCore::GlyphBuffer::glyphs):
        (WebCore::GlyphBuffer::advances):
        (WebCore::GlyphBuffer::fontDataAt):
        (WebCore::GlyphBuffer::swap):
        (WebCore::GlyphBuffer::glyphAt):
        (WebCore::GlyphBuffer::advanceAt):
        (WebCore::GlyphBuffer::add):
        * platform/mac/FontData.mm:
        (WebCore::isSpace):
        (WebCore::isRoundingHackCharacter):
        (WebCore::FontData::widthForGlyph):
        (WebCore::m_ATSUMirrors):
        (WebCore::FontData::xHeight):
        (WebCore::FontData::smallCapsFontData):
        (WebCore::findSubstituteFont):
        (WebCore::rendererForAlternateFont):
        (WebCore::findSubstituteRenderer):
        (WebCore::FontData::findSubstituteFontData):
        (WebCore::computeWidthForSpace):
        (WebCore::FontData::updateGlyphMapEntry):
        (WebCore::extendGlyphMap):
        (WebCore::extendWidthMap):
        (WebCore::createATSULayoutParameters):
        (WebCore::FontData::glyphForCharacter):
        (WebCore::advanceWidthIterator):
        (WebCore::shouldUseATSU):
        * platform/mac/FontMac.mm:
        (WebCore::ATSULayoutParameters::m_padPerSpace):
        (WebCore::addDirectionalOverride):
        (WebCore::initializeATSUStyle):
        (WebCore::overrideLayoutOperation):
        (WebCore::ATSULayoutParameters::initialize):
        (WebCore::disposeATSULayoutParameters):
        (WebCore::Font::drawComplexText):
        (WebCore::Font::floatWidthForComplexText):
        (WebCore::Font::drawGlyphs):
        * platform/mac/WebCoreTextRenderer.mm:
        (WebCoreSetAlwaysUseATSU):

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

WebCore/ChangeLog
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/platform/Font.cpp
WebCore/platform/Font.h
WebCore/platform/FontData.h
WebCore/platform/GlyphBuffer.h [new file with mode: 0644]
WebCore/platform/mac/FontData.mm
WebCore/platform/mac/FontMac.mm
WebCore/platform/mac/WebCoreTextRenderer.mm

index 6a79644..4c7af10 100644 (file)
@@ -1,3 +1,73 @@
+2006-05-10  David Hyatt  <hyatt@apple.com>
+
+        Fix for bug 8809, lift the Mac-specific WebTextRenderer code for
+        drawing and measuring of runs up into the Font class.  The fast code
+        path is in Font.cpp and is designed to be mostly cross-platform.  The
+        slow code path is in FontMac.cpp and is Mac-specific.
+
+        Reviewed by darin
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * platform/Font.cpp:
+        (WebCore::isSpace):
+        (WebCore::):
+        (WebCore::isRoundingHackCharacter):
+        (WebCore::m_finalRoundingWidth):
+        (WebCore::WidthIterator::advance):
+        (WebCore::WidthIterator::normalizeVoicingMarks):
+        (WebCore::Font::primaryFont):
+        (WebCore::Font::setAlwaysUseComplexPath):
+        (WebCore::Font::canUseGlyphCache):
+        (WebCore::Font::drawSimpleText):
+        (WebCore::Font::drawText):
+        (WebCore::Font::floatWidth):
+        (WebCore::Font::floatWidthForSimpleText):
+        * platform/Font.h:
+        * platform/FontData.h:
+        (WebCore::FontData::platformData):
+        * platform/GlyphBuffer.h: Added.
+        (WebCore::GlyphBuffer::GlyphBuffer):
+        (WebCore::GlyphBuffer::isEmpty):
+        (WebCore::GlyphBuffer::size):
+        (WebCore::GlyphBuffer::glyphs):
+        (WebCore::GlyphBuffer::advances):
+        (WebCore::GlyphBuffer::fontDataAt):
+        (WebCore::GlyphBuffer::swap):
+        (WebCore::GlyphBuffer::glyphAt):
+        (WebCore::GlyphBuffer::advanceAt):
+        (WebCore::GlyphBuffer::add):
+        * platform/mac/FontData.mm:
+        (WebCore::isSpace):
+        (WebCore::isRoundingHackCharacter):
+        (WebCore::FontData::widthForGlyph):
+        (WebCore::m_ATSUMirrors):
+        (WebCore::FontData::xHeight):
+        (WebCore::FontData::smallCapsFontData):
+        (WebCore::findSubstituteFont):
+        (WebCore::rendererForAlternateFont):
+        (WebCore::findSubstituteRenderer):
+        (WebCore::FontData::findSubstituteFontData):
+        (WebCore::computeWidthForSpace):
+        (WebCore::FontData::updateGlyphMapEntry):
+        (WebCore::extendGlyphMap):
+        (WebCore::extendWidthMap):
+        (WebCore::createATSULayoutParameters):
+        (WebCore::FontData::glyphForCharacter):
+        (WebCore::advanceWidthIterator):
+        (WebCore::shouldUseATSU):
+        * platform/mac/FontMac.mm:
+        (WebCore::ATSULayoutParameters::m_padPerSpace):
+        (WebCore::addDirectionalOverride):
+        (WebCore::initializeATSUStyle):
+        (WebCore::overrideLayoutOperation):
+        (WebCore::ATSULayoutParameters::initialize):
+        (WebCore::disposeATSULayoutParameters):
+        (WebCore::Font::drawComplexText):
+        (WebCore::Font::floatWidthForComplexText):
+        (WebCore::Font::drawGlyphs):
+        * platform/mac/WebCoreTextRenderer.mm:
+        (WebCoreSetAlwaysUseATSU):
+
 2006-05-10  Darin Adler  <darin@apple.com>
 
         - another try at fixing the Windows build
index 7209c57..893fc30 100644 (file)
                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 */; };
+               BCC71A130A0FF94D0014EE6E /* GlyphBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = BCC71A120A0FF94D0014EE6E /* GlyphBuffer.h */; };
                BCC8CFCB0986CD2400140BF2 /* Color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BCC8CFC80986CD2400140BF2 /* Color.cpp */; };
                BCC8CFCC0986CD2400140BF2 /* Color.h in Headers */ = {isa = PBXBuildFile; fileRef = BCC8CFC90986CD2400140BF2 /* Color.h */; };
                BCC8D1730988301200140BF2 /* Pen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BCC8D1710988301200140BF2 /* Pen.cpp */; };
                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>"; };
+               BCC71A120A0FF94D0014EE6E /* GlyphBuffer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GlyphBuffer.h; sourceTree = "<group>"; };
                BCC8CFC80986CD2400140BF2 /* Color.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = Color.cpp; sourceTree = "<group>"; };
                BCC8CFC90986CD2400140BF2 /* Color.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Color.h; sourceTree = "<group>"; };
                BCC8CFCA0986CD2400140BF2 /* ColorData.gperf */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ColorData.gperf; sourceTree = "<group>"; };
                                BCC47E6A09A3FE4700ADB771 /* FontDescription.h */,
                                BCC47E2409A3D6F100ADB771 /* FontFamily.cpp */,
                                BCC47E2509A3D6F100ADB771 /* FontFamily.h */,
+                               BCC71A120A0FF94D0014EE6E /* GlyphBuffer.h */,
                                A823A75B09B6E53900B60641 /* GraphicsContext.cpp */,
                                935367E409AF77DD00D35CD6 /* GraphicsContext.h */,
                                938E685309F0BE04008A48EC /* GraphicsTypes.cpp */,
                                14DC0D3809FED073007B0235 /* JSNode.h in Headers */,
                                14D0C82509FF0EF5006B36D9 /* kjs_domnode.h in Headers */,
                                142011B70A003133008303F9 /* JSCSSStyleDeclaration.h in Headers */,
+                               BCC71A130A0FF94D0014EE6E /* GlyphBuffer.h in Headers */,
                                1A7629840A0740A000989F5B /* XPathExpressionNode.h in Headers */,
                                1A76298B0A0740A000989F5B /* XPathFunctions.h in Headers */,
                                1A76298F0A0740A000989F5B /* XPathPath.h in Headers */,
index 9009e68..83bb400 100644 (file)
 #include "GraphicsContext.h"
 #include "KWQKHTMLSettings.h"
 
+#include "GlyphBuffer.h"
+
+#include <unicode/umachine.h>
+
 namespace WebCore {
 
+#if __APPLE__
+
+// FIXME: Cross-platform eventually, but for now we compile only on OS X.
+#define SPACE 0x0020
+#define NO_BREAK_SPACE 0x00A0
+
+// According to http://www.unicode.org/Public/UNIDATA/UCD.html#Canonical_Combining_Class_Values
+#define HIRAGANA_KATAKANA_VOICING_MARKS 8
+
+inline bool isSpace(unsigned c)
+{
+    return c == SPACE || c == '\t' || c == '\n' || c == NO_BREAK_SPACE;
+}
+
+static const uint8_t isRoundingHackCharacterTable[0x100] = {
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*\t*/, 1 /*\n*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    1 /*space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*-*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*?*/,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    1 /*no-break space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+inline bool isRoundingHackCharacter(UChar32 c)
+{
+    return (((c & ~0xFF) == 0 && isRoundingHackCharacterTable[c]));
+}
+
+struct WidthIterator {
+    WidthIterator(const Font* font, const UChar* str, int from, int to, int len,
+                  int tabWidth, int xpos, int toAdd, TextDirection dir, bool visuallyOrdered,
+                  bool applyWordRounding, bool applyRunRounding, const FontData* substituteFontData);
+
+    void advance(int to, GlyphBuffer* glyphBuffer = 0);
+
+    const Font* m_font;
+
+    const UChar* m_characters;
+    int m_from;
+    int m_to;
+    int m_len;
+    int m_tabWidth;
+    int m_xPos;
+    int m_toAdd;
+    TextDirection m_dir;
+    bool m_visuallyOrdered;
+    bool m_applyWordRounding;
+    bool m_applyRunRounding;
+    const FontData* m_substituteFontData;
+
+    unsigned m_currentCharacter;
+    float m_runWidthSoFar;
+    float m_widthToStart;
+    float m_padding;
+    float m_padPerSpace;
+    float m_finalRoundingWidth;
+    
+private:
+    UChar32 normalizeVoicingMarks();
+};
+
+WidthIterator::WidthIterator(const Font* font, const UChar* str, int from, int to, int len,
+                             int tabWidth, int xpos, int toAdd, TextDirection dir, bool visuallyOrdered,
+                             bool applyWordRounding, bool applyRunRounding, const FontData* substituteFontData)
+:m_font(font), m_characters((const UChar*)str), m_from(from), m_to(to), m_len(len),
+ m_tabWidth(tabWidth), m_xPos(xpos), m_toAdd(toAdd), m_dir(dir), m_visuallyOrdered(visuallyOrdered),
+ m_applyWordRounding(applyWordRounding), m_applyRunRounding(applyRunRounding), m_substituteFontData(substituteFontData),
+ m_currentCharacter(from), m_runWidthSoFar(0), m_finalRoundingWidth(0)
+{
+    // If the padding is non-zero, count the number of spaces in the run
+    // and divide that by the padding for per space addition.
+    if (!toAdd) {
+        m_padding = 0;
+        m_padPerSpace = 0;
+    } else {
+        float numSpaces = 0;
+        for (int i = from; i < to; i++)
+            if (isSpace(m_characters[i]))
+                numSpaces++;
+
+        m_padding = toAdd;
+        m_padPerSpace = ceilf(m_padding / numSpaces);
+    }
+    
+    // Calculate width up to starting position of the run.  This is
+    // necessary to ensure that our rounding hacks are always consistently
+    // applied.
+    if (m_from == 0)
+        m_widthToStart = 0;
+    else {
+        WidthIterator startPositionIterator(font, str, 0, len, len, tabWidth, xpos, toAdd, dir, visuallyOrdered,
+                                            applyWordRounding, applyRunRounding, substituteFontData);
+        startPositionIterator.advance(from);
+        m_widthToStart = startPositionIterator.m_runWidthSoFar;
+    }
+}
+
+void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
+{
+    if (offset > m_to)
+        offset = m_to;
+
+    int currentCharacter = m_currentCharacter;
+    const UChar* cp = &m_characters[currentCharacter];
+
+    bool rtl = (m_dir == RTL);
+    bool needCharTransform = rtl || m_font->isSmallCaps();
+    bool hasExtraSpacing = m_font->letterSpacing() || m_font->wordSpacing() || m_padding;
+
+    float runWidthSoFar = m_runWidthSoFar;
+    float lastRoundingWidth = m_finalRoundingWidth;
+
+    const FontData* primaryFontData = m_font->primaryFont();
+    
+    while (currentCharacter < offset) {
+        UChar32 c = *cp;
+        unsigned clusterLength = 1;
+        if (c >= 0x3041) {
+            if (c <= 0x30FE) {
+                // Deal with Hiragana and Katakana voiced and semi-voiced syllables.
+                // Normalize into composed form, and then look for glyph with base + combined mark.
+                // Check above for character range to minimize performance impact.
+                UChar32 normalized = normalizeVoicingMarks();
+                if (normalized) {
+                    c = normalized;
+                    clusterLength = 2;
+                }
+            } else if (U16_IS_SURROGATE(c)) {
+                if (!U16_IS_SURROGATE_LEAD(c))
+                    break;
+
+                // Do we have a surrogate pair?  If so, determine the full Unicode (32 bit)
+                // code point before glyph lookup.
+                // Make sure we have another character and it's a low surrogate.
+                if (currentCharacter + 1 >= m_len)
+                    break;
+                UniChar low = cp[1];
+                if (!U16_IS_TRAIL(low))
+                    break;
+                c = U16_GET_SUPPLEMENTARY(c, low);
+                clusterLength = 2;
+            }
+        }
+
+        const FontData* fontData = m_substituteFontData ? m_substituteFontData :primaryFontData;
+
+        if (needCharTransform) {
+            if (rtl)
+                c = u_charMirror(c);
+
+            // If small-caps, convert lowercase to upper.
+            if (m_font->isSmallCaps() && !u_isUUppercase(c)) {
+                UChar32 upperC = u_toupper(c);
+                if (upperC != c) {
+                    c = upperC;
+                    fontData = fontData->smallCapsFontData();
+                }
+            }
+        }
+
+        // FIXME: Should go through fallback list eventually when we rework the glyph map.
+        Glyph glyph = fontData->glyphForCharacter(&fontData, c);
+
+        // Try to find a substitute font if this font didn't have a glyph for a character in the
+        // string. If one isn't found we end up drawing and measuring the 0 glyph, usually a box.
+        if (glyph == 0 && !m_substituteFontData) {
+            // FIXME: Should go through fallback list eventually.
+            const FontData* substituteFontData = fontData->findSubstituteFontData(cp, clusterLength, m_font->fontDescription());
+            if (substituteFontData) {
+                GlyphBuffer localGlyphBuffer;
+                m_font->floatWidthForSimpleText((UChar*)cp, clusterLength, 0, clusterLength, 0, 0, 0, m_dir, m_visuallyOrdered, 
+                                                 m_applyWordRounding, false, substituteFontData, 0, &localGlyphBuffer);
+                if (localGlyphBuffer.size() == 1) {
+                    assert(substituteFontData == localGlyphBuffer.fontDataAt(0));
+                    glyph = localGlyphBuffer.glyphAt(0);
+                    fontData->updateGlyphMapEntry(c, glyph, substituteFontData);
+                    fontData = substituteFontData;
+                }
+            }
+        }
+
+        // Now that we have a glyph and font data, get its width.
+        float width;
+        if (c == '\t' && m_tabWidth)
+            width = m_tabWidth - fmodf(m_xPos + runWidthSoFar, m_tabWidth);
+        else {
+            width = fontData->widthForGlyph(glyph);
+            // We special case spaces in two ways when applying word rounding.
+            // First, we round spaces to an adjusted width in all fonts.
+            // Second, in fixed-pitch fonts we ensure that all characters that
+            // match the width of the space character have the same width as the space character.
+            if (width == fontData->m_spaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_applyWordRounding)
+                width = fontData->m_adjustedSpaceWidth;
+        }
+
+        if (hasExtraSpacing) {
+            // Account for letter-spacing.
+            if (width && m_font->letterSpacing())
+                width += m_font->letterSpacing();
+
+            if (isSpace(c)) {
+                // Account for padding. WebCore uses space padding to justify text.
+                // We distribute the specified padding over the available spaces in the run.
+                if (m_padding) {
+                    // Use left over padding if not evenly divisible by number of spaces.
+                    if (m_padding < m_padPerSpace) {
+                        width += m_padding;
+                        m_padding = 0;
+                    } else {
+                        width += m_padPerSpace;
+                        m_padding -= m_padPerSpace;
+                    }
+                }
+
+                // Account for word spacing.
+                // We apply additional space between "words" by adding width to the space character.
+                if (currentCharacter != 0 && !isSpace(cp[-1]) && m_font->wordSpacing())
+                    width += m_font->wordSpacing();
+            }
+        }
+
+        // Advance past the character we just dealt with.
+        cp += clusterLength;
+        currentCharacter += clusterLength;
+
+        // Account for float/integer impedance mismatch between CG and KHTML. "Words" (characters 
+        // followed by a character defined by isRoundingHackCharacter()) are always an integer width.
+        // We adjust the width of the last character of a "word" to ensure an integer width.
+        // If we move KHTML to floats we can remove this (and related) hacks.
+
+        float oldWidth = width;
+
+        // Force characters that are used to determine word boundaries for the rounding hack
+        // to be integer width, so following words will start on an integer boundary.
+        if (m_applyWordRounding && isRoundingHackCharacter(c))
+            width = ceilf(width);
+
+        // Check to see if the next character is a "rounding hack character", if so, adjust
+        // width so that the total run width will be on an integer boundary.
+        if ((m_applyWordRounding && currentCharacter < m_len && isRoundingHackCharacter(*cp))
+                || (m_applyRunRounding && currentCharacter >= m_to)) {
+            float totalWidth = m_widthToStart + runWidthSoFar + width;
+            width += ceilf(totalWidth) - totalWidth;
+        }
+
+        runWidthSoFar += width;
+
+        if (glyphBuffer)
+            glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
+
+        lastRoundingWidth = width - oldWidth;
+    }
+
+    m_currentCharacter = currentCharacter;
+    m_runWidthSoFar = runWidthSoFar;
+    m_finalRoundingWidth = lastRoundingWidth;
+}
+
+UChar32 WidthIterator::normalizeVoicingMarks()
+{
+    int currentCharacter = m_currentCharacter;
+    if (currentCharacter + 1 < m_to) {
+        if (u_getCombiningClass(m_characters[currentCharacter + 1]) == HIRAGANA_KATAKANA_VOICING_MARKS) {
+            // Normalize into composed form using 3.2 rules.
+            UChar normalizedCharacters[2] = { 0, 0 };
+            UErrorCode uStatus = (UErrorCode)0;  
+            int32_t resultLength = unorm_normalize(&m_characters[currentCharacter], 2,
+                UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], 2, &uStatus);
+            if (resultLength == 1 && uStatus == 0)
+                return normalizedCharacters[0];
+        }
+    }
+    return 0;
+}
+#endif
+
+// ============================================================================================
+// Font Implementation (Cross-Platform Portion)
+// ============================================================================================
+
 Font::Font() :m_fontList(0), m_letterSpacing(0), m_wordSpacing(0) {}
 Font::Font(const FontDescription& fd, short letterSpacing, short wordSpacing) 
 : m_fontDescription(fd),
@@ -64,6 +350,12 @@ Font::~Font()
 {
 }
 
+const FontData* Font::primaryFont() const
+{
+    assert(m_fontList);
+    return m_fontList->primaryFont(m_fontDescription);
+}
+
 void Font::update() const
 {
     // FIXME: It is pretty crazy that we are willing to just poke into a RefPtr, but it ends up 
@@ -116,4 +408,159 @@ bool Font::isFixedPitch() const
     return m_fontList->isFixedPitch(fontDescription());
 }
 
+#if __APPLE__
+// FIXME: These methods will eventually be cross-platform, but to keep Windows compiling we'll make this Apple-only for now.
+bool Font::gAlwaysUseComplexPath = false;
+void Font::setAlwaysUseComplexPath(bool alwaysUse)
+{
+    gAlwaysUseComplexPath = alwaysUse;
+}
+
+bool Font::canUseGlyphCache(const UChar* str, int to) const
+{
+    if (gAlwaysUseComplexPath)
+        return false;
+    
+    // Start from 0 since drawing and highlighting also measure the characters before run->from
+    for (int i = 0; i < to; i++) {
+        UChar c = str[i];
+        if (c < 0x300)      // U+0300 through U+036F Combining diacritical marks
+            continue;
+        if (c <= 0x36F)
+            return false;
+
+        if (c < 0x0591 || c == 0x05BE)     // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha
+            continue;
+        if (c <= 0x05CF)
+            return false;
+
+        if (c < 0x0600)     // U+0600 through U+1059 Arabic, Syriac, Thaana, Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar
+            continue;
+        if (c <= 0x1059)
+            return false;
+
+        if (c < 0x1100)     // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left here if you precompose; Modern Korean will be precomposed as a result of step A)
+            continue;
+        if (c <= 0x11FF)
+            return false;
+
+        if (c < 0x1780)     // U+1780 through U+18AF Khmer, Mongolian
+            continue;
+        if (c <= 0x18AF)
+            return false;
+
+        if (c < 0x1900)     // U+1900 through U+194F Limbu (Unicode 4.0)
+            continue;
+        if (c <= 0x194F)
+            return false;
+
+        if (c < 0x20D0)     // U+20D0 through U+20FF Combining marks for symbols
+            continue;
+        if (c <= 0x20FF)
+            return false;
+
+        if (c < 0xFE20)     // U+FE20 through U+FE2F Combining half marks
+            continue;
+        if (c <= 0xFE2F)
+            return false;
+    }
+
+    return true;
+
+}
+
+void Font::drawSimpleText(GraphicsContext* context, const IntPoint& point, int tabWidth, int xpos, const UChar* str, int len, int from, int to,
+                          int toAdd, TextDirection dir, bool visuallyOrdered) const
+{
+    // This glyph buffer holds our glyphs+advances+font data for each glyph.
+    GlyphBuffer glyphBuffer;
+
+    // Our measuring code will generate glyphs and advances for us.
+    float startX;
+    floatWidthForSimpleText(str, len, from, to, tabWidth, xpos, toAdd, dir, visuallyOrdered, 
+                            true, true, 0,
+                            &startX, &glyphBuffer);
+    
+    // We couldn't generate any glyphs for the run.  Give up.
+    if (glyphBuffer.isEmpty())
+        return;
+    
+    // Calculate the starting point of the glyphs to be displayed by adding
+    // all the advances up to the first glyph.
+    startX += point.x();
+    FloatPoint startPoint(startX, point.y());
+
+    // Swap the order of the glyphs if right-to-left.
+    if (dir == RTL)
+        for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end)
+            glyphBuffer.swap(i, end);
+
+    // Draw each contiguous run of glyphs that use the same font data.
+    const FontData* fontData = glyphBuffer.fontDataAt(0);
+    float nextX = startX;
+    int lastFrom = 0;
+    int nextGlyph = 0;
+    while (nextGlyph < glyphBuffer.size()) {
+        const FontData* nextFontData = glyphBuffer.fontDataAt(nextGlyph);
+        if (nextFontData != fontData) {
+            drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
+            lastFrom = nextGlyph;
+            fontData = nextFontData;
+            startX = nextX;
+        }
+        nextX += glyphBuffer.advanceAt(nextGlyph);
+        nextGlyph++;
+    }
+    drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
+}
+
+void Font::drawText(GraphicsContext* context, const IntPoint& point, int tabWidth, int xpos, const UChar* str, int len, int from, int to,
+                    int toAdd, TextDirection d, bool visuallyOrdered) const
+{
+    if (len == 0)
+        return;
+
+    if (from < 0)
+        from = 0;
+    if (to < 0)
+        to = len;
+
+    if (canUseGlyphCache(str, to))
+        drawSimpleText(context, point, tabWidth, xpos, str, len, from, to, toAdd, d, visuallyOrdered);
+    else
+        drawComplexText(context, point, tabWidth, xpos, str, len, from, to, toAdd, d, visuallyOrdered);
+}
+
+float Font::floatWidth(const UChar* str, int slen, int from, int len, int tabWidth, int xpos, bool runRounding) const
+{
+    int to = from + len;
+    if (canUseGlyphCache(str, to))
+        return floatWidthForSimpleText(str, slen, from, to, tabWidth, xpos, 0, LTR, false, true, runRounding, 0, 0, 0);
+    else
+        return floatWidthForComplexText(str, slen, from, to, tabWidth, xpos, runRounding);
+}
+
+float Font::floatWidthForSimpleText(const UChar* str, int len, int from, int to, int tabWidth, int xpos, int toAdd, 
+                                    TextDirection dir, bool visuallyOrdered, 
+                                    bool applyWordRounding, bool applyRunRounding,
+                                    const FontData* substituteFont,
+                                    float* startPosition, GlyphBuffer* glyphBuffer) const
+{
+    WidthIterator it(this, str, from, dir == LTR ? to : len, len, tabWidth, xpos, toAdd, dir, visuallyOrdered, 
+                     applyWordRounding, applyRunRounding, substituteFont);
+    it.advance(to, glyphBuffer);
+    float runWidth = it.m_runWidthSoFar;
+    if (startPosition) {
+        if (dir == LTR)
+            *startPosition = it.m_widthToStart;
+        else {
+            float finalRoundingWidth = it.m_finalRoundingWidth;
+            it.advance(len);
+            *startPosition = it.m_runWidthSoFar - runWidth + finalRoundingWidth;
+        }
+    }
+    return runWidth;
+}
+#endif
+
 }
index 268f516..5fa26ed 100644 (file)
@@ -29,6 +29,7 @@
 #include "Color.h"
 #include "FontDescription.h"
 #include "TextDirection.h"
+#include "GlyphBuffer.h"
 
 #if __APPLE__
 // FIXME: Should not be necessary.
@@ -41,6 +42,7 @@ class FontFallbackList;
 class GraphicsContext;
 class IntPoint;
 class IntRect;
+class FloatPoint;
 
 enum Pitch { UnknownPitch, FixedPitch, VariablePitch };
 
@@ -123,6 +125,35 @@ public:
     int lineSpacing() const;
     float xHeight() const;
 
+    const FontData* primaryFont() const;
+
+private:
+#if __APPLE__
+    // FIXME: This will eventually be cross-platform, but we want to keep Windows compiling for now.
+    bool canUseGlyphCache(const UChar* str, int to) const;
+    void drawSimpleText(GraphicsContext*, const IntPoint&, int tabWidth, int xpos,
+                        const UChar*, int len, int from, int to, int toAdd, 
+                        TextDirection, bool visuallyOrdered) const;
+    void drawGlyphs(GraphicsContext*, const FontData*, const GlyphBuffer&, int from, int to, const FloatPoint&) const;
+    void drawComplexText(GraphicsContext*, const IntPoint&, int tabWidth, int xpos,
+                         const UChar*, int len, int from, int to, int toAdd, 
+                         TextDirection, bool visuallyOrdered) const;
+    float floatWidthForSimpleText(const UChar*, int len, int from, int to, 
+                                  int tabWidth, int xpos, int toAdd, 
+                                  TextDirection, bool visuallyOrdered, 
+                                  bool applyWordRounding, bool applyRunRounding,
+                                  const FontData* substituteFont,
+                                  float* startX, GlyphBuffer*) const;
+    float floatWidthForComplexText(const UChar*, int slen, int pos, int len, int tabWidth, int xpos, bool runRounding = true) const;
+
+    friend struct WidthIterator;
+    
+    // Useful for debugging the complex font rendering code path.
+public:
+    static void setAlwaysUseComplexPath(bool);
+    static bool gAlwaysUseComplexPath;
+#endif
+
 private:
     FontDescription m_fontDescription;
     mutable RefPtr<FontFallbackList> m_fontList;
@@ -130,6 +161,11 @@ private:
     short m_wordSpacing;
 };
 
+#if __APPLE__
+    // FIXME: This will eventually be cross-platform, but we want to keep Windows compiling for now.
+    bool isSpace(unsigned c);
+    bool isRoundingHackCharacter(UChar c);
+#endif
 }
 
 #endif
index 736ee69..e1182e6 100644 (file)
@@ -33,12 +33,18 @@ class NSColor;
 
 #include "FloatPoint.h"
 #include "FontPlatformData.h"
+#include "GlyphBuffer.h"
+
+// FIXME: Temporary.  Only needed to support API that's going to move.
+#include <unicode/uchar.h>
+#include <unicode/unorm.h>
 
 namespace WebCore
 {
 
 class FloatRect;
 class Color;
+class FontDescription;
 
 struct WebCoreTextStyle
 {
@@ -89,8 +95,8 @@ public:
     ~FontData();
 
 public:
-    static void setAlwaysUseATSU(bool);
-    static bool gAlwaysUseATSU;
+    const FontPlatformData& platformData() const { return m_font; }
+    FontData* smallCapsFontData() const;
 
     // vertical metrics
     int ascent() const { return m_ascent; }
@@ -114,6 +120,14 @@ public:
     // selection point check 
     int pointToOffset(const WebCoreTextRun* run, const WebCoreTextStyle* style, int x, bool includePartialGlyphs);
 
+    // FIXME: These are temporary API and will eventually move to the fallback list.
+    Glyph glyphForCharacter(const FontData **renderer, unsigned c) const;
+    const FontData* findSubstituteFontData(const UChar* characters, unsigned numCharacters, const FontDescription&) const;
+    void updateGlyphMapEntry(UChar c, Glyph glyph, const FontData *substituteRenderer) const;
+    // End temporary API
+
+    float widthForGlyph(Glyph glyph) const;
+    
 private:
     int misspellingLinePatternWidth() const { return 4; }
     int misspellingLinePatternGapWidth() const { return 1; } // the number of transparent pixels after the dot
@@ -127,8 +141,8 @@ public:
     void* m_styleGroup;
     
     FontPlatformData m_font;
-    GlyphMap* m_characterToGlyphMap;
-    WidthMap* m_glyphToWidthMap;
+    mutable GlyphMap* m_characterToGlyphMap;
+    mutable WidthMap* m_glyphToWidthMap;
 
     bool m_treatAsFixedPitch;
     ATSGlyphRef m_spaceGlyph;
@@ -136,10 +150,10 @@ public:
     float m_adjustedSpaceWidth;
     float m_syntheticBoldOffset;
     
-    FontData* m_smallCapsRenderer;
-    ATSUStyle m_ATSUStyle;
-    bool m_ATSUStyleInitialized;
-    bool m_ATSUMirrors;
+    mutable FontData* m_smallCapsFontData;
+    mutable ATSUStyle m_ATSUStyle;
+    mutable bool m_ATSUStyleInitialized;
+    mutable bool m_ATSUMirrors;
 };
 
 }
diff --git a/WebCore/platform/GlyphBuffer.h b/WebCore/platform/GlyphBuffer.h
new file mode 100644 (file)
index 0000000..13343cb
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef GLYPH_BUFFER_H
+#define GLYPH_BUFFER_H
+
+// MAX_GLYPH_EXPANSION is the maximum numbers of glyphs that may be
+// use to represent a single Unicode code point.
+#define MAX_GLYPH_EXPANSION 4
+#define GLYPH_BUFFER_SIZE 2048
+
+#if __APPLE__
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+#include <wtf/Vector.h>
+
+namespace WebCore
+{
+typedef unsigned short Glyph;
+class FontData;
+
+class GlyphBuffer
+{
+public:
+    GlyphBuffer() {};
+    
+    bool isEmpty() const { return m_fontData.isEmpty(); }
+    int size() const { return m_fontData.size(); }
+    
+#if __APPLE__
+    Glyph* glyphs(int from) const { return ((Glyph*)m_glyphs.data()) + from; }
+    CGSize* advances(int from) const { return ((CGSize*)m_advances.data()) + from; }
+#endif
+
+    const FontData* fontDataAt(int index) const { return m_fontData[index]; }
+    
+    void swap(int index1, int index2)
+    {
+        const FontData* f = m_fontData[index1];
+        m_fontData[index1] = m_fontData[index2];
+        m_fontData[index2] = f;
+
+#if __APPLE__
+        Glyph g = m_glyphs[index1];
+        m_glyphs[index1] = m_glyphs[index2];
+        m_glyphs[index2] = g;
+
+        CGSize s = m_advances[index1];
+        m_advances[index1] = m_advances[index2];
+        m_advances[index2] = s;
+#endif
+    }
+
+    Glyph glyphAt(int index) const
+    {
+#if __APPLE__
+        return m_glyphs[index];
+#else
+        return 0;
+#endif
+    }
+
+    float advanceAt(int index) const
+    {
+#if __APPLE__
+        return m_advances[index].width;
+#else
+        return 0;
+#endif
+    }
+
+    void add(Glyph glyph, const FontData* font, float width)
+    {
+        m_fontData.append(font);
+#if __APPLE__
+        m_glyphs.append(glyph);
+        CGSize advance;
+        advance.width = width;
+        advance.height = 0;
+        m_advances.append(advance);
+#endif
+    }
+    
+private:
+    Vector<const FontData*, GLYPH_BUFFER_SIZE> m_fontData;
+#if __APPLE__
+    // Store the advances as CGSizes separately from the glyph indices.
+    Vector<Glyph, GLYPH_BUFFER_SIZE> m_glyphs;
+    Vector<CGSize, GLYPH_BUFFER_SIZE> m_advances;
+#else
+    // We will store cairo_glyphs, and they incorporate the glyph index as well as
+    // the advances.
+#endif
+};
+
+}
+#endif
index 36130b3..3606425 100644 (file)
@@ -28,6 +28,7 @@
  */
 
 #import "config.h"
+#import "Font.h"
 #import "FontData.h"
 #import "Color.h"
 #import "WebCoreTextRenderer.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"
+
 namespace WebCore
 {
 
@@ -98,13 +103,13 @@ struct WidthMap {
 };
 
 typedef struct GlyphEntry {
-    ATSGlyphRef glyph;
-    FontData *renderer;
+    Glyph glyph;
+    const FontData *renderer;
 } GlyphEntry;
 
 struct GlyphMap {
-    UChar32 startRange;
-    UChar32 endRange;
+    UChar startRange;
+    UChar endRange;
     GlyphMap *next;
     GlyphEntry *glyphs;
 };
@@ -133,15 +138,13 @@ typedef struct ATSULayoutParameters
     float padPerSpace;
 } ATSULayoutParameters;
 
-static FontData *rendererForAlternateFont(FontData *, FontPlatformData);
+static const FontData *rendererForAlternateFont(const FontData *, FontPlatformData);
 
-static WidthMap *extendWidthMap(FontData *, ATSGlyphRef);
-static ATSGlyphRef extendGlyphMap(FontData *, UChar32);
-static void updateGlyphMapEntry(FontData *, UChar32, ATSGlyphRef, FontData *substituteRenderer);
+static WidthMap *extendWidthMap(const FontData *, ATSGlyphRef);
+static ATSGlyphRef extendGlyphMap(const FontData *, UChar32);
 
 static void freeWidthMap(WidthMap *);
 static void freeGlyphMap(GlyphMap *);
-static inline ATSGlyphRef glyphForCharacter(FontData **, UChar32);
 
 // Measuring runs.
 static float CG_floatWidthForRun(FontData *, const WebCoreTextRun *, const WebCoreTextStyle *,
@@ -182,12 +185,9 @@ static NSString *pathFromFont(NSFont *font);
 static void createATSULayoutParameters(ATSULayoutParameters *params, FontData *renderer, const WebCoreTextRun *run, const WebCoreTextStyle *style);
 static void disposeATSULayoutParameters(ATSULayoutParameters *params);
 
-// Globals
-static bool alwaysUseATSU = NO;
-
 // Character property functions.
 
-static inline bool isSpace(UChar32 c)
+inline bool isSpace(UChar32 c)
 {
     return c == SPACE || c == '\t' || c == '\n' || c == NO_BREAK_SPACE;
 }
@@ -203,7 +203,7 @@ static const uint8_t isRoundingHackCharacterTable[0x100] = {
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 };
 
-static inline bool isRoundingHackCharacter(UChar32 c)
+inline bool isRoundingHackCharacter(UChar c)
 {
     return (((c & ~0xFF) == 0 && isRoundingHackCharacterTable[c]));
 }
@@ -247,19 +247,19 @@ void WebCoreInitializeEmptyTextGeometry(WebCoreTextGeometry *geometry)
 
 // Map utility functions
 
-static inline WebGlyphWidth widthForGlyph(FontData *renderer, ATSGlyphRef glyph)
+inline float FontData::widthForGlyph(Glyph glyph) const
 {
     WidthMap *map;
-    for (map = renderer->m_glyphToWidthMap; 1; map = map->next) {
+    for (map = m_glyphToWidthMap; 1; map = map->next) {
         if (!map)
-            map = extendWidthMap(renderer, glyph);
+            map = extendWidthMap(this, glyph);
         if (glyph >= map->startRange && glyph <= map->endRange)
             break;
     }
-    WebGlyphWidth width = map->widths[glyph - map->startRange];
+    float width = map->widths[glyph - map->startRange];
     if (width >= 0)
         return width;
-    NSFont *font = renderer->m_font.font;
+    NSFont *font = m_font.font;
     float pointSize = [font pointSize];
     CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSize);
     CGSize advance;
@@ -267,7 +267,7 @@ static inline WebGlyphWidth widthForGlyph(FontData *renderer, ATSGlyphRef glyph)
         LOG_ERROR("Unable to cache glyph widths for %@ %f", [font displayName], pointSize);
         advance.width = 0;
     }
-    width = advance.width + renderer->m_syntheticBoldOffset;
+    width = advance.width + m_syntheticBoldOffset;
     map->widths[glyph - map->startRange] = width;
     return width;
 }
@@ -397,7 +397,7 @@ static NSString *webFallbackFontFamily(void)
 
 FontData::FontData(const FontPlatformData& f)
 :m_styleGroup(0), m_font(f), m_characterToGlyphMap(0), m_glyphToWidthMap(0), m_treatAsFixedPitch(false),
- m_smallCapsRenderer(0), m_ATSUStyleInitialized(false), m_ATSUMirrors(false)
+ m_smallCapsFontData(0), m_ATSUStyleInitialized(false), m_ATSUMirrors(false)
 {    
     m_font = f;
 
@@ -508,7 +508,7 @@ 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.
-    FontData *renderer = [[WebTextRendererFactory sharedFactory] rendererWithFont:m_font];
+    const FontData *renderer = [[WebTextRendererFactory sharedFactory] rendererWithFont:m_font];
     NSGlyph xGlyph = glyphForCharacter(&renderer, 'x');
     if (xGlyph) {
         NSRect xBox = [m_font.font boundingRectForGlyph:xGlyph];
@@ -677,27 +677,20 @@ int FontData::pointToOffset(const WebCoreTextRun* run, const WebCoreTextStyle* s
     return CG_pointToOffset(this, run, style, x, includePartialGlyphs);
 }
 
-bool FontData::gAlwaysUseATSU = false;
-
-void FontData::setAlwaysUseATSU(bool alwaysUse)
+FontData* FontData::smallCapsFontData() const
 {
-    gAlwaysUseATSU = alwaysUse;
-}
-
-static FontData *getSmallCapsRenderer(FontData *renderer)
-{
-    if (!renderer->m_smallCapsRenderer) {
+    if (!m_smallCapsFontData) {
        NS_DURING
-            float size = [renderer->m_font.font pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER;
+            float size = [m_font.font pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER;
             FontPlatformData smallCapsFont;
             WebCoreInitializeFont(&smallCapsFont);
-            smallCapsFont.font = [[NSFontManager sharedFontManager] convertFont:renderer->m_font.font toSize:size];
-           renderer->m_smallCapsRenderer = rendererForAlternateFont(renderer, smallCapsFont);
+            smallCapsFont.font = [[NSFontManager sharedFontManager] convertFont:m_font.font toSize:size];
+           m_smallCapsFontData = (FontData*)rendererForAlternateFont(this, smallCapsFont);
        NS_HANDLER
             NSLog(@"uncaught exception selecting font for small caps: %@", localException);
        NS_ENDHANDLER
     }
-    return renderer->m_smallCapsRenderer;
+    return m_smallCapsFontData;
 }
 
 static inline bool fontContainsString(NSFont *font, NSString *string)
@@ -706,7 +699,7 @@ static inline bool fontContainsString(NSFont *font, NSString *string)
     return set && [string rangeOfCharacterFromSet:set].location == NSNotFound;
 }
 
-static NSFont *findSubstituteFont(FontData *renderer, NSString *string, NSString **families)
+static NSFont *findSubstituteFont(const FontData *renderer, NSString *string, NSString **families)
 {
     NSFont *substituteFont = nil;
 
@@ -752,7 +745,7 @@ static NSFont *findSubstituteFont(FontData *renderer, NSString *string, NSString
     return substituteFont;
 }
 
-static FontData *rendererForAlternateFont(FontData *renderer, FontPlatformData alternateFont)
+static const FontData *rendererForAlternateFont(const FontData *renderer, FontPlatformData alternateFont)
 {
     if (!alternateFont.font)
         return nil;
@@ -772,7 +765,7 @@ static FontData *rendererForAlternateFont(FontData *renderer, FontPlatformData a
     return [[WebTextRendererFactory sharedFactory] rendererWithFont:alternateFont];
 }
 
-static FontData *findSubstituteRenderer(FontData *renderer, const unichar *characters, int numCharacters, NSString **families)
+static const FontData *findSubstituteRenderer(const FontData *renderer, const unichar *characters, int numCharacters, NSString **families)
 {
     FontPlatformData substituteFont;
     WebCoreInitializeFont(&substituteFont);
@@ -782,6 +775,12 @@ static FontData *findSubstituteRenderer(FontData *renderer, const unichar *chara
     return rendererForAlternateFont(renderer, substituteFont);
 }
 
+const FontData* FontData::findSubstituteFontData(const UChar* characters, unsigned numCharacters, const FontDescription& fontDescription) const
+{
+    CREATE_FAMILY_ARRAY(fontDescription, families);
+    return findSubstituteRenderer(this, (const unichar*)characters, numCharacters, families);
+}
+
 // 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.
@@ -791,7 +790,7 @@ static bool computeWidthForSpace(FontData *renderer)
     if (renderer->m_spaceGlyph == 0)
         return NO;
 
-    float width = widthForGlyph(renderer, renderer->m_spaceGlyph);
+    float width = renderer->widthForGlyph(renderer->m_spaceGlyph);
 
     renderer->m_spaceWidth = width;
 
@@ -1071,10 +1070,10 @@ static float CG_floatWidthForRun(FontData *renderer, const WebCoreTextRun *run,
     return runWidth;
 }
 
-static void updateGlyphMapEntry(FontData *renderer, UChar32 c, ATSGlyphRef glyph, FontData *substituteRenderer)
+void FontData::updateGlyphMapEntry(UChar c, ATSGlyphRef glyph, const FontData *substituteRenderer) const
 {
     GlyphMap *map;
-    for (map = renderer->m_characterToGlyphMap; map; map = map->next) {
+    for (map = m_characterToGlyphMap; map; map = map->next) {
         UChar32 start = map->startRange;
         if (c >= start && c <= map->endRange) {
             int i = c - start;
@@ -1088,7 +1087,7 @@ static void updateGlyphMapEntry(FontData *renderer, UChar32 c, ATSGlyphRef glyph
     }
 }
 
-static ATSGlyphRef extendGlyphMap(FontData *renderer, UChar32 c)
+static ATSGlyphRef extendGlyphMap(const FontData *renderer, UChar32 c)
 {
     GlyphMap *map = new GlyphMap;
     ATSLayoutRecord *glyphRecord;
@@ -1184,7 +1183,7 @@ static ATSGlyphRef extendGlyphMap(FontData *renderer, UChar32 c)
     return glyph;
 }
 
-static WidthMap *extendWidthMap(FontData *renderer, ATSGlyphRef glyph)
+static WidthMap *extendWidthMap(const FontData *renderer, ATSGlyphRef glyph)
 {
     WidthMap *map = new WidthMap;
     unsigned end;
@@ -1356,7 +1355,7 @@ static void createATSULayoutParameters(ATSULayoutParameters *params, FontData *r
         lastOffset = substituteOffset;
         status = ATSUMatchFontsToText(layout, substituteOffset, kATSUToTextEnd, &ATSUSubstituteFont, &substituteOffset, &substituteLength);
         if (status == kATSUFontsMatched || status == kATSUFontsNotMatched) {
-            substituteRenderer = findSubstituteRenderer(renderer, run->characters+substituteOffset, substituteLength, style->families);
+            substituteRenderer = (FontData*)findSubstituteRenderer(renderer, run->characters+substituteOffset, substituteLength, style->families);
             if (substituteRenderer) {
                 initializeATSUStyle(substituteRenderer);
                 if (substituteRenderer->m_ATSUStyle)
@@ -1376,8 +1375,8 @@ static void createATSULayoutParameters(ATSULayoutParameters *params, FontData *r
             if (i == substituteOffset || i == substituteOffset + substituteLength) {
                 if (isSmallCap) {
                     isSmallCap = false;
-                    initializeATSUStyle(getSmallCapsRenderer(r));
-                    ATSUSetRunStyle(layout, getSmallCapsRenderer(r)->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
+                    initializeATSUStyle(r->smallCapsFontData());
+                    ATSUSetRunStyle(layout, r->smallCapsFontData()->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
                 }
                 if (i == substituteOffset && substituteLength > 0)
                     r = substituteRenderer;
@@ -1390,19 +1389,19 @@ static void createATSULayoutParameters(ATSULayoutParameters *params, FontData *r
                 UniChar c = charBuffer[i];
                 UniChar newC;
                 if (U_GET_GC_MASK(c) & U_GC_M_MASK)
-                    renderers[i] = isSmallCap ? getSmallCapsRenderer(r) : r;
+                    renderers[i] = isSmallCap ? r->smallCapsFontData() : r;
                 else if (!u_isUUppercase(c) && (newC = u_toupper(c)) != c) {
                     charBuffer[i] = newC;
                     if (!isSmallCap) {
                         isSmallCap = true;
                         firstSmallCap = i;
                     }
-                    renderers[i] = getSmallCapsRenderer(r);
+                    renderers[i] = r->smallCapsFontData();
                 } else {
                     if (isSmallCap) {
                         isSmallCap = false;
-                        initializeATSUStyle(getSmallCapsRenderer(r));
-                        ATSUSetRunStyle(layout, getSmallCapsRenderer(r)->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
+                        initializeATSUStyle(r->smallCapsFontData());
+                        ATSUSetRunStyle(layout, r->smallCapsFontData()->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
                     }
                     renderers[i] = r;
                 }
@@ -1738,13 +1737,13 @@ static void freeGlyphMap(GlyphMap *map)
     }
 }
 
-static inline ATSGlyphRef glyphForCharacter(FontData **renderer, UChar32 c)
+inline Glyph FontData::glyphForCharacter(const FontData **renderer, unsigned c) const
 {
     // this loop is hot, so it is written to avoid LSU stalls
     GlyphMap *map;
     GlyphMap *nextMap;
     for (map = (*renderer)->m_characterToGlyphMap; map; map = nextMap) {
-        UChar32 start = map->startRange;
+        UChar start = map->startRange;
         nextMap = map->next;
         if (c >= start && c <= map->endRange) {
             GlyphEntry *ge = &map->glyphs[c - start];
@@ -1828,8 +1827,8 @@ static unsigned advanceWidthIterator(WidthIterator *iterator, unsigned offset, f
 
     const WebCoreTextStyle *style = iterator->style;
     bool rtl = style->rtl;
-    bool needCharTransform = rtl | style->smallCaps;
-    bool hasExtraSpacing = style->letterSpacing | style->wordSpacing | style->padding;
+    bool needCharTransform = rtl || style->smallCaps;
+    bool hasExtraSpacing = style->letterSpacing || style->wordSpacing || style->padding;
 
     float runWidthSoFar = iterator->runWidthSoFar;
     float lastRoundingWidth = iterator->finalRoundingWidth;
@@ -1865,7 +1864,7 @@ static unsigned advanceWidthIterator(WidthIterator *iterator, unsigned offset, f
             }
         }
 
-        FontData *renderer = iterator->renderer;
+        const FontData *renderer = iterator->renderer;
 
         if (needCharTransform) {
             if (rtl)
@@ -1876,19 +1875,19 @@ static unsigned advanceWidthIterator(WidthIterator *iterator, unsigned offset, f
                 UChar32 upperC = u_toupper(c);
                 if (upperC != c) {
                     c = upperC;
-                    renderer = getSmallCapsRenderer(renderer);
+                    renderer = renderer->smallCapsFontData();
                 }
             }
         }
 
-        ATSGlyphRef glyph = glyphForCharacter(&renderer, c);
+        Glyph glyph = renderer->glyphForCharacter(&renderer, c);
 
         // Now that we have glyph and font, get its width.
         WebGlyphWidth width;
         if (c == '\t' && style->tabWidth) {
             width = style->tabWidth - fmodf(style->xpos + runWidthSoFar, style->tabWidth);
         } else {
-            width = widthForGlyph(renderer, glyph);
+            width = renderer->widthForGlyph(glyph);
             // We special case spaces in two ways when applying word rounding.
             // First, we round spaces to an adjusted width in all fonts.
             // Second, in fixed-pitch fonts we ensure that all characters that
@@ -1900,7 +1899,7 @@ static unsigned advanceWidthIterator(WidthIterator *iterator, unsigned offset, f
         // Try to find a substitute font if this font didn't have a glyph for a character in the
         // string. If one isn't found we end up drawing and measuring the 0 glyph, usually a box.
         if (glyph == 0 && style->attemptFontSubstitution) {
-            FontData *substituteRenderer = findSubstituteRenderer(renderer, cp, clusterLength, style->families);
+            FontData *substituteRenderer = (FontData*)findSubstituteRenderer(renderer, cp, clusterLength, style->families);
             if (substituteRenderer) {
                 WebCoreTextRun clusterRun = { cp, clusterLength, 0, clusterLength };
                 WebCoreTextStyle clusterStyle = *style;
@@ -1917,7 +1916,7 @@ static unsigned advanceWidthIterator(WidthIterator *iterator, unsigned offset, f
                     assert(substituteRenderer == localRendererBuffer[0]);
                     width = localWidthBuffer[0];
                     glyph = localGlyphBuffer[0];
-                    updateGlyphMapEntry(renderer, c, glyph, substituteRenderer);
+                    renderer->updateGlyphMapEntry(c, glyph, substituteRenderer);
                     renderer = substituteRenderer;
                 }
             }
@@ -1982,7 +1981,7 @@ static unsigned advanceWidthIterator(WidthIterator *iterator, unsigned offset, f
             assert(renderersUsed);
             assert(glyphsUsed);
             *widths++ = (rtl ? oldWidth + lastRoundingWidth : width);
-            *renderersUsed++ = renderer;
+            *renderersUsed++ = (FontData*)renderer;
             *glyphsUsed++ = glyph;
         }
 
@@ -2015,7 +2014,7 @@ static bool fillStyleWithAttributes(ATSUStyle style, NSFont *theFont)
 
 static bool shouldUseATSU(const WebCoreTextRun *run)
 {
-    if (alwaysUseATSU)
+    if (Font::gAlwaysUseComplexPath)
         return YES;
         
     const UniChar *characters = run->characters;
index d44d88a..5a43491 100644 (file)
 
 #import "IntRect.h"
 
+#import "WebCoreSystemInterface.h"
+#import "WebCoreTextRenderer.h"
+
+#define SYNTHETIC_OBLIQUE_ANGLE 14
+
+#define POP_DIRECTIONAL_FORMATTING 0x202C
+#define LEFT_TO_RIGHT_OVERRIDE 0x202D
+#define RIGHT_TO_LEFT_OVERRIDE 0x202E
+
 using namespace std;
 
 namespace WebCore {
@@ -98,6 +107,373 @@ void FontFallbackList::invalidate()
     m_pitch = UnknownPitch;
 }
 
+// =================================================================
+// Font Class (Platform-Specific Portion)
+// =================================================================
+
+struct ATSULayoutParameters
+{
+    ATSULayoutParameters(UniChar* characters, int len, int from, int to, int toAdd, TextDirection dir, bool applyWordRounding, bool applyRunRounding)
+    :m_characters(characters), m_len(len), m_from(from), m_to(to), m_padding(toAdd), m_rtl(dir == RTL),
+     m_applyWordRounding(applyWordRounding), m_applyRunRounding(applyRunRounding),
+     m_font(0), m_fonts(0), m_charBuffer(0), m_hasSyntheticBold(false), m_syntheticBoldPass(false), m_padPerSpace(0)
+    {}
+
+    void initialize(const Font* font);
+
+    UniChar* m_characters;
+    int m_len;
+    int m_from;
+    int m_to;
+    int m_padding;
+    bool m_rtl;
+    bool m_applyWordRounding;
+    bool m_applyRunRounding;
+    
+    const Font* m_font;
+    
+    ATSUTextLayout m_layout;
+    const FontData **m_fonts;
+    
+    UniChar *m_charBuffer;
+    bool m_hasSyntheticBold;
+    bool m_syntheticBoldPass;
+    float m_padPerSpace;
+};
+
+// Be sure to free the array allocated by this function.
+static UniChar* addDirectionalOverride(UniChar* characters, int& len, int& from, int& to, bool rtl)
+{
+    UniChar *charactersWithOverride = new UniChar[len + 2];
+
+    charactersWithOverride[0] = rtl ? RIGHT_TO_LEFT_OVERRIDE : LEFT_TO_RIGHT_OVERRIDE;
+    memcpy(&charactersWithOverride[1], &characters[0], sizeof(UniChar) * len);
+    charactersWithOverride[len + 1] = POP_DIRECTIONAL_FORMATTING;
+
+    from++;
+    to++;
+    len += 2;
+
+    return charactersWithOverride;
+}
+
+static void initializeATSUStyle(const FontData* fontData)
+{
+    // The two NSFont calls in this method (pointSize and _atsFontID) do not raise exceptions.
+
+    if (!fontData->m_ATSUStyleInitialized) {
+        OSStatus status;
+        ByteCount propTableSize;
+        
+        status = ATSUCreateStyle(&fontData->m_ATSUStyle);
+        if (status != noErr)
+            LOG_ERROR("ATSUCreateStyle failed (%d)", status);
+    
+        ATSUFontID fontID = wkGetNSFontATSUFontId(fontData->m_font.font);
+        if (fontID == 0) {
+            ATSUDisposeStyle(fontData->m_ATSUStyle);
+            LOG_ERROR("unable to get ATSUFontID for %@", fontData->m_font.font);
+            return;
+        }
+        
+        CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
+        if (fontData->m_font.syntheticOblique)
+            transform = CGAffineTransformConcat(transform, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0)); 
+        Fixed fontSize = FloatToFixed([fontData->m_font.font pointSize]);
+        // Turn off automatic kerning until it is supported in the CG code path (6136 in bugzilla)
+        Fract kerningInhibitFactor = FloatToFract(1.0);
+        ATSUAttributeTag styleTags[4] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag, kATSUKerningInhibitFactorTag };
+        ByteCount styleSizes[4] = { sizeof(Fixed), sizeof(ATSUFontID), sizeof(CGAffineTransform), sizeof(Fract) };
+        ATSUAttributeValuePtr styleValues[4] = { &fontSize, &fontID, &transform, &kerningInhibitFactor };
+        status = ATSUSetAttributes(fontData->m_ATSUStyle, 4, styleTags, styleSizes, styleValues);
+        if (status != noErr)
+            LOG_ERROR("ATSUSetAttributes failed (%d)", status);
+        status = ATSFontGetTable(fontID, 'prop', 0, 0, 0, &propTableSize);
+        if (status == noErr)    // naively assume that if a 'prop' table exists then it contains mirroring info
+            fontData->m_ATSUMirrors = true;
+        else if (status == kATSInvalidFontTableAccess)
+            fontData->m_ATSUMirrors = false;
+        else
+            LOG_ERROR("ATSFontGetTable failed (%d)", status);
+
+        // Turn off ligatures such as 'fi' to match the CG code path's behavior, until bugzilla 6135 is fixed.
+        // Don't be too aggressive: if the font doesn't contain 'a', then assume that any ligatures it contains are
+        // in characters that always go through ATSUI, and therefore allow them. Geeza Pro is an example.
+        // See bugzilla 5166.
+        if ([[fontData->m_font.font coveredCharacterSet] characterIsMember:'a']) {
+            ATSUFontFeatureType featureTypes[] = { kLigaturesType };
+            ATSUFontFeatureSelector featureSelectors[] = { kCommonLigaturesOffSelector };
+            status = ATSUSetFontFeatures(fontData->m_ATSUStyle, 1, featureTypes, featureSelectors);
+        }
+
+        fontData->m_ATSUStyleInitialized = true;
+    }
+}
+
+static OSStatus overrideLayoutOperation(ATSULayoutOperationSelector iCurrentOperation, ATSULineRef iLineRef, UInt32 iRefCon,
+                                        void *iOperationCallbackParameterPtr, ATSULayoutOperationCallbackStatus *oCallbackStatus)
+{
+    ATSULayoutParameters *params = (ATSULayoutParameters *)iRefCon;
+    OSStatus status;
+    ItemCount count;
+    ATSLayoutRecord *layoutRecords;
+
+    if (params->m_applyWordRounding) {
+        status = ATSUDirectGetLayoutDataArrayPtrFromLineRef(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, true, (void **)&layoutRecords, &count);
+        if (status != noErr) {
+            *oCallbackStatus = kATSULayoutOperationCallbackStatusContinue;
+            return status;
+        }
+        
+        Fixed lastNativePos = 0;
+        float lastAdjustedPos = 0;
+        const UniChar *characters = params->m_characters + params->m_from;
+        const FontData **renderers = params->m_fonts + params->m_from;
+        const FontData *renderer;
+        const FontData *lastRenderer = 0;
+        UniChar ch, nextCh;
+        ByteCount offset = layoutRecords[0].originalOffset;
+        nextCh = *(UniChar *)(((char *)characters)+offset);
+        bool shouldRound = false;
+        bool syntheticBoldPass = params->m_syntheticBoldPass;
+        Fixed syntheticBoldOffset = 0;
+        ATSGlyphRef spaceGlyph = 0;
+        bool hasExtraSpacing = params->m_font->letterSpacing() || params->m_font->wordSpacing() | params->m_padding;
+        float padding = params->m_padding;
+        // In the CoreGraphics code path, the rounding hack is applied in logical order.
+        // Here it is applied in visual left-to-right order, which may be better.
+        ItemCount lastRoundingChar = 0;
+        ItemCount i;
+        for (i = 1; i < count; i++) {
+            bool isLastChar = i == count - 1;
+            renderer = renderers[offset / 2];
+            if (renderer != lastRenderer) {
+                lastRenderer = renderer;
+                // The CoreGraphics interpretation of NSFontAntialiasedIntegerAdvancementsRenderingMode seems
+                // to be "round each glyph's width to the nearest integer". This is not the same as ATSUI
+                // does in any of its device-metrics modes.
+                shouldRound = [renderer->m_font.font renderingMode] == NSFontAntialiasedIntegerAdvancementsRenderingMode;
+                if (syntheticBoldPass) {
+                    syntheticBoldOffset = FloatToFixed(renderer->m_syntheticBoldOffset);
+                    spaceGlyph = renderer->m_spaceGlyph;
+                }
+            }
+            float width = FixedToFloat(layoutRecords[i].realPos - lastNativePos);
+            lastNativePos = layoutRecords[i].realPos;
+            if (shouldRound)
+                width = roundf(width);
+            width += renderer->m_syntheticBoldOffset;
+            if (renderer->m_treatAsFixedPitch ? width == renderer->m_spaceWidth : (layoutRecords[i-1].flags & kATSGlyphInfoIsWhiteSpace))
+                width = renderer->m_adjustedSpaceWidth;
+
+            if (hasExtraSpacing) {
+                if (width && params->m_font->letterSpacing())
+                    width +=params->m_font->letterSpacing();
+                if (isSpace(nextCh)) {
+                    if (params->m_padding) {
+                        if (padding < params->m_padPerSpace) {
+                            width += padding;
+                            padding = 0;
+                        } else {
+                            width += params->m_padPerSpace;
+                            padding -= params->m_padPerSpace;
+                        }
+                    }
+                    if (offset != 0 && !isSpace(*((UniChar *)(((char *)characters)+offset) - 1)) && params->m_font->wordSpacing())
+                        width += params->m_font->wordSpacing();
+                }
+            }
+
+            ch = nextCh;
+            offset = layoutRecords[i].originalOffset;
+            // Use space for nextCh at the end of the loop so that we get inside the rounding hack code.
+            // We won't actually round unless the other conditions are satisfied.
+            nextCh = isLastChar ? ' ' : *(UniChar *)(((char *)characters)+offset);
+
+            if (isRoundingHackCharacter(ch))
+                width = ceilf(width);
+            lastAdjustedPos = lastAdjustedPos + width;
+            if (isRoundingHackCharacter(nextCh)
+                && (!isLastChar
+                    || params->m_applyRunRounding
+                    || (params->m_to < (int)params->m_len && isRoundingHackCharacter(characters[params->m_to - params->m_from])))) {
+                if (!params->m_rtl)
+                    lastAdjustedPos = ceilf(lastAdjustedPos);
+                else {
+                    float roundingWidth = ceilf(lastAdjustedPos) - lastAdjustedPos;
+                    Fixed rw = FloatToFixed(roundingWidth);
+                    ItemCount j;
+                    for (j = lastRoundingChar; j < i; j++)
+                        layoutRecords[j].realPos += rw;
+                    lastRoundingChar = i;
+                    lastAdjustedPos += roundingWidth;
+                }
+            }
+            if (syntheticBoldPass) {
+                if (syntheticBoldOffset)
+                    layoutRecords[i-1].realPos += syntheticBoldOffset;
+                else
+                    layoutRecords[i-1].glyphID = spaceGlyph;
+            }
+            layoutRecords[i].realPos = FloatToFixed(lastAdjustedPos);
+        }
+        
+        status = ATSUDirectReleaseLayoutDataArrayPtr(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&layoutRecords);
+    }
+    *oCallbackStatus = kATSULayoutOperationCallbackStatusHandled;
+    return noErr;
+}
+
+void ATSULayoutParameters::initialize(const Font* font)
+{
+    m_font = font;
+
+    // FIXME: It is probably best to always allocate a buffer for RTL, since even if for this
+    // fontData ATSUMirrors is true, for a substitute fontData it might be false.
+    const FontData* fontData = font->primaryFont();
+    m_fonts = new const FontData*[m_len];
+    m_charBuffer = (UniChar*)((font->isSmallCaps() || (m_rtl && !fontData->m_ATSUMirrors)) ? new UniChar[m_len] : 0);
+    
+    // The only Cocoa calls here are to NSGraphicsContext, which does not raise exceptions.
+
+    ATSUTextLayout layout;
+    OSStatus status;
+    ATSULayoutOperationOverrideSpecifier overrideSpecifier;
+    
+    initializeATSUStyle(fontData);
+    
+    // FIXME: This is currently missing the following required features that the CoreGraphics code path has:
+    // - \n, \t, and nonbreaking space render as a space.
+    // - Other control characters do not render (other code path uses zero-width spaces).
+
+    UniCharCount totalLength = m_len;
+    UniCharArrayOffset runTo = (m_to == -1 ? totalLength : (unsigned int)m_to);
+    UniCharArrayOffset runFrom = m_from;
+    
+    if (m_charBuffer)
+        memcpy(m_charBuffer, m_characters, totalLength * sizeof(UniChar));
+
+    UniCharCount runLength = runTo - runFrom;
+    
+    status = ATSUCreateTextLayoutWithTextPtr(
+            (m_charBuffer ? m_charBuffer : m_characters),
+            runFrom,        // offset
+            runLength,      // length
+            totalLength,    // total length
+            1,              // styleRunCount
+            &runLength,     // length of style run
+            &fontData->m_ATSUStyle, 
+            &layout);
+    if (status != noErr)
+        LOG_ERROR("ATSUCreateTextLayoutWithTextPtr failed(%d)", status);
+    m_layout = layout;
+    ATSUSetTextLayoutRefCon(m_layout, (UInt32)this);
+
+    CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+    ATSLineLayoutOptions lineLayoutOptions = kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers;
+    Boolean rtl = m_rtl;
+    overrideSpecifier.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
+    overrideSpecifier.overrideUPP = overrideLayoutOperation;
+    ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineLayoutOptionsTag, kATSULineDirectionTag, kATSULayoutOperationOverrideTag };
+    ByteCount sizes[] = { sizeof(CGContextRef), sizeof(ATSLineLayoutOptions), sizeof(Boolean), sizeof(ATSULayoutOperationOverrideSpecifier) };
+    ATSUAttributeValuePtr values[] = { &cgContext, &lineLayoutOptions, &rtl, &overrideSpecifier };
+    
+    status = ATSUSetLayoutControls(layout, (m_applyWordRounding ? 4 : 3), tags, sizes, values);
+    if (status != noErr)
+        LOG_ERROR("ATSUSetLayoutControls failed(%d)", status);
+
+    status = ATSUSetTransientFontMatching(layout, YES);
+    if (status != noErr)
+        LOG_ERROR("ATSUSetTransientFontMatching failed(%d)", status);
+
+    m_hasSyntheticBold = false;
+    ATSUFontID ATSUSubstituteFont;
+    UniCharArrayOffset substituteOffset = runFrom;
+    UniCharCount substituteLength;
+    UniCharArrayOffset lastOffset;
+    const FontData* substituteFontData = 0;
+
+    while (substituteOffset < runTo) {
+        lastOffset = substituteOffset;
+        status = ATSUMatchFontsToText(layout, substituteOffset, kATSUToTextEnd, &ATSUSubstituteFont, &substituteOffset, &substituteLength);
+        if (status == kATSUFontsMatched || status == kATSUFontsNotMatched) {
+            // FIXME: Should go through fallback list eventually.
+            substituteFontData = fontData->findSubstituteFontData(m_characters + substituteOffset, substituteLength, m_font->fontDescription());
+            if (substituteFontData) {
+                initializeATSUStyle(substituteFontData);
+                if (substituteFontData->m_ATSUStyle)
+                    ATSUSetRunStyle(layout, substituteFontData->m_ATSUStyle, substituteOffset, substituteLength);
+            } else
+                substituteFontData = fontData;
+        } else {
+            substituteOffset = runTo;
+            substituteLength = 0;
+        }
+
+        bool isSmallCap = false;
+        UniCharArrayOffset firstSmallCap = 0;
+        const FontData *r = fontData;
+        UniCharArrayOffset i;
+        for (i = lastOffset;  ; i++) {
+            if (i == substituteOffset || i == substituteOffset + substituteLength) {
+                if (isSmallCap) {
+                    isSmallCap = false;
+                    initializeATSUStyle(r->smallCapsFontData());
+                    ATSUSetRunStyle(layout, r->smallCapsFontData()->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
+                }
+                if (i == substituteOffset && substituteLength > 0)
+                    r = substituteFontData;
+                else
+                    break;
+            }
+            if (m_rtl && m_charBuffer && !r->m_ATSUMirrors)
+                m_charBuffer[i] = u_charMirror(m_charBuffer[i]);
+            if (m_font->isSmallCaps()) {
+                UniChar c = m_charBuffer[i];
+                UniChar newC;
+                if (U_GET_GC_MASK(c) & U_GC_M_MASK)
+                    m_fonts[i] = isSmallCap ? r->smallCapsFontData() : r;
+                else if (!u_isUUppercase(c) && (newC = u_toupper(c)) != c) {
+                    m_charBuffer[i] = newC;
+                    if (!isSmallCap) {
+                        isSmallCap = true;
+                        firstSmallCap = i;
+                    }
+                    m_fonts[i] = r->smallCapsFontData();
+                } else {
+                    if (isSmallCap) {
+                        isSmallCap = false;
+                        initializeATSUStyle(r->smallCapsFontData());
+                        ATSUSetRunStyle(layout, r->smallCapsFontData()->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
+                    }
+                    m_fonts[i] = r;
+                }
+            } else
+                m_fonts[i] = r;
+            if (m_fonts[i]->m_syntheticBoldOffset)
+                m_hasSyntheticBold = true;
+        }
+        substituteOffset += substituteLength;
+    }
+    if (m_padding) {
+        float numSpaces = 0;
+        unsigned k;
+        for (k = 0; k < totalLength; k++)
+            if (isSpace(m_characters[k]))
+                numSpaces++;
+
+        m_padPerSpace = ceilf(m_padding / numSpaces);
+    } else
+        m_padPerSpace = 0;
+}
+
+static void disposeATSULayoutParameters(ATSULayoutParameters *params)
+{
+    ATSUDisposeTextLayout(params->m_layout);
+    delete []params->m_charBuffer;
+    delete []params->m_fonts;
+}
+
 const FontPlatformData& Font::platformFont() const
 {
     return m_fontList->platformFont(fontDescription());
@@ -138,38 +514,51 @@ IntRect Font::selectionRectForText(const IntPoint& point, int h, int tabWidth, i
     geometry.useFontMetricsForSelectionYAndHeight = false;
     return enclosingIntRect(m_fontList->primaryFont(fontDescription())->selectionRectForRun(&run, &style, &geometry));
 }
-                     
-void Font::drawText(GraphicsContext* context, const IntPoint& point, int tabWidth, int xpos, const UChar* str, int len, int from, int to,
-                    int toAdd, TextDirection d, bool visuallyOrdered) const
+void Font::drawComplexText(GraphicsContext* graphicsContext, const IntPoint& point, int tabWidth, int xpos, const UChar* str, int len, int from, int to,
+                           int toAdd, TextDirection d, bool visuallyOrdered) const
 {
-    // Avoid allocations, use stack array to pass font families.  Normally these
-    // css fallback lists are small <= 3.
-    CREATE_FAMILY_ARRAY(*this, families);
+    int runLength = to - from;
+    if (runLength <= 0)
+        return;
 
-    if (from < 0)
-        from = 0;
-    if (to < 0)
-        to = len;
-        
-    WebCoreTextRun run;
-    WebCoreInitializeTextRun(&run, str, len, from, to);    
-    WebCoreTextStyle style;
-    WebCoreInitializeEmptyTextStyle(&style);
-    style.textColor = nsColor(context->pen().color());
-    style.backgroundColor = nil;
-    style.rtl = d == RTL;
-    style.directionalOverride = visuallyOrdered;
-    style.letterSpacing = letterSpacing();
-    style.wordSpacing = wordSpacing();
-    style.smallCaps = isSmallCaps();
-    style.families = families;
-    style.padding = toAdd;
-    style.tabWidth = tabWidth;
-    style.xpos = xpos;
-    WebCoreTextGeometry geometry;
-    WebCoreInitializeEmptyTextGeometry(&geometry);
-    geometry.point = point;
-    m_fontList->primaryFont(fontDescription())->drawRun(&run, &style, &geometry);
+    OSStatus status;
+    UniChar* characters = (UniChar*)str;
+    if (visuallyOrdered)
+        characters = addDirectionalOverride(characters, len, from, to, d == RTL);
+
+    ATSULayoutParameters params(characters, len, 0, len, toAdd, d, true, true);
+    params.initialize(this);
+
+    [nsColor(graphicsContext->pen().color()) set];
+
+    // ATSUI can't draw beyond -32768 to +32767 so we translate the CTM and tell ATSUI to draw at (0, 0).
+    // FIXME: Cut the dependency on currentContext here.
+    NSGraphicsContext *gContext = [NSGraphicsContext currentContext];
+    CGContextRef context = (CGContextRef)[gContext graphicsPort];
+    CGContextTranslateCTM(context, point.x(), point.y());
+    bool flipped = [gContext isFlipped];
+    if (!flipped)
+        CGContextScaleCTM(context, 1.0, -1.0);
+    status = ATSUDrawText(params.m_layout, from, runLength, 0, 0);
+    if (status == noErr && params.m_hasSyntheticBold) {
+        // Force relayout for the bold pass
+        ATSUClearLayoutCache(params.m_layout, 0);
+        params.m_syntheticBoldPass = true;
+        status = ATSUDrawText(params.m_layout, from, runLength, 0, 0);
+    }
+    if (!flipped)
+        CGContextScaleCTM(context, 1.0, -1.0);
+    CGContextTranslateCTM(context, -point.x(), -point.y());
+
+    if (status != noErr) {
+        // Nothing to do but report the error (dev build only).
+        LOG_ERROR("ATSUDrawText() failed(%d)", status);
+    }
+
+    disposeATSULayoutParameters(&params);
+    
+    if (visuallyOrdered)
+        delete []characters;
 }
 
 void Font::drawHighlightForText(GraphicsContext* context, const IntPoint& point, int h, int tabWidth, int xpos, const UChar* str,
@@ -224,25 +613,28 @@ int Font::misspellingLineThickness(GraphicsContext* context) const
     return m_fontList->primaryFont(fontDescription())->misspellingLineThickness();
 }
 
-float Font::floatWidth(const UChar* uchars, int slen, int pos, int len, int tabWidth, int xpos, bool runRounding) const
+float Font::floatWidthForComplexText(const UChar* uchars, int len, int from, int to, int tabWidth, int xpos, bool runRounding) const
 {
-    assert(m_fontList);
-    CREATE_FAMILY_ARRAY(fontDescription(), families);
+    if (to - from <= 0)
+        return 0;
 
-    WebCoreTextRun run;
-    WebCoreInitializeTextRun(&run, uchars, slen, pos, pos + len);
+    ATSULayoutParameters params((UniChar*)uchars, len, from, to, 0, LTR, true, runRounding);
+    params.initialize(this);
     
-    WebCoreTextStyle style;
-    WebCoreInitializeEmptyTextStyle(&style);
-    style.tabWidth = tabWidth;
-    style.xpos = xpos;
-    style.letterSpacing = letterSpacing();
-    style.wordSpacing = wordSpacing();
-    style.smallCaps = fontDescription().smallCaps();
-    style.families = families;
-    style.applyRunRounding = runRounding;
+    OSStatus status;
+    
+    ATSTrapezoid firstGlyphBounds;
+    ItemCount actualNumBounds;
+    status = ATSUGetGlyphBounds(params.m_layout, 0, 0, from, to - from, kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds);    
+    if (status != noErr)
+        LOG_ERROR("ATSUGetGlyphBounds() failed(%d)", status);
+    if (actualNumBounds != 1)
+        LOG_ERROR("unexpected result from ATSUGetGlyphBounds(): actualNumBounds(%d) != 1", actualNumBounds);
 
-    return m_fontList->primaryFont(fontDescription())->floatWidthForRun(&run, &style);
+    disposeATSULayoutParameters(&params);
+
+    return MAX(FixedToFloat(firstGlyphBounds.upperRight.x), FixedToFloat(firstGlyphBounds.lowerRight.x)) -
+           MIN(FixedToFloat(firstGlyphBounds.upperLeft.x), FixedToFloat(firstGlyphBounds.lowerLeft.x));
 }
 
 int Font::checkSelectionPoint(const UChar* s, int slen, int pos, int len, int toAdd, int tabWidth, int xpos, int x, TextDirection d, bool visuallyOrdered, bool includePartialGlyphs) const
@@ -268,4 +660,56 @@ int Font::checkSelectionPoint(const UChar* s, int slen, int pos, int len, int to
     return m_fontList->primaryFont(fontDescription())->pointToOffset(&run, &style, x, includePartialGlyphs);
 }
 
+void Font::drawGlyphs(GraphicsContext* context, const FontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
+{
+    // FIXME: Grab the CGContext from the GraphicsContext eventually, when we have made sure to shield against flipping caused by calls into us
+    // from Safari.
+    NSGraphicsContext *gContext = [NSGraphicsContext currentContext];
+    CGContextRef cgContext = (CGContextRef)[gContext graphicsPort];
+
+    bool originalShouldUseFontSmoothing = wkCGContextGetShouldSmoothFonts(cgContext);
+    CGContextSetShouldSmoothFonts(cgContext, WebCoreShouldUseFontSmoothing());
+    
+    const FontPlatformData& platformData = font->platformData();
+    NSFont* drawFont;
+    if ([gContext isDrawingToScreen]) {
+        drawFont = [platformData.font screenFont];
+        if (drawFont != platformData.font)
+            // We are getting this in too many places (3406411); use ERROR so it only prints on debug versions for now. (We should debug this also, eventually).
+            LOG_ERROR("Attempting to set non-screen font (%@) when drawing to screen.  Using screen font anyway, may result in incorrect metrics.",
+                [[[platformData.font fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]);
+    } else {
+        drawFont = [platformData.font printerFont];
+        if (drawFont != platformData.font)
+            NSLog(@"Attempting to set non-printer font (%@) when printing.  Using printer font anyway, may result in incorrect metrics.",
+                [[[platformData.font fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]);
+    }
+    
+    CGContextSetFont(cgContext, wkGetCGFontFromNSFont(drawFont));
+
+    CGAffineTransform matrix;
+    memcpy(&matrix, [drawFont matrix], sizeof(matrix));
+    if ([gContext isFlipped]) {
+        matrix.b = -matrix.b;
+        matrix.d = -matrix.d;
+    }
+    if (platformData.syntheticOblique)
+        matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0)); 
+    CGContextSetTextMatrix(cgContext, matrix);
+
+    wkSetCGFontRenderingMode(cgContext, drawFont);
+    CGContextSetFontSize(cgContext, 1.0f);
+
+    [nsColor(context->pen().color()) set];
+
+    CGContextSetTextPosition(cgContext, point.x(), point.y());
+    CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
+    if (font->m_syntheticBoldOffset) {
+        CGContextSetTextPosition(cgContext, point.x() + font->m_syntheticBoldOffset, point.y());
+        CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
+    }
+
+    CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing);
+}
+
 }
index 5ba8e74..728a066 100644 (file)
@@ -26,6 +26,7 @@
 #import "config.h"
 #import "WebCoreTextRenderer.h"
 
+#import "Font.h"
 #import "FontData.h"
 #import "WebTextRendererFactory.h"
 
@@ -81,7 +82,7 @@ bool WebCoreShouldUseFontSmoothing()
 
 void WebCoreSetAlwaysUseATSU(bool useATSU)
 {
-    FontData::setAlwaysUseATSU(useATSU);
+    Font::setAlwaysUseComplexPath(useATSU);
 }
 
 NSFont* WebCoreFindFont(NSString* familyName, NSFontTraitMask traits, int size)