7e47991a50680b459d725e570265be995bbe175c
[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 == '\t' || 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, 1 /*\t*/, 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, float leadWidth, 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, style->xpos + startPosition, 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, style->xpos + startPosition + backgroundWidth, 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, completeRunWidth, 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, _totalWidth+style->xpos, &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 \n, \t, and nonbreaking space must render as a space.
1343         buffer['\n'] = ' ';
1344         buffer['\t'] = ' ';
1345         buffer[NO_BREAK_SPACE] = ' ';
1346     }
1347
1348     OSStatus status = WKInitializeGlyphVector(count, &glyphVector);
1349     if (status != noErr) {
1350         // This should never happen, perhaps indicates a bad font!  If it does the
1351         // font substitution code will find an alternate font.
1352         free(map);
1353         return 0;
1354     }
1355
1356     [self _convertCharacters: &buffer[0] length: count toGlyphs: &glyphVector];
1357     unsigned numGlyphs = WKGetGlyphVectorNumGlyphs(&glyphVector);
1358     if (numGlyphs != count){
1359         // This should never happen, perhaps indicates a bad font!  If it does the
1360         // font substitution code will find an alternate font.
1361         free(map);
1362         return 0;
1363     }
1364             
1365     map->glyphs = (GlyphEntry *)malloc (count * sizeof(GlyphEntry));
1366     glyphRecord = (ATSLayoutRecord *)WKGetGlyphVectorFirstRecord(glyphVector);
1367     for (i = 0; i < count; i++) {
1368         map->glyphs[i].glyph = glyphRecord->glyphID;
1369         map->glyphs[i].font = 0;
1370         glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + WKGetGlyphVectorRecordSize(glyphVector));
1371     }
1372     WKClearGlyphVector(&glyphVector);
1373     
1374     if (characterToGlyphMap == 0)
1375         characterToGlyphMap = map;
1376     else {
1377         GlyphMap *lastMap = characterToGlyphMap;
1378         while (lastMap->next != 0)
1379             lastMap = lastMap->next;
1380         lastMap->next = map;
1381     }
1382
1383     glyphID = map->glyphs[c - start].glyph;
1384     
1385     // Special case for characters 007F-00A0.
1386     if (glyphID == 0 && c >= 0x7F && c <= 0xA0){
1387         glyphID = WKGetDefaultGlyphForChar(font, c);
1388         map->glyphs[c - start].glyph = glyphID;
1389         map->glyphs[c - start].font = 0;
1390     }
1391
1392     return glyphID;
1393 }
1394
1395
1396 - (WidthMap *)_extendGlyphToWidthMapToInclude:(ATSGlyphRef)glyphID font:(NSFont *)subFont
1397 {
1398     WidthMap *map = (WidthMap *)calloc (1, sizeof(WidthMap)), **rootMap;
1399     unsigned end;
1400     ATSGlyphRef start;
1401     unsigned blockSize;
1402     unsigned i, count;
1403     
1404     if (subFont && subFont != font)
1405         rootMap = &mapForSubstituteFont(self,subFont)->map;
1406     else
1407         rootMap = &glyphToWidthMap;
1408         
1409     if (*rootMap == 0){
1410         if ([(subFont ? subFont : font) numberOfGlyphs] < INITIAL_BLOCK_SIZE)
1411             blockSize = [font numberOfGlyphs];
1412          else
1413             blockSize = INITIAL_BLOCK_SIZE;
1414     }
1415     else
1416         blockSize = INCREMENTAL_BLOCK_SIZE;
1417     start = (glyphID / blockSize) * blockSize;
1418     end = ((unsigned)start) + blockSize; 
1419     if (end > 0xffff)
1420         end = 0xffff;
1421
1422     LOG(FontCache, "%@ (0x%04x) adding widths for range 0x%04x to 0x%04x", font, glyphID, start, end);
1423
1424     map->startRange = start;
1425     map->endRange = end;
1426     count = end - start + 1;
1427
1428     map->widths = (WidthEntry *)malloc (count * sizeof(WidthEntry));
1429
1430     for (i = 0; i < count; i++){
1431         map->widths[i].width = UNINITIALIZED_GLYPH_WIDTH;
1432     }
1433
1434     if (*rootMap == 0)
1435         *rootMap = map;
1436     else {
1437         WidthMap *lastMap = *rootMap;
1438         while (lastMap->next != 0)
1439             lastMap = lastMap->next;
1440         lastMap->next = map;
1441     }
1442
1443 #ifdef _TIMING
1444     LOG(FontCache, "%@ total time to advances lookup %f seconds", font, totalCGGetAdvancesTime);
1445 #endif
1446     return map;
1447 }
1448
1449
1450 - (void)_initializeATSUStyle
1451 {
1452     // The two NSFont calls in this method (pointSize and _atsFontID)
1453     // are both exception-safe.
1454
1455     if (!ATSUStyleInitialized){
1456         OSStatus status;
1457         
1458         status = ATSUCreateStyle(&_ATSUSstyle);
1459         if(status != noErr)
1460             FATAL_ALWAYS ("ATSUCreateStyle failed (%d)", status);
1461     
1462         ATSUFontID fontID = WKGetNSFontATSUFontId(font);
1463         if (fontID == 0){
1464             ATSUDisposeStyle(_ATSUSstyle);
1465             ERROR ("unable to get ATSUFontID for %@", font);
1466             return;
1467         }
1468         
1469         CGAffineTransform transform = CGAffineTransformMakeScale (1,-1);
1470         Fixed fontSize = FloatToFixed([font pointSize]);
1471         ATSUAttributeTag styleTags[] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag};
1472         ByteCount styleSizes[] = {  sizeof(Fixed), sizeof(ATSUFontID), sizeof(CGAffineTransform) };
1473         ATSUAttributeValuePtr styleValues[] = { &fontSize, &fontID, &transform  };
1474         status = ATSUSetAttributes (_ATSUSstyle, 3, styleTags, styleSizes, styleValues);
1475         if(status != noErr)
1476             FATAL_ALWAYS ("ATSUSetAttributes failed (%d)", status);
1477
1478         ATSUStyleInitialized = YES;
1479     }
1480 }
1481
1482 - (ATSUTextLayout)_createATSUTextLayoutForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style
1483 {
1484     // The only Cocoa calls here are to NSGraphicsContext and the self
1485     // call to _initializeATSUStyle, which are all exception-safe.
1486
1487     ATSUTextLayout layout;
1488     UniCharCount runLength;
1489     OSStatus status;
1490     
1491     [self _initializeATSUStyle];
1492     
1493     // FIXME: This is missing the following features that the CoreGraphics code path has:
1494     // - Both \n and nonbreaking space render as a space.
1495     // - All other control characters must not render at all (other code path uses zero-width spaces).
1496
1497     runLength = run->to - run->from;
1498     status = ATSUCreateTextLayoutWithTextPtr(
1499             run->characters,
1500             run->from,           // offset
1501             runLength,        // length
1502             run->length,         // total length
1503             1,              // styleRunCount
1504             &runLength,    // length of style run
1505             &_ATSUSstyle, 
1506             &layout);
1507     if(status != noErr)
1508         FATAL_ALWAYS ("ATSUCreateTextLayoutWithTextPtr failed(%d)", status);
1509
1510     CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
1511     ATSLineLayoutOptions lineLayoutOptions = (kATSLineFractDisable | kATSLineDisableAutoAdjustDisplayPos | kATSLineUseDeviceMetrics |
1512                                               kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers);
1513     Boolean rtl = style->rtl;
1514     ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineLayoutOptionsTag, kATSULineDirectionTag };
1515     ByteCount sizes[] = { sizeof(CGContextRef), sizeof(ATSLineLayoutOptions), sizeof(Boolean)  };
1516     ATSUAttributeValuePtr values[] = { &cgContext, &lineLayoutOptions, &rtl };
1517     
1518     status = ATSUSetLayoutControls(layout, 3, tags, sizes, values);
1519     if(status != noErr)
1520         FATAL_ALWAYS ("ATSUSetLayoutControls failed(%d)", status);
1521
1522     status = ATSUSetTransientFontMatching (layout, YES);
1523     if(status != noErr)
1524         FATAL_ALWAYS ("ATSUSetTransientFontMatching failed(%d)", status);
1525         
1526     return layout;
1527 }
1528
1529
1530 - (ATSTrapezoid)_trapezoidForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style atPoint:(NSPoint )p
1531 {
1532     // The only Cocoa call here is the self call to
1533     // _createATSUTextLayoutForRun:, which is exception-safe.
1534
1535     OSStatus status;
1536     
1537     if (run->to - run->from <= 0){
1538         ATSTrapezoid nilTrapezoid = { {0,0} , {0,0}, {0,0}, {0,0} };
1539         return nilTrapezoid;
1540     }
1541         
1542     ATSUTextLayout layout = [self _createATSUTextLayoutForRun:run style:style];
1543
1544     ATSTrapezoid firstGlyphBounds;
1545     ItemCount actualNumBounds;
1546     status = ATSUGetGlyphBounds (layout, FloatToFixed(p.x), FloatToFixed(p.y), run->from, run->to - run->from, kATSUseDeviceOrigins, 1, &firstGlyphBounds, &actualNumBounds);    
1547     if(status != noErr)
1548         FATAL_ALWAYS ("ATSUGetGlyphBounds() failed(%d)", status);
1549     
1550     if (actualNumBounds != 1)
1551         FATAL_ALWAYS ("unexpected result from ATSUGetGlyphBounds():  actualNumBounds(%d) != 1", actualNumBounds);
1552
1553     ATSUDisposeTextLayout (layout); // Ignore the error.  Nothing we can do anyway.
1554             
1555     return firstGlyphBounds;
1556 }
1557
1558
1559 - (float)_ATSU_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style
1560 {
1561     ATSTrapezoid oGlyphBounds;
1562     
1563     oGlyphBounds = [self _trapezoidForRun:run style:style atPoint:NSMakePoint (0,0)];
1564     
1565     float width = 
1566         MAX(FixedToFloat(oGlyphBounds.upperRight.x), FixedToFloat(oGlyphBounds.lowerRight.x)) - 
1567         MIN(FixedToFloat(oGlyphBounds.upperLeft.x), FixedToFloat(oGlyphBounds.lowerLeft.x));
1568     
1569     return width;
1570 }
1571
1572 // Be sure to free the run.characters allocated by this function.
1573 static WebCoreTextRun reverseCharactersInRun(const WebCoreTextRun *run)
1574 {
1575     WebCoreTextRun swappedRun;
1576     
1577     UniChar *swappedCharacters = (UniChar *)malloc(sizeof(UniChar)*(run->length+2));
1578     memcpy(swappedCharacters+1, run->characters, sizeof(UniChar)*run->length);
1579     swappedRun.from = run->from;
1580     swappedRun.to = (run->to == -1 ? -1 : run->to+2);
1581     swappedRun.length = run->length+2;
1582     swappedCharacters[(swappedRun.from == -1 ? 0 : swappedRun.from)] = LEFT_TO_RIGHT_OVERRIDE;
1583     swappedCharacters[(swappedRun.to == -1 ? swappedRun.length : (unsigned)swappedRun.to) - 1] = POP_DIRECTIONAL_FORMATTING;
1584     swappedRun.characters = swappedCharacters;
1585
1586     return swappedRun;
1587 }
1588
1589 - (void)_ATSU_drawHighlightForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry
1590 {
1591     // The only Cocoa calls made here are to NSColor and NSBezierPath,
1592     // plus the self calls to _createATSUTextLayoutForRun: and
1593     // _trapezoidForRun:. These are all exception-safe.
1594
1595     ATSUTextLayout layout;
1596     int from, to;
1597     float selectedLeftX;
1598     const WebCoreTextRun *aRun = run;
1599     WebCoreTextRun swappedRun;
1600
1601     if (style->backgroundColor == nil)
1602         return;
1603     
1604     if (style->visuallyOrdered) {
1605         swappedRun = reverseCharactersInRun(run);
1606         aRun = &swappedRun;
1607     }
1608
1609     from = aRun->from;
1610     to = aRun->to;
1611     if (from == -1)
1612         from = 0;
1613     if (to == -1)
1614         to = run->length;
1615    
1616     int runLength = to - from;
1617     if (runLength <= 0){
1618         return;
1619     }
1620
1621     layout = [self _createATSUTextLayoutForRun:aRun style:style];
1622
1623     WebCoreTextRun leadingRun = *aRun;
1624     leadingRun.from = 0;
1625     leadingRun.to = run->from;
1626     
1627     // ATSU provides the bounds of the glyphs for the run with an origin of
1628     // (0,0), so we need to find the width of the glyphs immediately before
1629     // the actually selected glyphs.
1630     ATSTrapezoid leadingTrapezoid = [self _trapezoidForRun:&leadingRun style:style atPoint:geometry->point];
1631     ATSTrapezoid selectedTrapezoid = [self _trapezoidForRun:run style:style atPoint:geometry->point];
1632
1633     float backgroundWidth = 
1634             MAX(FixedToFloat(selectedTrapezoid.upperRight.x), FixedToFloat(selectedTrapezoid.lowerRight.x)) - 
1635             MIN(FixedToFloat(selectedTrapezoid.upperLeft.x), FixedToFloat(selectedTrapezoid.lowerLeft.x));
1636
1637     if (run->from == 0)
1638         selectedLeftX = geometry->point.x;
1639     else
1640         selectedLeftX = MIN(FixedToFloat(leadingTrapezoid.upperRight.x), FixedToFloat(leadingTrapezoid.lowerRight.x));
1641     
1642     [style->backgroundColor set];
1643
1644     float yPos = geometry->useFontMetricsForSelectionYAndHeight ? geometry->point.y - [self ascent] : geometry->selectionY;
1645     float height = geometry->useFontMetricsForSelectionYAndHeight ? [self lineSpacing] : geometry->selectionHeight;
1646     if (style->rtl || style->visuallyOrdered){
1647         WebCoreTextRun completeRun = *aRun;
1648         completeRun.from = 0;
1649         completeRun.to = aRun->length;
1650         float completeRunWidth = [self floatWidthForRun:&completeRun style:style widths:0];
1651         [NSBezierPath fillRect:NSMakeRect(geometry->point.x + completeRunWidth - (selectedLeftX-geometry->point.x) - backgroundWidth, yPos, backgroundWidth, height)];
1652     }
1653     else {
1654         [NSBezierPath fillRect:NSMakeRect(selectedLeftX, yPos, backgroundWidth, height)];
1655     }
1656
1657     ATSUDisposeTextLayout (layout); // Ignore the error.  Nothing we can do anyway.
1658
1659     if (style->visuallyOrdered)
1660         free ((void *)swappedRun.characters);
1661 }
1662
1663
1664 - (void)_ATSU_drawRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style geometry:(const WebCoreTextGeometry *)geometry
1665 {
1666     // The only Cocoa calls made here are to NSColor and NSGraphicsContext, plus the self
1667     // calls to _createATSUTextLayoutForRun: and
1668     // _ATSU_drawHighlightForRun:. These are all exception-safe.
1669
1670     ATSUTextLayout layout;
1671     OSStatus status;
1672     int from, to;
1673     const WebCoreTextRun *aRun = run;
1674     WebCoreTextRun swappedRun;
1675     
1676     if (style->visuallyOrdered) {
1677         swappedRun = reverseCharactersInRun(run);
1678         aRun = &swappedRun;
1679     }
1680
1681     from = aRun->from;
1682     to = aRun->to;
1683     if (from == -1)
1684         from = 0;
1685     if (to == -1)
1686         to = run->length;
1687
1688     int runLength = to - from;
1689     if (runLength <= 0)
1690         return;
1691
1692     layout = [self _createATSUTextLayoutForRun:aRun style:style];
1693
1694     if (style->backgroundColor != nil)
1695         [self _ATSU_drawHighlightForRun:run style:style geometry:geometry];
1696
1697     [style->textColor set];
1698
1699     // ATSUI can't draw beyond -32768 to +32767 so we translate the CTM and tell ATSUI to draw at (0, 0).
1700     CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
1701     CGContextTranslateCTM(context, geometry->point.x, geometry->point.y);
1702     status = ATSUDrawText(layout, aRun->from, runLength, 0, 0);
1703     CGContextTranslateCTM(context, -geometry->point.x, -geometry->point.y);
1704
1705     if (status != noErr){
1706         // Nothing to do but report the error (dev build only).
1707         ERROR ("ATSUDrawText() failed(%d)", status);
1708     }
1709
1710     ATSUDisposeTextLayout (layout); // Ignore the error.  Nothing we can do anyway.
1711     
1712     if (style->visuallyOrdered)
1713         free ((void *)swappedRun.characters);
1714 }
1715
1716 - (int)_ATSU_pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed includePartialGlyphs:(BOOL)includePartialGlyphs
1717 {
1718     // The only Cocoa calls made here is to the self call to
1719     // _createATSUTextLayoutForRun:. This is exception-safe.
1720
1721     unsigned offset = 0;
1722     ATSUTextLayout layout;
1723     UniCharArrayOffset primaryOffset = 0;
1724     UniCharArrayOffset secondaryOffset = 0;
1725     OSStatus status;
1726     Boolean isLeading;
1727     const WebCoreTextRun *aRun = run;
1728     WebCoreTextRun swappedRun;
1729     
1730     // Enclose in LRO-PDF to force ATSU to render visually.
1731     if (style->visuallyOrdered) {
1732         swappedRun = reverseCharactersInRun(run);
1733         aRun = &swappedRun;
1734     }
1735
1736     layout = [self _createATSUTextLayoutForRun:aRun style:style];
1737
1738     primaryOffset = aRun->from;
1739     
1740     // FIXME: No idea how to avoid including partial glyphs.   Not even sure if that's the behavior
1741     // this yields now.
1742     status = ATSUPositionToOffset(layout, FloatToFixed(x), FloatToFixed(-1), &primaryOffset, &isLeading, &secondaryOffset);
1743     if (status == noErr){
1744         offset = (unsigned)primaryOffset;
1745     }
1746     else {
1747         // Failed to find offset!  Return 0 offset.
1748     }
1749        
1750     if (style->visuallyOrdered) {
1751         free ((void *)swappedRun.characters);
1752     }
1753
1754     return offset - aRun->from;
1755 }
1756
1757 - (int)_CG_pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed includePartialGlyphs:(BOOL)includePartialGlyphs
1758 {
1759     float delta = (float)x;
1760     float width;   ///  FIX: CHECK THIS
1761     float leadWidth = style->xpos;
1762     unsigned offset = run->from;
1763     CharacterWidthIterator widthIterator;
1764     
1765     initializeCharacterWidthIterator(&widthIterator, self, run, style);
1766
1767     if (reversed) {
1768         width = [self floatWidthForRun:run style:style widths:nil];
1769         delta -= width;
1770         while (offset < run->length) {
1771             float w = widthForNextCharacter(&widthIterator, leadWidth, 0, 0);
1772             leadWidth += w;
1773             if (w == INVALID_WIDTH) {
1774                 // Something very bad happened, like we only have half of a surrogate pair.
1775                 break;
1776             }
1777             else {
1778                 if (w) {
1779                     if (includePartialGlyphs)
1780                        w -= w/2;
1781                     delta += w;
1782                     if(delta >= 0)
1783                         break;
1784                     if (includePartialGlyphs)
1785                         delta += w;
1786                 }
1787                 offset = widthIterator.currentCharacter;
1788             }
1789         }
1790     } else {
1791         while (offset < run->length) {
1792             float w = widthForNextCharacter(&widthIterator, leadWidth, 0, 0);
1793             leadWidth += w;
1794             if (w == INVALID_WIDTH) {
1795                 // Something very bad happened, like we only have half of a surrogate pair.
1796                 break;
1797             }
1798             else {
1799                 if (w) {
1800                     if (includePartialGlyphs)
1801                         w -= w/2;
1802                     delta -= w;
1803                     if(delta <= 0) 
1804                         break;
1805                     if (includePartialGlyphs)
1806                         delta -= w;
1807                 }
1808                 offset = widthIterator.currentCharacter;
1809             }
1810         }
1811     }
1812     
1813     return offset - run->from;
1814 }
1815
1816 @end
1817
1818 // ------------------- Private functions -------------------
1819
1820 static void freeWidthMap(WidthMap *map)
1821 {
1822     while (map) {
1823         WidthMap *next = map->next;
1824         free(map->widths);
1825         free(map);
1826         map = next;
1827     }
1828 }
1829
1830
1831 static void freeGlyphMap(GlyphMap *map)
1832 {
1833     while (map) {
1834         GlyphMap *next = map->next;
1835         free(map->glyphs);
1836         free(map);
1837         map = next;
1838     }
1839 }
1840
1841
1842 static void freeUnicodeGlyphMap(UnicodeGlyphMap *map)
1843 {
1844     while (map) {
1845         UnicodeGlyphMap *next = map->next;
1846         free(map->glyphs);
1847         free(map);
1848         map = next;
1849     }
1850 }
1851
1852
1853 static inline ATSGlyphRef glyphForCharacter (GlyphMap *map, UniChar c, NSFont **font)
1854 {
1855     if (map == 0)
1856         return nonGlyphID;
1857         
1858     while (map) {
1859         if (c >= map->startRange && c <= map->endRange){
1860             *font = map->glyphs[c-map->startRange].font;
1861             return map->glyphs[c-map->startRange].glyph;
1862         }
1863         map = map->next;
1864     }
1865     return nonGlyphID;
1866 }
1867  
1868  
1869 static inline ATSGlyphRef glyphForUnicodeCharacter (UnicodeGlyphMap *map, UnicodeChar c, NSFont **font)
1870 {
1871     if (map == 0)
1872         return nonGlyphID;
1873         
1874     while (map) {
1875         if (c >= map->startRange && c <= map->endRange){
1876             *font = map->glyphs[c-map->startRange].font;
1877             return map->glyphs[c-map->startRange].glyph;
1878         }
1879         map = map->next;
1880     }
1881     return nonGlyphID;
1882 }
1883  
1884
1885 #ifdef _TIMING        
1886 static double totalCGGetAdvancesTime = 0;
1887 #endif
1888
1889 static inline SubstituteFontWidthMap *mapForSubstituteFont(WebTextRenderer *renderer, NSFont *font)
1890 {
1891     int i;
1892     
1893     for (i = 0; i < renderer->numSubstituteFontWidthMaps; i++){
1894         if (font == renderer->substituteFontWidthMaps[i].font)
1895             return &renderer->substituteFontWidthMaps[i];
1896     }
1897     
1898     if (renderer->numSubstituteFontWidthMaps == renderer->maxSubstituteFontWidthMaps){
1899         renderer->maxSubstituteFontWidthMaps = renderer->maxSubstituteFontWidthMaps * 2;
1900         renderer->substituteFontWidthMaps = realloc (renderer->substituteFontWidthMaps, renderer->maxSubstituteFontWidthMaps * sizeof(SubstituteFontWidthMap));
1901         for (i = renderer->numSubstituteFontWidthMaps; i < renderer->maxSubstituteFontWidthMaps; i++){
1902             renderer->substituteFontWidthMaps[i].font = 0;
1903             renderer->substituteFontWidthMaps[i].map = 0;
1904         }
1905     }
1906     
1907     renderer->substituteFontWidthMaps[renderer->numSubstituteFontWidthMaps].font = font;
1908     return &renderer->substituteFontWidthMaps[renderer->numSubstituteFontWidthMaps++];
1909 }
1910
1911 static void initializeCharacterWidthIterator (CharacterWidthIterator *iterator, WebTextRenderer *renderer, const WebCoreTextRun *run , const WebCoreTextStyle *style) 
1912 {
1913     iterator->renderer = renderer;
1914     iterator->run = run;
1915     iterator->style = style;
1916     iterator->currentCharacter = run->from;
1917     iterator->runWidthSoFar = 0;
1918
1919     // If the padding is non-zero, count the number of spaces in the run
1920     // and divide that by the padding for per space addition.
1921     iterator->padding = style->padding;
1922     if (iterator->padding > 0){
1923         uint numSpaces = 0;
1924         int from = run->from;
1925         int len = run->to - from;
1926         int k;
1927         for (k = from; k < from + len; k++) {
1928             if (isSpace(run->characters[k])) {
1929                 numSpaces++;
1930             }
1931         }
1932         iterator->padPerSpace = CEIL_TO_INT ((((float)style->padding) / ((float)numSpaces)));
1933     }
1934     else {
1935         iterator->padPerSpace = 0;
1936     }
1937     
1938     // Calculate width up to starting position of the run.  This is
1939     // necessary to ensure that our rounding hacks are always consistently
1940     // applied.
1941     if (run->from != 0){
1942         WebCoreTextRun startPositionRun = *run;
1943         startPositionRun.from = 0;
1944         startPositionRun.to = run->from;
1945         CharacterWidthIterator startPositionIterator;
1946         initializeCharacterWidthIterator (&startPositionIterator, renderer, &startPositionRun, style);
1947         
1948         while (startPositionIterator.currentCharacter < (unsigned)startPositionRun.to){
1949             widthForNextCharacter(&startPositionIterator, startPositionIterator.runWidthSoFar+style->xpos, 0, 0);
1950         }
1951         iterator->widthToStart = startPositionIterator.runWidthSoFar;
1952     }
1953     else
1954         iterator->widthToStart = 0;
1955 }
1956
1957 static inline float ceilCurrentWidth (CharacterWidthIterator *iterator)
1958 {
1959     float delta = CEIL_TO_INT(iterator->widthToStart + iterator->runWidthSoFar) - (iterator->widthToStart + iterator->runWidthSoFar);
1960     iterator->runWidthSoFar += delta;
1961     return delta;
1962 }
1963
1964 // According to http://www.unicode.org/Public/UNIDATA/UCD.html#Canonical_Combining_Class_Values
1965 #define HIRAGANA_KATAKANA_VOICING_MARKS 8
1966
1967 // Return INVALID_WIDTH if an error is encountered or we're at the end of the range in the run.
1968 static float widthForNextCharacter(CharacterWidthIterator *iterator, float leadWidth, ATSGlyphRef *glyphUsed, NSFont **fontUsed)
1969 {
1970     WebTextRenderer *renderer = iterator->renderer;
1971     const WebCoreTextRun *run = iterator->run;
1972     const WebCoreTextStyle *style = iterator->style;
1973     unsigned currentCharacter = iterator->currentCharacter;
1974
1975     NSFont *_fontUsed = nil;
1976     ATSGlyphRef _glyphUsed;
1977
1978     if (!fontUsed)
1979         fontUsed = &_fontUsed;
1980     if (!glyphUsed)
1981         glyphUsed = &_glyphUsed;
1982         
1983     if (currentCharacter >= (unsigned)run->to)
1984         // Error! Offset specified beyond end of run.
1985         return INVALID_WIDTH;
1986
1987     const UniChar *cp = &run->characters[currentCharacter];
1988     
1989     UnicodeChar c = *cp;
1990
1991     if (U16_IS_TRAIL(c))
1992         return INVALID_WIDTH;
1993
1994     // Do we have a surrogate pair?  If so, determine the full Unicode (32 bit)
1995     // code point before glyph lookup.
1996     unsigned clusterLength = 1;
1997     if (U16_IS_LEAD(c)) {
1998         // Make sure we have another character and it's a low surrogate.
1999         UniChar low;
2000         if (currentCharacter + 1 >= run->length || !U16_IS_TRAIL((low = cp[1]))) {
2001             // Error!  The second component of the surrogate pair is missing.
2002             return INVALID_WIDTH;
2003         }
2004
2005         c = U16_GET_SUPPLEMENTARY(c, low);
2006         clusterLength = 2;
2007     }
2008
2009     // If small-caps convert lowercase to upper.
2010     BOOL useSmallCapsFont = NO;
2011     if (renderer->isSmallCapsRenderer) {
2012         if (!u_isUUppercase(c)) {
2013             // Only use small cap font if the the uppercase version of the character
2014             // is different than the lowercase.
2015             UnicodeChar newC = u_toupper(c);
2016             if (newC != c) {
2017                 useSmallCapsFont = YES;
2018                 c = newC;
2019             }
2020         }
2021     }
2022     
2023     // Deal with Hiragana and Katakana voiced and semi-voiced syllables.  Normalize into
2024     // composed form, and then look for glyph with base + combined mark.
2025     if (c >= 0x3041 && c <= 0x30FE) { // Early out to minimize performance impact.  Do we have a Hiragana/Katakana character?
2026         if (currentCharacter < (unsigned)run->to) {
2027             UnicodeChar nextCharacter = run->characters[currentCharacter+1];
2028             if (u_getCombiningClass(nextCharacter) == HIRAGANA_KATAKANA_VOICING_MARKS) {
2029                 UChar normalizedCharacters[2] = { 0, 0 };
2030                 UErrorCode uStatus = 0;
2031                 int32_t resultLength;
2032                 
2033                 // Normalize into composed form using 3.2 rules.
2034                 resultLength = unorm_normalize(&run->characters[currentCharacter], 2,
2035                                 UNORM_NFC, UNORM_UNICODE_3_2,
2036                                 &normalizedCharacters[0], 2,
2037                                 &uStatus);
2038                 if (resultLength == 1 && uStatus == 0){
2039                     c = normalizedCharacters[0];
2040                     clusterLength = 2;
2041                 }
2042             }
2043         }
2044     }
2045
2046     if (style->rtl) {
2047         c = u_charMirror(c);
2048     }
2049     
2050     if (c <= 0xFFFF) {
2051         *glyphUsed = glyphForCharacter(renderer->characterToGlyphMap, c, fontUsed);
2052         if (*glyphUsed == nonGlyphID) {
2053             *glyphUsed = [renderer _extendCharacterToGlyphMapToInclude:c];
2054         }
2055     } else {
2056         *glyphUsed = glyphForUnicodeCharacter(renderer->unicodeCharacterToGlyphMap, c, fontUsed);
2057         if (*glyphUsed == nonGlyphID) {
2058             *glyphUsed = [renderer _extendUnicodeCharacterToGlyphMapToInclude:c];
2059         }
2060     }
2061
2062     // Check to see if we're rendering in 'small-caps' mode.
2063     // ASSUMPTION:  We assume the same font in a smaller size has
2064     // the same glyphs as the large font.
2065     if (useSmallCapsFont) {
2066         if (*fontUsed == nil)
2067             *fontUsed = [renderer _smallCapsFont];
2068         else {
2069             // Potential for optimization.  This path should only be taken if we're
2070             // using a cached substituted font.
2071             *fontUsed = [[NSFontManager sharedFontManager] convertFont:*fontUsed toSize:[*fontUsed pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER];
2072         }
2073     }
2074
2075     // Now that we have glyph and font, get its width.
2076     WebGlyphWidth width;
2077     if (c == '\t' && style->tabWidth != 0) {
2078         width = style->tabWidth - (CEIL_TO_INT(leadWidth) % style->tabWidth);
2079     } else {
2080         width = widthForGlyph(renderer, *glyphUsed, *fontUsed);
2081         // We special case spaces in two ways when applying word rounding.
2082         // First, we round spaces to an adjusted width in all fonts.
2083         // Second, in fixed-pitch fonts we ensure that all characters that
2084         // match the width of the space character have the same width as the space character.
2085         if ((renderer->treatAsFixedPitch ? width == renderer->spaceWidth : *glyphUsed == renderer->spaceGlyph) && iterator->style->applyWordRounding)
2086             width = renderer->adjustedSpaceWidth;
2087     }
2088     
2089     // Try to find a substitute font if this font didn't have a glyph for a character in the
2090     // string.  If one isn't found we end up drawing and measuring the 0 glyph, usually a box.
2091     if (*glyphUsed == 0 && iterator->style->attemptFontSubstitution) {
2092         UniChar characterArray[2];
2093         unsigned characterArrayLength;
2094         
2095         if (c <= 0xFFFF) {
2096             characterArray[0] = c;
2097             characterArrayLength = 1;
2098         } else {
2099             characterArray[0] = U16_LEAD(c);
2100             characterArray[1] = U16_TRAIL(c);
2101             characterArrayLength = 2;
2102         }
2103         
2104         NSFont *substituteFont = [renderer _substituteFontForCharacters:characterArray length:characterArrayLength
2105             families:iterator->style->families];
2106         if (substituteFont) {
2107             int cNumGlyphs = 0;
2108             ATSGlyphRef localGlyphBuffer[MAX_GLYPH_EXPANSION];
2109             
2110             WebCoreTextRun clusterRun;
2111             WebCoreInitializeTextRun(&clusterRun, characterArray, characterArrayLength, 0, characterArrayLength);
2112             WebCoreTextStyle clusterStyle = *iterator->style;
2113             clusterStyle.padding = 0;
2114             clusterStyle.applyRunRounding = false;
2115             clusterStyle.attemptFontSubstitution = false;
2116             
2117             WebTextRenderer *substituteRenderer;
2118             substituteRenderer = [[WebTextRendererFactory sharedFactory] rendererWithFont:substituteFont usingPrinterFont:renderer->usingPrinterFont];
2119             width = [substituteRenderer
2120                             _floatWidthForRun:&clusterRun
2121                             style:&clusterStyle 
2122                             widths: nil
2123                             fonts: nil
2124                             glyphs: &localGlyphBuffer[0]
2125                             startPosition:nil
2126                             numGlyphs:&cNumGlyphs];
2127             
2128             *fontUsed = substituteFont;
2129             *glyphUsed = localGlyphBuffer[0];
2130             
2131             if (c <= 0xFFFF && cNumGlyphs == 1 && localGlyphBuffer[0] != 0){
2132                 [renderer _updateGlyphEntryForCharacter:c glyphID:localGlyphBuffer[0] font:substituteFont];
2133             }
2134         }
2135     }
2136
2137     if (!*fontUsed)
2138         *fontUsed = renderer->font;
2139
2140     // Force characters that are used to determine word boundaries for the rounding hack
2141     // to be integer width, so following words will start on an integer boundary.
2142     if (isRoundingHackCharacter(c) && iterator->style->applyWordRounding) {
2143         width = CEIL_TO_INT(width);
2144     }
2145     
2146     // Account for letter-spacing
2147     if (iterator->style->letterSpacing && width > 0)
2148         width += iterator->style->letterSpacing;
2149
2150     // Account for padding.  khtml uses space padding to justify text.  We
2151     // distribute the specified padding over the available spaces in the run.
2152     if (isSpace(c)) {
2153         if (iterator->padding > 0) {
2154             // Only use left over padding if note evenly divisible by 
2155             // number of spaces.
2156             if (iterator->padding < iterator->padPerSpace){
2157                 width += iterator->padding;
2158                 iterator->padding = 0;
2159             }
2160             else {
2161                 width += iterator->padPerSpace;
2162                 iterator->padding -= iterator->padPerSpace;
2163             }
2164         }
2165         
2166         // Account for word-spacing.  We apply additional space between "words" by
2167         // adding width to the space character.
2168         if (currentCharacter > 0 && !isSpace(cp[-1]))
2169             width += iterator->style->wordSpacing;
2170     }
2171
2172     iterator->runWidthSoFar += width;
2173
2174     // Advance past the character we just dealt with.
2175     currentCharacter += clusterLength;
2176     iterator->currentCharacter = currentCharacter;
2177
2178     int len = run->to - run->from;
2179
2180     // Account for float/integer impedance mismatch between CG and khtml.  "Words" (characters 
2181     // followed by a character defined by isSpace()) are always an integer width.  We adjust the 
2182     // width of the last character of a "word" to ensure an integer width.  When we move khtml to
2183     // floats we can remove this (and related) hacks.
2184     //
2185     // Check to see if the next character is a "RoundingHackCharacter", if so, adjust.
2186     if (currentCharacter < run->length && isRoundingHackCharacter(cp[clusterLength]) && iterator->style->applyWordRounding) {
2187         width += ceilCurrentWidth(iterator);
2188     }
2189     else if (currentCharacter >= (unsigned)run->to && (len > 1 || run->length == 1) && iterator->style->applyRunRounding) {
2190         width += ceilCurrentWidth(iterator);
2191     }
2192     
2193     return width;
2194 }
2195
2196
2197 static BOOL fillStyleWithAttributes(ATSUStyle style, NSFont *theFont)
2198 {
2199     if (theFont) {
2200         ATSUFontID fontId = WKGetNSFontATSUFontId(theFont);
2201         LOG (FontCache, "fillStyleWithAttributes:  font = %p,%@, _atsFontID = %x\n", theFont, theFont, (unsigned)fontId);
2202         ATSUAttributeTag tag = kATSUFontTag;
2203         ByteCount size = sizeof(ATSUFontID);
2204         ATSUFontID *valueArray[1] = {&fontId};
2205         OSStatus status;
2206
2207         if (fontId) {
2208             status = ATSUSetAttributes(style, 1, &tag, &size, (void *)valueArray);
2209             if (status != noErr){
2210                 LOG (FontCache, "fillStyleWithAttributes failed(%d):  font = %p,%@, _atsFontID = %x\n", (int)status, theFont, theFont, (unsigned)fontId);
2211                 return NO;
2212             }
2213         }
2214         else {
2215             return NO;
2216         }
2217         return YES;
2218     }
2219     return NO;
2220 }
2221
2222 static BOOL shouldUseATSU(const WebCoreTextRun *run)
2223 {
2224     UniChar c;
2225     const UniChar *characters = run->characters;
2226     int i, from = run->from, to = run->to;
2227     
2228     if (alwaysUseATSU)
2229         return YES;
2230         
2231     for (i = from; i < to; i++){
2232         c = characters[i];
2233         if (c < 0x300)                      // Early continue to avoid other checks for the common case.
2234             continue;
2235             
2236         if (c >= 0x300 && c <= 0x36F)       // U+0300 through U+036F Combining diacritical marks
2237             return YES;
2238         if (c >= 0x20D0 && c <= 0x20FF)     // U+20D0 through U+20FF Combining marks for symbols
2239             return YES;
2240         if (c >= 0xFE20 && c <= 0xFE2f)     // U+FE20 through U+FE2F Combining half marks
2241             return YES;
2242         if (c >= 0x591 && c <= 0x1059)      // U+0591 through U+1059 Arabic, Hebrew, Syriac, Thaana, Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar
2243             return YES;
2244         if (c >= 0x1100 && c <= 0x11FF)     // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left here if you precompose; Modern Korean will be precomposed as a result of step A)
2245             return YES;
2246         if (c >= 0x1780 && c <= 0x18AF)     // U+1780 through U+18AF Khmer, Mongolian
2247             return YES;
2248         if (c >= 0x1900 && c <= 0x194F)     // U+1900 through U+194F Limbu (Unicode 4.0)
2249             return YES;
2250     }
2251     
2252     return NO;
2253 }