[SCM] WebKit Debian packaging branch, debian/unstable, updated. debian/1.1.15-1-40151-g37bb677

rjw rjw at 268f45cc-cd09-0410-ab3c-d52691b4dbfc
Sat Sep 26 08:01:12 UTC 2009


The following commit has been merged in the debian/unstable branch:
commit 94038753882c03e047370608e1086e9fde3ea77f
Author: rjw <rjw at 268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Tue Oct 14 00:39:11 2003 +0000

    	Added support for small-caps.
    
    	Reworked drawing and measuring to use new iterators.  Position checking was already using
    	the new iterator code, but I was reluctant to switch the mainline drawing and measuring
    	code over to the new approach until now.
    
    	Lots of other code cleanup.
    
            Reviewed by John.
    
            * Misc.subproj/WebUnicode.m:
            (initializeCharacterShapeIterator):
            * WebCoreSupport.subproj/WebTextRenderer.h:
            * WebCoreSupport.subproj/WebTextRenderer.m:
            (+[WebTextRenderer shouldBufferTextDrawing]):
            (+[WebTextRenderer initialize]):
            (-[WebTextRenderer initWithFont:usingPrinterFont:]):
            (-[WebTextRenderer dealloc]):
            (-[WebTextRenderer widthForCharacters:length:]):
            (-[WebTextRenderer widthForString:]):
            (-[WebTextRenderer ascent]):
            (-[WebTextRenderer descent]):
            (-[WebTextRenderer lineSpacing]):
            (-[WebTextRenderer xHeight]):
            (-[WebTextRenderer drawRun:style:atPoint:]):
            (-[WebTextRenderer floatWidthForRun:style:widths:]):
            (-[WebTextRenderer drawLineForCharacters:yOffset:withWidth:withColor:]):
            (-[WebTextRenderer drawHighlightForRun:style:atPoint:]):
            (-[WebTextRenderer pointToOffset:style:position:reversed:]):
            (-[WebTextRenderer _setIsSmallCapsRenderer:]):
            (-[WebTextRenderer _isSmallCapsRenderer]):
            (-[WebTextRenderer _smallCapsRenderer]):
            (-[WebTextRenderer _smallCapsFont]):
            (-[WebTextRenderer _substituteFontForString:families:]):
            (-[WebTextRenderer _substituteFontForCharacters:length:families:]):
            (-[WebTextRenderer _convertCharacters:length:toGlyphs:skipControlCharacters:]):
            (-[WebTextRenderer _convertUnicodeCharacters:length:toGlyphs:]):
            (-[WebTextRenderer _computeWidthForSpace]):
            (-[WebTextRenderer _setupFont]):
            (_drawGlyphs):
            (-[WebTextRenderer _CG_drawHighlightForRun:style:atPoint:]):
            (-[WebTextRenderer _CG_drawRun:style:atPoint:]):
            (-[WebTextRenderer _floatWidthForRun:style:widths:fonts:glyphs:startPosition:numGlyphs:]):
            (-[WebTextRenderer _CG_floatWidthForRun:style:widths:fonts:glyphs:startPosition:numGlyphs:]):
            (-[WebTextRenderer _extendUnicodeCharacterToGlyphMapToInclude:]):
            (-[WebTextRenderer _updateGlyphEntryForCharacter:glyphID:font:]):
            (-[WebTextRenderer _extendCharacterToGlyphMapToInclude:]):
            (-[WebTextRenderer _extendGlyphToWidthMapToInclude:font:]):
            (-[WebTextRenderer _trapezoidForRun:style:atPoint:]):
            (-[WebTextRenderer _ATSU_floatWidthForRun:style:]):
            (-[WebTextRenderer _ATSU_drawHighlightForRun:style:atPoint:]):
            (-[WebTextRenderer _ATSU_drawRun:style:atPoint:]):
            (-[WebTextRenderer _ATSU_pointToOffset:style:position:reversed:]):
            (-[WebTextRenderer _CG_pointToOffset:style:position:reversed:]):
            (freeWidthMap):
            (freeGlyphMap):
            (glyphForCharacter):
            (glyphForUnicodeCharacter):
            (mapForSubstituteFont):
            (widthFromMap):
            (widthForGlyph):
            (initializeCharacterWidthIterator):
            (widthAndGlyphForSurrogate):
            (ceilCurrentWidth):
            (widthForNextCharacter):
            (fillStyleWithAttributes):
            (findLengthOfCharacterCluster):
            (shouldUseATSU):
            (isControlCharacter):
            (isAlternateSpace):
            (isSpace):
            (fontContainsString):
            (GetScratchUniCharString):
            (toUpper):
            (isUpper):
    
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@5175 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/WebKit/ChangeLog b/WebKit/ChangeLog
index 4c63296..8397ab0 100644
--- a/WebKit/ChangeLog
+++ b/WebKit/ChangeLog
@@ -1,3 +1,81 @@
+2003-10-13  Richard Williamson  <rjw at apple.com>
+
+	Added support for small-caps.
+
+	Reworked drawing and measuring to use new iterators.  Position checking was already using
+	the new iterator code, but I was reluctant to switch the mainline drawing and measuring
+	code over to the new approach until now.
+
+	Lots of other code cleanup.
+
+        Reviewed by John.
+
+        * Misc.subproj/WebUnicode.m:
+        (initializeCharacterShapeIterator):
+        * WebCoreSupport.subproj/WebTextRenderer.h:
+        * WebCoreSupport.subproj/WebTextRenderer.m:
+        (+[WebTextRenderer shouldBufferTextDrawing]):
+        (+[WebTextRenderer initialize]):
+        (-[WebTextRenderer initWithFont:usingPrinterFont:]):
+        (-[WebTextRenderer dealloc]):
+        (-[WebTextRenderer widthForCharacters:length:]):
+        (-[WebTextRenderer widthForString:]):
+        (-[WebTextRenderer ascent]):
+        (-[WebTextRenderer descent]):
+        (-[WebTextRenderer lineSpacing]):
+        (-[WebTextRenderer xHeight]):
+        (-[WebTextRenderer drawRun:style:atPoint:]):
+        (-[WebTextRenderer floatWidthForRun:style:widths:]):
+        (-[WebTextRenderer drawLineForCharacters:yOffset:withWidth:withColor:]):
+        (-[WebTextRenderer drawHighlightForRun:style:atPoint:]):
+        (-[WebTextRenderer pointToOffset:style:position:reversed:]):
+        (-[WebTextRenderer _setIsSmallCapsRenderer:]):
+        (-[WebTextRenderer _isSmallCapsRenderer]):
+        (-[WebTextRenderer _smallCapsRenderer]):
+        (-[WebTextRenderer _smallCapsFont]):
+        (-[WebTextRenderer _substituteFontForString:families:]):
+        (-[WebTextRenderer _substituteFontForCharacters:length:families:]):
+        (-[WebTextRenderer _convertCharacters:length:toGlyphs:skipControlCharacters:]):
+        (-[WebTextRenderer _convertUnicodeCharacters:length:toGlyphs:]):
+        (-[WebTextRenderer _computeWidthForSpace]):
+        (-[WebTextRenderer _setupFont]):
+        (_drawGlyphs):
+        (-[WebTextRenderer _CG_drawHighlightForRun:style:atPoint:]):
+        (-[WebTextRenderer _CG_drawRun:style:atPoint:]):
+        (-[WebTextRenderer _floatWidthForRun:style:widths:fonts:glyphs:startPosition:numGlyphs:]):
+        (-[WebTextRenderer _CG_floatWidthForRun:style:widths:fonts:glyphs:startPosition:numGlyphs:]):
+        (-[WebTextRenderer _extendUnicodeCharacterToGlyphMapToInclude:]):
+        (-[WebTextRenderer _updateGlyphEntryForCharacter:glyphID:font:]):
+        (-[WebTextRenderer _extendCharacterToGlyphMapToInclude:]):
+        (-[WebTextRenderer _extendGlyphToWidthMapToInclude:font:]):
+        (-[WebTextRenderer _trapezoidForRun:style:atPoint:]):
+        (-[WebTextRenderer _ATSU_floatWidthForRun:style:]):
+        (-[WebTextRenderer _ATSU_drawHighlightForRun:style:atPoint:]):
+        (-[WebTextRenderer _ATSU_drawRun:style:atPoint:]):
+        (-[WebTextRenderer _ATSU_pointToOffset:style:position:reversed:]):
+        (-[WebTextRenderer _CG_pointToOffset:style:position:reversed:]):
+        (freeWidthMap):
+        (freeGlyphMap):
+        (glyphForCharacter):
+        (glyphForUnicodeCharacter):
+        (mapForSubstituteFont):
+        (widthFromMap):
+        (widthForGlyph):
+        (initializeCharacterWidthIterator):
+        (widthAndGlyphForSurrogate):
+        (ceilCurrentWidth):
+        (widthForNextCharacter):
+        (fillStyleWithAttributes):
+        (findLengthOfCharacterCluster):
+        (shouldUseATSU):
+        (isControlCharacter):
+        (isAlternateSpace):
+        (isSpace):
+        (fontContainsString):
+        (GetScratchUniCharString):
+        (toUpper):
+        (isUpper):
+
 2003-10-10  Maciej Stachowiak  <mjs at apple.com>
 
         * English.lproj/StringsNotToBeLocalized.txt: Fixed for Private
diff --git a/WebKit/Misc.subproj/WebUnicode.m b/WebKit/Misc.subproj/WebUnicode.m
index 93b8b4f..bc27522 100644
--- a/WebKit/Misc.subproj/WebUnicode.m
+++ b/WebKit/Misc.subproj/WebUnicode.m
@@ -586,7 +586,7 @@ bool initializeCharacterShapeIterator (CharacterShapeIterator *iterator, const W
 
     int i;
     for (i = from; i < from+len; i++){
-        if (uc[i] > 0x7f)
+        if (uc[i] >= 0x591 && uc[i] <= 0x700)
             break;
     }
     if (i == from+len)
@@ -616,6 +616,7 @@ bool initializeCharacterShapeIterator (CharacterShapeIterator *iterator, const W
         shapeBufSize = len;
     }
     
+    iterator->currentCharacter = uc + run->from;
     iterator->run = run;
     
     return true;
diff --git a/WebKit/WebCoreSupport.subproj/WebTextRenderer.h b/WebKit/WebCoreSupport.subproj/WebTextRenderer.h
index 39ce16f..a52574f 100644
--- a/WebKit/WebCoreSupport.subproj/WebTextRenderer.h
+++ b/WebKit/WebCoreSupport.subproj/WebTextRenderer.h
@@ -21,11 +21,11 @@ typedef struct CharacterWidthIterator CharacterWidthIterator;
     int ascent;
     int descent;
     int lineSpacing;
-    ATSGlyphRef spaceGlyph;
     
     ATSStyleGroupPtr styleGroup;
     
 @public
+    ATSGlyphRef spaceGlyph;
     NSFont *font;
     GlyphMap *characterToGlyphMap;			// Used for 16bit clean unicode characters.
     UnicodeGlyphMap *unicodeCharacterToGlyphMap; 	// Used for surrogates.
@@ -39,8 +39,11 @@ typedef struct CharacterWidthIterator CharacterWidthIterator;
     int maxSubstituteFontWidthMaps;
     SubstituteFontWidthMap *substituteFontWidthMaps;
     BOOL usingPrinterFont;
+    BOOL isSmallCapsRenderer;
     
 @private
+    WebTextRenderer *smallCapsRenderer;
+    NSFont *smallCapsFont;
     ATSUStyle _ATSUSstyle;
     BOOL ATSUStyleInitialized;
 }
diff --git a/WebKit/WebCoreSupport.subproj/WebTextRenderer.m b/WebKit/WebCoreSupport.subproj/WebTextRenderer.m
index be02a46..b083f64 100644
--- a/WebKit/WebCoreSupport.subproj/WebTextRenderer.m
+++ b/WebKit/WebCoreSupport.subproj/WebTextRenderer.m
@@ -5,10 +5,13 @@
 
 #import "WebTextRenderer.h"
 
+#import <ApplicationServices/ApplicationServices.h>
 #import <Cocoa/Cocoa.h>
 
-#import <ApplicationServices/ApplicationServices.h>
 #import <CoreGraphics/CoreGraphicsPrivate.h>
+#import <QD/ATSUnicodePriv.h>
+
+#import <AppKit/NSFont_Private.h>
 
 #import <WebCore/WebCoreUnicode.h>
 
@@ -17,40 +20,10 @@
 #import <WebKit/WebTextRendererFactory.h>
 #import <WebKit/WebUnicode.h>
 
-#import <QD/ATSUnicodePriv.h>
-
-#import <AppKit/NSFont_Private.h>
-
 #import <float.h>
 
-#define SPACE 0x0020
-
-#define ROUND_TO_INT(x) (unsigned)((x)+.5)
-
-// Lose precision beyond 1000ths place. This is to work around an apparent
-// bug in CoreGraphics where there seem to be small errors to some metrics.
-#define CEIL_TO_INT(x) ((int)(x + 0.999)) /* ((int)(x + 1.0 - FLT_EPSILON)) */
-
-// 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 LOCAL_BUFFER_SIZE 2048
-
-// Covers Latin1.
-#define INITIAL_BLOCK_SIZE 0x200
-
-// Get additional blocks of glyphs and widths in bigger chunks.
-// This will typically be for other character sets.
-#define INCREMENTAL_BLOCK_SIZE 0x400
-
-#define UNINITIALIZED_GLYPH_WIDTH 65535
-
-// combining char, hangul jamo, or Apple corporate variant tag
-#define JunseongStart 0x1160
-#define JonseongEnd 0x11F9
-#define IsHangulConjoiningJamo(X) (X >= JunseongStart && X <= JonseongEnd)
-#define IsNonBaseChar(X) ((CFCharacterSetIsCharacterMember(nonBaseChars, X) || IsHangulConjoiningJamo(X) || (((X) & 0x1FFFF0) == 0xF870)))
 
+// Datatypes
 typedef float WebGlyphWidth;
 typedef UInt32 UnicodeChar;
 
@@ -99,58 +72,89 @@ struct CharacterWidthIterator
     CharacterShapeIterator shapeIterator;
     unsigned needsShaping;
     float runWidthSoFar;
