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