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