[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