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