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