Bug 8880, remove the remaining drawing/hit testing code from
[WebKit-https.git] / WebCore / platform / mac / FontData.mm
1 /*
2  * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2006 Alexey Proskuryakov
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer. 
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution. 
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission. 
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #import "config.h"
31 #import "Font.h"
32 #import "FontData.h"
33 #import "Color.h"
34 #import "WebCoreTextRenderer.h"
35
36 #import <wtf/Assertions.h>
37
38 #import <ApplicationServices/ApplicationServices.h>
39 #import <Cocoa/Cocoa.h>
40
41 #import <WebTextRendererFactory.h>
42
43 #import "WebCoreSystemInterface.h"
44
45 #import "FloatRect.h"
46 #import "FontDescription.h"
47
48 #import <float.h>
49
50 #import <unicode/uchar.h>
51 #import <unicode/unorm.h>
52
53 // FIXME: Just temporary for the #defines of constants that we will eventually stop using.
54 #import "GlyphBuffer.h"
55
56 namespace WebCore
57 {
58
59 // FIXME: FATAL seems like a bad idea; lets stop using it.
60
61 #define SMALLCAPS_FONTSIZE_MULTIPLIER 0.7f
62 #define SYNTHETIC_OBLIQUE_ANGLE 14
63
64 // Should be more than enough for normal usage.
65 #define NUM_SUBSTITUTE_FONT_MAPS 10
66
67 #define SPACE 0x0020
68 #define NO_BREAK_SPACE 0x00A0
69 #define ZERO_WIDTH_SPACE 0x200B
70 #define POP_DIRECTIONAL_FORMATTING 0x202C
71 #define LEFT_TO_RIGHT_OVERRIDE 0x202D
72 #define RIGHT_TO_LEFT_OVERRIDE 0x202E
73
74 // MAX_GLYPH_EXPANSION is the maximum numbers of glyphs that may be
75 // use to represent a single Unicode code point.
76 #define MAX_GLYPH_EXPANSION 4
77 #define LOCAL_BUFFER_SIZE 2048
78
79 // Covers Latin-1.
80 #define INITIAL_BLOCK_SIZE 0x200
81
82 // Get additional blocks of glyphs and widths in bigger chunks.
83 // This will typically be for other character sets.
84 #define INCREMENTAL_BLOCK_SIZE 0x400
85
86 #define CONTEXT_DPI (72.0)
87 #define SCALE_EM_TO_UNITS(X, U_PER_EM) (X * ((1.0 * CONTEXT_DPI) / (CONTEXT_DPI * U_PER_EM)))
88
89 #define WKGlyphVectorSize (50 * 32)
90
91 typedef float WebGlyphWidth;
92
93 struct WidthMap {
94     WidthMap() :next(0), widths(0) {}
95
96     ATSGlyphRef startRange;
97     ATSGlyphRef endRange;
98     WidthMap *next;
99     WebGlyphWidth *widths;
100 };
101
102 typedef struct GlyphEntry {
103     Glyph glyph;
104     const FontData *renderer;
105 } GlyphEntry;
106
107 struct GlyphMap {
108     UChar startRange;
109     UChar endRange;
110     GlyphMap *next;
111     GlyphEntry *glyphs;
112 };
113
114 static const FontData *rendererForAlternateFont(const FontData *, FontPlatformData);
115
116 static WidthMap *extendWidthMap(const FontData *, ATSGlyphRef);
117 static ATSGlyphRef extendGlyphMap(const FontData *, UChar32);
118
119 static void freeWidthMap(WidthMap *);
120 static void freeGlyphMap(GlyphMap *);
121
122 static bool setUpFont(FontData *);
123
124 static bool fillStyleWithAttributes(ATSUStyle style, NSFont *theFont);
125
126 #if !ERROR_DISABLED
127 static NSString *pathFromFont(NSFont *font);
128 #endif
129
130 // Map utility functions
131
132 float FontData::widthForGlyph(Glyph glyph) const
133 {
134     WidthMap *map;
135     for (map = m_glyphToWidthMap; 1; map = map->next) {
136         if (!map)
137             map = extendWidthMap(this, glyph);
138         if (glyph >= map->startRange && glyph <= map->endRange)
139             break;
140     }
141     float width = map->widths[glyph - map->startRange];
142     if (width >= 0)
143         return width;
144     NSFont *font = m_font.font;
145     float pointSize = [font pointSize];
146     CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSize);
147     CGSize advance;
148     if (!wkGetGlyphTransformedAdvances(font, &m, &glyph, &advance)) {
149         LOG_ERROR("Unable to cache glyph widths for %@ %f", [font displayName], pointSize);
150         advance.width = 0;
151     }
152     width = advance.width + m_syntheticBoldOffset;
153     map->widths[glyph - map->startRange] = width;
154     return width;
155 }
156
157 static NSString *webFallbackFontFamily(void)
158 {
159     static NSString *webFallbackFontFamily = nil;
160     if (!webFallbackFontFamily)
161         webFallbackFontFamily = [[[NSFont systemFontOfSize:16.0] familyName] retain];
162     return webFallbackFontFamily;
163 }
164
165 FontData::FontData(const FontPlatformData& f)
166 :m_styleGroup(0), m_font(f), m_characterToGlyphMap(0), m_glyphToWidthMap(0), m_treatAsFixedPitch(false),
167  m_smallCapsFontData(0), m_ATSUStyleInitialized(false), m_ATSUMirrors(false)
168 {    
169     m_font = f;
170
171     m_syntheticBoldOffset = f.syntheticBold ? ceilf([f.font pointSize] / 24.0f) : 0.f;
172     
173     bool failedSetup = false;
174     if (!setUpFont(this)) {
175         // Ack! Something very bad happened, like a corrupt font.
176         // Try looking for an alternate 'base' font for this renderer.
177
178         // Special case hack to use "Times New Roman" in place of "Times".
179         // "Times RO" is a common font whose family name is "Times".
180         // It overrides the normal "Times" family font.
181         // It also appears to have a corrupt regular variant.
182         NSString *fallbackFontFamily;
183         if ([[m_font.font familyName] isEqual:@"Times"])
184             fallbackFontFamily = @"Times New Roman";
185         else
186             fallbackFontFamily = webFallbackFontFamily();
187         
188         // Try setting up the alternate font.
189         // This is a last ditch effort to use a substitute font when something has gone wrong.
190 #if !ERROR_DISABLED
191         NSFont *initialFont = m_font.font;
192 #endif
193         m_font.font = [[NSFontManager sharedFontManager] convertFont:m_font.font toFamily:fallbackFontFamily];
194 #if !ERROR_DISABLED
195         NSString *filePath = pathFromFont(initialFont);
196         if (!filePath)
197             filePath = @"not known";
198 #endif
199         if (!setUpFont(this)) {
200             if ([fallbackFontFamily isEqual:@"Times New Roman"]) {
201                 // OK, couldn't setup Times New Roman as an alternate to Times, fallback
202                 // on the system font.  If this fails we have no alternative left.
203                 m_font.font = [[NSFontManager sharedFontManager] convertFont:m_font.font toFamily:webFallbackFontFamily()];
204                 if (!setUpFont(this)) {
205                     // We tried, Times, Times New Roman, and the system font. No joy. We have to give up.
206                     LOG_ERROR("unable to initialize with font %@ at %@", initialFont, filePath);
207                     failedSetup = true;
208                 }
209             } else {
210                 // We tried the requested font and the system font. No joy. We have to give up.
211                 LOG_ERROR("unable to initialize with font %@ at %@", initialFont, filePath);
212                 failedSetup = true;
213             }
214         }
215
216         // Report the problem.
217         LOG_ERROR("Corrupt font detected, using %@ in place of %@ located at \"%@\".",
218             [m_font.font familyName], [initialFont familyName], filePath);
219     }
220
221     // If all else fails, try to set up using the system font.
222     // This is probably because Times and Times New Roman are both unavailable.
223     if (failedSetup) {
224         m_font.font = [NSFont systemFontOfSize:[m_font.font pointSize]];
225         LOG_ERROR("failed to set up font, using system font %s", m_font.font);
226         setUpFont(this);
227     }
228     
229     int iAscent;
230     int iDescent;
231     int iLineGap;
232     unsigned unitsPerEm;
233     wkGetFontMetrics(m_font.font, &iAscent, &iDescent, &iLineGap, &unitsPerEm); 
234     float pointSize = [m_font.font pointSize];
235     float fAscent = SCALE_EM_TO_UNITS(iAscent, unitsPerEm) * pointSize;
236     float fDescent = -SCALE_EM_TO_UNITS(iDescent, unitsPerEm) * pointSize;
237     float fLineGap = SCALE_EM_TO_UNITS(iLineGap, unitsPerEm) * pointSize;
238
239     // We need to adjust Times, Helvetica, and Courier to closely match the
240     // vertical metrics of their Microsoft counterparts that are the de facto
241     // web standard. The AppKit adjustment of 20% is too big and is
242     // incorrectly added to line spacing, so we use a 15% adjustment instead
243     // and add it to the ascent.
244     NSString *familyName = [m_font.font familyName];
245     if ([familyName isEqualToString:@"Times"] || [familyName isEqualToString:@"Helvetica"] || [familyName isEqualToString:@"Courier"])
246         fAscent += floorf(((fAscent + fDescent) * 0.15f) + 0.5f);
247
248     m_ascent = lroundf(fAscent);
249     m_descent = lroundf(fDescent);
250     m_lineGap = lroundf(fLineGap);
251
252     m_lineSpacing = m_ascent + m_descent + m_lineGap;
253
254     [m_font.font retain];
255 }
256
257 FontData::~FontData()
258 {
259     if (m_styleGroup)
260         wkReleaseStyleGroup(m_styleGroup);
261
262     freeWidthMap(m_glyphToWidthMap);
263     freeGlyphMap(m_characterToGlyphMap);
264
265     if (m_ATSUStyleInitialized)
266         ATSUDisposeStyle(m_ATSUStyle);
267         
268     [m_font.font release];
269     
270     // We only get deleted when the cache gets cleared.  Since the smallCapsRenderer is also in that cache,
271     // it will be deleted then, so we don't need to do anything here.
272 }
273
274 float FontData::xHeight() const
275 {
276     // Measure the actual character "x", because AppKit synthesizes X height rather than getting it from the font.
277     // Unfortunately, NSFont will round this for us so we don't quite get the right value.
278     const FontData *renderer = [[WebTextRendererFactory sharedFactory] rendererWithFont:m_font];
279     NSGlyph xGlyph = glyphForCharacter(&renderer, 'x');
280     if (xGlyph) {
281         NSRect xBox = [m_font.font boundingRectForGlyph:xGlyph];
282         // Use the maximum of either width or height because "x" is nearly square
283         // and web pages that foolishly use this metric for width will be laid out
284         // poorly if we return an accurate height. Classic case is Times 13 point,
285         // which has an "x" that is 7x6 pixels.
286         return MAX(NSMaxX(xBox), NSMaxY(xBox));
287     }
288
289     return [m_font.font xHeight];
290 }
291
292 FontData* FontData::smallCapsFontData() const
293 {
294     if (!m_smallCapsFontData) {
295         NS_DURING
296             float size = [m_font.font pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER;
297             FontPlatformData smallCapsFont([[NSFontManager sharedFontManager] convertFont:m_font.font toSize:size]);
298             m_smallCapsFontData = (FontData*)rendererForAlternateFont(this, smallCapsFont);
299         NS_HANDLER
300             NSLog(@"uncaught exception selecting font for small caps: %@", localException);
301         NS_ENDHANDLER
302     }
303     return m_smallCapsFontData;
304 }
305
306 static inline bool fontContainsString(NSFont *font, NSString *string)
307 {
308     NSCharacterSet *set = [[font coveredCharacterSet] invertedSet];
309     return set && [string rangeOfCharacterFromSet:set].location == NSNotFound;
310 }
311
312 static NSFont *findSubstituteFont(const FontData *renderer, NSString *string, NSString **families)
313 {
314     NSFont *substituteFont = nil;
315
316     // First search the CSS family fallback list.
317     // Start at 1 (2nd font) because we've already failed on the first lookup.
318     NSString *family = nil;
319     int i = 1;
320     while (families && families[i]) {
321         family = families[i++];
322         NSFont *f = [[WebTextRendererFactory sharedFactory] cachedFontFromFamily:family
323             traits:[[NSFontManager sharedFontManager] traitsOfFont:renderer->m_font.font]
324             size:[renderer->m_font.font pointSize]];
325         if (f && fontContainsString(f, string)) {
326             substituteFont = f; 
327             break;
328         }
329     }
330     
331     // Now do string based lookup.
332     if (substituteFont == nil)
333         substituteFont = wkGetFontInLanguageForRange(renderer->m_font.font, string, NSMakeRange(0, [string length]));
334
335     // Now do character based lookup.
336     if (substituteFont == nil && [string length] == 1)
337         substituteFont = wkGetFontInLanguageForCharacter(renderer->m_font.font, [string characterAtIndex:0]);
338
339     // Check to make sure this is a distinct font.
340     if (substituteFont && [[substituteFont screenFont] isEqual:[renderer->m_font.font screenFont]])
341         substituteFont = nil;
342
343     // Now that we have a substitute font, attempt to match it to the best variation.
344     // If we have a good match return that, otherwise return the font the AppKit has found.
345     if (substituteFont) {
346         NSFontManager *manager = [NSFontManager sharedFontManager];
347         NSFont *bestVariation = [manager fontWithFamily:[substituteFont familyName]
348             traits:[manager traitsOfFont:renderer->m_font.font]
349             weight:[manager weightOfFont:renderer->m_font.font]
350             size:[renderer->m_font.font pointSize]];
351         if (bestVariation)
352             substituteFont = bestVariation;
353     }
354
355     return substituteFont;
356 }
357
358 static const FontData *rendererForAlternateFont(const FontData *renderer, FontPlatformData alternateFont)
359 {
360     if (!alternateFont.font)
361         return nil;
362
363     NSFontManager *fontManager = [NSFontManager sharedFontManager];
364     NSFontTraitMask fontTraits = [fontManager traitsOfFont:renderer->m_font.font];
365     if (renderer->m_font.syntheticBold)
366         fontTraits |= NSBoldFontMask;
367     if (renderer->m_font.syntheticOblique)
368         fontTraits |= NSItalicFontMask;
369     NSFontTraitMask alternateFontTraits = [fontManager traitsOfFont:alternateFont.font];
370
371     alternateFont.syntheticBold = (fontTraits & NSBoldFontMask) && !(alternateFontTraits & NSBoldFontMask);
372     alternateFont.syntheticOblique = (fontTraits & NSItalicFontMask) && !(alternateFontTraits & NSItalicFontMask);
373     alternateFont.forPrinter = renderer->m_font.forPrinter;
374
375     return [[WebTextRendererFactory sharedFactory] rendererWithFont:alternateFont];
376 }
377
378 static const FontData *findSubstituteRenderer(const FontData *renderer, const unichar *characters, int numCharacters, NSString **families)
379 {
380     NSString *string = [[NSString alloc] initWithCharactersNoCopy:(unichar *)characters length: numCharacters freeWhenDone: NO];
381     FontPlatformData substituteFont(findSubstituteFont(renderer, string, families));
382     [string release];
383     return rendererForAlternateFont(renderer, substituteFont);
384 }
385
386 const FontData* FontData::findSubstituteFontData(const UChar* characters, unsigned numCharacters, const FontDescription& fontDescription) const
387 {
388     CREATE_FAMILY_ARRAY(fontDescription, families);
389     return findSubstituteRenderer(this, (const unichar*)characters, numCharacters, families);
390 }
391
392 // Nasty hack to determine if we should round or ceil space widths.
393 // If the font is monospace or fake monospace we ceil to ensure that 
394 // every character and the space are the same width.  Otherwise we round.
395 static bool computeWidthForSpace(FontData *renderer)
396 {
397     renderer->m_spaceGlyph = extendGlyphMap(renderer, SPACE);
398     if (renderer->m_spaceGlyph == 0)
399         return NO;
400
401     float width = renderer->widthForGlyph(renderer->m_spaceGlyph);
402
403     renderer->m_spaceWidth = width;
404
405     renderer->m_treatAsFixedPitch = [[WebTextRendererFactory sharedFactory] isFontFixedPitch:renderer->m_font];
406     renderer->m_adjustedSpaceWidth = renderer->m_treatAsFixedPitch ? ceilf(width) : roundf(width);
407     
408     return YES;
409 }
410
411 static bool setUpFont(FontData *renderer)
412 {
413     renderer->m_font.font = renderer->m_font.forPrinter ? [renderer->m_font.font printerFont] : [renderer->m_font.font screenFont];
414
415     ATSUStyle fontStyle;
416     if (ATSUCreateStyle(&fontStyle) != noErr)
417         return NO;
418
419     if (!fillStyleWithAttributes(fontStyle, renderer->m_font.font)) {
420         ATSUDisposeStyle(fontStyle);
421         return NO;
422     }
423
424     if (wkGetATSStyleGroup(fontStyle, &renderer->m_styleGroup) != noErr) {
425         ATSUDisposeStyle(fontStyle);
426         return NO;
427     }
428
429     ATSUDisposeStyle(fontStyle);
430
431     if (!computeWidthForSpace(renderer)) {
432         freeGlyphMap(renderer->m_characterToGlyphMap);
433         renderer->m_characterToGlyphMap = 0;
434         wkReleaseStyleGroup(renderer->m_styleGroup);
435         renderer->m_styleGroup = 0;
436         return NO;
437     }
438     
439     return YES;
440 }
441
442 #if !ERROR_DISABLED
443
444 static NSString *pathFromFont(NSFont *font)
445 {
446     FSSpec oFile;
447     OSStatus status = ATSFontGetFileSpecification(FMGetATSFontRefFromFont((FMFont)wkGetNSFontATSUFontId(font)), &oFile);
448     if (status == noErr) {
449         OSErr err;
450         FSRef fileRef;
451         err = FSpMakeFSRef(&oFile, &fileRef);
452         if (err == noErr) {
453             UInt8 filePathBuffer[PATH_MAX];
454             status = FSRefMakePath(&fileRef, filePathBuffer, PATH_MAX);
455             if (status == noErr)
456                 return [NSString stringWithUTF8String:(const char *)filePathBuffer];
457         }
458     }
459     return nil;
460 }
461
462 #endif
463
464 void FontData::updateGlyphMapEntry(UChar c, ATSGlyphRef glyph, const FontData *substituteRenderer) const
465 {
466     GlyphMap *map;
467     for (map = m_characterToGlyphMap; map; map = map->next) {
468         UChar32 start = map->startRange;
469         if (c >= start && c <= map->endRange) {
470             int i = c - start;
471             map->glyphs[i].glyph = glyph;
472             // This renderer will leak.
473             // No problem though; we want it to stick around forever.
474             // Max theoretical retain counts applied here will be num_fonts_on_system * num_glyphs_in_font.
475             map->glyphs[i].renderer = substituteRenderer;
476             break;
477         }
478     }
479 }
480
481 static ATSGlyphRef extendGlyphMap(const FontData *renderer, UChar32 c)
482 {
483     GlyphMap *map = new GlyphMap;
484     ATSLayoutRecord *glyphRecord;
485     char glyphVector[WKGlyphVectorSize];
486     UChar32 end, start;
487     unsigned blockSize;
488     
489     if (renderer->m_characterToGlyphMap == 0)
490         blockSize = INITIAL_BLOCK_SIZE;
491     else
492         blockSize = INCREMENTAL_BLOCK_SIZE;
493     start = (c / blockSize) * blockSize;
494     end = start + (blockSize - 1);
495
496     map->startRange = start;
497     map->endRange = end;
498     map->next = 0;
499     
500     unsigned i;
501     unsigned count = end - start + 1;
502     unsigned short buffer[INCREMENTAL_BLOCK_SIZE * 2 + 2];
503     unsigned bufferLength;
504
505     if (start < 0x10000) {
506         bufferLength = count;
507         for (i = 0; i < count; i++)
508             buffer[i] = i + start;
509
510         if (start == 0) {
511             // Control characters must not render at all.
512             for (i = 0; i < 0x20; ++i)
513                 buffer[i] = ZERO_WIDTH_SPACE;
514             buffer[0x7F] = ZERO_WIDTH_SPACE;
515
516             // But \n, \t, and nonbreaking space must render as a space.
517             buffer[(int)'\n'] = ' ';
518             buffer[(int)'\t'] = ' ';
519             buffer[NO_BREAK_SPACE] = ' ';
520         }
521     } else {
522         bufferLength = count * 2;
523         for (i = 0; i < count; i++) {
524             int c = i + start;
525             buffer[i * 2] = U16_LEAD(c);
526             buffer[i * 2 + 1] = U16_TRAIL(c);
527         }
528     }
529
530     OSStatus status = wkInitializeGlyphVector(count, &glyphVector);
531     if (status != noErr) {
532         // This should never happen, perhaps indicates a bad font!  If it does the
533         // font substitution code will find an alternate font.
534         delete map;
535         return 0;
536     }
537
538     wkConvertCharToGlyphs(renderer->m_styleGroup, &buffer[0], bufferLength, &glyphVector);
539     unsigned numGlyphs = wkGetGlyphVectorNumGlyphs(&glyphVector);
540     if (numGlyphs != count) {
541         // This should never happen, perhaps indicates a bad font?
542         // If it does happen, the font substitution code will find an alternate font.
543         wkClearGlyphVector(&glyphVector);
544         delete map;
545         return 0;
546     }
547
548     map->glyphs = new GlyphEntry[count];
549     glyphRecord = (ATSLayoutRecord *)wkGetGlyphVectorFirstRecord(glyphVector);
550     for (i = 0; i < count; i++) {
551         map->glyphs[i].glyph = glyphRecord->glyphID;
552         map->glyphs[i].renderer = renderer;
553         glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + wkGetGlyphVectorRecordSize(glyphVector));
554     }
555     wkClearGlyphVector(&glyphVector);
556     
557     if (renderer->m_characterToGlyphMap == 0)
558         renderer->m_characterToGlyphMap = map;
559     else {
560         GlyphMap *lastMap = renderer->m_characterToGlyphMap;
561         while (lastMap->next != 0)
562             lastMap = lastMap->next;
563         lastMap->next = map;
564     }
565
566     ATSGlyphRef glyph = map->glyphs[c - start].glyph;
567
568     // Special case for characters 007F-00A0.
569     if (glyph == 0 && c >= 0x7F && c <= 0xA0) {
570         glyph = wkGetDefaultGlyphForChar(renderer->m_font.font, c);
571         map->glyphs[c - start].glyph = glyph;
572     }
573
574     return glyph;
575 }
576
577 static WidthMap *extendWidthMap(const FontData *renderer, ATSGlyphRef glyph)
578 {
579     WidthMap *map = new WidthMap;
580     unsigned end;
581     ATSGlyphRef start;
582     unsigned blockSize;
583     unsigned i, count;
584     
585     NSFont *f = renderer->m_font.font;
586     if (renderer->m_glyphToWidthMap == 0) {
587         if ([f numberOfGlyphs] < INITIAL_BLOCK_SIZE)
588             blockSize = [f numberOfGlyphs];
589          else
590             blockSize = INITIAL_BLOCK_SIZE;
591     } else {
592         blockSize = INCREMENTAL_BLOCK_SIZE;
593     }
594     if (blockSize == 0) {
595         start = 0;
596     } else {
597         start = (glyph / blockSize) * blockSize;
598     }
599     end = ((unsigned)start) + blockSize; 
600
601     map->startRange = start;
602     map->endRange = end;
603     count = end - start + 1;
604
605     map->widths = new WebGlyphWidth[count];
606     for (i = 0; i < count; i++)
607         map->widths[i] = NAN;
608
609     if (renderer->m_glyphToWidthMap == 0)
610         renderer->m_glyphToWidthMap = map;
611     else {
612         WidthMap *lastMap = renderer->m_glyphToWidthMap;
613         while (lastMap->next != 0)
614             lastMap = lastMap->next;
615         lastMap->next = map;
616     }
617
618     return map;
619 }
620
621 static void freeWidthMap(WidthMap *map)
622 {
623     while (map) {
624         WidthMap *next = map->next;
625         delete []map->widths;
626         delete map;
627         map = next;
628     }
629 }
630
631 static void freeGlyphMap(GlyphMap *map)
632 {
633     while (map) {
634         GlyphMap *next = map->next;
635         delete []map->glyphs;
636         delete map;
637         map = next;
638     }
639 }
640
641 Glyph FontData::glyphForCharacter(const FontData **renderer, unsigned c) const
642 {
643     // this loop is hot, so it is written to avoid LSU stalls
644     GlyphMap *map;
645     GlyphMap *nextMap;
646     for (map = (*renderer)->m_characterToGlyphMap; map; map = nextMap) {
647         UChar start = map->startRange;
648         nextMap = map->next;
649         if (c >= start && c <= map->endRange) {
650             GlyphEntry *ge = &map->glyphs[c - start];
651             *renderer = ge->renderer;
652             return ge->glyph;
653         }
654     }
655
656     return extendGlyphMap(*renderer, c);
657 }
658
659 static bool fillStyleWithAttributes(ATSUStyle style, NSFont *theFont)
660 {
661     if (!theFont)
662         return NO;
663     ATSUFontID fontId = wkGetNSFontATSUFontId(theFont);
664     if (!fontId)
665         return NO;
666     ATSUAttributeTag tag = kATSUFontTag;
667     ByteCount size = sizeof(ATSUFontID);
668     ATSUFontID *valueArray[1] = {&fontId};
669     OSStatus status = ATSUSetAttributes(style, 1, &tag, &size, (void* const*)valueArray);
670     if (status != noErr)
671         return NO;
672     return YES;
673 }
674
675 }