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