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