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