Use the AppKit's font rendering mode. This fixes 3905347, but we still need to...
[WebKit-https.git] / WebKit / WebCoreSupport.subproj / WebTextRenderer.m
1 /*      
2     WebTextRenderer.m       
3     Copyright 2004, Apple, Inc. All rights reserved.
4 */
5
6 #import "WebTextRenderer.h"
7
8 #import <ApplicationServices/ApplicationServices.h>
9 #import <Cocoa/Cocoa.h>
10
11 #import <AppKit/NSFont_Private.h>
12 #import <CoreGraphics/CoreGraphicsPrivate.h>
13 #import <QD/ATSUnicodePriv.h>
14
15 #import <WebCore/WebCoreUnicode.h>
16
17 #import <WebKit/WebGlyphBuffer.h>
18 #import <WebKit/WebGraphicsBridge.h>
19 #import <WebKit/WebKitLogging.h>
20 #import <WebKit/WebNSObjectExtras.h>
21 #import <WebKit/WebTextRendererFactory.h>
22 #import <WebKit/WebUnicode.h>
23
24 #import <float.h>
25
26 #import <unicode/uchar.h>
27
28 // FIXME: FATAL_ALWAYS seems like a bad idea; lets stop using it.
29
30 // SPI from other frameworks.
31
32 @interface NSLanguage : NSObject 
33 + (NSLanguage *)defaultLanguage;
34 @end
35
36 @interface NSFont (WebPrivate)
37 - (ATSUFontID)_atsFontID;
38 - (CGFontRef)_backingCGSFont;
39 // Private method to find a font for a character.
40 + (NSFont *) findFontLike:(NSFont *)aFont forCharacter:(UInt32)c inLanguage:(NSLanguage *) language;
41 + (NSFont *) findFontLike:(NSFont *)aFont forString:(NSString *)string withRange:(NSRange)range inLanguage:(NSLanguage *) language;
42 - (NSGlyph)_defaultGlyphForChar:(unichar)uu;
43 - (BOOL)_isFakeFixedPitch;
44 @end
45
46 // Macros
47 #define SPACE 0x0020
48 #define NO_BREAK_SPACE 0x00A0
49 #define ZERO_WIDTH_SPACE 0x200B
50
51 #define ROUND_TO_INT(x) (int)((x)+.5)
52
53 // Lose precision beyond 1000ths place. This is to work around an apparent
54 // bug in CoreGraphics where there seem to be small errors to some metrics.
55 #define CEIL_TO_INT(x) ((int)(x + 0.999)) /* ((int)(x + 1.0 - FLT_EPSILON)) */
56
57 // MAX_GLYPH_EXPANSION is the maximum numbers of glyphs that may be
58 // use to represent a single Unicode code point.
59 #define MAX_GLYPH_EXPANSION 4
60 #define LOCAL_BUFFER_SIZE 2048
61
62 // Covers Latin-1.
63 #define INITIAL_BLOCK_SIZE 0x200
64
65 // Get additional blocks of glyphs and widths in bigger chunks.
66 // This will typically be for other character sets.
67 #define INCREMENTAL_BLOCK_SIZE 0x400
68
69 #define UNINITIALIZED_GLYPH_WIDTH 65535
70
71 #define ATSFontRefFromNSFont(font) (FMGetATSFontRefFromFont((FMFont)[font _atsFontID]))
72
73 #define SMALLCAPS_FONTSIZE_MULTIPLIER 0.7
74 #define INVALID_WIDTH -(__FLT_MAX__)
75
76 #if !defined(ScaleEmToUnits)
77 #define CONTEXT_DPI     (72.0)
78
79 #define ScaleEmToUnits(X, U_PER_EM)     (X * ((1.0 * CONTEXT_DPI) / (CONTEXT_DPI * U_PER_EM)))
80 #endif
81
82 // Datatypes
83 typedef float WebGlyphWidth;
84 typedef UInt32 UnicodeChar;
85
86 struct WidthEntry {
87     WebGlyphWidth width;
88 };
89
90 struct WidthMap {
91     ATSGlyphRef startRange;
92     ATSGlyphRef endRange;
93     WidthMap *next;
94     WidthEntry *widths;
95 };
96
97 struct GlyphEntry
98 {
99     ATSGlyphRef glyph;
100     NSFont *font;
101 };
102
103 struct GlyphMap {
104     UniChar startRange;
105     UniChar endRange;
106     GlyphMap *next;
107     GlyphEntry *glyphs;
108 };
109
110 struct UnicodeGlyphMap {
111     UnicodeChar startRange;
112     UnicodeChar endRange;
113     UnicodeGlyphMap *next;
114     GlyphEntry *glyphs;
115 };
116
117 struct SubstituteFontWidthMap {
118     NSFont *font;
119     WidthMap *map;
120 };
121
122 struct CharacterWidthIterator
123 {
124     WebTextRenderer *renderer;
125     const WebCoreTextRun *run;
126     const WebCoreTextStyle *style;
127     unsigned currentCharacter;
128     float runWidthSoFar;
129     float widthToStart;
130     int padding;
131     int padPerSpace;
132 };
133
134 // Internal API
135 @interface WebTextRenderer (WebInternal)
136
137 - (NSFont *)_substituteFontForCharacters: (const unichar *)characters length: (int)numCharacters families: (NSString **)families;
138
139 - (WidthMap *)_extendGlyphToWidthMapToInclude:(ATSGlyphRef)glyphID font:(NSFont *)font;
140 - (ATSGlyphRef)_extendCharacterToGlyphMapToInclude:(UniChar) c;
141 - (ATSGlyphRef)_extendUnicodeCharacterToGlyphMapToInclude: (UnicodeChar)c;
142 - (void)_updateGlyphEntryForCharacter: (UniChar)c glyphID: (ATSGlyphRef)glyphID font: (NSFont *)substituteFont;
143
144 - (float)_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths:(float *)widthBuffer fonts:(NSFont **)fontBuffer glyphs:(CGGlyph *)glyphBuffer startPosition:(float *)startPosition numGlyphs:(int *)_numGlyphs;
145
146 // Measuring runs.
147 - (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;
148 - (float)_ATSU_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style;
149
150 // Drawing runs.
151 - (void)_CG_drawRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry;
152 - (void)_ATSU_drawRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry;
153
154 // Selection point detection in runs.
155 - (int)_CG_pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed includePartialGlyphs:(BOOL)includePartialGlyphs;
156 - (int)_ATSU_pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed includePartialGlyphs:(BOOL)includePartialGlyphs;
157
158 // Drawing highlight for runs.
159 - (void)_CG_drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry;
160 - (void)_ATSU_drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry;
161
162 - (BOOL)_setupFont;
163
164 // Small caps
165 - (void)_setIsSmallCapsRenderer:(BOOL)flag;
166 - (BOOL)_isSmallCapsRenderer;
167 - (WebTextRenderer *)_smallCapsRenderer;
168 - (NSFont *)_smallCapsFont;
169
170 @end
171
172
173 // Character property functions.
174
175 static inline BOOL isSpace(UniChar c)
176 {
177     return c == SPACE || c == '\n' || c == NO_BREAK_SPACE;
178 }
179
180 static const uint8_t isRoundingHackCharacterTable[0x100] = {
181     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*\n*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
182     1 /*space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*-*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*?*/,
183     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
184     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
185     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
186     1 /*no-break space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
187     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
188     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
189 };
190
191 static inline BOOL isRoundingHackCharacter(UniChar c)
192 {
193     return (c & ~0xFF) == 0 && isRoundingHackCharacterTable[c];
194 }
195
196 // Map utility functions
197 static void freeWidthMap(WidthMap *map);
198 static void freeGlyphMap(GlyphMap *map);
199 static void freeUnicodeGlyphMap(UnicodeGlyphMap *map);
200 static inline ATSGlyphRef glyphForUnicodeCharacter (UnicodeGlyphMap *map, UnicodeChar c, NSFont **font);
201 static inline SubstituteFontWidthMap *mapForSubstituteFont(WebTextRenderer *renderer, NSFont *font);
202 static inline ATSGlyphRef glyphForCharacter (GlyphMap *map, UniChar c, NSFont **font);
203 static inline SubstituteFontWidthMap *mapForSubstituteFont(WebTextRenderer *renderer, NSFont *font);
204 static inline WebGlyphWidth widthFromMap (WebTextRenderer *renderer, WidthMap *map, ATSGlyphRef glyph, NSFont *font);
205 static inline WebGlyphWidth widthForGlyph (WebTextRenderer *renderer, ATSGlyphRef glyph, NSFont *font);
206
207 #if BUILDING_ON_PANTHER
208
209 static WebGlyphWidth getUncachedWidth(WebTextRenderer *renderer, WidthMap *map, ATSGlyphRef glyph, NSFont *font)
210 {
211     WebGlyphWidth width;
212
213     if (font == NULL)
214         font = renderer->font;
215
216     if (!CGFontGetGlyphScaledAdvances ([font _backingCGSFont], &glyph, 1, &width, [font pointSize]))
217         FATAL_ALWAYS ("Unable to cache glyph widths for %@ %f",  [font displayName], [font pointSize]);
218
219     return width;
220 }
221
222 #else
223
224 static inline CGFontRenderingMode _AppkitGetCGRenderingMode(NSFont *font) {
225     switch ([font renderingMode]) {
226         case NSFontIntegerAdvancementsRenderingMode: return kCGFontRenderingMode1BitPixelAligned;
227         case NSFontAntialiasedIntegerAdvancementsRenderingMode: return kCGFontRenderingModeAntialiasedPixelAligned;
228         default: return kCGFontRenderingModeAntialiased;
229     }
230 }
231
232 static WebGlyphWidth getUncachedWidth(WebTextRenderer *renderer, WidthMap *map, ATSGlyphRef glyph, NSFont *font)
233 {
234     float pointSize;
235     CGAffineTransform m;
236     CGSize advance;
237
238     if (font == NULL)
239         font = renderer->font;
240
241     pointSize = [font pointSize];
242     m = CGAffineTransformMakeScale(pointSize, pointSize);
243     if (!CGFontGetGlyphTransformedAdvances([font _backingCGSFont], &m, _AppkitGetCGRenderingMode(font), &glyph, 1, &advance))
244         FATAL_ALWAYS ("Unable to cache glyph widths for %@ %f", [font displayName], pointSize);
245
246     return advance.width;
247 }
248
249 #endif
250
251 static inline WebGlyphWidth widthFromMap (WebTextRenderer *renderer, WidthMap *map, ATSGlyphRef glyph, NSFont *font)
252 {
253     WebGlyphWidth width = UNINITIALIZED_GLYPH_WIDTH;
254     
255     while (1){
256         if (map == 0)
257             map = [renderer _extendGlyphToWidthMapToInclude: glyph font:font];
258
259         if (glyph >= map->startRange && glyph <= map->endRange){
260             width = map->widths[glyph - map->startRange].width;
261             if (width == UNINITIALIZED_GLYPH_WIDTH){
262                 width = getUncachedWidth (renderer, map, glyph, font);
263                 map->widths[glyph - map->startRange].width = width;
264             }
265         }
266         else {
267             map = map->next;
268             continue;
269         }
270         
271         return width;
272     }
273 }    
274
275 static inline WebGlyphWidth widthForGlyph (WebTextRenderer *renderer, ATSGlyphRef glyph, NSFont *font)
276 {
277     WidthMap *map;
278
279     if (font && font != renderer->font)
280         map = mapForSubstituteFont(renderer, font)->map;
281     else
282         map = renderer->glyphToWidthMap;
283
284     return widthFromMap (renderer, map, glyph, font);
285 }
286
287 // Iterator functions
288 static void initializeCharacterWidthIterator (CharacterWidthIterator *iterator, WebTextRenderer *renderer, const WebCoreTextRun *run , const WebCoreTextStyle *style);
289 static float widthForNextCharacter (CharacterWidthIterator *iterator, ATSGlyphRef *glyphUsed, NSFont **fontUsed);
290
291
292 // Misc.
293 static BOOL fillStyleWithAttributes(ATSUStyle style, NSFont *theFont);
294 static BOOL shouldUseATSU(const WebCoreTextRun *run);
295 static NSString *pathFromFont(NSFont *font);
296
297
298 // Globals
299 static CFCharacterSetRef nonBaseChars = NULL;
300 static BOOL bufferTextDrawing = NO;
301 static BOOL alwaysUseATSU = NO;
302
303
304 @implementation WebTextRenderer
305
306 + (NSString *)webFallbackFontFamily
307 {
308     static NSString *webFallbackFontFamily = nil;
309     if (!webFallbackFontFamily)
310         webFallbackFontFamily = [[[NSFont systemFontOfSize:16.0] familyName] retain];
311     return webFallbackFontFamily;
312 }
313
314 + (BOOL)shouldBufferTextDrawing
315 {
316     return bufferTextDrawing;
317 }
318
319 + (void)initialize
320 {
321     nonBaseChars = CFCharacterSetGetPredefined(kCFCharacterSetNonBase);
322     bufferTextDrawing = [[[NSUserDefaults standardUserDefaults] stringForKey:@"BufferTextDrawing"] isEqual: @"YES"];
323 }
324
325 - initWithFont:(NSFont *)f usingPrinterFont:(BOOL)p
326 {
327     [super init];
328     
329     // Quartz can only handle fonts with these glyph packings.  Other packings have
330     // been deprecated.
331     if ([f glyphPacking] != NSNativeShortGlyphPacking &&
332         [f glyphPacking] != NSTwoByteGlyphPacking) {
333         // Apparantly there are many deprecated fonts out there with unsupported packing types.
334         // Log and use fallback font.
335         // This change fixes the many crashes reported in 3782533.  Most likely, the
336         // problem is encountered when people upgrade from OS 9, or have OS 9
337         // fonts installed on OS X.
338         NSLog (@"%s:%d  Unable to use deprecated font %@ %f, using system font instead", __FILE__, __LINE__, [f displayName], [f pointSize]);
339         f = [NSFont systemFontOfSize:[f pointSize]];
340     }
341         
342     maxSubstituteFontWidthMaps = NUM_SUBSTITUTE_FONT_MAPS;
343     substituteFontWidthMaps = calloc (1, maxSubstituteFontWidthMaps * sizeof(SubstituteFontWidthMap));
344     font = [(p ? [f printerFont] : [f screenFont]) retain];
345     usingPrinterFont = p;
346     
347     if (![self _setupFont]){
348         // Ack!  Something very bad happened, like a corrupt font.  Try
349         // looking for an alternate 'base' font for this renderer.
350
351         // Special case hack to use "Times New Roman" in place of "Times".  "Times RO" is a common font
352         // whose family name is "Times".  It overrides the normal "Times" family font.  It also
353         // appears to have a corrupt regular variant.
354         NSString *fallbackFontFamily;
355
356         if ([[font familyName] isEqual:@"Times"])
357             fallbackFontFamily = @"Times New Roman";
358         else {
359             fallbackFontFamily = [WebTextRenderer webFallbackFontFamily];
360         }
361         
362         // Try setting up the alternate font.  This is a last ditch effort to use a
363         // substitute font when something has gone wrong.
364         NSFont *initialFont = font;
365         [initialFont autorelease];
366         NSFont *af = [[NSFontManager sharedFontManager] convertFont:font toFamily:fallbackFontFamily];
367         font = [(p ? [af printerFont] : [af screenFont]) retain];
368         NSString *filePath = pathFromFont(initialFont);
369         filePath = filePath ? filePath : @"not known";
370         if (![self _setupFont]){
371             if ([fallbackFontFamily isEqual:@"Times New Roman"]) {
372                 // OK, couldn't setup Times New Roman as an alternate to Times, fallback
373                 // on the system font.  If this fails we have no alternative left.
374                 af = [[NSFontManager sharedFontManager] convertFont:font toFamily:[WebTextRenderer webFallbackFontFamily]];
375                 font = [(p ? [af printerFont] : [af screenFont]) retain];
376                 if (![self _setupFont]){
377                     // We tried, Times, Times New Roman, and the system font.  No joy.  We have to give up.
378                     FATAL_ALWAYS ("%@ unable to initialize with font %@ at %@", self, initialFont, filePath);
379                 }
380             }
381             else {
382                 // We tried the requested font and the syste, font.  No joy.  We have to give up.
383                 FATAL_ALWAYS ("%@ unable to initialize with font %@ at %@", self, initialFont, filePath);
384             }
385         }
386
387         // Report the problem.
388         ERROR ("Corrupt font detected, using %@ in place of %@ located at \"%@\".", 
389                     [font familyName], 
390                     [initialFont familyName],
391                     filePath);
392     }
393
394     // We emulate the appkit metrics by applying rounding as is done
395     // in the appkit.
396     CGFontRef cgFont = [font _backingCGSFont];
397     const CGFontHMetrics *metrics = CGFontGetHMetrics(cgFont);
398     unsigned unitsPerEm = CGFontGetUnitsPerEm(cgFont);
399     float pointSize = [font pointSize];
400     float asc = (ScaleEmToUnits(metrics->ascent, unitsPerEm)*pointSize);
401     float dsc = (-ScaleEmToUnits(metrics->descent, unitsPerEm)*pointSize);
402     float _lineGap = ScaleEmToUnits(metrics->lineGap, unitsPerEm)*pointSize;
403     float adjustment;
404
405     // We need to adjust Times, Helvetica, and Courier to closely match the
406     // vertical metrics of their Microsoft counterparts that are the de facto
407     // web standard.  The AppKit adjustment of 20% is too big and is
408     // incorrectly added to line spacing, so we use a 15% adjustment instead
409     // and add it to the ascent.
410     if ([[font familyName] isEqualToString:@"Times"] ||
411         [[font familyName] isEqualToString:@"Helvetica"] ||
412         [[font familyName] isEqualToString:@"Courier"]) {
413         adjustment = floor(((asc + dsc) * 0.15) + 0.5);
414     } else {
415         adjustment = 0.0;
416     }
417
418     ascent = ROUND_TO_INT(asc + adjustment);
419     descent = ROUND_TO_INT(dsc);
420
421     _lineGap = (_lineGap > 0.0 ? floor(_lineGap + 0.5) : 0.0);
422     lineGap = (int)_lineGap;
423     lineSpacing =  ascent + descent + lineGap;
424
425 #ifdef COMPARE_APPKIT_CG_METRICS
426     printf ("\nCG/Appkit metrics for font %s, %f, lineGap %f, adjustment %f\n", [[font displayName] cString], [font pointSize], lineGap, adjustment);
427     if (ROUND_TO_INT([font ascender]) != ascent ||
428         ROUND_TO_INT(-[font descender]) != descent ||
429         ROUND_TO_INT([font defaultLineHeightForFont]) != lineSpacing){
430         printf ("\nCG/Appkit mismatched metrics for font %s, %f (%s)\n", [[font displayName] cString], [font pointSize],
431                 ([font screenFont] ? [[[font screenFont] displayName] cString] : "none"));
432         printf ("ascent(%s), descent(%s), lineSpacing(%s)\n",
433                 (ROUND_TO_INT([font ascender]) != ascent) ? "different" : "same",
434                 (ROUND_TO_INT(-[font descender]) != descent) ? "different" : "same",
435                 (ROUND_TO_INT([font defaultLineHeightForFont]) != lineSpacing) ? "different" : "same");
436         printf ("CG:  ascent %f, ", asc);
437         printf ("descent %f, ", dsc);
438         printf ("lineGap %f, ", lineGap);
439         printf ("lineSpacing %d\n", lineSpacing);
440         
441         printf ("NSFont:  ascent %f, ", [font ascender]);
442         printf ("descent %f, ", [font descender]);
443         printf ("lineSpacing %f\n", [font defaultLineHeightForFont]);
444     }
445 #endif
446      
447     isSmallCapsRenderer = NO;
448     
449     return self;
450 }
451
452 - (void)dealloc
453 {
454     [font release];
455     [smallCapsFont release];
456     [smallCapsRenderer release];
457
458     if (styleGroup)
459         ATSUDisposeStyleGroup(styleGroup);
460
461     freeWidthMap(glyphToWidthMap);
462     freeGlyphMap(characterToGlyphMap);
463     freeUnicodeGlyphMap(unicodeCharacterToGlyphMap);
464
465     if (ATSUStyleInitialized)
466         ATSUDisposeStyle(_ATSUSstyle);
467     
468     [super dealloc];
469 }
470
471 - (void)finalize
472 {
473     if (styleGroup)
474         ATSUDisposeStyleGroup(styleGroup);
475
476     freeWidthMap(glyphToWidthMap);
477     freeGlyphMap(characterToGlyphMap);
478     freeUnicodeGlyphMap(unicodeCharacterToGlyphMap);
479
480     if (ATSUStyleInitialized)
481         ATSUDisposeStyle(_ATSUSstyle);
482     
483     [super finalize];
484 }
485
486 - (int)ascent
487 {
488     // This simple return obviously can't throw an exception.
489     return ascent;
490 }
491
492 - (int)descent
493 {
494     // This simple return obviously can't throw an exception.
495     return descent;
496 }
497
498 - (int)lineSpacing
499 {
500     // This simple return obviously can't throw an exception.
501     return lineSpacing;
502 }
503
504 - (float)xHeight
505 {
506     // Measure the actual character "x", because AppKit synthesizes X height rather
507     // than getting it from the font. Unfortunately, NSFont will round this for us
508     // so we don't quite get the right value.
509     NSGlyph xGlyph = [font glyphWithName:@"x"];
510     if (xGlyph) {
511         NSRect xBox = [font boundingRectForGlyph:xGlyph];
512         return NSMaxY(xBox);
513     }
514
515     return [font xHeight];
516 }
517
518 - (void)drawRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry
519 {
520     if (style->smallCaps && !isSmallCapsRenderer) {
521         [[self _smallCapsRenderer] drawRun:run style:style geometry:geometry];
522     }
523     else {
524         if (shouldUseATSU(run))
525             [self _ATSU_drawRun:run style:style geometry:geometry];
526         else
527             [self _CG_drawRun:run style:style geometry:geometry];
528     }
529 }
530
531 - (float)floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths:(float *)widthBuffer
532 {
533     if (style->smallCaps && !isSmallCapsRenderer) {
534         return [[self _smallCapsRenderer] _floatWidthForRun:run style:style widths:widthBuffer fonts:nil glyphs:nil startPosition:nil numGlyphs:nil];
535     }
536     return [self _floatWidthForRun:run style:style widths:widthBuffer fonts:nil glyphs:nil startPosition:nil numGlyphs:nil];
537 }
538
539 - (void)drawLineForCharacters:(NSPoint)point yOffset:(float)yOffset withWidth: (int)width withColor:(NSColor *)color
540 {
541     // XXX MJS
542
543     NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
544     CGContextRef cgContext;
545     float lineWidth;
546
547     // This will draw the text from the top of the bounding box down.
548     // Qt expects to draw from the baseline.
549     // Remember that descender is negative.
550     point.y -= [self lineSpacing] - [self descent];
551     
552     BOOL flag = [graphicsContext shouldAntialias];
553
554     [graphicsContext setShouldAntialias: NO];
555
556     [color set];
557
558     cgContext = (CGContextRef)[graphicsContext graphicsPort];
559     lineWidth = 0.0;
560     if ([graphicsContext isDrawingToScreen] && lineWidth == 0.0) {
561         CGSize size = CGSizeApplyAffineTransform(CGSizeMake(1.0, 1.0), CGAffineTransformInvert(CGContextGetCTM(cgContext)));
562         lineWidth = size.width;
563     }
564     CGContextSetLineWidth(cgContext, lineWidth);
565     CGContextMoveToPoint(cgContext, point.x, point.y + [self lineSpacing] + 1.5 - [self descent] + yOffset);
566     // Subtract 1 to ensure that the line is always within bounds of element.
567     CGContextAddLineToPoint(cgContext, point.x + width - 1.0, point.y + [self lineSpacing] + 1.5 - [self descent] + yOffset);
568     CGContextStrokePath(cgContext);
569
570     [graphicsContext setShouldAntialias: flag];
571 }
572
573
574 - (void)drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry
575 {
576     if (style->smallCaps && !isSmallCapsRenderer) {
577         [[self _smallCapsRenderer] drawHighlightForRun:run style:style geometry:geometry];
578     }
579     else {
580         if (shouldUseATSU(run))
581             [self _ATSU_drawHighlightForRun:run style:style geometry:geometry];
582         else
583             [self _CG_drawHighlightForRun:run style:style geometry:geometry];
584     }
585 }
586
587 // Constants for pattern underline
588 #define patternWidth 4
589 #define patternHeight 3
590
591 - (void)drawLineForMisspelling:(NSPoint)point withWidth:(int)width
592 {
593     // Constants for pattern color
594     static NSColor *spellingPatternColor = nil;
595     static bool usingDot = false;
596  
597     // Initialize pattern color if needed
598     if (!spellingPatternColor) {
599         NSImage *image = [NSImage imageNamed:@"SpellingDot"];
600         ASSERT(image); // if image is not available, we want to know
601         NSColor *color = (image ? [NSColor colorWithPatternImage:image] : nil);
602         if (color)
603             usingDot = true;
604         else
605             color = [NSColor redColor];
606         spellingPatternColor = [color retain];
607     }
608
609     // Width must be divisible by 4 to make sure we always draw full misspelling dots under words.
610     // Do a small adjustment to shift the underline back to the left if the pattern was
611     // expanded to the right "too much" to accomodate the drawing of a full dot.
612     if (usingDot) {
613         int w = (width + patternWidth) - (width % patternWidth);
614         if (w - width > 2) 
615             point.x -= 1;
616         width = w;
617     }
618
619     // Compute the appropriate phase relative to the top level view in the window.
620     NSPoint originInWindow = [[NSView focusView] convertPoint:point toView:nil];
621     // WebCore may translate the focus, and thus need an extra phase correction
622     NSPoint extraPhase = [[WebGraphicsBridge sharedBridge] additionalPatternPhase];
623     originInWindow.x += extraPhase.x;
624     originInWindow.y += extraPhase.y;
625     CGSize phase = CGSizeMake(fmodf(originInWindow.x, patternWidth), fmodf(originInWindow.y, patternHeight));
626
627     // Draw underline
628     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
629     [currentContext saveGraphicsState];
630     [spellingPatternColor set];
631     CGContextSetPatternPhase((CGContextRef)[currentContext graphicsPort], phase);
632     NSRectFillUsingOperation(NSMakeRect(point.x, point.y, width, patternHeight), NSCompositeSourceOver);
633     [currentContext restoreGraphicsState];
634 }
635
636 #undef patternWidth
637 #undef patternHeight
638
639 - (int)pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed includePartialGlyphs:(BOOL)includePartialGlyphs
640 {
641     if (style->smallCaps && !isSmallCapsRenderer) {
642         return [[self _smallCapsRenderer] pointToOffset:run style:style position:x reversed:reversed includePartialGlyphs:includePartialGlyphs];
643     }
644
645     if (shouldUseATSU(run))
646         return [self _ATSU_pointToOffset:run style:style position:x reversed:reversed includePartialGlyphs:includePartialGlyphs];
647     return [self _CG_pointToOffset:run style:style position:x reversed:reversed includePartialGlyphs:includePartialGlyphs];
648 }
649
650 @end
651
652
653 // ------------------- Private API -------------------
654
655
656 @implementation WebTextRenderer (WebInternal)
657
658 + (void)_setAlwaysUseATSU:(BOOL)f
659 {
660     alwaysUseATSU = f;
661 }
662
663 - (void)_setIsSmallCapsRenderer:(BOOL)flag
664 {
665     isSmallCapsRenderer = flag;
666 }
667
668 - (BOOL)_isSmallCapsRenderer
669 {
670     return isSmallCapsRenderer;
671 }
672
673 - (WebTextRenderer *)_smallCapsRenderer
674 {
675     if (!smallCapsRenderer) {
676         NS_DURING
677             smallCapsRenderer = [[WebTextRenderer alloc] initWithFont:font usingPrinterFont:usingPrinterFont];
678         NS_HANDLER
679             if (ASSERT_DISABLED) {
680                 NSLog(@"Uncaught exception - %@\n", localException);
681             } else {
682                 ASSERT_WITH_MESSAGE(0, "Uncaught exception - %@", localException);
683             } 
684         NS_ENDHANDLER
685
686         [smallCapsRenderer _setIsSmallCapsRenderer:YES];
687     }
688     return smallCapsRenderer;
689 }
690
691 - (NSFont *)_smallCapsFont
692 {
693     if (!smallCapsFont)
694         smallCapsFont = [[NSFontManager sharedFontManager] convertFont:font toSize:([font pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER)];
695     return smallCapsFont;
696 }
697
698 static inline BOOL fontContainsString(NSFont *font, NSString *string)
699 {
700     NSCharacterSet *set = [[font coveredCharacterSet] invertedSet];
701     return set && [string rangeOfCharacterFromSet:set].location == NSNotFound;
702 }
703
704 - (NSFont *)_substituteFontForString: (NSString *)string families: (NSString **)families
705 {
706     NSFont *substituteFont = nil;
707
708     // First search the CSS family fallback list.  Start at 1 (2nd font)
709     // because we've already failed on the first lookup.
710     NSString *family = nil;
711     int i = 1;
712     while (families && families[i] != 0) {
713         family = families[i++];
714         substituteFont = [[WebTextRendererFactory sharedFactory] cachedFontFromFamily: family traits:[[NSFontManager sharedFontManager] traitsOfFont:font] size:[font pointSize]];
715         if (substituteFont) {
716             if (fontContainsString(substituteFont, string))
717                 break;
718             substituteFont = nil; 
719         }
720     }
721     
722     // Now do string based lookup.
723     if (substituteFont == nil)
724         substituteFont = [NSFont findFontLike:font forString:string withRange:NSMakeRange (0,[string length]) inLanguage:[NSLanguage defaultLanguage]];
725
726     // Now do character based lookup.
727     if (substituteFont == nil && [string length] == 1)
728         substituteFont = [NSFont findFontLike:font forCharacter: [string characterAtIndex: 0] inLanguage:[NSLanguage defaultLanguage]];
729
730     // Get the screen or printer variation of the font.
731     substituteFont = usingPrinterFont ? [substituteFont printerFont] : [substituteFont screenFont];
732
733     if ([substituteFont isEqual: font])
734         substituteFont = nil;
735
736     return substituteFont;
737 }
738
739 - (NSFont *)_substituteFontForCharacters: (const unichar *)characters length: (int)numCharacters families: (NSString **)families
740 {
741     NSString *string = [[NSString alloc] initWithCharactersNoCopy:(unichar *)characters length: numCharacters freeWhenDone: NO];
742     NSFont *substituteFont = [self _substituteFontForString: string families: families];
743     [string release];
744     return substituteFont;
745 }
746
747 - (void)_convertCharacters: (const UniChar *)characters length: (unsigned)numCharacters toGlyphs: (ATSGlyphVector *)glyphs
748 {
749     OSStatus status = ATSUConvertCharToGlyphs(styleGroup, characters, 0, numCharacters, 0, glyphs);
750     if (status != noErr){
751         FATAL_ALWAYS ("unable to get glyphsfor %@ %f error = (%d)", self, [font displayName], [font pointSize], status);
752     }
753
754 #ifdef DEBUG_GLYPHS
755     int foundGlyphs = 0;
756     ATSLayoutRecord *glyphRecord;
757     for (i = 0; i < numCharacters; i++) {
758         glyphRecord = (ATSLayoutRecord *)glyphs->firstRecord;
759         for (i = 0; i < numCharacters; i++) {
760             if (glyphRecord->glyphID != 0)
761                 foundGlyphs++;
762             glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + glyphs->recordSize);
763         }
764     }
765     printf ("For %s found %d glyphs in range 0x%04x to 0x%04x\n", [[font displayName] cString], foundGlyphs, characters[0], characters[numCharacters-1]);
766 #endif
767 }
768
769 - (void)_convertUnicodeCharacters: (const UnicodeChar *)characters length: (unsigned)numCharacters toGlyphs: (ATSGlyphVector *)glyphs
770 {
771     UniChar localBuffer[LOCAL_BUFFER_SIZE];
772     UniChar *buffer = localBuffer;
773     unsigned i, bufPos = 0;
774     
775     if (numCharacters*2 > LOCAL_BUFFER_SIZE) {
776         buffer = (UniChar *)malloc(sizeof(UniChar) * numCharacters * 2);
777     }
778     
779     for (i = 0; i < numCharacters; i++) {
780         UnicodeChar c = characters[i];
781         UniChar h = HighSurrogatePair(c);
782         UniChar l = LowSurrogatePair(c);
783         buffer[bufPos++] = h;
784         buffer[bufPos++] = l;
785     }
786         
787     OSStatus status = ATSUConvertCharToGlyphs(styleGroup, buffer, 0, numCharacters*2, 0, glyphs);
788     if (status != noErr){
789         FATAL_ALWAYS ("unable to get glyphsfor %@ %f error = (%d)", self, [font displayName], [font pointSize], status);
790     }
791     
792     if (buffer != localBuffer) {
793         free(buffer);
794     }
795 }
796
797 // Nasty hack to determine if we should round or ceil space widths.
798 // If the font is monospace or fake monospace we ceil to ensure that 
799 // every character and the space are the same width.  Otherwise we round.
800 - (BOOL)_computeWidthForSpace
801 {
802     spaceGlyph = [self _extendCharacterToGlyphMapToInclude:SPACE];
803     if (spaceGlyph == 0) {
804         return NO;
805     }
806
807     float width = widthForGlyph(self, spaceGlyph, 0);
808     spaceWidth = width;
809
810     treatAsFixedPitch = [font isFixedPitch] || [font _isFakeFixedPitch];
811     adjustedSpaceWidth = treatAsFixedPitch ? CEIL_TO_INT(width) : ROUND_TO_INT(width);
812     
813     return YES;
814 }
815
816 - (BOOL)_setupFont
817 {
818     ATSUStyle fontStyle;
819     if (ATSUCreateStyle(&fontStyle) != noErr)
820         return NO;
821
822     if (!fillStyleWithAttributes(fontStyle, font)) {
823         ATSUDisposeStyle(fontStyle);
824         return NO;
825     }
826
827     if (ATSUGetStyleGroup(fontStyle, &styleGroup) != noErr) {
828         ATSUDisposeStyle(fontStyle);
829         return NO;
830     }
831     
832     ATSUDisposeStyle(fontStyle);
833
834     if (![self _computeWidthForSpace]) {
835         freeGlyphMap(characterToGlyphMap);
836         characterToGlyphMap = NULL;
837         ATSUDisposeStyleGroup(styleGroup);
838         styleGroup = NULL;
839         return NO;
840     }
841     
842     return YES;
843 }
844
845 static NSString *pathFromFont (NSFont *font)
846 {
847     UInt8 _filePathBuffer[PATH_MAX];
848     NSString *filePath = nil;
849     FSSpec oFile;
850     OSStatus status = ATSFontGetFileSpecification(
851             ATSFontRefFromNSFont(font),
852             &oFile);
853     if (status == noErr){
854         OSErr err;
855         FSRef fileRef;
856         err = FSpMakeFSRef(&oFile,&fileRef);
857         if (err == noErr){
858             status = FSRefMakePath(&fileRef,_filePathBuffer, PATH_MAX);
859             if (status == noErr){
860                 filePath = [NSString stringWithUTF8String:&_filePathBuffer[0]];
861             }
862         }
863     }
864     return filePath;
865 }
866
867 // Useful page for testing http://home.att.net/~jameskass
868 static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *advances, float x, float y, int numGlyphs)
869 {
870     CGContextRef cgContext;
871
872     if ([WebTextRenderer shouldBufferTextDrawing] && [[WebTextRendererFactory sharedFactory] coalesceTextDrawing]){
873         // Add buffered glyphs and advances
874         // FIXME:  If we ever use this again, need to add RTL.
875         WebGlyphBuffer *gBuffer = [[WebTextRendererFactory sharedFactory] glyphBufferForFont: font andColor: color];
876         [gBuffer addGlyphs: glyphs advances: advances count: numGlyphs at: x : y];
877     }
878     else {
879         NSGraphicsContext *gContext = [NSGraphicsContext currentContext];
880         cgContext = (CGContextRef)[gContext graphicsPort];
881         // Setup the color and font.
882         
883 #if BUILDING_ON_PANTHER        
884         if ([gContext isDrawingToScreen]){
885             NSFont *screenFont = [font screenFont];
886             if (screenFont != font){
887                 // We are getting this in too many places (3406411); use ERROR so it only prints on
888                 // debug versions for now. (We should debug this also, eventually).
889                 ERROR ("Attempting to set non-screen font (%@) when drawing to screen.  Using screen font anyway, may result in incorrect metrics.", [[[font fontDescriptor] fontAttributes] objectForKey: NSFontNameAttribute]);
890             }
891             [screenFont set];
892         }
893         else {
894             NSFont *printerFont = [font printerFont];
895             if (printerFont != font){
896                 NSLog (@"Attempting to set non-printer font (%@) when printing.  Using printer font anyway, may result in incorrect metrics.", [[[font fontDescriptor] fontAttributes] objectForKey: NSFontNameAttribute]);
897             }
898             [printerFont set];
899         }
900 #else
901         NSFont *drawFont;
902         
903         if ([gContext isDrawingToScreen]){
904             drawFont = [font screenFont];
905             if (drawFont != font){
906                 // We are getting this in too many places (3406411); use ERROR so it only prints on
907                 // debug versions for now. (We should debug this also, eventually).
908                 ERROR ("Attempting to set non-screen font (%@) when drawing to screen.  Using screen font anyway, may result in incorrect metrics.", [[[font fontDescriptor] fontAttributes] objectForKey: NSFontNameAttribute]);
909             }
910         }
911         else {
912             drawFont = [font printerFont];
913             if (drawFont != font){
914                 NSLog (@"Attempting to set non-printer font (%@) when printing.  Using printer font anyway, may result in incorrect metrics.", [[[font fontDescriptor] fontAttributes] objectForKey: NSFontNameAttribute]);
915             }
916         }
917         
918         CGContextSetFont (cgContext, [drawFont _backingCGSFont]);
919         
920         // Deal will flipping flippyness.
921         const float *matrix = [drawFont matrix];
922         float flip = [[NSView focusView] isFlipped] ? -1 : 1;
923         CGContextSetTextMatrix(cgContext, CGAffineTransformMake(matrix[0], matrix[1] * flip, matrix[2], matrix[3] * flip, matrix[4], matrix[5]));
924         CGContextSetFontRenderingMode (cgContext, _AppkitGetCGRenderingMode(drawFont));
925         CGContextSetFontSize(cgContext, 1.0);
926 #endif
927
928         [color set];
929
930         CGContextSetTextPosition (cgContext, x, y);
931         CGContextShowGlyphsWithAdvances (cgContext, glyphs, advances, numGlyphs);
932     }
933 }
934
935
936 - (void)_CG_drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry
937 {
938     if (run->length == 0)
939         return;
940
941     CharacterWidthIterator widthIterator;
942     WebCoreTextRun completeRun = *run;
943     completeRun.from = 0;
944     completeRun.to = run->length;
945     initializeCharacterWidthIterator(&widthIterator, self, &completeRun, style);
946     
947     float startPosition = 0;
948
949     // The starting point needs to be adjusted to account for the width of
950     // the glyphs at the start of the run.
951     while (widthIterator.currentCharacter < (unsigned)run->from) {
952         startPosition += widthForNextCharacter(&widthIterator, 0, 0);
953     }
954     float startX = startPosition + geometry->point.x;
955     
956     float backgroundWidth = 0.0;
957     while (widthIterator.currentCharacter < (unsigned)run->to) {
958         backgroundWidth += widthForNextCharacter(&widthIterator, 0, 0);
959     }
960
961     if (style->backgroundColor != nil){
962         // Calculate the width of the selection background by adding
963         // up the advances of all the glyphs in the selection.
964         
965         [style->backgroundColor set];
966
967         float yPos = geometry->useFontMetricsForSelectionYAndHeight ? geometry->point.y - [self ascent] - (lineGap/2) : geometry->selectionY;
968         float height = geometry->useFontMetricsForSelectionYAndHeight ? [self lineSpacing] : geometry->selectionHeight;
969         if (style->rtl){
970             float completeRunWidth = startPosition + backgroundWidth;
971             while (widthIterator.currentCharacter < run->length) {
972                 completeRunWidth += widthForNextCharacter(&widthIterator, 0, 0);
973             }
974
975             [NSBezierPath fillRect:NSMakeRect(geometry->point.x + completeRunWidth - startPosition - backgroundWidth, yPos, backgroundWidth, height)];
976         }
977         else {
978             [NSBezierPath fillRect:NSMakeRect(startX, yPos, backgroundWidth, height)];
979         }
980     }
981 }
982
983
984 - (void)_CG_drawRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry
985 {
986     float *widthBuffer, localWidthBuffer[LOCAL_BUFFER_SIZE];
987     CGGlyph *glyphBuffer, localGlyphBuffer[LOCAL_BUFFER_SIZE];
988     NSFont **fontBuffer, *localFontBuffer[LOCAL_BUFFER_SIZE];
989     CGSize *advances, localAdvanceBuffer[LOCAL_BUFFER_SIZE];
990     int numGlyphs = 0, i;
991     float startX;
992     unsigned length = run->length;
993     
994     if (run->length == 0)
995         return;
996
997     if (length*MAX_GLYPH_EXPANSION > LOCAL_BUFFER_SIZE) {
998         advances = (CGSize *)calloc(length*MAX_GLYPH_EXPANSION, sizeof(CGSize));
999         widthBuffer = (float *)calloc(length*MAX_GLYPH_EXPANSION, sizeof(float));
1000         glyphBuffer = (CGGlyph *)calloc(length*MAX_GLYPH_EXPANSION, sizeof(ATSGlyphRef));
1001         fontBuffer = (NSFont **)calloc(length*MAX_GLYPH_EXPANSION, sizeof(NSFont *));
1002     } else {
1003         advances = localAdvanceBuffer;
1004         widthBuffer = localWidthBuffer;
1005         glyphBuffer = localGlyphBuffer;
1006         fontBuffer = localFontBuffer;
1007     }
1008
1009     [self _floatWidthForRun:run
1010         style:style
1011         widths:widthBuffer 
1012         fonts:fontBuffer
1013         glyphs:glyphBuffer
1014         startPosition:&startX
1015         numGlyphs: &numGlyphs];
1016         
1017     // Eek.  We couldn't generate ANY glyphs for the run.
1018     if (numGlyphs <= 0)
1019         return;
1020         
1021     // Fill the advances array.
1022     for (i = 0; i < numGlyphs; i++){
1023         advances[i].width = widthBuffer[i];
1024         advances[i].height = 0;
1025     }
1026
1027     // Calculate the starting point of the glyphs to be displayed by adding
1028     // all the advances up to the first glyph.
1029     startX += geometry->point.x;
1030
1031     if (style->backgroundColor != nil)
1032         [self _CG_drawHighlightForRun:run style:style geometry:geometry];
1033     
1034     // Finally, draw the glyphs.
1035     int lastFrom = 0;
1036     int pos = 0;
1037
1038     // Swap the order of the glyphs if right-to-left.
1039     if (style->rtl && numGlyphs > 1){
1040         int i;
1041         int end = numGlyphs;
1042         CGGlyph gswap1, gswap2;
1043         CGSize aswap1, aswap2;
1044         NSFont *fswap1, *fswap2;
1045         
1046         for (i = pos, end = numGlyphs-1; i < (numGlyphs - pos)/2; i++){
1047             gswap1 = glyphBuffer[i];
1048             gswap2 = glyphBuffer[--end];
1049             glyphBuffer[i] = gswap2;
1050             glyphBuffer[end] = gswap1;
1051         }
1052         for (i = pos, end = numGlyphs - 1; i < (numGlyphs - pos)/2; i++){
1053             aswap1 = advances[i];
1054             aswap2 = advances[--end];
1055             advances[i] = aswap2;
1056             advances[end] = aswap1;
1057         }
1058         for (i = pos, end = numGlyphs - 1; i < (numGlyphs - pos)/2; i++){
1059             fswap1 = fontBuffer[i];
1060             fswap2 = fontBuffer[--end];
1061             fontBuffer[i] = fswap2;
1062             fontBuffer[end] = fswap1;
1063         }
1064     }
1065
1066     // Draw each contiguous run of glyphs that are included in the same font.
1067     NSFont *currentFont = fontBuffer[pos];
1068     float nextX = startX;
1069     int nextGlyph = pos;
1070
1071     while (nextGlyph < numGlyphs){
1072         if ((fontBuffer[nextGlyph] != 0 && fontBuffer[nextGlyph] != currentFont)){
1073             _drawGlyphs(currentFont, style->textColor, &glyphBuffer[lastFrom], &advances[lastFrom], startX, geometry->point.y, nextGlyph - lastFrom);
1074             lastFrom = nextGlyph;
1075             currentFont = fontBuffer[nextGlyph];
1076             startX = nextX;
1077         }
1078         nextX += advances[nextGlyph].width;
1079         nextGlyph++;
1080     }
1081     _drawGlyphs(currentFont, style->textColor, &glyphBuffer[lastFrom], &advances[lastFrom], startX, geometry->point.y, nextGlyph - lastFrom);
1082
1083     if (advances != localAdvanceBuffer) {
1084         free(advances);
1085         free(widthBuffer);
1086         free(glyphBuffer);
1087         free(fontBuffer);
1088     }
1089 }
1090
1091 #ifdef DEBUG_COMBINING
1092 static const char *directionNames[] = {
1093         "DirectionL",   // Left Letter 
1094         "DirectionR",   // Right Letter
1095         "DirectionEN",  // European Number
1096         "DirectionES",  // European Separator
1097         "DirectionET",  // European Terminator (post/prefix e.g. $ and %)
1098         "DirectionAN",  // Arabic Number
1099         "DirectionCS",  // Common Separator 
1100         "DirectionB",   // Paragraph Separator (aka as PS)
1101         "DirectionS",   // Segment Separator (TAB)
1102         "DirectionWS",  // White space
1103         "DirectionON",  // Other Neutral
1104
1105         // types for explicit controls
1106         "DirectionLRE", 
1107         "DirectionLRO", 
1108
1109         "DirectionAL",  // Arabic Letter (Right-to-left)
1110
1111         "DirectionRLE", 
1112         "DirectionRLO", 
1113         "DirectionPDF", 
1114
1115         "DirectionNSM",         // Non-spacing Mark
1116         "DirectionBN"   // Boundary neutral (type of RLE etc after explicit levels)
1117 };
1118
1119 static const char *joiningNames[] = {
1120         "JoiningOther",
1121         "JoiningDual",
1122         "JoiningRight",
1123         "JoiningCausing"
1124 };
1125 #endif
1126
1127 - (float)_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths:(float *)widthBuffer fonts:(NSFont **)fontBuffer glyphs:(CGGlyph *)glyphBuffer startPosition:(float *)startPosition numGlyphs:(int *)_numGlyphs
1128 {
1129     if (shouldUseATSU(run))
1130         return [self _ATSU_floatWidthForRun:run style:style];
1131     
1132     return [self _CG_floatWidthForRun:run style:style widths:widthBuffer fonts:fontBuffer glyphs:glyphBuffer startPosition:startPosition numGlyphs:_numGlyphs];
1133
1134 }
1135
1136 - (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
1137 {
1138     float _totalWidth = 0, _nextWidth;
1139     CharacterWidthIterator widthIterator;
1140     NSFont *fontUsed = 0;
1141     ATSGlyphRef glyphUsed;
1142     int numGlyphs = 0;
1143     
1144     initializeCharacterWidthIterator(&widthIterator, self, run, style);
1145     if (startPosition)
1146         *startPosition = widthIterator.widthToStart;
1147     while ((_nextWidth = widthForNextCharacter(&widthIterator, &glyphUsed, &fontUsed)) != INVALID_WIDTH){
1148         if (fontBuffer)
1149             fontBuffer[numGlyphs] = fontUsed;
1150         if (glyphBuffer)
1151             glyphBuffer[numGlyphs] = glyphUsed;
1152         if (widthBuffer)
1153             widthBuffer[numGlyphs] = _nextWidth;
1154         numGlyphs++;
1155         _totalWidth += _nextWidth;
1156     }
1157         
1158     if (_numGlyphs)
1159         *_numGlyphs = numGlyphs;
1160
1161     return _totalWidth;
1162 }
1163
1164 - (ATSGlyphRef)_extendUnicodeCharacterToGlyphMapToInclude:(UnicodeChar)c
1165 {
1166     UnicodeGlyphMap *map = (UnicodeGlyphMap *)calloc (1, sizeof(UnicodeGlyphMap));
1167     ATSLayoutRecord *glyphRecord;
1168     ATSGlyphVector glyphVector;
1169     UnicodeChar end, start;
1170     unsigned blockSize;
1171     ATSGlyphRef glyphID;
1172     
1173     if (unicodeCharacterToGlyphMap == 0)
1174         blockSize = INITIAL_BLOCK_SIZE;
1175     else
1176         blockSize = INCREMENTAL_BLOCK_SIZE;
1177     start = (c / blockSize) * blockSize;
1178     end = start + (blockSize - 1);
1179         
1180     LOG(FontCache, "%@ (0x%04x) adding glyphs for 0x%04x to 0x%04x", font, c, start, end);
1181
1182     map->startRange = start;
1183     map->endRange = end;
1184     
1185     unsigned i, count = end - start + 1;
1186     UnicodeChar buffer[INCREMENTAL_BLOCK_SIZE+2];
1187     
1188     for (i = 0; i < count; i++){
1189         buffer[i] = i+start;
1190     }
1191
1192     OSStatus status;
1193     status = ATSInitializeGlyphVector(count*2, 0, &glyphVector);
1194     if (status != noErr){
1195         // This should never happen, indicates a bad font!  If it does the
1196         // font substitution code will find an alternate font.
1197         free(map);
1198         return 0;
1199     }
1200     
1201     [self _convertUnicodeCharacters: &buffer[0] length: count toGlyphs: &glyphVector];
1202     unsigned numGlyphs = glyphVector.numGlyphs;
1203     if (numGlyphs != count){
1204         // This should never happen, indicates a bad font!  If it does the
1205         // font substitution code will find an alternate font.
1206         free(map);
1207         return 0;
1208     }
1209             
1210     map->glyphs = (GlyphEntry *)malloc (count * sizeof(GlyphEntry));
1211     glyphRecord = (ATSLayoutRecord *)glyphVector.firstRecord;
1212     for (i = 0; i < count; i++) {
1213         map->glyphs[i].glyph = glyphRecord->glyphID;
1214         map->glyphs[i].font = 0;
1215         glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + glyphVector.recordSize);
1216     }
1217     ATSClearGlyphVector(&glyphVector);
1218     
1219     if (unicodeCharacterToGlyphMap == 0)
1220         unicodeCharacterToGlyphMap = map;
1221     else {
1222         UnicodeGlyphMap *lastMap = unicodeCharacterToGlyphMap;
1223         while (lastMap->next != 0)
1224             lastMap = lastMap->next;
1225         lastMap->next = map;
1226     }
1227
1228     glyphID = map->glyphs[c - start].glyph;
1229     
1230     return glyphID;
1231 }
1232
1233 - (void)_updateGlyphEntryForCharacter:(UniChar)c glyphID:(ATSGlyphRef)glyphID font:(NSFont *)substituteFont
1234 {
1235     GlyphMap *lastMap = characterToGlyphMap;
1236     while (lastMap != 0){
1237         if (c >= lastMap->startRange && c <= lastMap->endRange){
1238             lastMap->glyphs[c - lastMap->startRange].glyph = glyphID;
1239             // This font will leak.  No problem though, it has to stick around
1240             // forever.  Max theoretical retain counts applied here will be
1241             // num_fonts_on_system * num_glyphs_in_font.
1242             lastMap->glyphs[c - lastMap->startRange].font = [substituteFont retain];
1243             break;
1244         }
1245         lastMap = lastMap->next;
1246     }
1247 }
1248
1249 - (ATSGlyphRef)_extendCharacterToGlyphMapToInclude:(UniChar) c
1250 {
1251     GlyphMap *map = (GlyphMap *)calloc (1, sizeof(GlyphMap));
1252     ATSLayoutRecord *glyphRecord;
1253     ATSGlyphVector glyphVector;
1254     UniChar end, start;
1255     unsigned blockSize;
1256     ATSGlyphRef glyphID;
1257     
1258     if (characterToGlyphMap == 0)
1259         blockSize = INITIAL_BLOCK_SIZE;
1260     else
1261         blockSize = INCREMENTAL_BLOCK_SIZE;
1262     start = (c / blockSize) * blockSize;
1263     end = start + (blockSize - 1);
1264         
1265     LOG(FontCache, "%@ (0x%04x) adding glyphs for 0x%04x to 0x%04x", font, c, start, end);
1266
1267     map->startRange = start;
1268     map->endRange = end;
1269     
1270     unsigned i, count = end - start + 1;
1271     short unsigned buffer[INCREMENTAL_BLOCK_SIZE+2];
1272     
1273     for (i = 0; i < count; i++) {
1274         buffer[i] = i+start;
1275     }
1276
1277     if (start == 0) {
1278         // Control characters must not render at all.
1279         for (i = 0; i < 0x20; ++i)
1280             buffer[i] = ZERO_WIDTH_SPACE;
1281         buffer[0x7F] = ZERO_WIDTH_SPACE;
1282
1283         // But both \n and nonbreaking space must render as a space.
1284         buffer['\n'] = ' ';
1285         buffer[NO_BREAK_SPACE] = ' ';
1286     }
1287
1288     OSStatus status = ATSInitializeGlyphVector(count, 0, &glyphVector);
1289     if (status != noErr) {
1290         // This should never happen, perhaps indicates a bad font!  If it does the
1291         // font substitution code will find an alternate font.
1292         free(map);
1293         return 0;
1294     }
1295
1296     [self _convertCharacters: &buffer[0] length: count toGlyphs: &glyphVector];
1297     unsigned numGlyphs = glyphVector.numGlyphs;
1298     if (numGlyphs != count){
1299         // This should never happen, perhaps indicates a bad font!  If it does the
1300         // font substitution code will find an alternate font.
1301         free(map);
1302         return 0;
1303     }
1304             
1305     map->glyphs = (GlyphEntry *)malloc (count * sizeof(GlyphEntry));
1306     glyphRecord = (ATSLayoutRecord *)glyphVector.firstRecord;
1307     for (i = 0; i < count; i++) {
1308         map->glyphs[i].glyph = glyphRecord->glyphID;
1309         map->glyphs[i].font = 0;
1310         glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + glyphVector.recordSize);
1311     }
1312     ATSClearGlyphVector(&glyphVector);
1313     
1314     if (characterToGlyphMap == 0)
1315         characterToGlyphMap = map;
1316     else {
1317         GlyphMap *lastMap = characterToGlyphMap;
1318         while (lastMap->next != 0)
1319             lastMap = lastMap->next;
1320         lastMap->next = map;
1321     }
1322
1323     glyphID = map->glyphs[c - start].glyph;
1324     
1325     // Special case for characters 007F-00A0.
1326     if (glyphID == 0 && c >= 0x7F && c <= 0xA0){
1327         glyphID = [font _defaultGlyphForChar: c];
1328         map->glyphs[c - start].glyph = glyphID;
1329         map->glyphs[c - start].font = 0;
1330     }
1331
1332     return glyphID;
1333 }
1334
1335
1336 - (WidthMap *)_extendGlyphToWidthMapToInclude:(ATSGlyphRef)glyphID font:(NSFont *)subFont
1337 {
1338     WidthMap *map = (WidthMap *)calloc (1, sizeof(WidthMap)), **rootMap;
1339     unsigned end;
1340     ATSGlyphRef start;
1341     unsigned blockSize;
1342     unsigned i, count;
1343     
1344     if (subFont && subFont != font)
1345         rootMap = &mapForSubstituteFont(self,subFont)->map;
1346     else
1347         rootMap = &glyphToWidthMap;
1348         
1349     if (*rootMap == 0){
1350         if ([(subFont ? subFont : font) numberOfGlyphs] < INITIAL_BLOCK_SIZE)
1351             blockSize = [font numberOfGlyphs];
1352          else
1353             blockSize = INITIAL_BLOCK_SIZE;
1354     }
1355     else
1356         blockSize = INCREMENTAL_BLOCK_SIZE;
1357     start = (glyphID / blockSize) * blockSize;
1358     end = ((unsigned)start) + blockSize; 
1359     if (end > 0xffff)
1360         end = 0xffff;
1361
1362     LOG(FontCache, "%@ (0x%04x) adding widths for range 0x%04x to 0x%04x", font, glyphID, start, end);
1363
1364     map->startRange = start;
1365     map->endRange = end;
1366     count = end - start + 1;
1367
1368     map->widths = (WidthEntry *)malloc (count * sizeof(WidthEntry));
1369
1370     for (i = 0; i < count; i++){
1371         map->widths[i].width = UNINITIALIZED_GLYPH_WIDTH;
1372     }
1373
1374     if (*rootMap == 0)
1375         *rootMap = map;
1376     else {
1377         WidthMap *lastMap = *rootMap;
1378         while (lastMap->next != 0)
1379             lastMap = lastMap->next;
1380         lastMap->next = map;
1381     }
1382
1383 #ifdef _TIMING
1384     LOG(FontCache, "%@ total time to advances lookup %f seconds", font, totalCGGetAdvancesTime);
1385 #endif
1386     return map;
1387 }
1388
1389
1390 - (void)_initializeATSUStyle
1391 {
1392     // The two NSFont calls in this method (pointSize and _atsFontID)
1393     // are both exception-safe.
1394
1395     if (!ATSUStyleInitialized){
1396         OSStatus status;
1397         
1398         status = ATSUCreateStyle(&_ATSUSstyle);
1399         if(status != noErr)
1400             FATAL_ALWAYS ("ATSUCreateStyle failed (%d)", status);
1401     
1402         ATSUFontID fontID = [font _atsFontID];
1403         if (fontID == 0){
1404             ATSUDisposeStyle(_ATSUSstyle);
1405             ERROR ("unable to get ATSUFontID for %@", font);
1406             return;
1407         }
1408         
1409         CGAffineTransform transform = CGAffineTransformMakeScale (1,-1);
1410         Fixed fontSize = FloatToFixed([font pointSize]);
1411         ATSUAttributeTag styleTags[] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag};
1412         ByteCount styleSizes[] = {  sizeof(Fixed), sizeof(ATSUFontID), sizeof(CGAffineTransform) };
1413         ATSUAttributeValuePtr styleValues[] = { &fontSize, &fontID, &transform  };
1414         status = ATSUSetAttributes (_ATSUSstyle, 3, styleTags, styleSizes, styleValues);
1415         if(status != noErr)
1416             FATAL_ALWAYS ("ATSUSetAttributes failed (%d)", status);
1417
1418         ATSUStyleInitialized = YES;
1419     }
1420 }
1421
1422 - (ATSUTextLayout)_createATSUTextLayoutForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style
1423 {
1424     // The only Cocoa calls here are to NSGraphicsContext and the self
1425     // call to _initializeATSUStyle, which are all exception-safe.
1426
1427     ATSUTextLayout layout;
1428     UniCharCount runLength;
1429     OSStatus status;
1430     
1431     [self _initializeATSUStyle];
1432     
1433     // FIXME: This is missing the following features that the CoreGraphics code path has:
1434     // - Both \n and nonbreaking space render as a space.
1435     // - All other control characters must not render at all (other code path uses zero-width spaces).
1436
1437     runLength = run->to - run->from;
1438     status = ATSUCreateTextLayoutWithTextPtr(
1439             run->characters,
1440             run->from,           // offset
1441             runLength,        // length
1442             run->length,         // total length
1443             1,              // styleRunCount
1444             &runLength,    // length of style run
1445             &_ATSUSstyle, 
1446             &layout);
1447     if(status != noErr)
1448         FATAL_ALWAYS ("ATSUCreateTextLayoutWithTextPtr failed(%d)", status);
1449
1450     CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
1451     ATSLineLayoutOptions lineLayoutOptions = (kATSLineFractDisable | kATSLineDisableAutoAdjustDisplayPos | kATSLineUseDeviceMetrics);
1452     Boolean rtl = style->rtl;
1453     ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineLayoutOptionsTag, kATSULineDirectionTag };
1454     ByteCount sizes[] = { sizeof(CGContextRef), sizeof(ATSLineLayoutOptions), sizeof(Boolean)  };
1455     ATSUAttributeValuePtr values[] = { &cgContext, &lineLayoutOptions, &rtl };
1456     
1457     status = ATSUSetLayoutControls(layout, 3, tags, sizes, values);
1458     if(status != noErr)
1459         FATAL_ALWAYS ("ATSUSetLayoutControls failed(%d)", status);
1460
1461     status = ATSUSetTransientFontMatching (layout, YES);
1462     if(status != noErr)
1463         FATAL_ALWAYS ("ATSUSetTransientFontMatching failed(%d)", status);
1464         
1465     return layout;
1466 }
1467
1468
1469 - (ATSTrapezoid)_trapezoidForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style atPoint:(NSPoint )p
1470 {
1471     // The only Cocoa call here is the self call to
1472     // _createATSUTextLayoutForRun:, which is exception-safe.
1473
1474     OSStatus status;
1475     
1476     if (run->to - run->from <= 0){
1477         ATSTrapezoid nilTrapezoid = { {0,0} , {0,0}, {0,0}, {0,0} };
1478         return nilTrapezoid;
1479     }
1480         
1481     ATSUTextLayout layout = [self _createATSUTextLayoutForRun:run style:style];
1482
1483     ATSTrapezoid firstGlyphBounds;
1484     ItemCount actualNumBounds;
1485     status = ATSUGetGlyphBounds (layout, FloatToFixed(p.x), FloatToFixed(p.y), run->from, run->to - run->from, kATSUseDeviceOrigins, 1, &firstGlyphBounds, &actualNumBounds);    
1486     if(status != noErr)
1487         FATAL_ALWAYS ("ATSUGetGlyphBounds() failed(%d)", status);
1488     
1489     if (actualNumBounds != 1)
1490         FATAL_ALWAYS ("unexpected result from ATSUGetGlyphBounds():  actualNumBounds(%d) != 1", actualNumBounds);
1491
1492     ATSUDisposeTextLayout (layout); // Ignore the error.  Nothing we can do anyway.
1493             
1494     return firstGlyphBounds;
1495 }
1496
1497
1498 - (float)_ATSU_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style
1499 {
1500     ATSTrapezoid oGlyphBounds;
1501     
1502     oGlyphBounds = [self _trapezoidForRun:run style:style atPoint:NSMakePoint (0,0)];
1503     
1504     float width = 
1505         MAX(FixedToFloat(oGlyphBounds.upperRight.x), FixedToFloat(oGlyphBounds.lowerRight.x)) - 
1506         MIN(FixedToFloat(oGlyphBounds.upperLeft.x), FixedToFloat(oGlyphBounds.lowerLeft.x));
1507     
1508     return width;
1509 }
1510
1511 // Be sure to free the run.characters allocated by this function.
1512 static WebCoreTextRun reverseCharactersInRun(const WebCoreTextRun *run)
1513 {
1514     WebCoreTextRun swappedRun;
1515     unsigned int i;
1516     
1517     UniChar *swappedCharacters = (UniChar *)malloc(sizeof(UniChar)*run->length);
1518     for (i = 0; i < run->length; i++) {
1519         swappedCharacters[i] = run->characters[run->length-i-1];
1520     }
1521     swappedRun.characters = swappedCharacters;
1522     swappedRun.from = run->length - (run->to == -1 ? (int)run->length : run->to);
1523     swappedRun.to = run->length - (run->from == -1 ? 0 : run->from);
1524     swappedRun.length = run->length;
1525
1526     return swappedRun;
1527 }
1528
1529 - (void)_ATSU_drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry
1530 {
1531     // The only Cocoa calls made here are to NSColor and NSBezierPath,
1532     // plus the self calls to _createATSUTextLayoutForRun: and
1533     // _trapezoidForRun:. These are all exception-safe.
1534
1535     ATSUTextLayout layout;
1536     int from, to;
1537     float selectedLeftX;
1538     const WebCoreTextRun *aRun = run;
1539     WebCoreTextRun swappedRun;
1540
1541     if (style->backgroundColor == nil)
1542         return;
1543     
1544     if (style->visuallyOrdered) {
1545         swappedRun = reverseCharactersInRun(run);
1546         aRun = &swappedRun;
1547     }
1548
1549     from = aRun->from;
1550     to = aRun->to;
1551     if (from == -1)
1552         from = 0;
1553     if (to == -1)
1554         to = run->length;
1555    
1556     int runLength = to - from;
1557     if (runLength <= 0){
1558         return;
1559     }
1560
1561     layout = [self _createATSUTextLayoutForRun:aRun style:style];
1562
1563     WebCoreTextRun leadingRun = *aRun;
1564     leadingRun.from = 0;
1565     leadingRun.to = run->from;
1566     
1567     // ATSU provides the bounds of the glyphs for the run with an origin of
1568     // (0,0), so we need to find the width of the glyphs immediately before
1569     // the actually selected glyphs.
1570     ATSTrapezoid leadingTrapezoid = [self _trapezoidForRun:&leadingRun style:style atPoint:geometry->point];
1571     ATSTrapezoid selectedTrapezoid = [self _trapezoidForRun:run style:style atPoint:geometry->point];
1572
1573     float backgroundWidth = 
1574             MAX(FixedToFloat(selectedTrapezoid.upperRight.x), FixedToFloat(selectedTrapezoid.lowerRight.x)) - 
1575             MIN(FixedToFloat(selectedTrapezoid.upperLeft.x), FixedToFloat(selectedTrapezoid.lowerLeft.x));
1576
1577     if (run->from == 0)
1578         selectedLeftX = geometry->point.x;
1579     else
1580         selectedLeftX = MIN(FixedToFloat(leadingTrapezoid.upperRight.x), FixedToFloat(leadingTrapezoid.lowerRight.x));
1581     
1582     [style->backgroundColor set];
1583
1584     float yPos = geometry->useFontMetricsForSelectionYAndHeight ? geometry->point.y - [self ascent] : geometry->selectionY;
1585     float height = geometry->useFontMetricsForSelectionYAndHeight ? [self lineSpacing] : geometry->selectionHeight;
1586     if (style->rtl || style->visuallyOrdered){
1587         WebCoreTextRun completeRun = *aRun;
1588         completeRun.from = 0;
1589         completeRun.to = aRun->length;
1590         float completeRunWidth = [self floatWidthForRun:&completeRun style:style widths:0];
1591         [NSBezierPath fillRect:NSMakeRect(geometry->point.x + completeRunWidth - (selectedLeftX-geometry->point.x) - backgroundWidth, yPos, backgroundWidth, height)];
1592     }
1593     else {
1594         [NSBezierPath fillRect:NSMakeRect(selectedLeftX, yPos, backgroundWidth, height)];
1595     }
1596
1597     ATSUDisposeTextLayout (layout); // Ignore the error.  Nothing we can do anyway.
1598
1599     if (style->visuallyOrdered)
1600         free ((void *)swappedRun.characters);
1601 }
1602
1603
1604 - (void)_ATSU_drawRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry
1605 {
1606     // The only Cocoa calls made here are to NSColor, plus the self
1607     // calls to _createATSUTextLayoutForRun: and
1608     // _ATSU_drawHighlightForRun:. These are all exception-safe.
1609
1610     ATSUTextLayout layout;
1611     OSStatus status;
1612     int from, to;
1613     const WebCoreTextRun *aRun = run;
1614     WebCoreTextRun swappedRun;
1615     
1616     if (style->visuallyOrdered) {
1617         swappedRun = reverseCharactersInRun(run);
1618         aRun = &swappedRun;
1619     }
1620
1621     from = aRun->from;
1622     to = aRun->to;
1623     if (from == -1)
1624         from = 0;
1625     if (to == -1)
1626         to = run->length;
1627
1628     int runLength = to - from;
1629     if (runLength <= 0)
1630         return;
1631
1632     layout = [self _createATSUTextLayoutForRun:aRun style:style];
1633
1634     if (style->backgroundColor != nil)
1635         [self _ATSU_drawHighlightForRun:run style:style geometry:geometry];
1636
1637     [style->textColor set];
1638
1639     status = ATSUDrawText(layout, 
1640             aRun->from,
1641             runLength,
1642             FloatToFixed(geometry->point.x),   // these values are
1643             FloatToFixed(geometry->point.y));  // also of type Fixed
1644     if (status != noErr){
1645         // Nothing to do but report the error (dev build only).
1646         ERROR ("ATSUDrawText() failed(%d)", status);
1647     }
1648
1649     ATSUDisposeTextLayout (layout); // Ignore the error.  Nothing we can do anyway.
1650     
1651     if (style->visuallyOrdered)
1652         free ((void *)swappedRun.characters);
1653 }
1654
1655 - (int)_ATSU_pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed includePartialGlyphs:(BOOL)includePartialGlyphs
1656 {
1657     // The only Cocoa calls made here is to the self call to
1658     // _createATSUTextLayoutForRun:. This is exception-safe.
1659
1660     unsigned offset = 0;
1661     ATSUTextLayout layout;
1662     UniCharArrayOffset primaryOffset = 0;
1663     UniCharArrayOffset secondaryOffset = 0;
1664     OSStatus status;
1665     Boolean isLeading;
1666     const WebCoreTextRun *aRun = run;
1667     WebCoreTextRun swappedRun;
1668     
1669     // Reverse the visually ordered characters.  ATSU will re-reverse.  Ick!
1670     if (style->visuallyOrdered) {
1671         swappedRun = reverseCharactersInRun(run);
1672         aRun = &swappedRun;
1673     }
1674
1675     layout = [self _createATSUTextLayoutForRun:aRun style:style];
1676
1677     primaryOffset = aRun->from;
1678     
1679     // FIXME: No idea how to avoid including partial glyphs.   Not even sure if that's the behavior
1680     // this yields now.
1681     status = ATSUPositionToOffset(layout, FloatToFixed(x), FloatToFixed(-1), &primaryOffset, &isLeading, &secondaryOffset);
1682     if (status == noErr){
1683         offset = (unsigned)primaryOffset;
1684     }
1685     else {
1686         // Failed to find offset!  Return 0 offset.
1687     }
1688        
1689     if (style->visuallyOrdered) {
1690         free ((void *)swappedRun.characters);
1691     }
1692
1693     return offset - aRun->from;
1694 }
1695
1696 - (int)_CG_pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed includePartialGlyphs:(BOOL)includePartialGlyphs
1697 {
1698     float delta = (float)x;
1699     float width;
1700     unsigned offset = run->from;
1701     CharacterWidthIterator widthIterator;
1702     
1703     initializeCharacterWidthIterator(&widthIterator, self, run, style);
1704
1705     if (reversed) {
1706         width = [self floatWidthForRun:run style:style widths:nil];
1707         delta -= width;
1708         while (offset < run->length) {
1709             float w = widthForNextCharacter(&widthIterator, 0, 0);
1710             if (w == INVALID_WIDTH) {
1711                 // Something very bad happened, like we only have half of a surrogate pair.
1712                 break;
1713             }
1714             else {
1715                 if (w) {
1716                     if (includePartialGlyphs)
1717                        w -= w/2;
1718                     delta += w;
1719                     if(delta >= 0)
1720                         break;
1721                     if (includePartialGlyphs)
1722                         delta += w;
1723                 }
1724                 offset = widthIterator.currentCharacter;
1725             }
1726         }
1727     } else {
1728         while (offset < run->length) {
1729             float w = widthForNextCharacter(&widthIterator, 0, 0);
1730             if (w == INVALID_WIDTH) {
1731                 // Something very bad happened, like we only have half of a surrogate pair.
1732                 break;
1733             }
1734             else {
1735                 if (w) {
1736                     if (includePartialGlyphs)
1737                         w -= w/2;
1738                     delta -= w;
1739                     if(delta <= 0) 
1740                         break;
1741                     if (includePartialGlyphs)
1742                         delta -= w;
1743                 }
1744                 offset = widthIterator.currentCharacter;
1745             }
1746         }
1747     }
1748     
1749     return offset - run->from;
1750 }
1751
1752 @end
1753
1754 // ------------------- Private functions -------------------
1755
1756 static void freeWidthMap(WidthMap *map)
1757 {
1758     while (map) {
1759         WidthMap *next = map->next;
1760         free(map->widths);
1761         free(map);
1762         map = next;
1763     }
1764 }
1765
1766
1767 static void freeGlyphMap(GlyphMap *map)
1768 {
1769     while (map) {
1770         GlyphMap *next = map->next;
1771         free(map->glyphs);
1772         free(map);
1773         map = next;
1774     }
1775 }
1776
1777
1778 static void freeUnicodeGlyphMap(UnicodeGlyphMap *map)
1779 {
1780     while (map) {
1781         UnicodeGlyphMap *next = map->next;
1782         free(map->glyphs);
1783         free(map);
1784         map = next;
1785     }
1786 }
1787
1788
1789 static inline ATSGlyphRef glyphForCharacter (GlyphMap *map, UniChar c, NSFont **font)
1790 {
1791     if (map == 0)
1792         return nonGlyphID;
1793         
1794     while (map) {
1795         if (c >= map->startRange && c <= map->endRange){
1796             *font = map->glyphs[c-map->startRange].font;
1797             return map->glyphs[c-map->startRange].glyph;
1798         }
1799         map = map->next;
1800     }
1801     return nonGlyphID;
1802 }
1803  
1804  
1805 static inline ATSGlyphRef glyphForUnicodeCharacter (UnicodeGlyphMap *map, UnicodeChar c, NSFont **font)
1806 {
1807     if (map == 0)
1808         return nonGlyphID;
1809         
1810     while (map) {
1811         if (c >= map->startRange && c <= map->endRange){
1812             *font = map->glyphs[c-map->startRange].font;
1813             return map->glyphs[c-map->startRange].glyph;
1814         }
1815         map = map->next;
1816     }
1817     return nonGlyphID;
1818 }
1819  
1820
1821 #ifdef _TIMING        
1822 static double totalCGGetAdvancesTime = 0;
1823 #endif
1824
1825 static inline SubstituteFontWidthMap *mapForSubstituteFont(WebTextRenderer *renderer, NSFont *font)
1826 {
1827     int i;
1828     
1829     for (i = 0; i < renderer->numSubstituteFontWidthMaps; i++){
1830         if (font == renderer->substituteFontWidthMaps[i].font)
1831             return &renderer->substituteFontWidthMaps[i];
1832     }
1833     
1834     if (renderer->numSubstituteFontWidthMaps == renderer->maxSubstituteFontWidthMaps){
1835         renderer->maxSubstituteFontWidthMaps = renderer->maxSubstituteFontWidthMaps * 2;
1836         renderer->substituteFontWidthMaps = realloc (renderer->substituteFontWidthMaps, renderer->maxSubstituteFontWidthMaps * sizeof(SubstituteFontWidthMap));
1837         for (i = renderer->numSubstituteFontWidthMaps; i < renderer->maxSubstituteFontWidthMaps; i++){
1838             renderer->substituteFontWidthMaps[i].font = 0;
1839             renderer->substituteFontWidthMaps[i].map = 0;
1840         }
1841     }
1842     
1843     renderer->substituteFontWidthMaps[renderer->numSubstituteFontWidthMaps].font = font;
1844     return &renderer->substituteFontWidthMaps[renderer->numSubstituteFontWidthMaps++];
1845 }
1846
1847 static void initializeCharacterWidthIterator (CharacterWidthIterator *iterator, WebTextRenderer *renderer, const WebCoreTextRun *run , const WebCoreTextStyle *style) 
1848 {
1849     iterator->renderer = renderer;
1850     iterator->run = run;
1851     iterator->style = style;
1852     iterator->currentCharacter = run->from;
1853     iterator->runWidthSoFar = 0;
1854
1855     // If the padding is non-zero, count the number of spaces in the run
1856     // and divide that by the padding for per space addition.
1857     iterator->padding = style->padding;
1858     if (iterator->padding > 0){
1859         uint numSpaces = 0;
1860         int from = run->from;
1861         int len = run->to - from;
1862         int k;
1863         for (k = from; k < from + len; k++) {
1864             if (isSpace(run->characters[k])) {
1865                 numSpaces++;
1866             }
1867         }
1868         iterator->padPerSpace = CEIL_TO_INT ((((float)style->padding) / ((float)numSpaces)));
1869     }
1870     else {
1871         iterator->padPerSpace = 0;
1872     }
1873     
1874     // Calculate width up to starting position of the run.  This is
1875     // necessary to ensure that our rounding hacks are always consistently
1876     // applied.
1877     if (run->from != 0){
1878         WebCoreTextRun startPositionRun = *run;
1879         startPositionRun.from = 0;
1880         startPositionRun.to = run->from;
1881         CharacterWidthIterator startPositionIterator;
1882         initializeCharacterWidthIterator (&startPositionIterator, renderer, &startPositionRun, style);
1883         
1884         while (startPositionIterator.currentCharacter < (unsigned)startPositionRun.to){
1885             widthForNextCharacter(&startPositionIterator, 0, 0);
1886         }
1887         iterator->widthToStart = startPositionIterator.runWidthSoFar;
1888     }
1889     else
1890         iterator->widthToStart = 0;
1891 }
1892
1893 static inline float ceilCurrentWidth (CharacterWidthIterator *iterator)
1894 {
1895     float delta = CEIL_TO_INT(iterator->widthToStart + iterator->runWidthSoFar) - (iterator->widthToStart + iterator->runWidthSoFar);
1896     iterator->runWidthSoFar += delta;
1897     return delta;
1898 }
1899
1900 // Return INVALID_WIDTH if an error is encountered or we're at the end of the range in the run.
1901 static float widthForNextCharacter(CharacterWidthIterator *iterator, ATSGlyphRef *glyphUsed, NSFont **fontUsed)
1902 {
1903     WebTextRenderer *renderer = iterator->renderer;
1904     const WebCoreTextRun *run = iterator->run;
1905     unsigned currentCharacter = iterator->currentCharacter;
1906
1907     NSFont *_fontUsed = nil;
1908     ATSGlyphRef _glyphUsed;
1909
1910     if (!fontUsed)
1911         fontUsed = &_fontUsed;
1912     if (!glyphUsed)
1913         glyphUsed = &_glyphUsed;
1914         
1915     if (currentCharacter >= (unsigned)run->to)
1916         // Error! Offset specified beyond end of run.
1917         return INVALID_WIDTH;
1918
1919     const UniChar *cp = &run->characters[currentCharacter];
1920     UnicodeChar c = *cp;
1921
1922     if (IsLowSurrogatePair(c))
1923         return INVALID_WIDTH;
1924
1925     // Do we have a surrogate pair?  If so, determine the full Unicode (32 bit)
1926     // code point before glyph lookup.
1927     unsigned clusterLength = 1;
1928     if (IsHighSurrogatePair(c)) {
1929         // Make sure we have another character and it's a low surrogate.
1930         UniChar low;
1931         if (currentCharacter + 1 >= run->length || !IsLowSurrogatePair((low = cp[1]))) {
1932             // Error!  The second component of the surrogate pair is missing.
1933             return INVALID_WIDTH;
1934         }
1935
1936         c = UnicodeValueForSurrogatePair(c, low);
1937         clusterLength = 2;
1938     }
1939
1940     // If small-caps convert lowercase to upper.
1941     BOOL useSmallCapsFont = NO;
1942     if (renderer->isSmallCapsRenderer) {
1943         if (!u_isUUppercase(c)) {
1944             // Only use small cap font if the the uppercase version of the character
1945             // is different than the lowercase.
1946             UnicodeChar newC = u_toupper(c);
1947             if (newC != c) {
1948                 useSmallCapsFont = YES;
1949                 c = newC;
1950             }
1951         }
1952     }
1953
1954     if (c <= 0xFFFF) {
1955         *glyphUsed = glyphForCharacter(renderer->characterToGlyphMap, c, fontUsed);
1956         if (*glyphUsed == nonGlyphID) {
1957             *glyphUsed = [renderer _extendCharacterToGlyphMapToInclude:c];
1958         }
1959     } else {
1960         *glyphUsed = glyphForUnicodeCharacter(renderer->unicodeCharacterToGlyphMap, c, fontUsed);
1961         if (*glyphUsed == nonGlyphID) {
1962             *glyphUsed = [renderer _extendUnicodeCharacterToGlyphMapToInclude:c];
1963         }
1964     }
1965
1966     // Check to see if we're rendering in 'small-caps' mode.
1967     // ASSUMPTION:  We assume the same font in a smaller size has
1968     // the same glyphs as the large font.
1969     if (useSmallCapsFont) {
1970         if (*fontUsed == nil)
1971             *fontUsed = [renderer _smallCapsFont];
1972         else {
1973             // Potential for optimization.  This path should only be taken if we're
1974             // using a cached substituted font.
1975             *fontUsed = [[NSFontManager sharedFontManager] convertFont:*fontUsed toSize:[*fontUsed pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER];
1976         }
1977     }
1978
1979     // Now that we have glyph and font, get its width.
1980     WebGlyphWidth width = widthForGlyph(renderer, *glyphUsed, *fontUsed);
1981
1982     // We special case spaces in two ways when applying word rounding.
1983     // First, we round spaces to an adjusted width in all fonts.
1984     // Second, in fixed-pitch fonts we ensure that all characters that
1985     // match the width of the space character have the same width as the space character.
1986     if ((renderer->treatAsFixedPitch ? width == renderer->spaceWidth : *glyphUsed == renderer->spaceGlyph) && iterator->style->applyWordRounding)
1987         width = renderer->adjustedSpaceWidth;
1988
1989     // Try to find a substitute font if this font didn't have a glyph for a character in the
1990     // string.  If one isn't found we end up drawing and measuring the 0 glyph, usually a box.
1991     if (*glyphUsed == 0 && iterator->style->attemptFontSubstitution) {
1992         UniChar characterArray[2];
1993         unsigned characterArrayLength;
1994         
1995         if (c <= 0xFFFF) {
1996             characterArray[0] = c;
1997             characterArrayLength = 1;
1998         } else {
1999             characterArray[0] = HighSurrogatePair(c);
2000             characterArray[1] = LowSurrogatePair(c);
2001             characterArrayLength = 2;
2002         }
2003         
2004         NSFont *substituteFont = [renderer _substituteFontForCharacters:characterArray length:characterArrayLength
2005             families:iterator->style->families];
2006         if (substituteFont) {
2007             int cNumGlyphs = 0;
2008             ATSGlyphRef localGlyphBuffer[MAX_GLYPH_EXPANSION];
2009             
2010             WebCoreTextRun clusterRun;
2011             WebCoreInitializeTextRun(&clusterRun, characterArray, characterArrayLength, 0, characterArrayLength);
2012             WebCoreTextStyle clusterStyle = *iterator->style;
2013             clusterStyle.padding = 0;
2014             clusterStyle.applyRunRounding = false;
2015             clusterStyle.attemptFontSubstitution = false;
2016             
2017             WebTextRenderer *substituteRenderer;
2018             substituteRenderer = [[WebTextRendererFactory sharedFactory] rendererWithFont:substituteFont usingPrinterFont:renderer->usingPrinterFont];
2019             width = [substituteRenderer
2020                             _floatWidthForRun:&clusterRun
2021                             style:&clusterStyle 
2022                             widths: nil
2023                             fonts: nil
2024                             glyphs: &localGlyphBuffer[0]
2025                             startPosition:nil
2026                             numGlyphs:&cNumGlyphs];
2027             
2028             *fontUsed = substituteFont;
2029             *glyphUsed = localGlyphBuffer[0];
2030             
2031             if (c <= 0xFFFF && cNumGlyphs == 1 && localGlyphBuffer[0] != 0){
2032                 [renderer _updateGlyphEntryForCharacter:c glyphID:localGlyphBuffer[0] font:substituteFont];
2033             }
2034         }
2035     }
2036
2037     if (!*fontUsed)
2038         *fontUsed = renderer->font;
2039
2040     // Force characters that are used to determine word boundaries for the rounding hack
2041     // to be integer width, so following words will start on an integer boundary.
2042     if (isRoundingHackCharacter(c) && iterator->style->applyWordRounding) {
2043         width = CEIL_TO_INT(width);
2044     }
2045     
2046     // Account for letter-spacing
2047     if (iterator->style->letterSpacing && width > 0)
2048         width += iterator->style->letterSpacing;
2049
2050     // Account for padding.  khtml uses space padding to justify text.  We
2051     // distribute the specified padding over the available spaces in the run.
2052     if (isSpace(c)) {
2053         if (iterator->padding > 0) {
2054             // Only use left over padding if note evenly divisible by 
2055             // number of spaces.
2056             if (iterator->padding < iterator->padPerSpace){
2057                 width += iterator->padding;
2058                 iterator->padding = 0;
2059             }
2060             else {
2061                 width += iterator->padPerSpace;
2062                 iterator->padding -= iterator->padPerSpace;
2063             }
2064         }
2065         
2066         // Account for word-spacing.  We apply additional space between "words" by
2067         // adding width to the space character.
2068         if (currentCharacter > 0 && !isSpace(cp[-1]))
2069             width += iterator->style->wordSpacing;
2070     }
2071
2072     iterator->runWidthSoFar += width;
2073
2074     // Advance past the character we just dealt with.
2075     currentCharacter += clusterLength;
2076     iterator->currentCharacter = currentCharacter;
2077
2078     int len = run->to - run->from;
2079
2080     // Account for float/integer impedance mismatch between CG and khtml.  "Words" (characters 
2081     // followed by a character defined by isSpace()) are always an integer width.  We adjust the 
2082     // width of the last character of a "word" to ensure an integer width.  When we move khtml to
2083     // floats we can remove this (and related) hacks.
2084     //
2085     // Check to see if the next character is a "RoundingHackCharacter", if so, adjust.
2086     if (currentCharacter < run->length && isRoundingHackCharacter(cp[clusterLength]) && iterator->style->applyWordRounding) {
2087         width += ceilCurrentWidth(iterator);
2088     }
2089     else if (currentCharacter >= (unsigned)run->to && (len > 1 || run->length == 1) && iterator->style->applyRunRounding) {
2090         width += ceilCurrentWidth(iterator);
2091     }
2092     
2093     return width;
2094 }
2095
2096
2097 static BOOL fillStyleWithAttributes(ATSUStyle style, NSFont *theFont)
2098 {
2099     if (theFont) {
2100         ATSUFontID fontId = [theFont _atsFontID];
2101         LOG (FontCache, "fillStyleWithAttributes:  font = %p,%@, _atsFontID = %x\n", theFont, theFont, (unsigned)fontId);
2102         ATSUAttributeTag tag = kATSUFontTag;
2103         ByteCount size = sizeof(ATSUFontID);
2104         ATSUFontID *valueArray[1] = {&fontId};
2105         OSStatus status;
2106
2107         if (fontId) {
2108             status = ATSUSetAttributes(style, 1, &tag, &size, (void *)valueArray);
2109             if (status != noErr){
2110                 LOG (FontCache, "fillStyleWithAttributes failed(%d):  font = %p,%@, _atsFontID = %x\n", (int)status, theFont, theFont, (unsigned)fontId);
2111                 return NO;
2112             }
2113         }
2114         else {
2115             return NO;
2116         }
2117         return YES;
2118     }
2119     return NO;
2120 }
2121
2122 static BOOL shouldUseATSU(const WebCoreTextRun *run)
2123 {
2124     UniChar c;
2125     const UniChar *characters = run->characters;
2126     int i, from = run->from, to = run->to;
2127     
2128     if (alwaysUseATSU)
2129         return YES;
2130         
2131     for (i = from; i < to; i++){
2132         c = characters[i];
2133         if (c < 0x300)                      // Early continue to avoid other checks for the common case.
2134             continue;
2135             
2136         if (c >= 0x300 && c <= 0x36F)       // U+0300 through U+036F Combining diacritical marks
2137             return YES;
2138         if (c >= 0x20D0 && c <= 0x20FF)     // U+20D0 through U+20FF Combining marks for symbols
2139             return YES;
2140         if (c >= 0xFE20 && c <= 0xFE2f)     // U+FE20 through U+FE2F Combining half marks
2141             return YES;
2142         if (c >= 0x591 && c <= 0x1059)      // U+0591 through U+1059 Arabic, Hebrew, Syriac, Thaana, Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar
2143             return YES;
2144         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)
2145             return YES;
2146         if (c >= 0x1780 && c <= 0x18AF)     // U+1780 through U+18AF Khmer, Mongolian
2147             return YES;
2148         if (c >= 0x1900 && c <= 0x194F)     // U+1900 through U+194F Limbu (Unicode 4.0)
2149             return YES;
2150     }
2151     
2152     return NO;
2153 }