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