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