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