+    float widthToStart;
     int padding;
     int padPerSpace;
 };
 
-static inline BOOL shouldUseATSU(const WebCoreTextRun *run)
-{
-    UniChar c;
-    const UniChar *characters = run->characters;
-    int i, from = run->from, to = run->to;
-    
-    for (i = from; i < to; i++){
-        c = characters[i];
-        if (c < 0x300)                      // Early continue to avoid  other checks.
-            continue;
-            
-        if (c >= 0x300 && c <= 0x36F)       // U+0300 through U+036F Combining diacritical marks
-            return YES;
-        if (c >= 0x20D0 && c <= 0x20FF)     // U+20D0 through U+20FF Combining marks for symbols
-            return YES;
-        if (c >= 0xFE20 && c <= 0xFE2f)     // U+FE20 through U+FE2F Combining half marks
-            return YES;
-        if (c >= 0x700 && c <= 0x1059)      // U+0700 through U+1059 Syriac, Thaana, Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar
-            return YES;
-        if (c >= 0x1100 && c <= 0x11FF)     // 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)
-            return YES;
-        if (c >= 0x1780 && c <= 0x18AF)     // U+1780 through U+18AF Khmer, Mongolian
-            return YES;
-        if (c >= 0x1900 && c <= 0x194F)     // U+1900 through U+194F Limbu (Unicode 4.0)
-            return YES;
-    }
-    
-    return NO;
-}
 
-static inline BOOL isControlCharacter(UniChar c)
-{
-    return c < 0x0020 || c == 0x007F;
-}
+// Character property functions.
+static inline BOOL isControlCharacter(UniChar c);
+static inline BOOL isAlternateSpace(UniChar c);
+static inline BOOL isSpace(UniChar c);
+static inline UniChar toUpper(UniChar c);
+static inline BOOL isUpper(UniChar c);
 
-static inline BOOL isAlternateSpace(UniChar c)
-{
-    return c == '\n' || c == 0xA0;
-}
 
-static inline BOOL isSpace(UniChar c)
-{
-    return c == SPACE || isAlternateSpace(c);
-}
+// Map utility functions
+static void freeWidthMap (WidthMap *map);
+static void freeGlyphMap (GlyphMap *map);
+static inline ATSGlyphRef glyphForUnicodeCharacter (UnicodeGlyphMap *map, UnicodeChar c, NSFont **font);
+static inline SubstituteFontWidthMap *mapForSubstituteFont(WebTextRenderer *renderer, NSFont *font);
+static inline ATSGlyphRef glyphForCharacter (GlyphMap *map, UniChar c, NSFont **font);
+static inline SubstituteFontWidthMap *mapForSubstituteFont(WebTextRenderer *renderer, NSFont *font);
+static inline WebGlyphWidth widthFromMap (WebTextRenderer *renderer, WidthMap *map, ATSGlyphRef glyph, NSFont *font);
+static inline WebGlyphWidth widthForGlyph (WebTextRenderer *renderer, ATSGlyphRef glyph, NSFont *font);
+
+
+// Iterator functions
+static void initializeCharacterWidthIterator (CharacterWidthIterator *iterator, WebTextRenderer *renderer, const WebCoreTextRun *run , const WebCoreTextStyle *style);
+static float widthForNextCharacter (CharacterWidthIterator *iterator, ATSGlyphRef *glyphUsed, NSFont **fontUsed);
+
+
+// Misc.
+static BOOL fillStyleWithAttributes(ATSUStyle style, NSFont *theFont);
+#ifdef NEED_FINDLENGTHOFCHARACTERCLUSTER
+static unsigned findLengthOfCharacterCluster(const UniChar *characters, unsigned length);
+#endif
+static inline BOOL fontContainsString (NSFont *font, NSString *string);
+static inline BOOL shouldUseATSU(const WebCoreTextRun *run);
+static NSString *pathFromFont (NSFont *font);
+
+
+// Globals
+static CFCharacterSetRef nonBaseChars = NULL;
+static BOOL bufferTextDrawing = NO;
+static NSString *WebFallbackFontFamily = nil;
 
+
+// Macros
+#define SPACE 0x0020
+
+#define ROUND_TO_INT(x) (unsigned)((x)+.5)
+
+// Lose precision beyond 1000ths place. This is to work around an apparent
+// bug in CoreGraphics where there seem to be small errors to some metrics.
+#define CEIL_TO_INT(x) ((int)(x + 0.999)) /* ((int)(x + 1.0 - FLT_EPSILON)) */
+
+// 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 LOCAL_BUFFER_SIZE 2048
+
+// Covers Latin1.
+#define INITIAL_BLOCK_SIZE 0x200
+
+// Get additional blocks of glyphs and widths in bigger chunks.
+// This will typically be for other character sets.
+#define INCREMENTAL_BLOCK_SIZE 0x400
+
+#define UNINITIALIZED_GLYPH_WIDTH 65535
+
+// combining char, hangul jamo, or Apple corporate variant tag
+#define JunseongStart 0x1160
+#define JonseongEnd 0x11F9
+#define IsHangulConjoiningJamo(X) (X >= JunseongStart && X <= JonseongEnd)
+#define IsNonBaseChar(X) ((CFCharacterSetIsCharacterMember(nonBaseChars, X) || IsHangulConjoiningJamo(X) || (((X) & 0x1FFFF0) == 0xF870)))
+
+#define ATSFontRefFromNSFont(font) (FMGetATSFontRefFromFont((FMFont)[font _atsFontID]))
+
+#define SMALLCAPS_FONTSIZE_MULTIPLIER 0.7
+#define INVALID_WIDTH -(__FLT_MAX__)
+
+// SPI from other frameworks.
 @interface NSLanguage : NSObject 
-{
-}
+{}
 + (NSLanguage *)defaultLanguage;
 @end
 
@@ -161,40 +165,25 @@ static inline BOOL isSpace(UniChar c)
 + (NSFont *) findFontLike:(NSFont *)aFont forCharacter:(UInt32)c inLanguage:(NSLanguage *) language;
 + (NSFont *) findFontLike:(NSFont *)aFont forString:(NSString *)string withRange:(NSRange)range inLanguage:(NSLanguage *) language;
 - (NSGlyph)_defaultGlyphForChar:(unichar)uu;
-- (BOOL)_forceAscenderDelta;
 - (BOOL)_canDrawOutsideLineHeight;
 - (BOOL)_isSystemFont;
 - (BOOL)_isFakeFixedPitch;
 @end
 
- at class NSCGSFont;
-
-// FIXME.  This is a horrible workaround to the problem described in 3129490
- at interface NSCGSFont : NSFont {
-    NSFaceInfo *_faceInfo;
-    id _otherFont;	// Try to get rid of this???
-    float *_matrix;
-    struct _NS_cgsResv *_reservedCGS;
-    float _constWidth;		// if isFixedPitch, the actual scaled width
-}
- at end
-
-static CFCharacterSetRef nonBaseChars = NULL;
-
-
- at interface WebTextRenderer (WebPrivate)
+// Internal API
+ at interface WebTextRenderer (WebInternal)
 
-- (NSFont *)substituteFontForCharacters: (const unichar *)characters length: (int)numCharacters families: (NSString **)families;
+- (NSFont *)_substituteFontForCharacters: (const unichar *)characters length: (int)numCharacters families: (NSString **)families;
 
-- (WidthMap *)extendGlyphToWidthMapToInclude:(ATSGlyphRef)glyphID font:(NSFont *)font;
-- (ATSGlyphRef)extendCharacterToGlyphMapToInclude:(UniChar) c;
-- (ATSGlyphRef)extendUnicodeCharacterToGlyphMapToInclude: (UnicodeChar)c;
-- (void)updateGlyphEntryForCharacter: (UniChar)c glyphID: (ATSGlyphRef)glyphID font: (NSFont *)substituteFont;
+- (WidthMap *)_extendGlyphToWidthMapToInclude:(ATSGlyphRef)glyphID font:(NSFont *)font;
+- (ATSGlyphRef)_extendCharacterToGlyphMapToInclude:(UniChar) c;
+- (ATSGlyphRef)_extendUnicodeCharacterToGlyphMapToInclude: (UnicodeChar)c;
+- (void)_updateGlyphEntryForCharacter: (UniChar)c glyphID: (ATSGlyphRef)glyphID font: (NSFont *)substituteFont;
 
-- (float)_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths:(float *)widthBuffer fonts:(NSFont **)fontBuffer glyphs:(CGGlyph *)glyphBuffer startGlyph:(int *)startGlyph endGlyph:(int *)endGlyph numGlyphs:(int *)_numGlyphs;
+- (float)_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths:(float *)widthBuffer fonts:(NSFont **)fontBuffer glyphs:(CGGlyph *)glyphBuffer startPosition:(float *)startPosition numGlyphs:(int *)_numGlyphs;
 
 // Measuring runs.
-- (float)_CG_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths: (float *)widthBuffer fonts: (NSFont **)fontBuffer glyphs: (CGGlyph *)glyphBuffer  startGlyph:(int *)startGlyph endGlyph:(int *)endGlyph numGlyphs: (int *)_numGlyphs;
+- (float)_CG_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths: (float *)widthBuffer fonts: (NSFont **)fontBuffer glyphs: (CGGlyph *)glyphBuffer startPosition:(float *)startPosition numGlyphs: (int *)_numGlyphs;
 - (float)_ATSU_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style;
 
 // Drawing runs.
@@ -209,379 +198,323 @@ static CFCharacterSetRef nonBaseChars = NULL;
 - (void)_CG_drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style atPoint:(NSPoint)point;
 - (void)_ATSU_drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style atPoint:(NSPoint)point;
 
- at end
-
+- (BOOL)_setupFont;
 
-static void freeWidthMap (WidthMap *map)
-{
-    if (!map)
-	return;
-    freeWidthMap (map->next);
-    free (map->widths);
-    free (map);
-}
+// Small caps
+- (void)_setIsSmallCapsRenderer:(BOOL)flag;
+- (BOOL)_isSmallCapsRenderer;
+- (WebTextRenderer *)_smallCapsRenderer;
+- (NSFont *)_smallCapsFont;
 
+ at end
 
-static void freeGlyphMap (GlyphMap *map)
-{
-    if (!map)
-	return;
-    freeGlyphMap (map->next);
-    free (map->glyphs);
-    free (map);
-}
 
+ at implementation WebTextRenderer
 
-static inline ATSGlyphRef glyphForCharacter (GlyphMap *map, UniChar c, NSFont **font)
++ (BOOL)shouldBufferTextDrawing
 {
-    if (map == 0)
-        return nonGlyphID;
-        
-    if (c >= map->startRange && c <= map->endRange){
-        *font = map->glyphs[c-map->startRange].font;
-        return map->glyphs[c-map->startRange].glyph;
-    }
-        
-    return glyphForCharacter (map->next, c, font);
+    return bufferTextDrawing;
 }
- 
- 
-static inline ATSGlyphRef glyphForUnicodeCharacter (UnicodeGlyphMap *map, UnicodeChar c, NSFont **font)
+
++ (void)initialize
 {
-    if (map == 0)
-        return nonGlyphID;
-        
-    if (c >= map->startRange && c <= map->endRange){
-        *font = map->glyphs[c-map->startRange].font;
-        return map->glyphs[c-map->startRange].glyph;
-    }
-        
-    return glyphForUnicodeCharacter (map->next, c, font);
+    nonBaseChars = CFCharacterSetGetPredefined(kCFCharacterSetNonBase);
+    bufferTextDrawing = [[[NSUserDefaults standardUserDefaults] stringForKey:@"BufferTextDrawing"] isEqual: @"YES"];
 }
- 
 
