WebCore:
[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 width: (int)width color:(NSColor *)color thickness:(float)thickness
540 {
541     NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
542     CGContextRef cgContext;
543
544     // This will draw the text from the top of the bounding box down.
545     // Qt expects to draw from the baseline.
546     // Remember that descender is negative.
547     point.y -= [self lineSpacing] - [self descent];
548     
549     BOOL flag = [graphicsContext shouldAntialias];
550
551     [graphicsContext setShouldAntialias: NO];
552
553     // We don't want antialiased lines on screen, but we do when printing (else they are too thick)
554     if ([graphicsContext isDrawingToScreen]) {
555         [graphicsContext setShouldAntialias:NO];
556     }
557     
558     [color set];
559
560     cgContext = (CGContextRef)[graphicsContext graphicsPort];
561
562     // hack to make thickness 2 underlines for internation text input look right
563     if (thickness > 1.5 && thickness < 2.5) {
564         yOffset += .5;
565     }
566
567     if (thickness == 0.0) {
568         CGSize size = CGSizeApplyAffineTransform(CGSizeMake(1.0, 1.0), CGAffineTransformInvert(CGContextGetCTM(cgContext)));
569         CGContextSetLineWidth(cgContext, size.width);
570     } else {
571         CGContextSetLineWidth(cgContext, thickness);
572     }
573
574
575 #if BUILDING_ON_PANTHER            
576     CGContextMoveToPoint(cgContext, point.x, point.y + [self lineSpacing] + 1.5 - [self descent] + yOffset);
577     // Subtract 1 to ensure that the line is always within bounds of element.
578     CGContextAddLineToPoint(cgContext, point.x + width - 1.0, point.y + [self lineSpacing] + 1.5 - [self descent] + yOffset);
579     CGContextStrokePath(cgContext);
580 #else
581     // Use CGContextStrokeLineSegments on Tiger.  J. Burkey says this will be a big performance win.
582
583     CGPoint linePoints[2];
584     linePoints[0].x = point.x;
585     linePoints[0].y = point.y + [self lineSpacing] + 1.5 - [self descent] + yOffset;
586     linePoints[1].x = point.x + width - 1.0;
587     linePoints[1].y = linePoints[0].y;
588     CGContextStrokeLineSegments (cgContext, linePoints, 2);
589 #endif
590
591     [graphicsContext setShouldAntialias: flag];
592 }
593
594
595 - (void)drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry
596 {
597     if (style->smallCaps && !isSmallCapsRenderer) {
598         [[self _smallCapsRenderer] drawHighlightForRun:run style:style geometry:geometry];
599     }
600     else {
601         if (shouldUseATSU(run))
602             [self _ATSU_drawHighlightForRun:run style:style geometry:geometry];
603         else
604             [self _CG_drawHighlightForRun:run style:style geometry:geometry];
605     }
606 }
607
608 // Constants for pattern underline
609 #define patternWidth 4
610 #define patternHeight 3
611
612 - (void)drawLineForMisspelling:(NSPoint)point withWidth:(int)width
613 {
614     // Constants for pattern color
615     static NSColor *spellingPatternColor = nil;
616     static bool usingDot = false;
617  
618     // Initialize pattern color if needed
619     if (!spellingPatternColor) {
620         NSImage *image = [NSImage imageNamed:@"SpellingDot"];
621         ASSERT(image); // if image is not available, we want to know
622         NSColor *color = (image ? [NSColor colorWithPatternImage:image] : nil);
623         if (color)
624             usingDot = true;
625         else
626             color = [NSColor redColor];
627         spellingPatternColor = [color retain];
628     }
629
630     // Width must be divisible by 4 to make sure we always draw full misspelling dots under words.
631     // Do a small adjustment to shift the underline back to the left if the pattern was
632     // expanded to the right "too much" to accomodate the drawing of a full dot.
633     if (usingDot) {
634         int w = (width + patternWidth) - (width % patternWidth);
635         if (w - width > 2) 
636             point.x -= 1;
637         width = w;
638     }
639
640     // Compute the appropriate phase relative to the top level view in the window.
641     NSPoint originInWindow = [[NSView focusView] convertPoint:point toView:nil];
642     // WebCore may translate the focus, and thus need an extra phase correction
643     NSPoint extraPhase = [[WebGraphicsBridge sharedBridge] additionalPatternPhase];
644     originInWindow.x += extraPhase.x;
645     originInWindow.y += extraPhase.y;
646     CGSize phase = CGSizeMake(fmodf(originInWindow.x, patternWidth), fmodf(originInWindow.y, patternHeight));
647
648     // Draw underline
649     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
650     [currentContext saveGraphicsState];
651     [spellingPatternColor set];
652     CGContextSetPatternPhase((CGContextRef)[currentContext graphicsPort], phase);
653     NSRectFillUsingOperation(NSMakeRect(point.x, point.y, width, patternHeight), NSCompositeSourceOver);
654     [currentContext restoreGraphicsState];
655 }
656
657 #undef patternWidth
658 #undef patternHeight
659
660 - (int)pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed includePartialGlyphs:(BOOL)includePartialGlyphs
661 {
662     if (style->smallCaps && !isSmallCapsRenderer) {
663         return [[self _smallCapsRenderer] pointToOffset:run style:style position:x reversed:reversed includePartialGlyphs:includePartialGlyphs];
664     }
665
666     if (shouldUseATSU(run))
667         return [self _ATSU_pointToOffset:run style:style position:x reversed:reversed includePartialGlyphs:includePartialGlyphs];
668     return [self _CG_pointToOffset:run style:style position:x reversed:reversed includePartialGlyphs:includePartialGlyphs];
669 }
670
671 @end
672
673
674 // ------------------- Private API -------------------
675
676
677 @implementation WebTextRenderer (WebInternal)
678
679 + (void)_setAlwaysUseATSU:(BOOL)f
680 {
681     alwaysUseATSU = f;
682 }
683
684 - (void)_setIsSmallCapsRenderer:(BOOL)flag
685 {
686     isSmallCapsRenderer = flag;
687 }
688
689 - (BOOL)_isSmallCapsRenderer
690 {
691     return isSmallCapsRenderer;
692 }
693
694 - (WebTextRenderer *)_smallCapsRenderer
695 {
696     if (!smallCapsRenderer) {
697         NS_DURING
698             smallCapsRenderer = [[WebTextRenderer alloc] initWithFont:font usingPrinterFont:usingPrinterFont];
699         NS_HANDLER
700             if (ASSERT_DISABLED) {
701                 NSLog(@"Uncaught exception - %@\n", localException);
702             } else {
703                 ASSERT_WITH_MESSAGE(0, "Uncaught exception - %@", localException);
704             } 
705         NS_ENDHANDLER
706
707         [smallCapsRenderer _setIsSmallCapsRenderer:YES];
708     }
709     return smallCapsRenderer;
710 }
711
712 - (NSFont *)_smallCapsFont
713 {
714     if (!smallCapsFont)
715         smallCapsFont = [[NSFontManager sharedFontManager] convertFont:font toSize:([font pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER)];
716     return smallCapsFont;
717 }
718
719 static inline BOOL fontContainsString(NSFont *font, NSString *string)
720 {
721     NSCharacterSet *set = [[font coveredCharacterSet] invertedSet];
722     return set && [string rangeOfCharacterFromSet:set].location == NSNotFound;
723 }
724
725 - (NSFont *)_substituteFontForString: (NSString *)string families: (NSString **)families
726 {
727     NSFont *substituteFont = nil;
728
729     // First search the CSS family fallback list.  Start at 1 (2nd font)
730     // because we've already failed on the first lookup.
731     NSString *family = nil;
732     int i = 1;
733     while (families && families[i] != 0) {
734         family = families[i++];
735         substituteFont = [[WebTextRendererFactory sharedFactory] cachedFontFromFamily: family traits:[[NSFontManager sharedFontManager] traitsOfFont:font] size:[font pointSize]];
736         if (substituteFont) {
737             if (fontContainsString(substituteFont, string))
738                 break;
739             substituteFont = nil; 
740         }
741     }
742     
743     // Now do string based lookup.
744     if (substituteFont == nil)
745         substituteFont = [NSFont findFontLike:font forString:string withRange:NSMakeRange (0,[string length]) inLanguage:[NSLanguage defaultLanguage]];
746
747     // Now do character based lookup.
748     if (substituteFont == nil && [string length] == 1)
749         substituteFont = [NSFont findFontLike:font forCharacter: [string characterAtIndex: 0] inLanguage:[NSLanguage defaultLanguage]];
750
751     // Get the screen or printer variation of the font.
752     substituteFont = usingPrinterFont ? [substituteFont printerFont] : [substituteFont screenFont];
753
754     if ([substituteFont isEqual: font])
755         substituteFont = nil;
756
757     return substituteFont;
758 }
759
760 - (NSFont *)_substituteFontForCharacters: (const unichar *)characters length: (int)numCharacters families: (NSString **)families
761 {
762     NSString *string = [[NSString alloc] initWithCharactersNoCopy:(unichar *)characters length: numCharacters freeWhenDone: NO];
763     NSFont *substituteFont = [self _substituteFontForString: string families: families];
764     [string release];
765     return substituteFont;
766 }
767
768 - (void)_convertCharacters: (const UniChar *)characters length: (unsigned)numCharacters toGlyphs: (ATSGlyphVector *)glyphs
769 {
770     OSStatus status = ATSUConvertCharToGlyphs(styleGroup, characters, 0, numCharacters, 0, glyphs);
771     if (status != noErr){
772         FATAL_ALWAYS ("unable to get glyphsfor %@ %f error = (%d)", self, [font displayName], [font pointSize], status);
773     }
774
775 #ifdef DEBUG_GLYPHS
776     int foundGlyphs = 0;
777     ATSLayoutRecord *glyphRecord;
778     for (i = 0; i < numCharacters; i++) {
779         glyphRecord = (ATSLayoutRecord *)glyphs->firstRecord;
780         for (i = 0; i < numCharacters; i++) {
781             if (glyphRecord->glyphID != 0)
782                 foundGlyphs++;
783             glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + glyphs->recordSize);
784         }
785     }
786     printf ("For %s found %d glyphs in range 0x%04x to 0x%04x\n", [[font displayName] cString], foundGlyphs, characters[0], characters[numCharacters-1]);
787 #endif
788 }
789
790 - (void)_convertUnicodeCharacters: (const UnicodeChar *)characters length: (unsigned)numCharacters toGlyphs: (ATSGlyphVector *)glyphs
791 {
792     UniChar localBuffer[LOCAL_BUFFER_SIZE];
793     UniChar *buffer = localBuffer;
794     unsigned i, bufPos = 0;
795     
796     if (numCharacters*2 > LOCAL_BUFFER_SIZE) {
797         buffer = (UniChar *)malloc(sizeof(UniChar) * numCharacters * 2);
798     }
799     
800     for (i = 0; i < numCharacters; i++) {
801         UnicodeChar c = characters[i];
802         UniChar h = HighSurrogatePair(c);
803         UniChar l = LowSurrogatePair(c);
804         buffer[bufPos++] = h;
805         buffer[bufPos++] = l;
806     }
807         
808     OSStatus status = ATSUConvertCharToGlyphs(styleGroup, buffer, 0, numCharacters*2, 0, glyphs);
809     if (status != noErr){
810         FATAL_ALWAYS ("unable to get glyphsfor %@ %f error = (%d)", self, [font displayName], [font pointSize], status);
811     }
812     
813     if (buffer != localBuffer) {
814         free(buffer);
815     }
816 }
817
818 // Nasty hack to determine if we should round or ceil space widths.
819 // If the font is monospace or fake monospace we ceil to ensure that 
820 // every character and the space are the same width.  Otherwise we round.
821 - (BOOL)_computeWidthForSpace
822 {
823     spaceGlyph = [self _extendCharacterToGlyphMapToInclude:SPACE];
824     if (spaceGlyph == 0) {
825         return NO;
826     }
827
828     float width = widthForGlyph(self, spaceGlyph, 0);
829     spaceWidth = width;
830
831     treatAsFixedPitch = [font isFixedPitch] || [font _isFakeFixedPitch];
832     adjustedSpaceWidth = treatAsFixedPitch ? CEIL_TO_INT(width) : ROUND_TO_INT(width);
833     
834     return YES;
835 }
836
837 - (BOOL)_setupFont
838 {
839     ATSUStyle fontStyle;
840     if (ATSUCreateStyle(&fontStyle) != noErr)
841         return NO;
842
843     if (!fillStyleWithAttributes(fontStyle, font)) {
844         ATSUDisposeStyle(fontStyle);
845         return NO;
846     }
847
848     if (ATSUGetStyleGroup(fontStyle, &styleGroup) != noErr) {
849         ATSUDisposeStyle(fontStyle);
850         return NO;
851     }
852     
853     ATSUDisposeStyle(fontStyle);
854
855     if (![self _computeWidthForSpace]) {
856         freeGlyphMap(characterToGlyphMap);
857         characterToGlyphMap = NULL;
858         ATSUDisposeStyleGroup(styleGroup);
859         styleGroup = NULL;
860         return NO;
861     }
862     
863     return YES;
864 }
865
866 static NSString *pathFromFont (NSFont *font)
867 {
868     UInt8 _filePathBuffer[PATH_MAX];
869     NSString *filePath = nil;
870     FSSpec oFile;
871     OSStatus status = ATSFontGetFileSpecification(
872             ATSFontRefFromNSFont(font),
873             &oFile);
874     if (status == noErr){
875         OSErr err;
876         FSRef fileRef;
877         err = FSpMakeFSRef(&oFile,&fileRef);
878         if (err == noErr){
879             status = FSRefMakePath(&fileRef,_filePathBuffer, PATH_MAX);
880             if (status == noErr){
881                 filePath = [NSString stringWithUTF8String:&_filePathBuffer[0]];
882             }
883         }
884     }
885     return filePath;
886 }
887
888 // Useful page for testing http://home.att.net/~jameskass
889 static void _drawGlyphs(NSFont *font, NSColor *color, CGGlyph *glyphs, CGSize *advances, float x, float y, int numGlyphs)
890 {
891     CGContextRef cgContext;
892
893     if ([WebTextRenderer shouldBufferTextDrawing] && [[WebTextRendererFactory sharedFactory] coalesceTextDrawing]){
894         // Add buffered glyphs and advances
895         // FIXME:  If we ever use this again, need to add RTL.
896         WebGlyphBuffer *gBuffer = [[WebTextRendererFactory sharedFactory] glyphBufferForFont: font andColor: color];
897         [gBuffer addGlyphs: glyphs advances: advances count: numGlyphs at: x : y];
898     }
899     else {
900         NSGraphicsContext *gContext = [NSGraphicsContext currentContext];
901         cgContext = (CGContextRef)[gContext graphicsPort];
902         // Setup the color and font.
903         
904 #if BUILDING_ON_PANTHER        
905         if ([gContext isDrawingToScreen]){
906             NSFont *screenFont = [font screenFont];
907             if (screenFont != font){
908                 // We are getting this in too many places (3406411); use ERROR so it only prints on
909                 // debug versions for now. (We should debug this also, eventually).
910                 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]);
911             }
912             [screenFont set];
913         }
914         else {
915             NSFont *printerFont = [font printerFont];
916             if (printerFont != font){
917                 NSLog (@"Attempting to set non-printer font (%@) when printing.  Using printer font anyway, may result in incorrect metrics.", [[[font fontDescriptor] fontAttributes] objectForKey: NSFontNameAttribute]);
918             }
919             [printerFont set];
920         }
921 #else
922         NSFont *drawFont;
923         
924         if ([gContext isDrawingToScreen]){
925             drawFont = [font screenFont];
926             if (drawFont != font){
927                 // We are getting this in too many places (3406411); use ERROR so it only prints on
928                 // debug versions for now. (We should debug this also, eventually).
929                 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]);
930             }
931         }
932         else {
933             drawFont = [font printerFont];
934             if (drawFont != font){
935                 NSLog (@"Attempting to set non-printer font (%@) when printing.  Using printer font anyway, may result in incorrect metrics.", [[[font fontDescriptor] fontAttributes] objectForKey: NSFontNameAttribute]);
936             }
937         }
938         
939         CGContextSetFont (cgContext, [drawFont _backingCGSFont]);
940         
941         // Deal will flipping flippyness.
942         const float *matrix = [drawFont matrix];
943         float flip = [[NSView focusView] isFlipped] ? -1 : 1;
944         CGContextSetTextMatrix(cgContext, CGAffineTransformMake(matrix[0], matrix[1] * flip, matrix[2], matrix[3] * flip, matrix[4], matrix[5]));
945         CGContextSetFontRenderingMode (cgContext, _AppkitGetCGRenderingMode(drawFont));
946         CGContextSetFontSize(cgContext, 1.0);
947 #endif
948
949         [color set];
950
951         CGContextSetTextPosition (cgContext, x, y);
952         CGContextShowGlyphsWithAdvances (cgContext, glyphs, advances, numGlyphs);
953     }
954 }
955
956
957 - (void)_CG_drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry
958 {
959     if (run->length == 0)
960         return;
961
962     CharacterWidthIterator widthIterator;
963     WebCoreTextRun completeRun = *run;
964     completeRun.from = 0;
965     completeRun.to = run->length;
966     initializeCharacterWidthIterator(&widthIterator, self, &completeRun, style);
967     
968     float startPosition = 0;
969
970     // The starting point needs to be adjusted to account for the width of
971     // the glyphs at the start of the run.
972     while (widthIterator.currentCharacter < (unsigned)run->from) {
973         startPosition += widthForNextCharacter(&widthIterator, 0, 0);
974     }
975     float startX = startPosition + geometry->point.x;
976     
977     float backgroundWidth = 0.0;
978     while (widthIterator.currentCharacter < (unsigned)run->to) {
979         backgroundWidth += widthForNextCharacter(&widthIterator, 0, 0);
980     }
981
982     if (style->backgroundColor != nil){
983         // Calculate the width of the selection background by adding
984         // up the advances of all the glyphs in the selection.
985         
986         [style->backgroundColor set];
987
988         float yPos = geometry->useFontMetricsForSelectionYAndHeight ? geometry->point.y - [self ascent] - (lineGap/2) : geometry->selectionY;
989         float height = geometry->useFontMetricsForSelectionYAndHeight ? [self lineSpacing] : geometry->selectionHeight;
990         if (style->rtl){
991             float completeRunWidth = startPosition + backgroundWidth;
992             while (widthIterator.currentCharacter < run->length) {
993                 completeRunWidth += widthForNextCharacter(&widthIterator, 0, 0);
994             }
995
996             [NSBezierPath fillRect:NSMakeRect(geometry->point.x + completeRunWidth - startPosition - backgroundWidth, yPos, backgroundWidth, height)];
997         }
998         else {
999             [NSBezierPath fillRect:NSMakeRect(startX, yPos, backgroundWidth, height)];
1000         }
1001     }
1002 }
1003
1004
1005 - (void)_CG_drawRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry
1006 {
1007     float *widthBuffer, localWidthBuffer[LOCAL_BUFFER_SIZE];
1008     CGGlyph *glyphBuffer, localGlyphBuffer[LOCAL_BUFFER_SIZE];
1009     NSFont **fontBuffer, *localFontBuffer[LOCAL_BUFFER_SIZE];
1010     CGSize *advances, localAdvanceBuffer[LOCAL_BUFFER_SIZE];
1011     int numGlyphs = 0, i;
1012     float startX;
1013     unsigned length = run->length;
1014     
1015     if (run->length == 0)
1016         return;
1017
1018     if (length*MAX_GLYPH_EXPANSION > LOCAL_BUFFER_SIZE) {
1019         advances = (CGSize *)calloc(length*MAX_GLYPH_EXPANSION, sizeof(CGSize));
1020         widthBuffer = (float *)calloc(length*MAX_GLYPH_EXPANSION, sizeof(float));
1021         glyphBuffer = (CGGlyph *)calloc(length*MAX_GLYPH_EXPANSION, sizeof(ATSGlyphRef));
1022         fontBuffer = (NSFont **)calloc(length*MAX_GLYPH_EXPANSION, sizeof(NSFont *));
1023     } else {
1024         advances = localAdvanceBuffer;
1025         widthBuffer = localWidthBuffer;
1026         glyphBuffer = localGlyphBuffer;
1027         fontBuffer = localFontBuffer;
1028     }
1029
1030     [self _floatWidthForRun:run
1031         style:style
1032         widths:widthBuffer 
1033         fonts:fontBuffer
1034         glyphs:glyphBuffer
1035         startPosition:&startX
1036         numGlyphs: &numGlyphs];
1037         
1038     // Eek.  We couldn't generate ANY glyphs for the run.
1039     if (numGlyphs <= 0)
1040         return;
1041         
1042     // Fill the advances array.
1043     for (i = 0; i < numGlyphs; i++){
1044         advances[i].width = widthBuffer[i];
1045         advances[i].height = 0;
1046     }
1047
1048     // Calculate the starting point of the glyphs to be displayed by adding
1049     // all the advances up to the first glyph.
1050     startX += geometry->point.x;
1051
1052     if (style->backgroundColor != nil)
1053         [self _CG_drawHighlightForRun:run style:style geometry:geometry];
1054     
1055     // Finally, draw the glyphs.
1056     int lastFrom = 0;
1057     int pos = 0;
1058
1059     // Swap the order of the glyphs if right-to-left.
1060     if (style->rtl && numGlyphs > 1){
1061         int i;
1062         int end = numGlyphs;
1063         CGGlyph gswap1, gswap2;
1064         CGSize aswap1, aswap2;
1065         NSFont *fswap1, *fswap2;
1066         
1067         for (i = pos, end = numGlyphs-1; i < (numGlyphs - pos)/2; i++){
1068             gswap1 = glyphBuffer[i];
1069             gswap2 = glyphBuffer[--end];
1070             glyphBuffer[i] = gswap2;
1071             glyphBuffer[end] = gswap1;
1072         }
1073         for (i = pos, end = numGlyphs - 1; i < (numGlyphs - pos)/2; i++){
1074             aswap1 = advances[i];
1075             aswap2 = advances[--end];
1076             advances[i] = aswap2;
1077             advances[end] = aswap1;
1078         }
1079         for (i = pos, end = numGlyphs - 1; i < (numGlyphs - pos)/2; i++){
1080             fswap1 = fontBuffer[i];
1081             fswap2 = fontBuffer[--end];
1082             fontBuffer[i] = fswap2;
1083             fontBuffer[end] = fswap1;
1084         }
1085     }
1086
1087     // Draw each contiguous run of glyphs that are included in the same font.
1088     NSFont *currentFont = fontBuffer[pos];
1089     float nextX = startX;
1090     int nextGlyph = pos;
1091
1092     while (nextGlyph < numGlyphs){
1093         if ((fontBuffer[nextGlyph] != 0 && fontBuffer[nextGlyph] != currentFont)){
1094             _drawGlyphs(currentFont, style->textColor, &glyphBuffer[lastFrom], &advances[lastFrom], startX, geometry->point.y, nextGlyph - lastFrom);
1095             lastFrom = nextGlyph;
1096             currentFont = fontBuffer[nextGlyph];
1097             startX = nextX;
1098         }
1099         nextX += advances[nextGlyph].width;
1100         nextGlyph++;
1101     }
1102     _drawGlyphs(currentFont, style->textColor, &glyphBuffer[lastFrom], &advances[lastFrom], startX, geometry->point.y, nextGlyph - lastFrom);
1103
1104     if (advances != localAdvanceBuffer) {
1105         free(advances);
1106         free(widthBuffer);
1107         free(glyphBuffer);
1108         free(fontBuffer);
1109     }
1110 }
1111
1112 #ifdef DEBUG_COMBINING
1113 static const char *directionNames[] = {
1114         "DirectionL",   // Left Letter 
1115         "DirectionR",   // Right Letter
1116         "DirectionEN",  // European Number
1117         "DirectionES",  // European Separator
1118         "DirectionET",  // European Terminator (post/prefix e.g. $ and %)
1119         "DirectionAN",  // Arabic Number
1120         "DirectionCS",  // Common Separator 
1121         "DirectionB",   // Paragraph Separator (aka as PS)
1122         "DirectionS",   // Segment Separator (TAB)
1123         "DirectionWS",  // White space
1124         "DirectionON",  // Other Neutral
1125
1126         // types for explicit controls
1127         "DirectionLRE", 
1128         "DirectionLRO", 
1129
1130         "DirectionAL",  // Arabic Letter (Right-to-left)
1131
1132         "DirectionRLE", 
1133         "DirectionRLO", 
1134         "DirectionPDF", 
1135
1136         "DirectionNSM",         // Non-spacing Mark
1137         "DirectionBN"   // Boundary neutral (type of RLE etc after explicit levels)
1138 };
1139
1140 static const char *joiningNames[] = {
1141         "JoiningOther",
1142         "JoiningDual",
1143         "JoiningRight",
1144         "JoiningCausing"
1145 };
1146 #endif
1147
1148 - (float)_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths:(float *)widthBuffer fonts:(NSFont **)fontBuffer glyphs:(CGGlyph *)glyphBuffer startPosition:(float *)startPosition numGlyphs:(int *)_numGlyphs
1149 {
1150     if (shouldUseATSU(run))
1151         return [self _ATSU_floatWidthForRun:run style:style];
1152     
1153     return [self _CG_floatWidthForRun:run style:style widths:widthBuffer fonts:fontBuffer glyphs:glyphBuffer startPosition:startPosition numGlyphs:_numGlyphs];
1154
1155 }
1156
1157 - (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
1158 {
1159     float _totalWidth = 0, _nextWidth;
1160     CharacterWidthIterator widthIterator;
1161     NSFont *fontUsed = 0;
1162     ATSGlyphRef glyphUsed;
1163     int numGlyphs = 0;
1164     
1165     initializeCharacterWidthIterator(&widthIterator, self, run, style);
1166     if (startPosition)
1167         *startPosition = widthIterator.widthToStart;
1168     while ((_nextWidth = widthForNextCharacter(&widthIterator, &glyphUsed, &fontUsed)) != INVALID_WIDTH){
1169         if (fontBuffer)
1170             fontBuffer[numGlyphs] = fontUsed;
1171         if (glyphBuffer)
1172             glyphBuffer[numGlyphs] = glyphUsed;
1173         if (widthBuffer)
1174             widthBuffer[numGlyphs] = _nextWidth;
1175         numGlyphs++;
1176         _totalWidth += _nextWidth;
1177     }
1178         
1179     if (_numGlyphs)
1180         *_numGlyphs = numGlyphs;
1181
1182     return _totalWidth;
1183 }
1184
1185 - (ATSGlyphRef)_extendUnicodeCharacterToGlyphMapToInclude:(UnicodeChar)c
1186 {
1187     UnicodeGlyphMap *map = (UnicodeGlyphMap *)calloc (1, sizeof(UnicodeGlyphMap));
1188     ATSLayoutRecord *glyphRecord;
1189     ATSGlyphVector glyphVector;
1190     UnicodeChar end, start;
1191     unsigned blockSize;
1192     ATSGlyphRef glyphID;
1193     
1194     if (unicodeCharacterToGlyphMap == 0)
1195         blockSize = INITIAL_BLOCK_SIZE;
1196     else
1197         blockSize = INCREMENTAL_BLOCK_SIZE;
1198     start = (c / blockSize) * blockSize;
1199     end = start + (blockSize - 1);
1200         
1201     LOG(FontCache, "%@ (0x%04x) adding glyphs for 0x%04x to 0x%04x", font, c, start, end);
1202
1203     map->startRange = start;
1204     map->endRange = end;
1205     
1206     unsigned i, count = end - start + 1;
1207     UnicodeChar buffer[INCREMENTAL_BLOCK_SIZE+2];
1208     
1209     for (i = 0; i < count; i++){
1210         buffer[i] = i+start;
1211     }
1212
1213     OSStatus status;
1214     status = ATSInitializeGlyphVector(count*2, 0, &glyphVector);
1215     if (status != noErr){
1216         // This should never happen, indicates a bad font!  If it does the
1217         // font substitution code will find an alternate font.
1218         free(map);
1219         return 0;
1220     }
1221     
1222     [self _convertUnicodeCharacters: &buffer[0] length: count toGlyphs: &glyphVector];
1223     unsigned numGlyphs = glyphVector.numGlyphs;
1224     if (numGlyphs != count){
1225         // This should never happen, indicates a bad font!  If it does the
1226         // font substitution code will find an alternate font.
1227         free(map);
1228         return 0;
1229     }
1230             
1231     map->glyphs = (GlyphEntry *)malloc (count * sizeof(GlyphEntry));
1232     glyphRecord = (ATSLayoutRecord *)glyphVector.firstRecord;
1233     for (i = 0; i < count; i++) {
1234         map->glyphs[i].glyph = glyphRecord->glyphID;
1235         map->glyphs[i].font = 0;
1236         glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + glyphVector.recordSize);
1237     }
1238     ATSClearGlyphVector(&glyphVector);
1239     
1240     if (unicodeCharacterToGlyphMap == 0)
1241         unicodeCharacterToGlyphMap = map;
1242     else {
1243         UnicodeGlyphMap *lastMap = unicodeCharacterToGlyphMap;
1244         while (lastMap->next != 0)
1245             lastMap = lastMap->next;
1246         lastMap->next = map;
1247     }
1248
1249     glyphID = map->glyphs[c - start].glyph;
1250     
1251     return glyphID;
1252 }
1253
1254 - (void)_updateGlyphEntryForCharacter:(UniChar)c glyphID:(ATSGlyphRef)glyphID font:(NSFont *)substituteFont
1255 {
1256     GlyphMap *lastMap = characterToGlyphMap;
1257     while (lastMap != 0){
1258         if (c >= lastMap->startRange && c <= lastMap->endRange){
1259             lastMap->glyphs[c - lastMap->startRange].glyph = glyphID;
1260             // This font will leak.  No problem though, it has to stick around
1261             // forever.  Max theoretical retain counts applied here will be
1262             // num_fonts_on_system * num_glyphs_in_font.
1263             lastMap->glyphs[c - lastMap->startRange].font = [substituteFont retain];
1264             break;
1265         }
1266         lastMap = lastMap->next;
1267     }
1268 }
1269
1270 - (ATSGlyphRef)_extendCharacterToGlyphMapToInclude:(UniChar) c
1271 {
1272     GlyphMap *map = (GlyphMap *)calloc (1, sizeof(GlyphMap));
1273     ATSLayoutRecord *glyphRecord;
1274     ATSGlyphVector glyphVector;
1275     UniChar end, start;
1276     unsigned blockSize;
1277     ATSGlyphRef glyphID;
1278     
1279     if (characterToGlyphMap == 0)
1280         blockSize = INITIAL_BLOCK_SIZE;
1281     else
1282         blockSize = INCREMENTAL_BLOCK_SIZE;
1283     start = (c / blockSize) * blockSize;
1284     end = start + (blockSize - 1);
1285         
1286     LOG(FontCache, "%@ (0x%04x) adding glyphs for 0x%04x to 0x%04x", font, c, start, end);
1287
1288     map->startRange = start;
1289     map->endRange = end;
1290     
1291     unsigned i, count = end - start + 1;
1292     short unsigned buffer[INCREMENTAL_BLOCK_SIZE+2];
1293     
1294     for (i = 0; i < count; i++) {
1295         buffer[i] = i+start;
1296     }
1297
1298     if (start == 0) {
1299         // Control characters must not render at all.
1300         for (i = 0; i < 0x20; ++i)
1301             buffer[i] = ZERO_WIDTH_SPACE;
1302         buffer[0x7F] = ZERO_WIDTH_SPACE;
1303
1304         // But both \n and nonbreaking space must render as a space.
1305         buffer['\n'] = ' ';
1306         buffer[NO_BREAK_SPACE] = ' ';
1307     }
1308
1309     OSStatus status = ATSInitializeGlyphVector(count, 0, &glyphVector);
1310     if (status != noErr) {
1311         // This should never happen, perhaps indicates a bad font!  If it does the
1312         // font substitution code will find an alternate font.
1313         free(map);
1314         return 0;
1315     }
1316
1317     [self _convertCharacters: &buffer[0] length: count toGlyphs: &glyphVector];
1318     unsigned numGlyphs = glyphVector.numGlyphs;
1319     if (numGlyphs != count){
1320         // This should never happen, perhaps indicates a bad font!  If it does the
1321         // font substitution code will find an alternate font.
1322         free(map);
1323         return 0;
1324     }
1325             
1326     map->glyphs = (GlyphEntry *)malloc (count * sizeof(GlyphEntry));
1327     glyphRecord = (ATSLayoutRecord *)glyphVector.firstRecord;
1328     for (i = 0; i < count; i++) {
1329         map->glyphs[i].glyph = glyphRecord->glyphID;
1330         map->glyphs[i].font = 0;
1331         glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + glyphVector.recordSize);
1332     }
1333     ATSClearGlyphVector(&glyphVector);
1334     
1335     if (characterToGlyphMap == 0)
1336         characterToGlyphMap = map;
1337     else {
1338         GlyphMap *lastMap = characterToGlyphMap;
1339         while (lastMap->next != 0)
1340             lastMap = lastMap->next;
1341         lastMap->next = map;
1342     }
1343
1344     glyphID = map->glyphs[c - start].glyph;
1345     
1346     // Special case for characters 007F-00A0.
1347     if (glyphID == 0 && c >= 0x7F && c <= 0xA0){
1348         glyphID = [font _defaultGlyphForChar: c];
1349         map->glyphs[c - start].glyph = glyphID;
1350         map->glyphs[c - start].font = 0;
1351     }
1352
1353     return glyphID;
1354 }
1355
1356
1357 - (WidthMap *)_extendGlyphToWidthMapToInclude:(ATSGlyphRef)glyphID font:(NSFont *)subFont
1358 {
1359     WidthMap *map = (WidthMap *)calloc (1, sizeof(WidthMap)), **rootMap;
1360     unsigned end;
1361     ATSGlyphRef start;
1362     unsigned blockSize;
1363     unsigned i, count;
1364     
1365     if (subFont && subFont != font)
1366         rootMap = &mapForSubstituteFont(self,subFont)->map;
1367     else
1368         rootMap = &glyphToWidthMap;
1369         
1370     if (*rootMap == 0){
1371         if ([(subFont ? subFont : font) numberOfGlyphs] < INITIAL_BLOCK_SIZE)
1372             blockSize = [font numberOfGlyphs];
1373          else
1374             blockSize = INITIAL_BLOCK_SIZE;
1375     }
1376     else
1377         blockSize = INCREMENTAL_BLOCK_SIZE;
1378     start = (glyphID / blockSize) * blockSize;
1379     end = ((unsigned)start) + blockSize; 
1380     if (end > 0xffff)
1381         end = 0xffff;
1382
1383     LOG(FontCache, "%@ (0x%04x) adding widths for range 0x%04x to 0x%04x", font, glyphID, start, end);
1384
1385     map->startRange = start;
1386     map->endRange = end;
1387     count = end - start + 1;
1388
1389     map->widths = (WidthEntry *)malloc (count * sizeof(WidthEntry));
1390
1391     for (i = 0; i < count; i++){
1392         map->widths[i].width = UNINITIALIZED_GLYPH_WIDTH;
1393     }
1394
1395     if (*rootMap == 0)
1396         *rootMap = map;
1397     else {
1398         WidthMap *lastMap = *rootMap;
1399         while (lastMap->next != 0)
1400             lastMap = lastMap->next;
1401         lastMap->next = map;
1402     }
1403
1404 #ifdef _TIMING
1405     LOG(FontCache, "%@ total time to advances lookup %f seconds", font, totalCGGetAdvancesTime);
1406 #endif
1407     return map;
1408 }
1409
1410
1411 - (void)_initializeATSUStyle
1412 {
1413     // The two NSFont calls in this method (pointSize and _atsFontID)
1414     // are both exception-safe.
1415
1416     if (!ATSUStyleInitialized){
1417         OSStatus status;
1418         
1419         status = ATSUCreateStyle(&_ATSUSstyle);
1420         if(status != noErr)
1421             FATAL_ALWAYS ("ATSUCreateStyle failed (%d)", status);
1422     
1423         ATSUFontID fontID = [font _atsFontID];
1424         if (fontID == 0){
1425             ATSUDisposeStyle(_ATSUSstyle);
1426             ERROR ("unable to get ATSUFontID for %@", font);
1427             return;
1428         }
1429         
1430         CGAffineTransform transform = CGAffineTransformMakeScale (1,-1);
1431         Fixed fontSize = FloatToFixed([font pointSize]);
1432         ATSUAttributeTag styleTags[] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag};
1433         ByteCount styleSizes[] = {  sizeof(Fixed), sizeof(ATSUFontID), sizeof(CGAffineTransform) };
1434         ATSUAttributeValuePtr styleValues[] = { &fontSize, &fontID, &transform  };
1435         status = ATSUSetAttributes (_ATSUSstyle, 3, styleTags, styleSizes, styleValues);
1436         if(status != noErr)
1437             FATAL_ALWAYS ("ATSUSetAttributes failed (%d)", status);
1438
1439         ATSUStyleInitialized = YES;
1440     }
1441 }
1442
1443 - (ATSUTextLayout)_createATSUTextLayoutForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style
1444 {
1445     // The only Cocoa calls here are to NSGraphicsContext and the self
1446     // call to _initializeATSUStyle, which are all exception-safe.
1447
1448     ATSUTextLayout layout;
1449     UniCharCount runLength;
1450     OSStatus status;
1451     
1452     [self _initializeATSUStyle];
1453     
1454     // FIXME: This is missing the following features that the CoreGraphics code path has:
1455     // - Both \n and nonbreaking space render as a space.
1456     // - All other control characters must not render at all (other code path uses zero-width spaces).
1457
1458     runLength = run->to - run->from;
1459     status = ATSUCreateTextLayoutWithTextPtr(
1460             run->characters,
1461             run->from,           // offset
1462             runLength,        // length
1463             run->length,         // total length
1464             1,              // styleRunCount
1465             &runLength,    // length of style run
1466             &_ATSUSstyle, 
1467             &layout);
1468     if(status != noErr)
1469         FATAL_ALWAYS ("ATSUCreateTextLayoutWithTextPtr failed(%d)", status);
1470
1471     CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
1472     ATSLineLayoutOptions lineLayoutOptions = (kATSLineFractDisable | kATSLineDisableAutoAdjustDisplayPos | kATSLineUseDeviceMetrics);
1473     Boolean rtl = style->rtl;
1474     ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineLayoutOptionsTag, kATSULineDirectionTag };
1475     ByteCount sizes[] = { sizeof(CGContextRef), sizeof(ATSLineLayoutOptions), sizeof(Boolean)  };
1476     ATSUAttributeValuePtr values[] = { &cgContext, &lineLayoutOptions, &rtl };
1477     
1478     status = ATSUSetLayoutControls(layout, 3, tags, sizes, values);
1479     if(status != noErr)
1480         FATAL_ALWAYS ("ATSUSetLayoutControls failed(%d)", status);
1481
1482     status = ATSUSetTransientFontMatching (layout, YES);
1483     if(status != noErr)
1484         FATAL_ALWAYS ("ATSUSetTransientFontMatching failed(%d)", status);
1485         
1486     return layout;
1487 }
1488
1489
1490 - (ATSTrapezoid)_trapezoidForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style atPoint:(NSPoint )p
1491 {
1492     // The only Cocoa call here is the self call to
1493     // _createATSUTextLayoutForRun:, which is exception-safe.
1494
1495     OSStatus status;
1496     
1497     if (run->to - run->from <= 0){
1498         ATSTrapezoid nilTrapezoid = { {0,0} , {0,0}, {0,0}, {0,0} };
1499         return nilTrapezoid;
1500     }
1501         
1502     ATSUTextLayout layout = [self _createATSUTextLayoutForRun:run style:style];
1503
1504     ATSTrapezoid firstGlyphBounds;
1505     ItemCount actualNumBounds;
1506     status = ATSUGetGlyphBounds (layout, FloatToFixed(p.x), FloatToFixed(p.y), run->from, run->to - run->from, kATSUseDeviceOrigins, 1, &firstGlyphBounds, &actualNumBounds);    
1507     if(status != noErr)
1508         FATAL_ALWAYS ("ATSUGetGlyphBounds() failed(%d)", status);
1509     
1510     if (actualNumBounds != 1)
1511         FATAL_ALWAYS ("unexpected result from ATSUGetGlyphBounds():  actualNumBounds(%d) != 1", actualNumBounds);
1512
1513     ATSUDisposeTextLayout (layout); // Ignore the error.  Nothing we can do anyway.
1514             
1515     return firstGlyphBounds;
1516 }
1517
1518
1519 - (float)_ATSU_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style
1520 {
1521     ATSTrapezoid oGlyphBounds;
1522     
1523     oGlyphBounds = [self _trapezoidForRun:run style:style atPoint:NSMakePoint (0,0)];
1524     
1525     float width = 
1526         MAX(FixedToFloat(oGlyphBounds.upperRight.x), FixedToFloat(oGlyphBounds.lowerRight.x)) - 
1527         MIN(FixedToFloat(oGlyphBounds.upperLeft.x), FixedToFloat(oGlyphBounds.lowerLeft.x));
1528     
1529     return width;
1530 }
1531
1532 // Be sure to free the run.characters allocated by this function.
1533 static WebCoreTextRun reverseCharactersInRun(const WebCoreTextRun *run)
1534 {
1535     WebCoreTextRun swappedRun;
1536     unsigned int i;
1537     
1538     UniChar *swappedCharacters = (UniChar *)malloc(sizeof(UniChar)*run->length);
1539     for (i = 0; i < run->length; i++) {
1540         swappedCharacters[i] = run->characters[run->length-i-1];
1541     }
1542     swappedRun.characters = swappedCharacters;
1543     swappedRun.from = run->length - (run->to == -1 ? (int)run->length : run->to);
1544     swappedRun.to = run->length - (run->from == -1 ? 0 : run->from);
1545     swappedRun.length = run->length;
1546
1547     return swappedRun;
1548 }
1549
1550 - (void)_ATSU_drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry
1551 {
1552     // The only Cocoa calls made here are to NSColor and NSBezierPath,
1553     // plus the self calls to _createATSUTextLayoutForRun: and
1554     // _trapezoidForRun:. These are all exception-safe.
1555
1556     ATSUTextLayout layout;
1557     int from, to;
1558     float selectedLeftX;
1559     const WebCoreTextRun *aRun = run;
1560     WebCoreTextRun swappedRun;
1561
1562     if (style->backgroundColor == nil)
1563         return;
1564     
1565     if (style->visuallyOrdered) {
1566         swappedRun = reverseCharactersInRun(run);
1567         aRun = &swappedRun;
1568     }
1569
1570     from = aRun->from;
1571     to = aRun->to;
1572     if (from == -1)
1573         from = 0;
1574     if (to == -1)
1575         to = run->length;
1576    
1577     int runLength = to - from;
1578     if (runLength <= 0){
1579         return;
1580     }
1581
1582     layout = [self _createATSUTextLayoutForRun:aRun style:style];
1583
1584     WebCoreTextRun leadingRun = *aRun;
1585     leadingRun.from = 0;
1586     leadingRun.to = run->from;
1587     
1588     // ATSU provides the bounds of the glyphs for the run with an origin of
1589     // (0,0), so we need to find the width of the glyphs immediately before
1590     // the actually selected glyphs.
1591     ATSTrapezoid leadingTrapezoid = [self _trapezoidForRun:&leadingRun style:style atPoint:geometry->point];
1592     ATSTrapezoid selectedTrapezoid = [self _trapezoidForRun:run style:style atPoint:geometry->point];
1593
1594     float backgroundWidth = 
1595             MAX(FixedToFloat(selectedTrapezoid.upperRight.x), FixedToFloat(selectedTrapezoid.lowerRight.x)) - 
1596             MIN(FixedToFloat(selectedTrapezoid.upperLeft.x), FixedToFloat(selectedTrapezoid.lowerLeft.x));
1597
1598     if (run->from == 0)
1599         selectedLeftX = geometry->point.x;
1600     else
1601         selectedLeftX = MIN(FixedToFloat(leadingTrapezoid.upperRight.x), FixedToFloat(leadingTrapezoid.lowerRight.x));
1602     
1603     [style->backgroundColor set];
1604
1605     float yPos = geometry->useFontMetricsForSelectionYAndHeight ? geometry->point.y - [self ascent] : geometry->selectionY;
1606     float height = geometry->useFontMetricsForSelectionYAndHeight ? [self lineSpacing] : geometry->selectionHeight;
1607     if (style->rtl || style->visuallyOrdered){
1608         WebCoreTextRun completeRun = *aRun;
1609         completeRun.from = 0;
1610         completeRun.to = aRun->length;
1611         float completeRunWidth = [self floatWidthForRun:&completeRun style:style widths:0];
1612         [NSBezierPath fillRect:NSMakeRect(geometry->point.x + completeRunWidth - (selectedLeftX-geometry->point.x) - backgroundWidth, yPos, backgroundWidth, height)];
1613     }
1614     else {
1615         [NSBezierPath fillRect:NSMakeRect(selectedLeftX, yPos, backgroundWidth, height)];
1616     }
1617
1618     ATSUDisposeTextLayout (layout); // Ignore the error.  Nothing we can do anyway.
1619
1620     if (style->visuallyOrdered)
1621         free ((void *)swappedRun.characters);
1622 }
1623
1624
1625 - (void)_ATSU_drawRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry
1626 {
1627     // The only Cocoa calls made here are to NSColor, plus the self
1628     // calls to _createATSUTextLayoutForRun: and
1629     // _ATSU_drawHighlightForRun:. These are all exception-safe.
1630
1631     ATSUTextLayout layout;
1632     OSStatus status;
1633     int from, to;
1634     const WebCoreTextRun *aRun = run;
1635     WebCoreTextRun swappedRun;
1636     
1637     if (style->visuallyOrdered) {
1638         swappedRun = reverseCharactersInRun(run);
1639         aRun = &swappedRun;
1640     }
1641
1642     from = aRun->from;
1643     to = aRun->to;
1644     if (from == -1)
1645         from = 0;
1646     if (to == -1)
1647         to = run->length;
1648
1649     int runLength = to - from;
1650     if (runLength <= 0)
1651         return;
1652
1653     layout = [self _createATSUTextLayoutForRun:aRun style:style];
1654
1655     if (style->backgroundColor != nil)
1656         [self _ATSU_drawHighlightForRun:run style:style geometry:geometry];
1657
1658     [style->textColor set];
1659
1660     status = ATSUDrawText(layout, 
1661             aRun->from,
1662             runLength,
1663             FloatToFixed(geometry->point.x),   // these values are
1664             FloatToFixed(geometry->point.y));  // also of type Fixed
1665     if (status != noErr){
1666         // Nothing to do but report the error (dev build only).
1667         ERROR ("ATSUDrawText() failed(%d)", status);
1668     }
1669
1670     ATSUDisposeTextLayout (layout); // Ignore the error.  Nothing we can do anyway.
1671     
1672     if (style->visuallyOrdered)
1673         free ((void *)swappedRun.characters);
1674 }
1675
1676 - (int)_ATSU_pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed includePartialGlyphs:(BOOL)includePartialGlyphs
1677 {
1678     // The only Cocoa calls made here is to the self call to
1679     // _createATSUTextLayoutForRun:. This is exception-safe.
1680
1681     unsigned offset = 0;
1682     ATSUTextLayout layout;
1683     UniCharArrayOffset primaryOffset = 0;
1684     UniCharArrayOffset secondaryOffset = 0;
1685     OSStatus status;
1686     Boolean isLeading;
1687     const WebCoreTextRun *aRun = run;
1688     WebCoreTextRun swappedRun;
1689     
1690     // Reverse the visually ordered characters.  ATSU will re-reverse.  Ick!
1691     if (style->visuallyOrdered) {
1692         swappedRun = reverseCharactersInRun(run);
1693         aRun = &swappedRun;
1694     }
1695
1696     layout = [self _createATSUTextLayoutForRun:aRun style:style];
1697
1698     primaryOffset = aRun->from;
1699     
1700     // FIXME: No idea how to avoid including partial glyphs.   Not even sure if that's the behavior
1701     // this yields now.
1702     status = ATSUPositionToOffset(layout, FloatToFixed(x), FloatToFixed(-1), &primaryOffset, &isLeading, &secondaryOffset);
1703     if (status == noErr){
1704         offset = (unsigned)primaryOffset;
1705     }
1706     else {
1707         // Failed to find offset!  Return 0 offset.
1708     }
1709        
1710     if (style->visuallyOrdered) {
1711         free ((void *)swappedRun.characters);
1712     }
1713
1714     return offset - aRun->from;
1715 }
1716
1717 - (int)_CG_pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed includePartialGlyphs:(BOOL)includePartialGlyphs
1718 {
1719     float delta = (float)x;
1720     float width;
1721     unsigned offset = run->from;
1722     CharacterWidthIterator widthIterator;
1723     
1724     initializeCharacterWidthIterator(&widthIterator, self, run, style);
1725
1726     if (reversed) {
1727         width = [self floatWidthForRun:run style:style widths:nil];
1728         delta -= width;
1729         while (offset < run->length) {
1730             float w = widthForNextCharacter(&widthIterator, 0, 0);
1731             if (w == INVALID_WIDTH) {
1732                 // Something very bad happened, like we only have half of a surrogate pair.
1733                 break;
1734             }
1735             else {
1736                 if (w) {
1737                     if (includePartialGlyphs)
1738                        w -= w/2;
1739                     delta += w;
1740                     if(delta >= 0)
1741                         break;
1742                     if (includePartialGlyphs)
1743                         delta += w;
1744                 }
1745                 offset = widthIterator.currentCharacter;
1746             }
1747         }
1748     } else {
1749         while (offset < run->length) {
1750             float w = widthForNextCharacter(&widthIterator, 0, 0);
1751             if (w == INVALID_WIDTH) {
1752                 // Something very bad happened, like we only have half of a surrogate pair.
1753                 break;
1754             }
1755             else {
1756                 if (w) {
1757                     if (includePartialGlyphs)
1758                         w -= w/2;
1759                     delta -= w;
1760                     if(delta <= 0) 
1761                         break;
1762                     if (includePartialGlyphs)
1763                         delta -= w;
1764                 }
1765                 offset = widthIterator.currentCharacter;
1766             }
1767         }
1768     }
1769     
1770     return offset - run->from;
1771 }
1772
1773 @end
1774
1775 // ------------------- Private functions -------------------
1776
1777 static void freeWidthMap(WidthMap *map)
1778 {
1779     while (map) {
1780         WidthMap *next = map->next;
1781         free(map->widths);
1782         free(map);
1783         map = next;
1784     }
1785 }
1786
1787
1788 static void freeGlyphMap(GlyphMap *map)
1789 {
1790     while (map) {
1791         GlyphMap *next = map->next;
1792         free(map->glyphs);
1793         free(map);
1794         map = next;
1795     }
1796 }
1797
1798
1799 static void freeUnicodeGlyphMap(UnicodeGlyphMap *map)
1800 {
1801     while (map) {
1802         UnicodeGlyphMap *next = map->next;
1803         free(map->glyphs);
1804         free(map);
1805         map = next;
1806     }
1807 }
1808
1809
1810 static inline ATSGlyphRef glyphForCharacter (GlyphMap *map, UniChar c, NSFont **font)
1811 {
1812     if (map == 0)
1813         return nonGlyphID;
1814         
1815     while (map) {
1816         if (c >= map->startRange && c <= map->endRange){
1817             *font = map->glyphs[c-map->startRange].font;
1818             return map->glyphs[c-map->startRange].glyph;
1819         }
1820         map = map->next;
1821     }
1822     return nonGlyphID;
1823 }
1824  
1825  
1826 static inline ATSGlyphRef glyphForUnicodeCharacter (UnicodeGlyphMap *map, UnicodeChar c, NSFont **font)
1827 {
1828     if (map == 0)
1829         return nonGlyphID;
1830         
1831     while (map) {
1832         if (c >= map->startRange && c <= map->endRange){
1833             *font = map->glyphs[c-map->startRange].font;
1834             return map->glyphs[c-map->startRange].glyph;
1835         }
1836         map = map->next;
1837     }
1838     return nonGlyphID;
1839 }
1840  
1841
1842 #ifdef _TIMING        
1843 static double totalCGGetAdvancesTime = 0;
1844 #endif
1845
1846 static inline SubstituteFontWidthMap *mapForSubstituteFont(WebTextRenderer *renderer, NSFont *font)
1847 {
1848     int i;
1849     
1850     for (i = 0; i < renderer->numSubstituteFontWidthMaps; i++){
1851         if (font == renderer->substituteFontWidthMaps[i].font)
1852             return &renderer->substituteFontWidthMaps[i];
1853     }
1854     
1855     if (renderer->numSubstituteFontWidthMaps == renderer->maxSubstituteFontWidthMaps){
1856         renderer->maxSubstituteFontWidthMaps = renderer->maxSubstituteFontWidthMaps * 2;
1857         renderer->substituteFontWidthMaps = realloc (renderer->substituteFontWidthMaps, renderer->maxSubstituteFontWidthMaps * sizeof(SubstituteFontWidthMap));
1858         for (i = renderer->numSubstituteFontWidthMaps; i < renderer->maxSubstituteFontWidthMaps; i++){
1859             renderer->substituteFontWidthMaps[i].font = 0;
1860             renderer->substituteFontWidthMaps[i].map = 0;
1861         }
1862     }
1863     
1864     renderer->substituteFontWidthMaps[renderer->numSubstituteFontWidthMaps].font = font;
1865     return &renderer->substituteFontWidthMaps[renderer->numSubstituteFontWidthMaps++];
1866 }
1867
1868 static void initializeCharacterWidthIterator (CharacterWidthIterator *iterator, WebTextRenderer *renderer, const WebCoreTextRun *run , const WebCoreTextStyle *style) 
1869 {
1870     iterator->renderer = renderer;
1871     iterator->run = run;
1872     iterator->style = style;
1873     iterator->currentCharacter = run->from;
1874     iterator->runWidthSoFar = 0;
1875
1876     // If the padding is non-zero, count the number of spaces in the run
1877     // and divide that by the padding for per space addition.
1878     iterator->padding = style->padding;
1879     if (iterator->padding > 0){
1880         uint numSpaces = 0;
1881         int from = run->from;
1882         int len = run->to - from;
1883         int k;
1884         for (k = from; k < from + len; k++) {
1885             if (isSpace(run->characters[k])) {
1886                 numSpaces++;
1887             }
1888         }
1889         iterator->padPerSpace = CEIL_TO_INT ((((float)style->padding) / ((float)numSpaces)));
1890     }
1891     else {
1892         iterator->padPerSpace = 0;
1893     }
1894     
1895     // Calculate width up to starting position of the run.  This is
1896     // necessary to ensure that our rounding hacks are always consistently
1897     // applied.
1898     if (run->from != 0){
1899         WebCoreTextRun startPositionRun = *run;
1900         startPositionRun.from = 0;
1901         startPositionRun.to = run->from;
1902         CharacterWidthIterator startPositionIterator;
1903         initializeCharacterWidthIterator (&startPositionIterator, renderer, &startPositionRun, style);
1904         
1905         while (startPositionIterator.currentCharacter < (unsigned)startPositionRun.to){
1906             widthForNextCharacter(&startPositionIterator, 0, 0);
1907         }
1908         iterator->widthToStart = startPositionIterator.runWidthSoFar;
1909     }
1910     else
1911         iterator->widthToStart = 0;
1912 }
1913
1914 static inline float ceilCurrentWidth (CharacterWidthIterator *iterator)
1915 {
1916     float delta = CEIL_TO_INT(iterator->widthToStart + iterator->runWidthSoFar) - (iterator->widthToStart + iterator->runWidthSoFar);
1917     iterator->runWidthSoFar += delta;
1918     return delta;
1919 }
1920
1921 // Return INVALID_WIDTH if an error is encountered or we're at the end of the range in the run.
1922 static float widthForNextCharacter(CharacterWidthIterator *iterator, ATSGlyphRef *glyphUsed, NSFont **fontUsed)
1923 {
1924     WebTextRenderer *renderer = iterator->renderer;
1925     const WebCoreTextRun *run = iterator->run;
1926     unsigned currentCharacter = iterator->currentCharacter;
1927
1928     NSFont *_fontUsed = nil;
1929     ATSGlyphRef _glyphUsed;
1930
1931     if (!fontUsed)
1932         fontUsed = &_fontUsed;
1933     if (!glyphUsed)
1934         glyphUsed = &_glyphUsed;
1935         
1936     if (currentCharacter >= (unsigned)run->to)
1937         // Error! Offset specified beyond end of run.
1938         return INVALID_WIDTH;
1939
1940     const UniChar *cp = &run->characters[currentCharacter];
1941     UnicodeChar c = *cp;
1942
1943     if (IsLowSurrogatePair(c))
1944         return INVALID_WIDTH;
1945
1946     // Do we have a surrogate pair?  If so, determine the full Unicode (32 bit)
1947     // code point before glyph lookup.
1948     unsigned clusterLength = 1;
1949     if (IsHighSurrogatePair(c)) {
1950         // Make sure we have another character and it's a low surrogate.
1951         UniChar low;
1952         if (currentCharacter + 1 >= run->length || !IsLowSurrogatePair((low = cp[1]))) {
1953             // Error!  The second component of the surrogate pair is missing.
1954             return INVALID_WIDTH;
1955         }
1956
1957         c = UnicodeValueForSurrogatePair(c, low);
1958         clusterLength = 2;
1959     }
1960
1961     // If small-caps convert lowercase to upper.
1962     BOOL useSmallCapsFont = NO;
1963     if (renderer->isSmallCapsRenderer) {
1964         if (!u_isUUppercase(c)) {
1965             // Only use small cap font if the the uppercase version of the character
1966             // is different than the lowercase.
1967             UnicodeChar newC = u_toupper(c);
1968             if (newC != c) {
1969                 useSmallCapsFont = YES;
1970                 c = newC;
1971             }
1972         }
1973     }
1974
1975     if (c <= 0xFFFF) {
1976         *glyphUsed = glyphForCharacter(renderer->characterToGlyphMap, c, fontUsed);
1977         if (*glyphUsed == nonGlyphID) {
1978             *glyphUsed = [renderer _extendCharacterToGlyphMapToInclude:c];
1979         }
1980     } else {
1981         *glyphUsed = glyphForUnicodeCharacter(renderer->unicodeCharacterToGlyphMap, c, fontUsed);
1982         if (*glyphUsed == nonGlyphID) {
1983             *glyphUsed = [renderer _extendUnicodeCharacterToGlyphMapToInclude:c];
1984         }
1985     }
1986
1987     // Check to see if we're rendering in 'small-caps' mode.
1988     // ASSUMPTION:  We assume the same font in a smaller size has
1989     // the same glyphs as the large font.
1990     if (useSmallCapsFont) {
1991         if (*fontUsed == nil)
1992             *fontUsed = [renderer _smallCapsFont];
1993         else {
1994             // Potential for optimization.  This path should only be taken if we're
1995             // using a cached substituted font.
1996             *fontUsed = [[NSFontManager sharedFontManager] convertFont:*fontUsed toSize:[*fontUsed pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER];
1997         }
1998     }
1999
2000     // Now that we have glyph and font, get its width.
2001     WebGlyphWidth width = widthForGlyph(renderer, *glyphUsed, *fontUsed);
2002
2003     // We special case spaces in two ways when applying word rounding.
2004     // First, we round spaces to an adjusted width in all fonts.
2005     // Second, in fixed-pitch fonts we ensure that all characters that
2006     // match the width of the space character have the same width as the space character.
2007     if ((renderer->treatAsFixedPitch ? width == renderer->spaceWidth : *glyphUsed == renderer->spaceGlyph) && iterator->style->applyWordRounding)
2008         width = renderer->adjustedSpaceWidth;
2009
2010     // Try to find a substitute font if this font didn't have a glyph for a character in the
2011     // string.  If one isn't found we end up drawing and measuring the 0 glyph, usually a box.
2012     if (*glyphUsed == 0 && iterator->style->attemptFontSubstitution) {
2013         UniChar characterArray[2];
2014         unsigned characterArrayLength;
2015         
2016         if (c <= 0xFFFF) {
2017             characterArray[0] = c;
2018             characterArrayLength = 1;
2019         } else {
2020             characterArray[0] = HighSurrogatePair(c);
2021             characterArray[1] = LowSurrogatePair(c);
2022             characterArrayLength = 2;
2023         }
2024         
2025         NSFont *substituteFont = [renderer _substituteFontForCharacters:characterArray length:characterArrayLength
2026             families:iterator->style->families];
2027         if (substituteFont) {
2028             int cNumGlyphs = 0;
2029             ATSGlyphRef localGlyphBuffer[MAX_GLYPH_EXPANSION];
2030             
2031             WebCoreTextRun clusterRun;
2032             WebCoreInitializeTextRun(&clusterRun, characterArray, characterArrayLength, 0, characterArrayLength);
2033             WebCoreTextStyle clusterStyle = *iterator->style;
2034             clusterStyle.padding = 0;
2035             clusterStyle.applyRunRounding = false;
2036             clusterStyle.attemptFontSubstitution = false;
2037             
2038             WebTextRenderer *substituteRenderer;
2039             substituteRenderer = [[WebTextRendererFactory sharedFactory] rendererWithFont:substituteFont usingPrinterFont:renderer->usingPrinterFont];
2040             width = [substituteRenderer
2041                             _floatWidthForRun:&clusterRun
2042                             style:&clusterStyle 
2043                             widths: nil
2044                             fonts: nil
2045                             glyphs: &localGlyphBuffer[0]
2046                             startPosition:nil
2047                             numGlyphs:&cNumGlyphs];
2048             
2049             *fontUsed = substituteFont;
2050             *glyphUsed = localGlyphBuffer[0];
2051             
2052             if (c <= 0xFFFF && cNumGlyphs == 1 && localGlyphBuffer[0] != 0){
2053                 [renderer _updateGlyphEntryForCharacter:c glyphID:localGlyphBuffer[0] font:substituteFont];
2054             }
2055         }
2056     }
2057
2058     if (!*fontUsed)
2059         *fontUsed = renderer->font;
2060
2061     // Force characters that are used to determine word boundaries for the rounding hack
2062     // to be integer width, so following words will start on an integer boundary.
2063     if (isRoundingHackCharacter(c) && iterator->style->applyWordRounding) {
2064         width = CEIL_TO_INT(width);
2065     }
2066     
2067     // Account for letter-spacing
2068     if (iterator->style->letterSpacing && width > 0)
2069         width += iterator->style->letterSpacing;
2070
2071     // Account for padding.  khtml uses space padding to justify text.  We
2072     // distribute the specified padding over the available spaces in the run.
2073     if (isSpace(c)) {
2074         if (iterator->padding > 0) {
2075             // Only use left over padding if note evenly divisible by 
2076             // number of spaces.
2077             if (iterator->padding < iterator->padPerSpace){
2078                 width += iterator->padding;
2079                 iterator->padding = 0;
2080             }
2081             else {
2082                 width += iterator->padPerSpace;
2083                 iterator->padding -= iterator->padPerSpace;
2084             }
2085         }
2086         
2087         // Account for word-spacing.  We apply additional space between "words" by
2088         // adding width to the space character.
2089         if (currentCharacter > 0 && !isSpace(cp[-1]))
2090             width += iterator->style->wordSpacing;
2091     }
2092
2093     iterator->runWidthSoFar += width;
2094
2095     // Advance past the character we just dealt with.
2096     currentCharacter += clusterLength;
2097     iterator->currentCharacter = currentCharacter;
2098
2099     int len = run->to - run->from;
2100
2101     // Account for float/integer impedance mismatch between CG and khtml.  "Words" (characters 
2102     // followed by a character defined by isSpace()) are always an integer width.  We adjust the 
2103     // width of the last character of a "word" to ensure an integer width.  When we move khtml to
2104     // floats we can remove this (and related) hacks.
2105     //
2106     // Check to see if the next character is a "RoundingHackCharacter", if so, adjust.
2107     if (currentCharacter < run->length && isRoundingHackCharacter(cp[clusterLength]) && iterator->style->applyWordRounding) {
2108         width += ceilCurrentWidth(iterator);
2109     }
2110     else if (currentCharacter >= (unsigned)run->to && (len > 1 || run->length == 1) && iterator->style->applyRunRounding) {
2111         width += ceilCurrentWidth(iterator);
2112     }
2113     
2114     return width;
2115 }
2116
2117
2118 static BOOL fillStyleWithAttributes(ATSUStyle style, NSFont *theFont)
2119 {
2120     if (theFont) {
2121         ATSUFontID fontId = [theFont _atsFontID];
2122         LOG (FontCache, "fillStyleWithAttributes:  font = %p,%@, _atsFontID = %x\n", theFont, theFont, (unsigned)fontId);
2123         ATSUAttributeTag tag = kATSUFontTag;
2124         ByteCount size = sizeof(ATSUFontID);
2125         ATSUFontID *valueArray[1] = {&fontId};
2126         OSStatus status;
2127
2128         if (fontId) {
2129             status = ATSUSetAttributes(style, 1, &tag, &size, (void *)valueArray);
2130             if (status != noErr){
2131                 LOG (FontCache, "fillStyleWithAttributes failed(%d):  font = %p,%@, _atsFontID = %x\n", (int)status, theFont, theFont, (unsigned)fontId);
2132                 return NO;
2133             }
2134         }
2135         else {
2136             return NO;
2137         }
2138         return YES;
2139     }
2140     return NO;
2141 }
2142
2143 static BOOL shouldUseATSU(const WebCoreTextRun *run)
2144 {
2145     UniChar c;
2146     const UniChar *characters = run->characters;
2147     int i, from = run->from, to = run->to;
2148     
2149     if (alwaysUseATSU)
2150         return YES;
2151         
2152     for (i = from; i < to; i++){
2153         c = characters[i];
2154         if (c < 0x300)                      // Early continue to avoid other checks for the common case.
2155             continue;
2156             
2157         if (c >= 0x300 && c <= 0x36F)       // U+0300 through U+036F Combining diacritical marks
2158             return YES;
2159         if (c >= 0x20D0 && c <= 0x20FF)     // U+20D0 through U+20FF Combining marks for symbols
2160             return YES;
2161         if (c >= 0xFE20 && c <= 0xFE2f)     // U+FE20 through U+FE2F Combining half marks
2162             return YES;
2163         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
2164             return YES;
2165         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)
2166             return YES;
2167         if (c >= 0x1780 && c <= 0x18AF)     // U+1780 through U+18AF Khmer, Mongolian
2168             return YES;
2169         if (c >= 0x1900 && c <= 0x194F)     // U+1900 through U+194F Limbu (Unicode 4.0)
2170             return YES;
2171     }
2172     
2173     return NO;
2174 }