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