-#ifdef _TIMING        
-static double totalCGGetAdvancesTime = 0;
-#endif
-
-static inline SubstituteFontWidthMap *mapForSubstituteFont(WebTextRenderer *renderer, NSFont *font)
+- initWithFont:(NSFont *)f usingPrinterFont:(BOOL)p
 {
-    int i;
+    [super init];
     
-    for (i = 0; i < renderer->numSubstituteFontWidthMaps; i++){
-        if (font == renderer->substituteFontWidthMaps[i].font)
-            return &renderer->substituteFontWidthMaps[i];
-    }
+    maxSubstituteFontWidthMaps = NUM_SUBSTITUTE_FONT_MAPS;
+    substituteFontWidthMaps = calloc (1, maxSubstituteFontWidthMaps * sizeof(SubstituteFontWidthMap));
+    font = [(p ? [f printerFont] : [f screenFont]) retain];
+    usingPrinterFont = p;
     
-    if (renderer->numSubstituteFontWidthMaps == renderer->maxSubstituteFontWidthMaps){
-        renderer->maxSubstituteFontWidthMaps = renderer->maxSubstituteFontWidthMaps * 2;
-        renderer->substituteFontWidthMaps = realloc (renderer->substituteFontWidthMaps, renderer->maxSubstituteFontWidthMaps * sizeof(SubstituteFontWidthMap));
-        for (i = renderer->numSubstituteFontWidthMaps; i < renderer->maxSubstituteFontWidthMaps; i++){
-            renderer->substituteFontWidthMaps[i].font = 0;
-            renderer->substituteFontWidthMaps[i].map = 0;
+    if ([font glyphPacking] != NSNativeShortGlyphPacking &&
+        [font glyphPacking] != NSTwoByteGlyphPacking)
+        FATAL_ALWAYS ("%@: Don't know how to pack glyphs for font %@ %f", self, [font displayName], [font pointSize]);
+        
+    if (![self _setupFont]){
+        // 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 ([[font familyName] isEqual:@"Times"])
+            fallbackFontFamily = @"Times New Roman";
+        else {
+            if (!WebFallbackFontFamily)
+                // Could use any size, we just care about the family of the system font.
+                WebFallbackFontFamily = [[[NSFont systemFontOfSize:16.0] familyName] retain];
+                
+            fallbackFontFamily = WebFallbackFontFamily;
+        }
+        
+        // Try setting up the alternate font.
+        NSFont *initialFont = font;
+        [initialFont autorelease];
+        NSFont *af = [[NSFontManager sharedFontManager] convertFont:font toFamily:fallbackFontFamily];
+        font = [(p ? [af printerFont] : [af screenFont]) retain];
+        NSString *filePath = pathFromFont(initialFont);
+        filePath = filePath ? filePath : @"not known";
+        if (![self _setupFont]){
+            // Give up!
+            FATAL_ALWAYS ("%@ unable to initialize with font %@ at %@", self, initialFont, filePath);
         }
+
+        // Report the problem.
+        ERROR ("Corrupt font detected, using %@ in place of %@ (%d glyphs) located at \"%@\".", 
+                    [font familyName], 
+                    [initialFont familyName],
+                    ATSFontGetGlyphCount(ATSFontRefFromNSFont(initialFont)),
+                    filePath);
     }
-    
-    renderer->substituteFontWidthMaps[renderer->numSubstituteFontWidthMaps].font = font;
-    return &renderer->substituteFontWidthMaps[renderer->numSubstituteFontWidthMaps++];
-}
 
-static inline WebGlyphWidth widthFromMap (WebTextRenderer *renderer, WidthMap *map, ATSGlyphRef glyph, NSFont *font)
-{
-    WebGlyphWidth width = UNINITIALIZED_GLYPH_WIDTH;
-    BOOL errorResult;
-    
-    while (1){
-        if (map == 0)
-            map = [renderer extendGlyphToWidthMapToInclude: glyph font:font];
+    // We emulate the appkit metrics by applying rounding as is done
+    // in the appkit.
+    CGFontRef cgFont = [font _backingCGSFont];
+    const CGFontHMetrics *metrics = CGFontGetHMetrics(cgFont);
+    unsigned unitsPerEm = CGFontGetUnitsPerEm(cgFont);
+    float pointSize = [font pointSize];
+    float asc = (ScaleEmToUnits(metrics->ascent, unitsPerEm)*pointSize);
+    float dsc = (-ScaleEmToUnits(metrics->descent, unitsPerEm)*pointSize);
+    float lineGap = ScaleEmToUnits(metrics->lineGap, unitsPerEm)*pointSize;
+    float adjustment;
 
-        if (glyph >= map->startRange && glyph <= map->endRange){
-            width = map->widths[glyph-map->startRange].width;
-            if (width == UNINITIALIZED_GLYPH_WIDTH){
+    // 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.
+    if ([[font familyName] isEqualToString:@"Times"] ||
+        [[font familyName] isEqualToString:@"Helvetica"] ||
+        [[font familyName] isEqualToString:@"Courier"]) {
+        adjustment = floor(((asc + dsc) * 0.15) + 0.5);
+    } else {
+        adjustment = 0.0;
+    }
 
-#ifdef _TIMING
-                double startTime = CFAbsoluteTimeGetCurrent();
-#endif
-                if (font)
-                    errorResult = CGFontGetGlyphScaledAdvances ([font _backingCGSFont], &glyph, 1, &map->widths[glyph-map->startRange].width, [renderer->font pointSize]);
-                else
-                    errorResult = CGFontGetGlyphScaledAdvances ([renderer->font _backingCGSFont], &glyph, 1, &map->widths[glyph-map->startRange].width, [renderer->font pointSize]);
-                if (errorResult == 0)
-                    FATAL_ALWAYS ("Unable to cache glyph widths for %@ %f",  [renderer->font displayName], [renderer->font pointSize]);
+    ascent = ROUND_TO_INT(asc + adjustment);
+    descent = ROUND_TO_INT(dsc);
 
-#ifdef _TIMING
-                double thisTime = CFAbsoluteTimeGetCurrent() - startTime;
-                totalCGGetAdvancesTime += thisTime;
-#endif
-                width = map->widths[glyph-map->startRange].width;
-            }
-        }
+    lineSpacing =  ascent + descent + (int)(lineGap > 0.0 ? floor(lineGap + 0.5) : 0.0);
 
-        if (width == UNINITIALIZED_GLYPH_WIDTH){
-            map = map->next;
-            continue;
-        }
+#ifdef COMPARE_APPKIT_CG_METRICS
+    printf ("\nCG/Appkit metrics for font %s, %f, lineGap %f, adjustment %f, _canDrawOutsideLineHeight %d, _isSystemFont %d\n", [[font displayName] cString], [font pointSize], lineGap, adjustment, (int)[font _canDrawOutsideLineHeight], (int)[font _isSystemFont]);
+    if ((int)ROUND_TO_INT([font ascender]) != ascent ||
+        (int)ROUND_TO_INT(-[font descender]) != descent ||
+        (int)ROUND_TO_INT([font defaultLineHeightForFont]) != lineSpacing){
+        printf ("\nCG/Appkit mismatched metrics for font %s, %f (%s)\n", [[font displayName] cString], [font pointSize],
+                ([font screenFont] ? [[[font screenFont] displayName] cString] : "none"));
+        printf ("ascent(%s), descent(%s), lineSpacing(%s)\n",
+                ((int)ROUND_TO_INT([font ascender]) != ascent) ? "different" : "same",
+                ((int)ROUND_TO_INT(-[font descender]) != descent) ? "different" : "same",
+                ((int)ROUND_TO_INT([font defaultLineHeightForFont]) != lineSpacing) ? "different" : "same");
+        printf ("CG:  ascent %f, ", asc);
+        printf ("descent %f, ", dsc);
+        printf ("lineGap %f, ", lineGap);
+        printf ("lineSpacing %d\n", lineSpacing);
         
-        // Hack to ensure that characters that match the width of the space character
-        // have the same integer width as the space character.  This is necessary so
-        // glyphs in fixed pitch fonts all have the same integer width.  We can't depend
-        // on the fixed pitch property of NSFont because that isn't set for all
-        // monospaced fonts, in particular Courier!  This has the downside of inappropriately
-        // adjusting the widths of characters in non-monospaced fonts that are coincidentally
-        // the same width as a space in that font.  In practice this is not an issue as the
-        // adjustment is always as the sub-pixel level.
-        if (width == renderer->spaceWidth)
-            return renderer->ceiledSpaceWidth;
-
-        return width;
+        printf ("NSFont:  ascent %f, ", [font ascender]);
+        printf ("descent %f, ", [font descender]);
+        printf ("lineSpacing %f\n", [font defaultLineHeightForFont]);
     }
-    // never get here.
-    return 0;
-}    
+#endif
+     
+    isSmallCapsRenderer = NO;
+    
+    return self;
+}
 
-static inline WebGlyphWidth widthForGlyph (WebTextRenderer *renderer, ATSGlyphRef glyph, NSFont *font)
+- (void)dealloc
 {
-    WidthMap *map;
+    [font release];
+    [smallCapsFont release];
+    [smallCapsRenderer release];
 
-    if (font && font != renderer->font)
-        map = mapForSubstituteFont(renderer, font)->map;
-    else
-        map = renderer->glyphToWidthMap;
+    if (styleGroup)
+        ATSUDisposeStyleGroup(styleGroup);
 
-    return widthFromMap (renderer, map, glyph, font);
-}
+    freeWidthMap (glyphToWidthMap);
+    freeGlyphMap (characterToGlyphMap);
 
+    if (ATSUStyleInitialized)
+        ATSUDisposeStyle(_ATSUSstyle);
+    
+    [super dealloc];
+}
 
-static void initializeCharacterWidthIterator (CharacterWidthIterator *iterator, WebTextRenderer *renderer, const WebCoreTextRun *run , const WebCoreTextStyle *style) 
+- (int)widthForCharacters:(const UniChar *)characters length:(unsigned)stringLength
 {
-    iterator->needsShaping = initializeCharacterShapeIterator (&iterator->shapeIterator, run);
-    iterator->renderer = renderer;
-    iterator->run = run;
-    iterator->style = style;
-    iterator->currentCharacter = run->from;
-    iterator->runWidthSoFar = 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.
-    iterator->padding = style->padding;
-    if (iterator->padding > 0){
-        uint numSpaces = 0;
-        int from = run->from;
-        int len = run->to - from;
-        int k;
-        for (k = from; k < from + len; k++) {
-            if (isSpace(run->characters[k])) {
-                numSpaces++;
-            }
-        }
-        iterator->padPerSpace = CEIL_TO_INT ((((float)style->padding) / ((float)numSpaces)));
-    }
-    else {
-        iterator->padPerSpace = 0;
-    }
+    WebCoreTextRun run;
+    WebCoreInitializeTextRun (&run, characters, stringLength, 0, stringLength);
+    WebCoreTextStyle style;
+    WebCoreInitializeEmptyTextStyle(&style);
+    return ROUND_TO_INT([self floatWidthForRun:&run style:&style widths: 0]);
 }
 
-static float widthAndGlyphForSurrogate (WebTextRenderer *renderer, UniChar high, UniChar low, ATSGlyphRef *glyphID, NSString **families)
+- (int)widthForString:(NSString *)string
 {
-    UnicodeChar uc = UnicodeValueForSurrogatePair(high, low);
-    NSFont *font = renderer->font;
-    float width;
+    UniChar localCharacterBuffer[LOCAL_BUFFER_SIZE];
+    UniChar *characterBuffer = localCharacterBuffer;
+    const UniChar *usedCharacterBuffer = CFStringGetCharactersPtr((CFStringRef)string);
+    unsigned length;
+    int width;
 
-    *glyphID = glyphForUnicodeCharacter(renderer->unicodeCharacterToGlyphMap, uc, &font);
-    if (*glyphID == nonGlyphID) {
-        *glyphID = [renderer extendUnicodeCharacterToGlyphMapToInclude: uc];
+    // Get the characters from the string into a buffer.
+    length = [string length];
+    if (!usedCharacterBuffer) {
+        if (length > LOCAL_BUFFER_SIZE)
+            characterBuffer = (UniChar *)malloc(length * sizeof(UniChar));
+        [string getCharacters:characterBuffer];
+        usedCharacterBuffer = characterBuffer;
     }
 
-    if (*glyphID == 0){
-        UniChar surrogates[2];
-        unsigned clusterLength;
-        
-        clusterLength = 2;
-        surrogates[0] = high;
-        surrogates[1] = low;
-        NSFont *substituteFont = [renderer substituteFontForCharacters:&surrogates[0] length: clusterLength families: families];
-        if (substituteFont){
-            WebTextRenderer *substituteRenderer = [[WebTextRendererFactory sharedFactory] rendererWithFont:substituteFont usingPrinterFont:renderer->usingPrinterFont];
-            *glyphID = glyphForUnicodeCharacter(substituteRenderer->unicodeCharacterToGlyphMap, uc, &font);
-            if (*glyphID == nonGlyphID)
-                *glyphID = [substituteRenderer extendUnicodeCharacterToGlyphMapToInclude: uc];
-            return widthForGlyph (substituteRenderer, *glyphID, substituteFont);
-       }
-    }
-    
-    width = widthForGlyph (renderer, *glyphID, font);
+    width = [self widthForCharacters:usedCharacterBuffer length:length];
     
+    if (characterBuffer != localCharacterBuffer)
+        free(characterBuffer);
+
     return width;
 }
 
-static float widthForNextCharacter (CharacterWidthIterator *iterator)
+- (int)ascent
 {
-    WebTextRenderer *renderer = iterator->renderer;
-    const WebCoreTextRun *run = iterator->run;
-    NSFont *font = renderer->font;
-    UniChar c;
-    unsigned offset = iterator->currentCharacter;
-    WebGlyphWidth width;
-
-    if (offset >= run->length)
-        // Error!  Offset specified beyond end of run.
-        return 0;
-        
-    // Determine if the string requires any shaping, i.e. Arabic.
-    if (iterator->needsShaping)
-        c = shapeForNextCharacter(&iterator->shapeIterator);
-    else
-        c = run->characters[offset];
-    iterator->currentCharacter++;
-    
-    // It's legit to sometimes get 0 from the shape iterator, return a zero width.
-    if (c == 0)
-        return 0;
-
+    return ascent;
+}
 
-    // Get a glyph for the next characters.  Somewhat complicated by surrogate
-    // pairs.
-    ATSGlyphRef glyphID;
+- (int)descent
+{
+    return descent;
+}
 
-    // Do we have a surrogate pair?  If so, determine the full Unicode (32bit)
-    // code point before glyph lookup.  We only provide a width when the offset
-    // specifies the first component of the surrogate pair.
-    if (c >= HighSurrogateRangeStart && c <= HighSurrogateRangeEnd) {
-        UniChar high = c, low;
+- (int)lineSpacing
+{
+    return lineSpacing;
+}
 
-        // Make sure we have another character and it's a low surrogate.
-        if (offset+1 >= run->length || !IsLowSurrogatePair((low = run->characters[offset+1]))) {
-            // Error!  The second component of the surrogate pair is missing.
-            return 0;
-        }
+- (float)xHeight
+{
+    return [font xHeight];
+}
 
-        width = widthAndGlyphForSurrogate (renderer, high, low, &glyphID, iterator->style->families);
-    }
-    else if (c >= LowSurrogateRangeStart && c <= LowSurrogateRangeEnd) {
-        // Return 0 width for second component of the surrogate pair.
-        return 0;
+- (void)drawRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style atPoint:(NSPoint)point
+{
+    if (style->smallCaps && !isSmallCapsRenderer) {
+        [[self _smallCapsRenderer] drawRun:run style:style atPoint:point];
     }
     else {
-        glyphID = glyphForCharacter(renderer->characterToGlyphMap, c, &font);
-
-        // Now that we have glyph get it's width.
-        width = widthForGlyph (renderer, glyphID, font);
+        if (shouldUseATSU(run))
+            [self _ATSU_drawRun:run style:style atPoint:point];
+        else
+            [self _CG_drawRun:run style:style atPoint:point];
     }
+}
 
-    // Account for letter-spacing
-    if (width > 0)
-        width += iterator->style->letterSpacing;
+- (float)floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths:(float *)widthBuffer
+{
+    if (style->smallCaps && !isSmallCapsRenderer) {
+        return [[self _smallCapsRenderer] _floatWidthForRun:run style:style widths:widthBuffer fonts:nil glyphs:nil startPosition:nil numGlyphs:nil];
+    }
+    return [self _floatWidthForRun:run style:style widths:widthBuffer fonts:nil glyphs:nil startPosition:nil numGlyphs:nil];
+}
 
+- (void)drawLineForCharacters:(NSPoint)point yOffset:(float)yOffset withWidth: (int)width withColor:(NSColor *)color
+{
+    NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
+    CGContextRef cgContext;
+    float lineWidth;
 
-    // Account for padding.  khtml uses space padding to justify text.  We
-    // distribute the specified padding over the available spaces in the run.
-    if (c == SPACE){
-        if (iterator->padding > 0){
-            // Only use left over padding if note evenly divisible by 
-            // number of spaces.
-            if (iterator->padding < iterator->padPerSpace){
-                width += iterator->padding;
-                iterator->padding = 0;
-            }
-            else {
-                width += iterator->padPerSpace;
-                iterator->padding -= iterator->padPerSpace;
-            }
-        }
-        
-        // Account for word-spacing.  We apply additional space between "words" by
-        // adding width to the space character.
-        width += iterator->style->wordSpacing;
-    }
+    // This will draw the text from the top of the bounding box down.
+    // Qt expects to draw from the baseline.
+    // Remember that descender is negative.
+    point.y -= [self lineSpacing] - [self descent];
     
-    iterator->runWidthSoFar += width;
+    BOOL flag = [graphicsContext shouldAntialias];
 
-    // Account for float/integer impedance mismatch between CG and khtml.  "Words" (characters 
-    // followed by a 0x32) are always an integer width.  We adjust the width of the last character of a
-    // "word" to ensure an integer width.  When we move khtml to floats we can remove this (and 
-    // related) hacks.
+    [graphicsContext setShouldAntialias: NO];
 
-    // Check to see if the next character is a space, if so, adjust.
-    if (offset+1 < run->length && run->characters[offset+1] == SPACE) {
-        float delta = CEIL_TO_INT(iterator->runWidthSoFar) - iterator->runWidthSoFar;
-        iterator->runWidthSoFar += delta;
-        width += delta;
+    [color set];
+
+    cgContext = (CGContextRef)[graphicsContext graphicsPort];
+    lineWidth = 0.0;
+    if ([graphicsContext isDrawingToScreen] && lineWidth == 0.0) {
+        CGSize size = CGSizeApplyAffineTransform(CGSizeMake(1.0, 1.0), CGAffineTransformInvert(CGContextGetCTM(cgContext)));
+        lineWidth = size.width;
     }
-    
-    return width;
+    CGContextSetLineWidth(cgContext, lineWidth);
+    CGContextMoveToPoint(cgContext, point.x, point.y + [self lineSpacing] + 1.5 - [self descent] + yOffset);
+    // Subtract 1 to ensure that the line is always within bounds of element.
+    CGContextAddLineToPoint(cgContext, point.x + width - 1.0, point.y + [self lineSpacing] + 1.5 - [self descent] + yOffset);
+    CGContextStrokePath(cgContext);
+
+    [graphicsContext setShouldAntialias: flag];
 }
 
 
-static BOOL FillStyleWithAttributes(ATSUStyle style, NSFont *theFont)
+- (void)drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style atPoint:(NSPoint)point
 {
-    if (theFont) {
-        ATSUFontID fontId = (ATSUFontID)[theFont _atsFontID];
-        LOG (FontCache, "FillStyleWithAttributes:  font = %p,%@, _atsFontID = %x\n", theFont, theFont, (unsigned)fontId);
-        ATSUAttributeTag tag = kATSUFontTag;
-        ByteCount size = sizeof(ATSUFontID);
-        ATSUFontID *valueArray[1] = {&fontId};
-        OSStatus status;
-
-        if (fontId) {
-            status = ATSUSetAttributes(style, 1, &tag, &size, (void *)valueArray);
-            if (status != noErr){
-                LOG (FontCache, "FillStyleWithAttributes failed(%d):  font = %p,%@, _atsFontID = %x\n", (int)status, theFont, theFont, (unsigned)fontId);
-                return NO;
-            }
-        }
-        else {
-            return NO;
-        }
-        return YES;
+    if (style->smallCaps && !isSmallCapsRenderer) {
+        [[self _smallCapsRenderer] drawHighlightForRun:run style:style atPoint:point];
+    }
+    else {
+        if (shouldUseATSU(run))
+            [self _ATSU_drawHighlightForRun:run style:style atPoint:point];
+        else
+            [self _CG_drawHighlightForRun:run style:style atPoint:point];
     }
-    return NO;
 }
 
-
-static unsigned findLengthOfCharacterCluster(const UniChar *characters, unsigned length)
+- (int)pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed
 {
-    unsigned k;
+    if (style->smallCaps && !isSmallCapsRenderer) {
+        return [[self _smallCapsRenderer] pointToOffset:run style:style position:x reversed:reversed];
+    }
 
-    if (length <= 1)
-        return length;
-    
-    if (IsHighSurrogatePair(characters[0]))
-        return 2;
-        
-    if (IsNonBaseChar(characters[0]))
-        return 1;
-    
-    // Find all the non base characters after the current character.
-    for (k = 1; k < length; k++)
-        if (!IsNonBaseChar(characters[k]))
-            break;
-    return k;
+    if (shouldUseATSU(run))
+        return [self _ATSU_pointToOffset:run style:style position:x reversed:reversed];
+    return [self _CG_pointToOffset:run style:style position:x reversed:reversed];
 }
 
- at implementation WebTextRenderer
+ at end
 
-static BOOL bufferTextDrawing = NO;
 
-+ (BOOL)shouldBufferTextDrawing
+// ------------------- Private API -------------------
+
+
+ at implementation WebTextRenderer (WebInternal)
+
+- (void)_setIsSmallCapsRenderer:(BOOL)flag
 {
-    return bufferTextDrawing;
+    isSmallCapsRenderer = flag;
 }
 
-+ (void)initialize
+- (BOOL)_isSmallCapsRenderer
 {
-    nonBaseChars = CFCharacterSetGetPredefined(kCFCharacterSetNonBase);
-    bufferTextDrawing = [[[NSUserDefaults standardUserDefaults] stringForKey:@"BufferTextDrawing"] isEqual: @"YES"];
+    return isSmallCapsRenderer;
 }
 
-static inline BOOL _fontContainsString (NSFont *font, NSString *string)
+- (WebTextRenderer *)_smallCapsRenderer
 {
-    if ([string rangeOfCharacterFromSet:[[font coveredCharacterSet] invertedSet]].location == NSNotFound) {
-        return YES;
+    if (!smallCapsRenderer) {
+        smallCapsRenderer = [[WebTextRenderer alloc] initWithFont:font usingPrinterFont:usingPrinterFont];
+        [smallCapsRenderer _setIsSmallCapsRenderer:YES];
     }
-    return NO;
+    return smallCapsRenderer;
 }
 
-- (NSFont *)substituteFontForString: (NSString *)string families: (NSString **)families
+- (NSFont *)_smallCapsFont
+{
+    if (!smallCapsFont)
+        smallCapsFont = [[NSFontManager sharedFontManager] convertFont:font toSize:([font pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER)];
+    return smallCapsFont;
+}
+
+- (NSFont *)_substituteFontForString: (NSString *)string families: (NSString **)families
 {
     NSFont *substituteFont = nil;
 
@@ -593,7 +526,7 @@ static inline BOOL _fontContainsString (NSFont *font, NSString *string)
         family = families[i++];
         substituteFont = [[WebTextRendererFactory sharedFactory] cachedFontFromFamily: family traits:[[NSFontManager sharedFontManager] traitsOfFont:font] size:[font pointSize]];
         if (substituteFont) {
-            if (_fontContainsString(substituteFont, string))
+            if (fontContainsString(substituteFont, string))
                 break;
             substituteFont = nil; 
         }
@@ -617,12 +550,12 @@ static inline BOOL _fontContainsString (NSFont *font, NSString *string)
 }
 
 
-- (NSFont *)substituteFontForCharacters: (const unichar *)characters length: (int)numCharacters families: (NSString **)families
+- (NSFont *)_substituteFontForCharacters: (const unichar *)characters length: (int)numCharacters families: (NSString **)families
 {
     NSFont *substituteFont;
     NSString *string = [[NSString alloc] initWithCharactersNoCopy:(unichar *)characters length: numCharacters freeWhenDone: NO];
 
-    substituteFont = [self substituteFontForString: string families: families];
+    substituteFont = [self _substituteFontForString: string families: families];
 
     [string release];
     
@@ -631,7 +564,7 @@ static inline BOOL _fontContainsString (NSFont *font, NSString *string)
 
 
 /* Convert newlines and non-breaking spaces into spaces, and skip control characters. */
-- (void)convertCharacters: (const UniChar *)characters length: (unsigned)numCharacters toGlyphs: (ATSGlyphVector *)glyphs skipControlCharacters:(BOOL)skipControlCharacters
+- (void)_convertCharacters: (const UniChar *)characters length: (unsigned)numCharacters toGlyphs: (ATSGlyphVector *)glyphs skipControlCharacters:(BOOL)skipControlCharacters
 {
     unsigned i, numCharactersInBuffer;
     UniChar localBuffer[LOCAL_BUFFER_SIZE];
@@ -688,7 +621,7 @@ static inline BOOL _fontContainsString (NSFont *font, NSString *string)
 }
 
 
-- (void)convertUnicodeCharacters: (const UnicodeChar *)characters length: (unsigned)numCharacters toGlyphs: (ATSGlyphVector *)glyphs
+- (void)_convertUnicodeCharacters: (const UnicodeChar *)characters length: (unsigned)numCharacters toGlyphs: (ATSGlyphVector *)glyphs
 {
     unsigned numCharactersInBuffer;
     UniChar localBuffer[LOCAL_BUFFER_SIZE];
@@ -727,7 +660,7 @@ static inline BOOL _fontContainsString (NSFont *font, NSString *string)
     UniChar c = ' ';
     float _spaceWidth;
 
-    spaceGlyph = [self extendCharacterToGlyphMapToInclude: c];
+    spaceGlyph = [self _extendCharacterToGlyphMapToInclude: c];
     if (spaceGlyph == 0){
         return NO;
     }
@@ -753,7 +686,7 @@ static inline BOOL _fontContainsString (NSFont *font, NSString *string)
     if ((errCode = ATSUCreateStyle(&fontStyle)) != noErr)
         return NO;
 
-    if (!FillStyleWithAttributes(fontStyle, font))
+    if (!fillStyleWithAttributes(fontStyle, font))
         return NO;
 
     if ((errCode = ATSUGetStyleGroup(fontStyle, &styleGroup)) != noErr){
@@ -774,7 +707,6 @@ static inline BOOL _fontContainsString (NSFont *font, NSString *string)
     return YES;
 }
 
-#define ATSFontRefFromNSFont(font) (FMGetATSFontRefFromFont((FMFont)[font _atsFontID]))
 static NSString *pathFromFont (NSFont *font)
 {
     UInt8 _filePathBuffer[PATH_MAX];
@@ -797,191 +729,6 @@ static NSString *pathFromFont (NSFont *font)
     return filePath;
 }
 
-static NSString *WebFallbackFontFamily;
-
-- initWithFont:(NSFont *)f usingPrinterFont:(BOOL)p
-{
-    [super init];
-    
-    maxSubstituteFontWidthMaps = NUM_SUBSTITUTE_FONT_MAPS;
-    substituteFontWidthMaps = calloc (1, maxSubstituteFontWidthMaps * sizeof(SubstituteFontWidthMap));
-    font = [(p ? [f printerFont] : [f screenFont]) retain];
-    usingPrinterFont = p;
-    
-    if ([font glyphPacking] != NSNativeShortGlyphPacking &&
-        [font glyphPacking] != NSTwoByteGlyphPacking)
-        FATAL_ALWAYS ("%@: Don't know how to pack glyphs for font %@ %f", self, [font displayName], [font pointSize]);
-        
-    if (![self _setupFont]){
-        // 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 ([[font familyName] isEqual:@"Times"])
-            fallbackFontFamily = @"Times New Roman";
-        else {
-            if (!WebFallbackFontFamily)
-                // Could use any size, we just care about the family of the system font.
-                WebFallbackFontFamily = [[[NSFont systemFontOfSize:16.0] familyName] retain];
-                
-            fallbackFontFamily = WebFallbackFontFamily;
-        }
-        
-        // Try setting up the alternate font.
-        NSFont *initialFont = font;
-        [initialFont autorelease];
-        NSFont *af = [[NSFontManager sharedFontManager] convertFont:font toFamily:fallbackFontFamily];
-        font = [(p ? [af printerFont] : [af screenFont]) retain];
-        NSString *filePath = pathFromFont(initialFont);
-        filePath = filePath ? filePath : @"not known";
-        if (![self _setupFont]){
-            // Give up!
-            FATAL_ALWAYS ("%@ unable to initialize with font %@ at %@", self, initialFont, filePath);
-        }
-
-        // Report the problem.
-        ERROR ("Corrupt font detected, using %@ in place of %@ (%d glyphs) located at \"%@\".", 
-                    [font familyName], 
-                    [initialFont familyName],
-                    ATSFontGetGlyphCount(ATSFontRefFromNSFont(initialFont)),
-                    filePath);
-    }
-
-    // We emulate the appkit metrics by applying rounding as is done
-    // in the appkit.
-    CGFontRef cgFont = [font _backingCGSFont];
-    const CGFontHMetrics *metrics = CGFontGetHMetrics(cgFont);
-    unsigned unitsPerEm = CGFontGetUnitsPerEm(cgFont);
-    float pointSize = [font pointSize];
-    float asc = (ScaleEmToUnits(metrics->ascent, unitsPerEm)*pointSize);
-    float dsc = (-ScaleEmToUnits(metrics->descent, unitsPerEm)*pointSize);
-    float lineGap = ScaleEmToUnits(metrics->lineGap, unitsPerEm)*pointSize;
-    float adjustment;
-
-    // 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.
-    if ([[font familyName] isEqualToString:@"Times"] ||
-        [[font familyName] isEqualToString:@"Helvetica"] ||
-        [[font familyName] isEqualToString:@"Courier"]) {
-        adjustment = floor(((asc + dsc) * 0.15) + 0.5);
-    } else {
-        adjustment = 0.0;
-    }
-
-    ascent = ROUND_TO_INT(asc + adjustment);
-    descent = ROUND_TO_INT(dsc);
-
-    lineSpacing =  ascent + descent + (int)(lineGap > 0.0 ? floor(lineGap + 0.5) : 0.0);
-
-#ifdef COMPARE_APPKIT_CG_METRICS
-    printf ("\nCG/Appkit metrics for font %s, %f, lineGap %f, adjustment %f, _canDrawOutsideLineHeight %d, _isSystemFont %d\n", [[font displayName] cString], [font pointSize], lineGap, adjustment, (int)[font _canDrawOutsideLineHeight], (int)[font _isSystemFont]);
-    if ((int)ROUND_TO_INT([font ascender]) != ascent ||
-        (int)ROUND_TO_INT(-[font descender]) != descent ||
-        (int)ROUND_TO_INT([font defaultLineHeightForFont]) != lineSpacing){
-        printf ("\nCG/Appkit mismatched metrics for font %s, %f (%s)\n", [[font displayName] cString], [font pointSize],
-                ([font screenFont] ? [[[font screenFont] displayName] cString] : "none"));
-        printf ("ascent(%s), descent(%s), lineSpacing(%s)\n",
-                ((int)ROUND_TO_INT([font ascender]) != ascent) ? "different" : "same",
-                ((int)ROUND_TO_INT(-[font descender]) != descent) ? "different" : "same",
-                ((int)ROUND_TO_INT([font defaultLineHeightForFont]) != lineSpacing) ? "different" : "same");
-        printf ("CG:  ascent %f, ", asc);
-        printf ("descent %f, ", dsc);
-        printf ("lineGap %f, ", lineGap);
-        printf ("lineSpacing %d\n", lineSpacing);
-        
-        printf ("NSFont:  ascent %f, ", [font ascender]);
-        printf ("descent %f, ", [font descender]);
-        printf ("lineSpacing %f\n", [font defaultLineHeightForFont]);
-    }
-#endif
-     
-    return self;
-}
-
-
-- (void)dealloc
-{
-    [font release];
-
-    if (styleGroup)
-        ATSUDisposeStyleGroup(styleGroup);
-
-    freeWidthMap (glyphToWidthMap);
-    freeGlyphMap (characterToGlyphMap);
-
-    if (ATSUStyleInitialized)
-        ATSUDisposeStyle(_ATSUSstyle);
-    
-    [super dealloc];
-}
-
-
-- (int)widthForCharacters:(const UniChar *)characters length:(unsigned)stringLength
-{
-    WebCoreTextRun run;
-    WebCoreInitializeTextRun (&run, characters, stringLength, 0, stringLength);
-    WebCoreTextStyle style;
-    WebCoreInitializeEmptyTextStyle(&style);
-    return ROUND_TO_INT([self floatWidthForRun:&run style:&style widths: 0]);
-}
-
-- (int)widthForString:(NSString *)string
-{
-    UniChar localCharacterBuffer[LOCAL_BUFFER_SIZE];
-    UniChar *characterBuffer = localCharacterBuffer;
-    const UniChar *usedCharacterBuffer = CFStringGetCharactersPtr((CFStringRef)string);
-    unsigned length;
-    int width;
-
-    // Get the characters from the string into a buffer.
-    length = [string length];
-    if (!usedCharacterBuffer) {
-        if (length > LOCAL_BUFFER_SIZE)
-            characterBuffer = (UniChar *)malloc(length * sizeof(UniChar));
-        [string getCharacters:characterBuffer];
-        usedCharacterBuffer = characterBuffer;
-    }
-
-    width = [self widthForCharacters:usedCharacterBuffer length:length];
-    
-    if (characterBuffer != localCharacterBuffer)
-        free(characterBuffer);
-
-    return width;
-}
-
-
-- (int)ascent
-{
-    return ascent;
-}
-
-
-- (int)descent
-{
-    return descent;
-}
-
-
-- (int)lineSpacing
-{
-    return lineSpacing;
-}
-
-
-- (float)xHeight
-{
-    return [font xHeight];
-}
-
-
 // Useful page for testing http://home.att.net/~jameskass
 static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *advances, float x, float y, int numGlyphs)
 {
@@ -1021,13 +768,6 @@ static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *a
     }
 }
 
-- (void)drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style atPoint:(NSPoint)point
-{
-    if (shouldUseATSU(run))
-        [self _ATSU_drawHighlightForRun:run style:style atPoint:point];
-    else
-        [self _CG_drawHighlightForRun:run style:style atPoint:point];
-}
 
 - (void)_CG_drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style atPoint:(NSPoint)point
 {
@@ -1035,8 +775,8 @@ static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *a
     CGGlyph *glyphBuffer, localGlyphBuffer[LOCAL_BUFFER_SIZE];
     NSFont **fontBuffer, *localFontBuffer[LOCAL_BUFFER_SIZE];
     CGSize *advances, localAdvanceBuffer[LOCAL_BUFFER_SIZE];
-    int numGlyphs = 0, i, startGlyph = 0, endGlyph = 0;
-    float startX;
+    int numGlyphs = 0, i;
+    float startX, startPosition;
     unsigned length = run->length;
     
     if (run->length == 0)
@@ -1059,8 +799,7 @@ static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *a
         widths:widthBuffer 
         fonts:fontBuffer
         glyphs:glyphBuffer
-        startGlyph:&startGlyph
-        endGlyph:&endGlyph
+        startPosition:&startPosition
         numGlyphs: &numGlyphs];
         
     // Eek.  We couldn't generate ANY glyphs for the run.
@@ -1068,23 +807,21 @@ static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *a
         return;
         
     // Fill the advances array.
-    for (i = 0; i <= endGlyph; i++){
+    for (i = 0; i <= numGlyphs; i++){
         advances[i].width = widthBuffer[i];
         advances[i].height = 0;
     }
 
-    // Calculate the starting point of the glyphs to be displayed by adding
-    // all the advances up to the first glyph.
-    startX = point.x;
-    for (i = 0; i < startGlyph; i++)
-        startX += advances[i].width;
+    // The starting point needs to be adjusted to account for the width of
+    // the glyphs at the start of the run.
+    startX = startPosition + point.x;
 
     if (style->backgroundColor != nil){
         // Calculate the width of the selection background by adding
-        // up teh advances of all the glyphs in the selection.
+        // up the advances of all the glyphs in the selection.
         float backgroundWidth = 0.0;
         
-        for (i = startGlyph; i <= endGlyph; i++)
+        for (i = 0; i < numGlyphs; i++)
             backgroundWidth += advances[i].width;
 
         [style->backgroundColor set];
@@ -1093,12 +830,15 @@ static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *a
             WebCoreTextRun completeRun = *run;
             completeRun.from = 0;
             completeRun.to = run->length;
-            float completeRunWidth = [self floatWidthForRun:&completeRun style:style widths:widthBuffer];
-            float leftX = 0;
-            for (i = 0; i < startGlyph; i++)
-                leftX += widthBuffer[i];
-
-            [NSBezierPath fillRect:NSMakeRect(point.x + completeRunWidth - leftX - backgroundWidth, point.y - [self ascent], backgroundWidth, [self lineSpacing])];
+            float completeRunWidth = [self _floatWidthForRun:&completeRun
+                        style:style
+                        widths:nil 
+                        fonts:nil
+                        glyphs:nil
+                        startPosition:nil
+                        numGlyphs: &numGlyphs];
+
+            [NSBezierPath fillRect:NSMakeRect(point.x + completeRunWidth - startPosition - backgroundWidth, point.y - [self ascent], backgroundWidth, [self lineSpacing])];
         }
         else
             [NSBezierPath fillRect:NSMakeRect(startX, point.y - [self ascent], backgroundWidth, [self lineSpacing])];
@@ -1112,13 +852,6 @@ static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *a
     }
 }
 
-- (void)drawRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style atPoint:(NSPoint)point
-{
-    if (shouldUseATSU(run))
-        [self _ATSU_drawRun:run style:style atPoint:point];
-    else
-        [self _CG_drawRun:run style:style atPoint:point];
-}
 
 - (void)_CG_drawRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style atPoint:(NSPoint)point
 {
@@ -1126,7 +859,7 @@ static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *a
     CGGlyph *glyphBuffer, localGlyphBuffer[LOCAL_BUFFER_SIZE];
     NSFont **fontBuffer, *localFontBuffer[LOCAL_BUFFER_SIZE];
     CGSize *advances, localAdvanceBuffer[LOCAL_BUFFER_SIZE];
-    int numGlyphs = 0, i, startGlyph = 0, endGlyph = 0;
+    int numGlyphs = 0, i;
     float startX;
     unsigned length = run->length;
     
@@ -1150,8 +883,7 @@ static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *a
         widths:widthBuffer 
         fonts:fontBuffer
         glyphs:glyphBuffer
-        startGlyph:&startGlyph
-        endGlyph:&endGlyph
+        startPosition:&startX
         numGlyphs: &numGlyphs];
         
     // Eek.  We couldn't generate ANY glyphs for the run.
@@ -1159,23 +891,21 @@ static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *a
         return;
         
     // Fill the advances array.
-    for (i = 0; i <= endGlyph; i++){
+    for (i = 0; i < numGlyphs; i++){
         advances[i].width = widthBuffer[i];
         advances[i].height = 0;
     }
 
     // Calculate the starting point of the glyphs to be displayed by adding
     // all the advances up to the first glyph.
-    startX = point.x;
-    for (i = 0; i < startGlyph; i++)
-        startX += advances[i].width;
+    startX += point.x;
 
     if (style->backgroundColor != nil)
         [self _CG_drawHighlightForRun:run style:style atPoint:point];
     
     // Finally, draw the glyphs.
-    int lastFrom = startGlyph;
-    int pos = startGlyph;
+    int lastFrom = 0;
+    int pos = 0;
 
     // Swap the order of the glyphs if right-to-left.
     if (style->rtl && numGlyphs > 1){
@@ -1185,19 +915,19 @@ static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *a
         CGSize aswap1, aswap2;
         NSFont *fswap1, *fswap2;
         
-        for (i = pos, end = numGlyphs; i < (numGlyphs - pos)/2; i++){
+        for (i = pos, end = numGlyphs-1; i < (numGlyphs - pos)/2; i++){
             gswap1 = glyphBuffer[i];
             gswap2 = glyphBuffer[--end];
             glyphBuffer[i] = gswap2;
             glyphBuffer[end] = gswap1;
         }
-        for (i = pos, end = numGlyphs; i < (numGlyphs - pos)/2; i++){
+        for (i = pos, end = numGlyphs - 1; i < (numGlyphs - pos)/2; i++){
             aswap1 = advances[i];
             aswap2 = advances[--end];
             advances[i] = aswap2;
             advances[end] = aswap1;
         }
-        for (i = pos, end = numGlyphs; i < (numGlyphs - pos)/2; i++){
+        for (i = pos, end = numGlyphs - 1; i < (numGlyphs - pos)/2; i++){
             fswap1 = fontBuffer[i];
             fswap2 = fontBuffer[--end];
             fontBuffer[i] = fswap2;
@@ -1210,7 +940,7 @@ static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *a
     float nextX = startX;
     int nextGlyph = pos;
 
-    while (nextGlyph <= endGlyph && nextGlyph < numGlyphs){
+    while (nextGlyph < numGlyphs){
         if ((fontBuffer[nextGlyph] != 0 && fontBuffer[nextGlyph] != currentFont)){
             _drawGlyphs(currentFont, style->textColor, &glyphBuffer[lastFrom], &advances[lastFrom], startX, point.y, nextGlyph - lastFrom);
             lastFrom = nextGlyph;
@@ -1230,85 +960,6 @@ static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *a
     }
 }
 
-- (void)drawLineForCharacters:(NSPoint)point yOffset:(float)yOffset withWidth: (int)width withColor:(NSColor *)color
-{
-    NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
-    CGContextRef cgContext;
-    float lineWidth;
-
-    // This will draw the text from the top of the bounding box down.
-    // Qt expects to draw from the baseline.
-    // Remember that descender is negative.
-    point.y -= [self lineSpacing] - [self descent];
-    
-    BOOL flag = [graphicsContext shouldAntialias];
-
-    [graphicsContext setShouldAntialias: NO];
-
-    [color set];
-
-    cgContext = (CGContextRef)[graphicsContext graphicsPort];
-    lineWidth = 0.0;
-    if ([graphicsContext isDrawingToScreen] && lineWidth == 0.0) {
-        CGSize size = CGSizeApplyAffineTransform(CGSizeMake(1.0, 1.0), CGAffineTransformInvert(CGContextGetCTM(cgContext)));
-        lineWidth = size.width;
-    }
-    CGContextSetLineWidth(cgContext, lineWidth);
-    CGContextMoveToPoint(cgContext, point.x, point.y + [self lineSpacing] + 1.5 - [self descent] + yOffset);
-    // Subtract 1 to ensure that the line is always within bounds of element.
-    CGContextAddLineToPoint(cgContext, point.x + width - 1.0, point.y + [self lineSpacing] + 1.5 - [self descent] + yOffset);
-    CGContextStrokePath(cgContext);
-
-    [graphicsContext setShouldAntialias: flag];
-}
-
-
-- (float)floatWidthForCharacters:(const UniChar *)characters stringLength:(unsigned)stringLength characterPosition: (int)pos
-{
-    // Return the width of the first complete character at the specified position.  Even though
-    // the first 'character' may contain more than one unicode characters this method will
-    // work correctly.
-    WebCoreTextRun run;
-    WebCoreInitializeTextRun (&run, characters, stringLength, pos, 1);
-    WebCoreTextStyle style;
-    WebCoreInitializeEmptyTextStyle(&style);
-        
-    return [self _floatWidthForRun:&run style:&style widths:nil fonts:nil  glyphs:nil startGlyph:nil endGlyph:nil numGlyphs:nil];
-}
-
-
-- (float)floatWidthForCharacters:(const UniChar *)characters stringLength:(unsigned)stringLength fromCharacterPosition: (int)pos numberOfCharacters: (int)len
-{
-    WebCoreTextRun run;
-    WebCoreInitializeTextRun (&run, characters, stringLength, 0, stringLength);
-    WebCoreTextStyle style;
-    WebCoreInitializeEmptyTextStyle(&style);
-        
-    return [self _floatWidthForRun:&run style:&style widths:nil fonts:nil  glyphs:nil startGlyph:nil endGlyph:nil numGlyphs:nil];
-}
-
-
-- (float)floatWidthForCharacters:(const UniChar *)characters stringLength:(unsigned)stringLength fromCharacterPosition:(int)pos numberOfCharacters:(int)len withPadding:(int)padding widths:(float *)widthBuffer letterSpacing: (int)letterSpacing wordSpacing:(int)wordSpacing smallCaps:(BOOL)smallCaps fontFamilies:(NSString **)families
-{
-    WebCoreTextRun run;
-    WebCoreInitializeTextRun (&run, characters, stringLength, pos, pos+len);
-    WebCoreTextStyle style;
-    WebCoreInitializeEmptyTextStyle(&style);
-    
-    style.padding = padding;
-    style.letterSpacing = letterSpacing;
-    style.wordSpacing = wordSpacing;
-    style.smallCaps = smallCaps;
-    style.families = families;
-    
-    return [self _floatWidthForRun:&run style:&style widths:widthBuffer fonts:nil glyphs:nil startGlyph:nil endGlyph:nil numGlyphs:nil];
-}
-
-- (float)floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths:(float *)widthBuffer
-{
-    return [self _floatWidthForRun:run style:style widths:widthBuffer fonts:nil glyphs:nil startGlyph:nil endGlyph:nil numGlyphs:nil];
-}
-
 #ifdef DEBUG_COMBINING
 static const char *directionNames[] = {
         "DirectionL", 	// Left Letter 
@@ -1345,346 +996,44 @@ static const char *joiningNames[] = {
 };
 #endif
 
-- (float)_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths:(float *)widthBuffer fonts:(NSFont **)fontBuffer glyphs:(CGGlyph *)glyphBuffer startGlyph:(int *)startGlyph endGlyph:(int *)endGlyph numGlyphs:(int *)_numGlyphs
+- (float)_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths:(float *)widthBuffer fonts:(NSFont **)fontBuffer glyphs:(CGGlyph *)glyphBuffer startPosition:(float *)startPosition numGlyphs:(int *)_numGlyphs
 {
     if (shouldUseATSU(run))
         return [self _ATSU_floatWidthForRun:run style:style];
     
-    return [self _CG_floatWidthForRun:run style:style widths:widthBuffer fonts:fontBuffer glyphs:glyphBuffer startGlyph:startGlyph endGlyph:endGlyph numGlyphs:_numGlyphs];
+    return [self _CG_floatWidthForRun:run style:style widths:widthBuffer fonts:fontBuffer glyphs:glyphBuffer startPosition:startPosition numGlyphs:_numGlyphs];
 
 }
 
-- (float)_CG_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths: (float *)widthBuffer fonts: (NSFont **)fontBuffer glyphs: (CGGlyph *)glyphBuffer  startGlyph:(int *)startGlyph endGlyph:(int *)endGlyph numGlyphs: (int *)_numGlyphs
+- (float)_CG_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths: (float *)widthBuffer fonts: (NSFont **)fontBuffer glyphs: (CGGlyph *)glyphBuffer startPosition:(float *)startPosition numGlyphs: (int *)_numGlyphs
 {
-    float totalWidth = 0, widthFromStart = 0;
-    unsigned i, clusterLength;
-    NSFont *substituteFont = nil;
-    ATSGlyphRef glyphID;
-    float lastWidth = 0;
-    uint numSpaces = 0;
-    int padPerSpace = 0;
+    float _totalWidth = 0, _nextWidth;
+    CharacterWidthIterator widthIterator;
+    NSFont *fontUsed = 0;
+    ATSGlyphRef glyphUsed;
     int numGlyphs = 0;
-    UniChar high = 0, low = 0;
-    int from = run->from;
-    int to = run->to;
-    int len = to - from;
-    const UniChar *characters = run->characters;
-    unsigned stringLength = run->length;
-    int pos = run->from;
-    int padding = style->padding;
-    
-    if (stringLength <= 0 || len <= 0) {
-        if (_numGlyphs)
-            *_numGlyphs = 0;
-        return 0;
-    }
-
-#define SHAPE_STRINGS
-#ifdef SHAPE_STRINGS
-     UniChar *munged = 0;
-     UniChar _localMunged[LOCAL_BUFFER_SIZE];
-    {
-        UniChar *shaped;
-        int lengthOut;
-
-        // FIXME:  Change to use the new CharacterShapeIterator internal API.
-        shaped = shapedString (run, &lengthOut);        
-        if (shaped){
-             if (stringLength < LOCAL_BUFFER_SIZE)
-                 munged = &_localMunged[0];
-             else
-                 munged = (UniChar *)malloc(stringLength * sizeof(UniChar));
-            //printf ("%d input, %d output\n", len, lengthOut);
-            //for (i = 0; i < (int)len; i++){
-            //    printf ("0x%04x shaped to 0x%04x\n", characters[i], shaped[i]);
-            //}
-            // FIXME:  Hack-o-rific, copy shaped buffer into munged
-            // character buffer.
-            int k;
-            for (k = 0; k < from; k++){
-                munged[k] = characters[k];
-            }
-            for (k = from; k < from + lengthOut; k++){
-                munged[k] = shaped[k-from];
-            }
-            characters = munged;
-            len = lengthOut;
-        }
-    }
-#endif
     
-    // If the padding is non-zero, count the number of spaces in the string
-    // and divide that by the padding for per space addition.
-    if (style->padding > 0){
-        int k;
-        // Distribute the padding over the spaces in the entire run.
-        for (k = 0; k < (int)stringLength; k++) {
-            if (isSpace(characters[k])) {
-                numSpaces++;
-            }
-        }
-        padPerSpace = CEIL_TO_INT ((((float)style->padding) / ((float)numSpaces)));
+    initializeCharacterWidthIterator(&widthIterator, self, run, style);
+    if (startPosition)
+        *startPosition = widthIterator.widthToStart;
+    while ((_nextWidth = widthForNextCharacter(&widthIterator, &glyphUsed, &fontUsed)) != INVALID_WIDTH){
+        if (fontBuffer)
+            fontBuffer[numGlyphs] = fontUsed;
+        if (glyphBuffer)
+            glyphBuffer[numGlyphs] = glyphUsed;
+        if (widthBuffer)
+            widthBuffer[numGlyphs] = _nextWidth;
+        numGlyphs++;
+        _totalWidth += _nextWidth;
     }
-
-    for (i = 0; i < stringLength; i++) {
-
-        UniChar c = characters[i];
-        BOOL foundMetrics = NO;
-        
-        if (isAlternateSpace(c)) {
-            c = SPACE;
-        }
-        
-        // Drop out early if we've measured to the end of the requested
-        // fragment.
-        if ((int)i - pos >= len) {
-            if (style->applyRounding) {
-                BOOL needsRounding = NO;
-                
-                // Check if next character is a space. If so, we have to apply rounding.
-                if (c == SPACE) {
-                    needsRounding = YES;
-                }
-                // Also check if we have a run of control characters followed by a space
-                // or end of string.  If so, we have to apply rounding.
-                else {
-                    i++;
-                    while (isControlCharacter(c) && i < stringLength){
-                        c = characters[i];
-                        i++;
-                    }
-                    if (i == stringLength || c == SPACE) {
-                        needsRounding = YES;
-                    }
-                }
-                
-                if (needsRounding) {
-                    float delta = CEIL_TO_INT(widthFromStart) - widthFromStart;
-                    if (i >= (unsigned)pos)
-                        totalWidth += delta;
-                    widthFromStart += delta;
-                    if (widthBuffer && numGlyphs > 0)
-                        widthBuffer[numGlyphs - 1] += delta;
-                }
-            }
-            if (glyphBuffer && i < (unsigned)to && endGlyph)
-                *endGlyph = numGlyphs-1;
-
-            break;
-        }
-
-        // Skip control characters.
-        if (isControlCharacter(c)) {
-            if (glyphBuffer && i < (unsigned)to && endGlyph)
-                *endGlyph = numGlyphs-1;
-            continue;
-        }
-        
-        // Deal with surrogate pairs
-        if (c >= HighSurrogateRangeStart && c <= HighSurrogateRangeEnd){
-            high = c;
-
-            // Make sure we have another character and it's a low surrogate.
-            if (i+1 >= stringLength || !IsLowSurrogatePair((low = characters[++i]))){
-                // Error!  Use 0 glyph.
-                glyphID = 0;
-            }
-            else {
-                UnicodeChar uc = UnicodeValueForSurrogatePair(high, low);
-                glyphID = glyphForUnicodeCharacter(unicodeCharacterToGlyphMap, uc, &substituteFont);
-                if (glyphID == nonGlyphID) {
-                    glyphID = [self extendUnicodeCharacterToGlyphMapToInclude: uc];
-                }
-            }
-        }
-        // Otherwise we have a valid 16bit unicode character.
-        else {
-            glyphID = glyphForCharacter(characterToGlyphMap, c, &substituteFont);
-            if (glyphID == nonGlyphID) {
-                glyphID = [self extendCharacterToGlyphMapToInclude: c];
-            }
-        }
-
-        // FIXME:  look at next character to determine if it is non-base, then
-        // determine the characters cluster from this base character.
-#ifdef DEBUG_DIACRITICAL
-        if (IsNonBaseChar(c)){
-            printf ("NonBaseCharacter 0x%04x, joining attribute %d(%s), combining class %d, direction %d, glyph %d, width %f\n", c, WebCoreUnicodeJoiningFunction(c), joiningNames(WebCoreUnicodeJoiningFunction(c)), WebCoreUnicodeCombiningClassFunction(c), WebCoreUnicodeDirectionFunction(c), glyphID, widthForGlyph(self, glyphID));
-        }
-#endif
         
-        // 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 (glyphID == 0 && style->attemptFontSubstitution) {
-            const UniChar *_characters;
-            UniChar surrogates[2];
-            
-            if (high && low){
-                clusterLength = 2;
-                surrogates[0] = high;
-                surrogates[1] = low;
-                _characters = &surrogates[0];
-            }
-            else {
-                clusterLength = findLengthOfCharacterCluster (&characters[i], stringLength - i);
-                _characters = &characters[i];
-            }
-            substituteFont = [self substituteFontForCharacters: _characters length: clusterLength families: style->families];
-            if (substituteFont) {
-                int cNumGlyphs = 0;
-                ATSGlyphRef localGlyphBuffer[clusterLength*4];
-                
-                WebCoreTextRun clusterRun;
-                WebCoreInitializeTextRun(&clusterRun, _characters, clusterLength, 0, clusterLength);
-                WebCoreTextStyle clusterStyle = *style;
-                clusterStyle.padding = 0;
-                clusterStyle.applyRounding = false;
-                clusterStyle.attemptFontSubstitution = false;
-                lastWidth = [[[WebTextRendererFactory sharedFactory] rendererWithFont:substituteFont usingPrinterFont:usingPrinterFont]
-                                _floatWidthForRun:&clusterRun
-                                style:&clusterStyle 
-                                widths: ((widthBuffer != 0 ) ? (&widthBuffer[numGlyphs]) : nil)
-                                fonts: nil
-                                glyphs: &localGlyphBuffer[0]
-                                startGlyph:nil
-                                endGlyph:nil
-                                numGlyphs: &cNumGlyphs];
-                
-                int j;
-                if (glyphBuffer){
-                    if (i-1 == (unsigned)from && startGlyph)
-                        *startGlyph = numGlyphs;
-                    for (j = 0; j < cNumGlyphs; j++){
-                        glyphBuffer[numGlyphs+j] = localGlyphBuffer[j];
-                    }
-                }
-                
-                if (fontBuffer){
-                    for (j = 0; j < cNumGlyphs; j++)
-                        fontBuffer[numGlyphs+j] = substituteFont;
-                }
-                
-                if (clusterLength == 1 && cNumGlyphs == 1 && localGlyphBuffer[0] != 0){
-                    [self updateGlyphEntryForCharacter: _characters[0] glyphID: localGlyphBuffer[0] font: substituteFont];
-                }
-
-                numGlyphs += cNumGlyphs;
-
-                if (glyphBuffer && i == (unsigned)(to-1) && endGlyph)
-                    *endGlyph = numGlyphs-1;
-
-                foundMetrics = YES;
-            }
-#ifdef DEBUG_MISSING_GLYPH
-            else {
-                BOOL hasFont = [[NSFont coveredCharacterCache] characterIsMember:c];
-                printf ("Unable to find glyph for base character 0x%04x in font %s(%s) hasFont1 = %d\n", c, [[font displayName] cString], [[substituteFont displayName] cString], (int)hasFont);
-            }
-#endif
-        }
-        
-        // If we have a valid glyph OR if we couldn't find a substitute font
-        // measure the glyph.
-        if ((glyphID > 0 || ((glyphID == 0) && substituteFont == nil)) && !foundMetrics) {
-            if (glyphID == spaceGlyph && style->applyRounding) {
-                if (lastWidth > 0){
-                    float delta = CEIL_TO_INT(widthFromStart) - widthFromStart;
-                    if (i >= (unsigned)pos)
-                        totalWidth += delta;
-                    widthFromStart += delta;
-                    if (widthBuffer)
-                        widthBuffer[numGlyphs - 1] += delta;
-                } 
-                lastWidth = adjustedSpaceWidth;
-                if (padding > 0){
-                    // Only use left over padding if note evenly divisible by 
-                    // number of spaces.
-                    if (padding < padPerSpace){
-                        lastWidth += padding;
-                        padding = 0;
-                    }
-                    else {
-                        lastWidth += padPerSpace;
-                        padding -= padPerSpace;
-                    }
-                }
-            }
-            else
-                lastWidth = widthForGlyph(self, glyphID, substituteFont);
-            
-            if (fontBuffer){
-                fontBuffer[numGlyphs] = (substituteFont ? substituteFont: font);
-            }
-            if (glyphBuffer) {
-                if (i == (unsigned)from && startGlyph)
-                    *startGlyph = numGlyphs;
-                glyphBuffer[numGlyphs] = glyphID;
-            }
-
-            // Account for the letter-spacing.  Only add width to base characters.
-            // Combining glyphs should have zero width.
-            if (lastWidth > 0)
-                lastWidth += style->letterSpacing;
-
-            // Account for word-spacing.  Make the size of the last character (grapheme cluster)
-            // in the word wider.
-            if (glyphID == spaceGlyph && numGlyphs > 0 && !isSpace(characters[i-1])) {
-                // Find the base glyph in the grapheme cluster.  Combining glyphs
-                // should have zero width.
-                if (widthBuffer){
-                    int ng = numGlyphs-1;
-                    if (ng >= 0){
-                        while (ng && widthBuffer[ng] == 0)
-                            ng--;
-                        widthBuffer[ng] += style->wordSpacing;
-                    }
-                }
-                if (i >= (unsigned)pos)
-                    totalWidth += style->wordSpacing;
-                widthFromStart += style->wordSpacing;
-            }
-            
-            if (widthBuffer){
-                widthBuffer[numGlyphs] = lastWidth;
-            }
-            numGlyphs++;
-
-            if (glyphBuffer && i == (unsigned)(to-1) && endGlyph)
-                *endGlyph = numGlyphs-1;
-        }
-#ifdef DEBUG_COMBINING        
-        printf ("Character 0x%04x, joining attribute %d(%s), combining class %d, direction %d(%s)\n", c, WebCoreUnicodeJoiningFunction(c), joiningNames[WebCoreUnicodeJoiningFunction(c)], WebCoreUnicodeCombiningClassFunction(c), WebCoreUnicodeDirectionFunction(c), directionNames[WebCoreUnicodeDirectionFunction(c)]);
-#endif
-        
-        if (i >= (unsigned)pos)
-            totalWidth += lastWidth;
-        widthFromStart += lastWidth;
-    }
-
-    // Ceil the last glyph, but only if
-    // 1) The string is longer than one character
-    // 2) or the entire stringLength is one character
-    if ((len > 1 || stringLength == 1) && style->applyRounding){
-        float delta = CEIL_TO_INT(widthFromStart) - widthFromStart;
-        if (i >= (unsigned)pos)
-            totalWidth += delta;
-        widthFromStart += delta;
-        if (widthBuffer && numGlyphs > 0)
-            widthBuffer[numGlyphs-1] += delta;
-    }
-
     if (_numGlyphs)
         *_numGlyphs = numGlyphs;
 
-#ifdef SHAPE_STRINGS
-     if (munged != &_localMunged[0])
-         free (munged);
-#endif
-    
-    return totalWidth;
+    return _totalWidth;
 }
 
-- (ATSGlyphRef)extendUnicodeCharacterToGlyphMapToInclude: (UnicodeChar)c
+- (ATSGlyphRef)_extendUnicodeCharacterToGlyphMapToInclude:(UnicodeChar)c
 {
     UnicodeGlyphMap *map = (UnicodeGlyphMap *)calloc (1, sizeof(UnicodeGlyphMap));
     ATSLayoutRecord *glyphRecord;
@@ -1720,7 +1069,7 @@ static const char *joiningNames[] = {
         return 0;
     }
     
-    [self convertUnicodeCharacters: &buffer[0] length: count toGlyphs: &glyphVector];
+    [self _convertUnicodeCharacters: &buffer[0] length: count toGlyphs: &glyphVector];
     unsigned numGlyphs = glyphVector.numGlyphs;
     if (numGlyphs != count){
         // This should never happen, indicates a bad font!  If it does the
@@ -1751,7 +1100,7 @@ static const char *joiningNames[] = {
     return glyphID;
 }
 
-- (void)updateGlyphEntryForCharacter: (UniChar)c glyphID: (ATSGlyphRef)glyphID font: (NSFont *)substituteFont
+- (void)_updateGlyphEntryForCharacter:(UniChar)c glyphID:(ATSGlyphRef)glyphID font:(NSFont *)substituteFont
 {
     GlyphMap *lastMap = characterToGlyphMap;
     while (lastMap != 0){
@@ -1767,7 +1116,7 @@ static const char *joiningNames[] = {
     }
 }
 
-- (ATSGlyphRef)extendCharacterToGlyphMapToInclude:(UniChar) c
+- (ATSGlyphRef)_extendCharacterToGlyphMapToInclude:(UniChar) c
 {
     GlyphMap *map = (GlyphMap *)calloc (1, sizeof(GlyphMap));
     ATSLayoutRecord *glyphRecord;
@@ -1802,7 +1151,7 @@ static const char *joiningNames[] = {
         return 0;
     }
 
-    [self convertCharacters: &buffer[0] length: count toGlyphs: &glyphVector skipControlCharacters: NO];
+    [self _convertCharacters: &buffer[0] length: count toGlyphs: &glyphVector skipControlCharacters: NO];
     unsigned numGlyphs = glyphVector.numGlyphs;
     if (numGlyphs != count){
         // This should never happen, perhaps indicates a bad font!  If it does the
@@ -1841,7 +1190,7 @@ static const char *joiningNames[] = {
 }
 
 
-- (WidthMap *)extendGlyphToWidthMapToInclude:(ATSGlyphRef)glyphID font:(NSFont *)subFont
+- (WidthMap *)_extendGlyphToWidthMapToInclude:(ATSGlyphRef)glyphID font:(NSFont *)subFont
 {
     WidthMap *map = (WidthMap *)calloc (1, sizeof(WidthMap)), **rootMap;
     unsigned end;
@@ -2098,14 +1447,6 @@ static const char *joiningNames[] = {
     ATSUDisposeTextLayout (layout); // Ignore the error.  Nothing we can do anyway.
 }
 
-- (int)pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed
-{
-    if (shouldUseATSU(run))
-        return [self _ATSU_pointToOffset:run style:style position:x reversed:reversed];
-    return [self _CG_pointToOffset:run style:style position:x reversed:reversed];
-}
-
-
 - (int)_ATSU_pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed
 {
     unsigned offset = 0;
@@ -2141,29 +1482,41 @@ static const char *joiningNames[] = {
         width = [self floatWidthForRun:run style:style widths:nil];
         delta -= width;
         while(offset < run->length) {
-            float w = widthForNextCharacter(&widthIterator);
-            if (w){
-                float w2 = w/2;
-                w -= w2;
-                delta += w2;
-                if(delta >= 0)
-                    break;
-                delta += w;
+            float w = widthForNextCharacter(&widthIterator, 0, 0);
+            if (w == INVALID_WIDTH){
+                // Something very bad happened, like we only have half of a surrogate pair.
+                break;
+            }
+            else {
+                if (w){
+                    float w2 = w/2;
+                    w -= w2;
+                    delta += w2;
+                    if(delta >= 0)
+                        break;
+                    delta += w;
+                }
+                offset++;
             }
-            offset++;
         }
     } else {
         while(offset < run->length) {
-            float w = widthForNextCharacter(&widthIterator);
-            if (w){
-                float w2 = w/2;
-                w -= w2;
-                delta -= w2;
-                if(delta <= 0) 
-                    break;
-                delta -= w;
+            float w = widthForNextCharacter(&widthIterator, 0, 0);
+            if (w == INVALID_WIDTH){
+                // Something very bad happened, like we only have half of a surrogate pair.
+                break;
+            }
+            else {
+                if (w){
+                    float w2 = w/2;
+                    w -= w2;
+                    delta -= w2;
+                    if(delta <= 0) 
+                        break;
+                    delta -= w;
+                }
+                offset++;
             }
-            offset++;
         }
     }
     
@@ -2171,3 +1524,542 @@ static const char *joiningNames[] = {
 }
 
 @end
+
+// ------------------- Private functions -------------------
+
+static void freeWidthMap (WidthMap *map)
+{
+    if (!map)
+	return;
+    freeWidthMap (map->next);
+    free (map->widths);
+    free (map);
+}
+
+
+static void freeGlyphMap (GlyphMap *map)
+{
+    if (!map)
+	return;
+    freeGlyphMap (map->next);
+    free (map->glyphs);
+    free (map);
+}
+
+
+static inline ATSGlyphRef glyphForCharacter (GlyphMap *map, UniChar c, NSFont **font)
+{
+    if (map == 0)
+        return nonGlyphID;
+        
+    if (c >= map->startRange && c <= map->endRange){
+        *font = map->glyphs[c-map->startRange].font;
+        return map->glyphs[c-map->startRange].glyph;
+    }
+        
+    return glyphForCharacter (map->next, c, font);
+}
+ 
+ 
+static inline ATSGlyphRef glyphForUnicodeCharacter (UnicodeGlyphMap *map, UnicodeChar c, NSFont **font)
+{
+    if (map == 0)
+        return nonGlyphID;
+        
+    if (c >= map->startRange && c <= map->endRange){
+        *font = map->glyphs[c-map->startRange].font;
+        return map->glyphs[c-map->startRange].glyph;
+    }
+        
+    return glyphForUnicodeCharacter (map->next, c, font);
+}
+ 
+
+#ifdef _TIMING        
+static double totalCGGetAdvancesTime = 0;
+#endif
+
+static inline SubstituteFontWidthMap *mapForSubstituteFont(WebTextRenderer *renderer, NSFont *font)
+{
+    int i;
+    
+    for (i = 0; i < renderer->numSubstituteFontWidthMaps; i++){
+        if (font == renderer->substituteFontWidthMaps[i].font)
+            return &renderer->substituteFontWidthMaps[i];
+    }
+    
+    if (renderer->numSubstituteFontWidthMaps == renderer->maxSubstituteFontWidthMaps){
+        renderer->maxSubstituteFontWidthMaps = renderer->maxSubstituteFontWidthMaps * 2;
+        renderer->substituteFontWidthMaps = realloc (renderer->substituteFontWidthMaps, renderer->maxSubstituteFontWidthMaps * sizeof(SubstituteFontWidthMap));
+        for (i = renderer->numSubstituteFontWidthMaps; i < renderer->maxSubstituteFontWidthMaps; i++){
+            renderer->substituteFontWidthMaps[i].font = 0;
+            renderer->substituteFontWidthMaps[i].map = 0;
+        }
+    }
+    
+    renderer->substituteFontWidthMaps[renderer->numSubstituteFontWidthMaps].font = font;
+    return &renderer->substituteFontWidthMaps[renderer->numSubstituteFontWidthMaps++];
+}
+
+static inline WebGlyphWidth widthFromMap (WebTextRenderer *renderer, WidthMap *map, ATSGlyphRef glyph, NSFont *font)
+{
+    WebGlyphWidth width = UNINITIALIZED_GLYPH_WIDTH;
+    BOOL errorResult;
+    
+    while (1){
+        if (map == 0)
+            map = [renderer _extendGlyphToWidthMapToInclude: glyph font:font];
+
+        if (glyph >= map->startRange && glyph <= map->endRange){
+            width = map->widths[glyph-map->startRange].width;
+            if (width == UNINITIALIZED_GLYPH_WIDTH){
+
+#ifdef _TIMING
+                double startTime = CFAbsoluteTimeGetCurrent();
+#endif
+                if (font)
+                    errorResult = CGFontGetGlyphScaledAdvances ([font _backingCGSFont], &glyph, 1, &map->widths[glyph-map->startRange].width, [font pointSize]);
+                else
+                    errorResult = CGFontGetGlyphScaledAdvances ([renderer->font _backingCGSFont], &glyph, 1, &map->widths[glyph-map->startRange].width, [renderer->font pointSize]);
+                if (errorResult == 0)
+                    FATAL_ALWAYS ("Unable to cache glyph widths for %@ %f",  [renderer->font displayName], [renderer->font pointSize]);
+
+#ifdef _TIMING
+                double thisTime = CFAbsoluteTimeGetCurrent() - startTime;
+                totalCGGetAdvancesTime += thisTime;
+#endif
+                width = map->widths[glyph-map->startRange].width;
+            }
+        }
+
+        if (width == UNINITIALIZED_GLYPH_WIDTH){
+            map = map->next;
+            continue;
+        }
+        
+        // Hack to ensure that characters that match the width of the space character
+        // have the same integer width as the space character.  This is necessary so
+        // glyphs in fixed pitch fonts all have the same integer width.  We can't depend
+        // on the fixed pitch property of NSFont because that isn't set for all
+        // monospaced fonts, in particular Courier!  This has the downside of inappropriately
+        // adjusting the widths of characters in non-monospaced fonts that are coincidentally
+        // the same width as a space in that font.  In practice this is not an issue as the
+        // adjustment is always as the sub-pixel level.
+        if (width == renderer->spaceWidth)
+            return renderer->ceiledSpaceWidth;
+
+        return width;
+    }
+    // never get here.
+    return 0;
+}    
+
+static inline WebGlyphWidth widthForGlyph (WebTextRenderer *renderer, ATSGlyphRef glyph, NSFont *font)
+{
+    WidthMap *map;
+
+    if (font && font != renderer->font)
+        map = mapForSubstituteFont(renderer, font)->map;
+    else
+        map = renderer->glyphToWidthMap;
+
+    return widthFromMap (renderer, map, glyph, font);
+}
+
+
+static void initializeCharacterWidthIterator (CharacterWidthIterator *iterator, WebTextRenderer *renderer, const WebCoreTextRun *run , const WebCoreTextStyle *style) 
+{
+    iterator->needsShaping = initializeCharacterShapeIterator (&iterator->shapeIterator, run);
+    iterator->renderer = renderer;
+    iterator->run = run;
+    iterator->style = style;
+    iterator->currentCharacter = run->from;
+    iterator->runWidthSoFar = 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.
+    iterator->padding = style->padding;
+    if (iterator->padding > 0){
+        uint numSpaces = 0;
+        int from = run->from;
+        int len = run->to - from;
+        int k;
+        for (k = from; k < from + len; k++) {
+            if (isSpace(run->characters[k])) {
+                numSpaces++;
+            }
+        }
+        iterator->padPerSpace = CEIL_TO_INT ((((float)style->padding) / ((float)numSpaces)));
+    }
+    else {
+        iterator->padPerSpace = 0;
+    }
+    
+    // Calculate width up to starting position of the run.  This is
+    // necessary to ensure that our rounding hacks are always consistently
+    // applied.
+    if (run->from != 0){
+        WebCoreTextRun startPositionRun = *run;
+        startPositionRun.from = 0;
+        startPositionRun.to = run->from;
+        CharacterWidthIterator startPositionIterator;
+        initializeCharacterWidthIterator (&startPositionIterator, renderer, &startPositionRun, style);
+        
+        while (startPositionIterator.currentCharacter < (unsigned)startPositionRun.to){
+            widthForNextCharacter(&startPositionIterator, 0, 0);
+        }
+        iterator->widthToStart = startPositionIterator.runWidthSoFar;
+    }
+    else
+        iterator->widthToStart = 0;
+}
+
+static float widthAndGlyphForSurrogate (WebTextRenderer *renderer, UniChar high, UniChar low, ATSGlyphRef *glyphID, NSString **families, NSFont **fontUsed)
+{
+    UnicodeChar uc = UnicodeValueForSurrogatePair(high, low);
+    float width;
+
+    *glyphID = glyphForUnicodeCharacter(renderer->unicodeCharacterToGlyphMap, uc, fontUsed);
+    if (*glyphID == nonGlyphID) {
+        *glyphID = [renderer _extendUnicodeCharacterToGlyphMapToInclude: uc];
+    }
+
+    if (*glyphID == 0){
+        UniChar surrogates[2];
+        unsigned clusterLength;
+        
+        clusterLength = 2;
+        surrogates[0] = high;
+        surrogates[1] = low;
+        *fontUsed = [renderer _substituteFontForCharacters:&surrogates[0] length: clusterLength families: families];
+        if (*fontUsed){
+            WebTextRenderer *substituteRenderer = [[WebTextRendererFactory sharedFactory] rendererWithFont:*fontUsed usingPrinterFont:renderer->usingPrinterFont];
+            *glyphID = glyphForUnicodeCharacter(substituteRenderer->unicodeCharacterToGlyphMap, uc, fontUsed);
+            if (*glyphID == nonGlyphID)
+                *glyphID = [substituteRenderer _extendUnicodeCharacterToGlyphMapToInclude: uc];
+            return widthForGlyph (substituteRenderer, *glyphID, *fontUsed);
+       }
+    }
+    
+    width = widthForGlyph (renderer, *glyphID, *fontUsed);
+    
+    return width;
+}
+
+static inline float ceilCurrentWidth (CharacterWidthIterator *iterator)
+{
+    float delta = CEIL_TO_INT(iterator->widthToStart + iterator->runWidthSoFar) - (iterator->widthToStart + iterator->runWidthSoFar);
+    iterator->runWidthSoFar += delta;
+    return delta;
+}
+
+// Return INVALID_WIDTH if an error is encountered or we're at the end of the range in the run.
+static float widthForNextCharacter (CharacterWidthIterator *iterator, ATSGlyphRef *glyphUsed, NSFont **fontUsed)
+{
+    WebTextRenderer *renderer = iterator->renderer;
+    const WebCoreTextRun *run = iterator->run;
+    UniChar c;
+    unsigned offset = iterator->currentCharacter;
+    WebGlyphWidth width;
+    NSFont *_fontUsed = 0;
+    ATSGlyphRef _glyphUsed;
+    BOOL useSmallCapsFont = NO;
+
+    if (!fontUsed)
+        fontUsed = &_fontUsed;
+    if (!glyphUsed)
+        glyphUsed = &_glyphUsed;
+        
+    if (offset >= (unsigned)run->to)
+        // Error!  Offset specified beyond end of run.
+        return INVALID_WIDTH;
+        
+    // Determine if the string requires any shaping, i.e. Arabic.
+    if (iterator->needsShaping)
+        c = shapeForNextCharacter(&iterator->shapeIterator);
+    else
+        c = run->characters[offset];
+    iterator->currentCharacter++;
+    
+    // It's legit to sometimes get 0 from the shape iterator, return a zero width.
+    if (c == 0)
+        return 0;
+
+    // If small-caps convert lowercase to upper.
+    if (renderer->isSmallCapsRenderer) {
+        if (!isUpper(c)) {
+            c = toUpper(c);
+            useSmallCapsFont = YES;
+        }
+        else
+            useSmallCapsFont = NO;
+    }
+            
+    // Get a glyph for the next characters.  Somewhat complicated by surrogate
+    // pairs.
+    //
+    // Do we have a surrogate pair?  If so, determine the full Unicode (32bit)
+    // code point before glyph lookup.  We only provide a width when the offset
+    // specifies the first component of the surrogate pair.
+    if (c >= HighSurrogateRangeStart && c <= HighSurrogateRangeEnd) {
+        UniChar high = c, low;
+
+        // Make sure we have another character and it's a low surrogate.
+        if (offset+1 >= run->length || !IsLowSurrogatePair((low = run->characters[offset+1]))) {
+            // Error!  The second component of the surrogate pair is missing.
+            return INVALID_WIDTH;
+        }
+
+        width = widthAndGlyphForSurrogate (renderer, high, low, glyphUsed, iterator->style->families, fontUsed);
+    }
+    else if (c >= LowSurrogateRangeStart && c <= LowSurrogateRangeEnd) {
+        // Return 0 width for second component of the surrogate pair.
+        return 0;
+    }
+    else {
+        *glyphUsed = glyphForCharacter(renderer->characterToGlyphMap, c, fontUsed);
+
+        if (*glyphUsed == nonGlyphID) {
+            *glyphUsed = [renderer _extendCharacterToGlyphMapToInclude: c];
+        }
+
+        // Check to see if we're rendering in 'small-caps' mode.
+        // ASSUMPTION:  We assume the same font in a smaller size has
+        // the same glyphs as the large font.
+        if (useSmallCapsFont) {
+            if (*fontUsed == 0)
+                *fontUsed = [renderer _smallCapsFont];
+            else {
+                // Potential for optimization.  This path should only be taken if we're
+                // using a cached substituted font.
+                *fontUsed = [[NSFontManager sharedFontManager] convertFont:*fontUsed toSize:[*fontUsed pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER];
+            }
+        }
+
+        // Now that we have glyph and font get it's width.  We special case spaces.
+        // They are always an even integer width.
+        if (*glyphUsed == renderer->spaceGlyph)
+            width = renderer->adjustedSpaceWidth;
+        else
+            width = widthForGlyph (renderer, *glyphUsed, *fontUsed);
+    }
+
+    // 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 (*glyphUsed == 0 && iterator->style->attemptFontSubstitution) {
+        UniChar _characters[1];
+        NSFont *substituteFont;
+
+        _characters[0] = c;
+        substituteFont = [renderer _substituteFontForCharacters:_characters length:1 families:iterator->style->families];
+        if (substituteFont) {
+            int cNumGlyphs = 0;
+            ATSGlyphRef localGlyphBuffer[4];
+            
+            WebCoreTextRun clusterRun;
+            WebCoreInitializeTextRun(&clusterRun, _characters, 1, 0, 1);
+            WebCoreTextStyle clusterStyle = *iterator->style;
+            clusterStyle.padding = 0;
+            clusterStyle.applyRounding = false;
+            clusterStyle.attemptFontSubstitution = false;
+            
+            WebTextRenderer *substituteRenderer;
+            substituteRenderer = [[WebTextRendererFactory sharedFactory] rendererWithFont:substituteFont usingPrinterFont:renderer->usingPrinterFont];
+            width = [substituteRenderer
+                            _floatWidthForRun:&clusterRun
+                            style:&clusterStyle 
+                            widths: nil
+                            fonts: nil
+                            glyphs: &localGlyphBuffer[0]
+                            startPosition:nil
+                            numGlyphs:&cNumGlyphs];
+            
+            *fontUsed = substituteFont;
+            *glyphUsed = localGlyphBuffer[0];
+            
+            if (cNumGlyphs == 1 && localGlyphBuffer[0] != 0){
+                [renderer _updateGlyphEntryForCharacter:_characters[0] glyphID:localGlyphBuffer[0] font:substituteFont];
+            }
+            else
+                NSLog (@"Unable to find appropriate match for character\n");
+        }
+    }
+    
+    if (!*fontUsed)
+        *fontUsed = renderer->font;
+
+    // Account for letter-spacing
+    if (width > 0)
+        width += iterator->style->letterSpacing;
+
+    // Account for padding.  khtml uses space padding to justify text.  We
+    // distribute the specified padding over the available spaces in the run.
+    if (c == SPACE){
+        if (iterator->padding > 0){
+            // Only use left over padding if note evenly divisible by 
+            // number of spaces.
+            if (iterator->padding < iterator->padPerSpace){
+                width += iterator->padding;
+                iterator->padding = 0;
+            }
+            else {
+                width += iterator->padPerSpace;
+                iterator->padding -= iterator->padPerSpace;
+            }
+        }
+        
+        // Account for word-spacing.  We apply additional space between "words" by
+        // adding width to the space character.
+        if (iterator->currentCharacter > 1 && !isSpace(run->characters[iterator->currentCharacter-2]))
+            width += iterator->style->wordSpacing;
+    }
+
+    iterator->runWidthSoFar += width;
+    
+    // Account for float/integer impedance mismatch between CG and khtml.  "Words" (characters 
+    // followed by a character defined by isSpace()) are always an integer width.  We adjust the 
+    // width of the last character of a "word" to ensure an integer width.  When we move khtml to
+    // floats we can remove this (and related) hacks.
+    //
+    // Check to see if the next character is a space, if so, adjust.
+    if (offset+1 < run->length && isSpace(run->characters[offset+1])) {
+        width += ceilCurrentWidth(iterator);
+    }
+
+    // Ceil the width of the last glyph in a run, but only if
+    // 1) The string is longer than one character
+    // 2) or the entire stringLength is one character
+    int len = run->to - run->from;
+    if (iterator->currentCharacter >= (unsigned)run->to && (len > 1 || run->length == 1) && iterator->style->applyRounding){
+        width += ceilCurrentWidth(iterator);
+    }
+    
+    return width;
+}
+
+
+static BOOL fillStyleWithAttributes(ATSUStyle style, NSFont *theFont)
+{
+    if (theFont) {
+        ATSUFontID fontId = (ATSUFontID)[theFont _atsFontID];
+        LOG (FontCache, "fillStyleWithAttributes:  font = %p,%@, _atsFontID = %x\n", theFont, theFont, (unsigned)fontId);
+        ATSUAttributeTag tag = kATSUFontTag;
+        ByteCount size = sizeof(ATSUFontID);
+        ATSUFontID *valueArray[1] = {&fontId};
+        OSStatus status;
+
+        if (fontId) {
+            status = ATSUSetAttributes(style, 1, &tag, &size, (void *)valueArray);
+            if (status != noErr){
+                LOG (FontCache, "fillStyleWithAttributes failed(%d):  font = %p,%@, _atsFontID = %x\n", (int)status, theFont, theFont, (unsigned)fontId);
+                return NO;
+            }
+        }
+        else {
+            return NO;
+        }
+        return YES;
+    }
+    return NO;
+}
+
+#ifdef NEED_FINDLENGTHOFCHARACTERCLUSTER
+static unsigned findLengthOfCharacterCluster(const UniChar *characters, unsigned length)
+{
+    unsigned k;
+
+    if (length <= 1)
+        return length;
+    
+    if (IsHighSurrogatePair(characters[0]))
+        return 2;
+        
+    if (IsNonBaseChar(characters[0]))
+        return 1;
+    
+    // Find all the non base characters after the current character.
+    for (k = 1; k < length; k++)
+        if (!IsNonBaseChar(characters[k]))
+            break;
+    return k;
+}
+#endif
+
+static inline BOOL shouldUseATSU(const WebCoreTextRun *run)
+{
+    UniChar c;
+    const UniChar *characters = run->characters;
+    int i, from = run->from, to = run->to;
+    
+    for (i = from; i < to; i++){
+        c = characters[i];
+        if (c < 0x300)                      // Early continue to avoid  other checks.
+            continue;
+            
+        if (c >= 0x300 && c <= 0x36F)       // U+0300 through U+036F Combining diacritical marks
+            return YES;
+        if (c >= 0x20D0 && c <= 0x20FF)     // U+20D0 through U+20FF Combining marks for symbols
+            return YES;
+        if (c >= 0xFE20 && c <= 0xFE2f)     // U+FE20 through U+FE2F Combining half marks
+            return YES;
+        if (c >= 0x700 && c <= 0x1059)      // U+0700 through U+1059 Syriac, Thaana, Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar
+            return YES;
+        if (c >= 0x1100 && c <= 0x11FF)     // 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)
+            return YES;
+        if (c >= 0x1780 && c <= 0x18AF)     // U+1780 through U+18AF Khmer, Mongolian
+            return YES;
+        if (c >= 0x1900 && c <= 0x194F)     // U+1900 through U+194F Limbu (Unicode 4.0)
+            return YES;
+    }
+    
+    return NO;
+}
+
+static inline BOOL isControlCharacter(UniChar c)
+{
+    return c < 0x0020 || c == 0x007F;
+}
+
+static inline BOOL isAlternateSpace(UniChar c)
+{
+    return c == '\n' || c == 0xA0;
+}
+
+static inline BOOL isSpace(UniChar c)
+{
+    return c == SPACE || isAlternateSpace(c);
+}
+
+static inline BOOL fontContainsString (NSFont *font, NSString *string)
+{
+    if ([string rangeOfCharacterFromSet:[[font coveredCharacterSet] invertedSet]].location == NSNotFound) {
+        return YES;
+    }
+    return NO;
+}
+
+static UniChar scratchUniChar;
+
+static CFMutableStringRef GetScratchUniCharString()
+{
+    static CFMutableStringRef s = NULL;
+    if (!s)
+        s = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault, &scratchUniChar, 1, 1, kCFAllocatorNull);
+    return s;
+}
+
+static inline UniChar toUpper(UniChar c)
+{
+    // Without this first quick case, this function was showing up in profiles.
+    if (c <= 0x7F) {
+        return (char)toupper(c);
+    }
+    scratchUniChar = c;
+    CFStringUppercase(GetScratchUniCharString(), NULL);
+    return scratchUniChar;
+}
+
+static inline BOOL isUpper(UniChar c)
+{
+    return (WebCoreUnicodeCategoryFunction(c) == Letter_Uppercase);
+}

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list