This patch makes drawHighlightForText a completely cross-platform method
[WebKit-https.git] / WebCore / platform / mac / FontMac.mm
1 /**
2  * This file is part of the html renderer for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  *           (C) 2000 Dirk Mueller (mueller@kde.org)
7  * Copyright (C) 2003, 2006 Apple Computer, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  *
24  */
25
26 #import "config.h"
27 #import "Font.h"
28
29 #import "Logging.h"
30 #import "BlockExceptions.h"
31 #import "FoundationExtras.h"
32
33 #import "FontFallbackList.h"
34 #import "GraphicsContext.h"
35 #import "KWQKHTMLSettings.h"
36
37 #import "FontData.h"
38 #import "WebTextRendererFactory.h"
39
40 #import "IntRect.h"
41
42 #import "WebCoreSystemInterface.h"
43 #import "WebCoreTextRenderer.h"
44
45 #define SYNTHETIC_OBLIQUE_ANGLE 14
46
47 #define POP_DIRECTIONAL_FORMATTING 0x202C
48 #define LEFT_TO_RIGHT_OVERRIDE 0x202D
49 #define RIGHT_TO_LEFT_OVERRIDE 0x202E
50
51 using namespace std;
52
53 namespace WebCore {
54
55 FontFallbackList::FontFallbackList()
56 :m_pitch(UnknownPitch), m_font(nil)
57 {
58     m_platformFont.font = nil;
59 }
60
61 FontFallbackList::~FontFallbackList()
62 {
63     KWQRelease(m_platformFont.font);
64 }
65
66 const FontPlatformData& FontFallbackList::platformFont(const FontDescription& fontDescription) const
67 {
68     if (!m_platformFont.font) {
69         CREATE_FAMILY_ARRAY(fontDescription, families);
70         BEGIN_BLOCK_OBJC_EXCEPTIONS;
71         int traits = 0;
72         if (fontDescription.italic())
73             traits |= NSItalicFontMask;
74         if (fontDescription.weight() >= WebCore::cBoldWeight)
75             traits |= NSBoldFontMask;
76         m_platformFont = [[WebTextRendererFactory sharedFactory] 
77                                      fontWithFamilies:families traits:traits size:fontDescription.computedPixelSize()];
78         KWQRetain(m_platformFont.font);
79         m_platformFont.forPrinter = fontDescription.usePrinterFont();
80         END_BLOCK_OBJC_EXCEPTIONS;
81     }
82     return m_platformFont;
83 }
84
85 FontData* FontFallbackList::primaryFont(const FontDescription& fontDescription) const
86 {
87     if (!m_font)
88         m_font = [[WebTextRendererFactory sharedFactory] rendererWithFont:platformFont(fontDescription)];
89     return m_font;
90 }
91
92 void FontFallbackList::determinePitch(const FontDescription& fontDescription) const
93 {
94     BEGIN_BLOCK_OBJC_EXCEPTIONS;
95     if ([[WebTextRendererFactory sharedFactory] isFontFixedPitch:platformFont(fontDescription)])
96         m_pitch = FixedPitch;
97     else
98         m_pitch = VariablePitch;
99     END_BLOCK_OBJC_EXCEPTIONS;
100 }
101
102 void FontFallbackList::invalidate()
103 {
104     m_font = 0;
105     KWQRelease(m_platformFont.font);
106     m_platformFont.font = nil;
107     m_pitch = UnknownPitch;
108 }
109
110 // =================================================================
111 // Font Class (Platform-Specific Portion)
112 // =================================================================
113
114 struct ATSULayoutParameters
115 {
116     ATSULayoutParameters(const TextRun& run, int toAdd, TextDirection dir, bool applyWordRounding, bool applyRunRounding)
117     :m_run(run), m_padding(toAdd), m_rtl(dir == RTL),
118      m_applyWordRounding(applyWordRounding), m_applyRunRounding(applyRunRounding),
119      m_font(0), m_fonts(0), m_charBuffer(0), m_hasSyntheticBold(false), m_syntheticBoldPass(false), m_padPerSpace(0)
120     {}
121
122     void initialize(const Font* font);
123
124     const TextRun& m_run;
125
126     int m_padding;
127     bool m_rtl;
128     bool m_applyWordRounding;
129     bool m_applyRunRounding;
130     
131     const Font* m_font;
132     
133     ATSUTextLayout m_layout;
134     const FontData **m_fonts;
135     
136     UChar *m_charBuffer;
137     bool m_hasSyntheticBold;
138     bool m_syntheticBoldPass;
139     float m_padPerSpace;
140 };
141
142 // Be sure to free the array allocated by this function.
143 static TextRun addDirectionalOverride(const TextRun& run, bool rtl)
144 {
145     UChar* charactersWithOverride = new UChar[run.length() + 2];
146     charactersWithOverride[0] = rtl ? RIGHT_TO_LEFT_OVERRIDE : LEFT_TO_RIGHT_OVERRIDE;
147     memcpy(&charactersWithOverride[1], run.data(0), sizeof(UChar) * run.length());
148     charactersWithOverride[run.length() + 1] = POP_DIRECTIONAL_FORMATTING;
149
150     return TextRun(charactersWithOverride, run.length() + 2, run.from() + 1, run.to() + 1);
151 }
152
153 static void initializeATSUStyle(const FontData* fontData)
154 {
155     // The two NSFont calls in this method (pointSize and _atsFontID) do not raise exceptions.
156
157     if (!fontData->m_ATSUStyleInitialized) {
158         OSStatus status;
159         ByteCount propTableSize;
160         
161         status = ATSUCreateStyle(&fontData->m_ATSUStyle);
162         if (status != noErr)
163             LOG_ERROR("ATSUCreateStyle failed (%d)", status);
164     
165         ATSUFontID fontID = wkGetNSFontATSUFontId(fontData->m_font.font);
166         if (fontID == 0) {
167             ATSUDisposeStyle(fontData->m_ATSUStyle);
168             LOG_ERROR("unable to get ATSUFontID for %@", fontData->m_font.font);
169             return;
170         }
171         
172         CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
173         if (fontData->m_font.syntheticOblique)
174             transform = CGAffineTransformConcat(transform, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0)); 
175         Fixed fontSize = FloatToFixed([fontData->m_font.font pointSize]);
176         // Turn off automatic kerning until it is supported in the CG code path (6136 in bugzilla)
177         Fract kerningInhibitFactor = FloatToFract(1.0);
178         ATSUAttributeTag styleTags[4] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag, kATSUKerningInhibitFactorTag };
179         ByteCount styleSizes[4] = { sizeof(Fixed), sizeof(ATSUFontID), sizeof(CGAffineTransform), sizeof(Fract) };
180         ATSUAttributeValuePtr styleValues[4] = { &fontSize, &fontID, &transform, &kerningInhibitFactor };
181         status = ATSUSetAttributes(fontData->m_ATSUStyle, 4, styleTags, styleSizes, styleValues);
182         if (status != noErr)
183             LOG_ERROR("ATSUSetAttributes failed (%d)", status);
184         status = ATSFontGetTable(fontID, 'prop', 0, 0, 0, &propTableSize);
185         if (status == noErr)    // naively assume that if a 'prop' table exists then it contains mirroring info
186             fontData->m_ATSUMirrors = true;
187         else if (status == kATSInvalidFontTableAccess)
188             fontData->m_ATSUMirrors = false;
189         else
190             LOG_ERROR("ATSFontGetTable failed (%d)", status);
191
192         // Turn off ligatures such as 'fi' to match the CG code path's behavior, until bugzilla 6135 is fixed.
193         // Don't be too aggressive: if the font doesn't contain 'a', then assume that any ligatures it contains are
194         // in characters that always go through ATSUI, and therefore allow them. Geeza Pro is an example.
195         // See bugzilla 5166.
196         if ([[fontData->m_font.font coveredCharacterSet] characterIsMember:'a']) {
197             ATSUFontFeatureType featureTypes[] = { kLigaturesType };
198             ATSUFontFeatureSelector featureSelectors[] = { kCommonLigaturesOffSelector };
199             status = ATSUSetFontFeatures(fontData->m_ATSUStyle, 1, featureTypes, featureSelectors);
200         }
201
202         fontData->m_ATSUStyleInitialized = true;
203     }
204 }
205
206 static OSStatus overrideLayoutOperation(ATSULayoutOperationSelector iCurrentOperation, ATSULineRef iLineRef, UInt32 iRefCon,
207                                         void *iOperationCallbackParameterPtr, ATSULayoutOperationCallbackStatus *oCallbackStatus)
208 {
209     ATSULayoutParameters *params = (ATSULayoutParameters *)iRefCon;
210     OSStatus status;
211     ItemCount count;
212     ATSLayoutRecord *layoutRecords;
213
214     if (params->m_applyWordRounding) {
215         status = ATSUDirectGetLayoutDataArrayPtrFromLineRef(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, true, (void **)&layoutRecords, &count);
216         if (status != noErr) {
217             *oCallbackStatus = kATSULayoutOperationCallbackStatusContinue;
218             return status;
219         }
220         
221         Fixed lastNativePos = 0;
222         float lastAdjustedPos = 0;
223         const UChar *characters = params->m_run.data(params->m_run.from());
224         const FontData **renderers = params->m_fonts + params->m_run.from();
225         const FontData *renderer;
226         const FontData *lastRenderer = 0;
227         UChar ch, nextCh;
228         ByteCount offset = layoutRecords[0].originalOffset;
229         nextCh = *(UChar *)(((char *)characters)+offset);
230         bool shouldRound = false;
231         bool syntheticBoldPass = params->m_syntheticBoldPass;
232         Fixed syntheticBoldOffset = 0;
233         ATSGlyphRef spaceGlyph = 0;
234         bool hasExtraSpacing = params->m_font->letterSpacing() || params->m_font->wordSpacing() | params->m_padding;
235         float padding = params->m_padding;
236         // In the CoreGraphics code path, the rounding hack is applied in logical order.
237         // Here it is applied in visual left-to-right order, which may be better.
238         ItemCount lastRoundingChar = 0;
239         ItemCount i;
240         for (i = 1; i < count; i++) {
241             bool isLastChar = i == count - 1;
242             renderer = renderers[offset / 2];
243             if (renderer != lastRenderer) {
244                 lastRenderer = renderer;
245                 // The CoreGraphics interpretation of NSFontAntialiasedIntegerAdvancementsRenderingMode seems
246                 // to be "round each glyph's width to the nearest integer". This is not the same as ATSUI
247                 // does in any of its device-metrics modes.
248                 shouldRound = [renderer->m_font.font renderingMode] == NSFontAntialiasedIntegerAdvancementsRenderingMode;
249                 if (syntheticBoldPass) {
250                     syntheticBoldOffset = FloatToFixed(renderer->m_syntheticBoldOffset);
251                     spaceGlyph = renderer->m_spaceGlyph;
252                 }
253             }
254             float width = FixedToFloat(layoutRecords[i].realPos - lastNativePos);
255             lastNativePos = layoutRecords[i].realPos;
256             if (shouldRound)
257                 width = roundf(width);
258             width += renderer->m_syntheticBoldOffset;
259             if (renderer->m_treatAsFixedPitch ? width == renderer->m_spaceWidth : (layoutRecords[i-1].flags & kATSGlyphInfoIsWhiteSpace))
260                 width = renderer->m_adjustedSpaceWidth;
261
262             if (hasExtraSpacing) {
263                 if (width && params->m_font->letterSpacing())
264                     width +=params->m_font->letterSpacing();
265                 if (Font::treatAsSpace(nextCh)) {
266                     if (params->m_padding) {
267                         if (padding < params->m_padPerSpace) {
268                             width += padding;
269                             padding = 0;
270                         } else {
271                             width += params->m_padPerSpace;
272                             padding -= params->m_padPerSpace;
273                         }
274                     }
275                     if (offset != 0 && !Font::treatAsSpace(*((UChar *)(((char *)characters)+offset) - 1)) && params->m_font->wordSpacing())
276                         width += params->m_font->wordSpacing();
277                 }
278             }
279
280             ch = nextCh;
281             offset = layoutRecords[i].originalOffset;
282             // Use space for nextCh at the end of the loop so that we get inside the rounding hack code.
283             // We won't actually round unless the other conditions are satisfied.
284             nextCh = isLastChar ? ' ' : *(UChar *)(((char *)characters)+offset);
285
286             if (Font::isRoundingHackCharacter(ch))
287                 width = ceilf(width);
288             lastAdjustedPos = lastAdjustedPos + width;
289             if (Font::isRoundingHackCharacter(nextCh)
290                 && (!isLastChar
291                     || params->m_applyRunRounding
292                     || (params->m_run.to() < (int)params->m_run.length() && 
293                         Font::isRoundingHackCharacter(characters[params->m_run.to() - params->m_run.from()])))) {
294                 if (!params->m_rtl)
295                     lastAdjustedPos = ceilf(lastAdjustedPos);
296                 else {
297                     float roundingWidth = ceilf(lastAdjustedPos) - lastAdjustedPos;
298                     Fixed rw = FloatToFixed(roundingWidth);
299                     ItemCount j;
300                     for (j = lastRoundingChar; j < i; j++)
301                         layoutRecords[j].realPos += rw;
302                     lastRoundingChar = i;
303                     lastAdjustedPos += roundingWidth;
304                 }
305             }
306             if (syntheticBoldPass) {
307                 if (syntheticBoldOffset)
308                     layoutRecords[i-1].realPos += syntheticBoldOffset;
309                 else
310                     layoutRecords[i-1].glyphID = spaceGlyph;
311             }
312             layoutRecords[i].realPos = FloatToFixed(lastAdjustedPos);
313         }
314         
315         status = ATSUDirectReleaseLayoutDataArrayPtr(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&layoutRecords);
316     }
317     *oCallbackStatus = kATSULayoutOperationCallbackStatusHandled;
318     return noErr;
319 }
320
321 void ATSULayoutParameters::initialize(const Font* font)
322 {
323     m_font = font;
324
325     // FIXME: It is probably best to always allocate a buffer for RTL, since even if for this
326     // fontData ATSUMirrors is true, for a substitute fontData it might be false.
327     const FontData* fontData = font->primaryFont();
328     m_fonts = new const FontData*[m_run.length()];
329     m_charBuffer = (UChar*)((font->isSmallCaps() || (m_rtl && !fontData->m_ATSUMirrors)) ? new UChar[m_run.length()] : 0);
330     
331     // The only Cocoa calls here are to NSGraphicsContext, which does not raise exceptions.
332
333     ATSUTextLayout layout;
334     OSStatus status;
335     ATSULayoutOperationOverrideSpecifier overrideSpecifier;
336     
337     initializeATSUStyle(fontData);
338     
339     // FIXME: This is currently missing the following required features that the CoreGraphics code path has:
340     // - \n, \t, and nonbreaking space render as a space.
341     // - Other control characters do not render (other code path uses zero-width spaces).
342
343     UniCharCount totalLength = m_run.length();
344     UniCharArrayOffset runTo = m_run.to();
345     UniCharArrayOffset runFrom = m_run.from();
346     
347     if (m_charBuffer)
348         memcpy(m_charBuffer, m_run.characters(), totalLength * sizeof(UChar));
349
350     UniCharCount runLength = runTo - runFrom;
351     
352     status = ATSUCreateTextLayoutWithTextPtr(
353             (m_charBuffer ? m_charBuffer : m_run.characters()),
354             runFrom,        // offset
355             runLength,      // length
356             totalLength,    // total length
357             1,              // styleRunCount
358             &runLength,     // length of style run
359             &fontData->m_ATSUStyle, 
360             &layout);
361     if (status != noErr)
362         LOG_ERROR("ATSUCreateTextLayoutWithTextPtr failed(%d)", status);
363     m_layout = layout;
364     ATSUSetTextLayoutRefCon(m_layout, (UInt32)this);
365
366     CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
367     ATSLineLayoutOptions lineLayoutOptions = kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers;
368     Boolean rtl = m_rtl;
369     overrideSpecifier.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
370     overrideSpecifier.overrideUPP = overrideLayoutOperation;
371     ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineLayoutOptionsTag, kATSULineDirectionTag, kATSULayoutOperationOverrideTag };
372     ByteCount sizes[] = { sizeof(CGContextRef), sizeof(ATSLineLayoutOptions), sizeof(Boolean), sizeof(ATSULayoutOperationOverrideSpecifier) };
373     ATSUAttributeValuePtr values[] = { &cgContext, &lineLayoutOptions, &rtl, &overrideSpecifier };
374     
375     status = ATSUSetLayoutControls(layout, (m_applyWordRounding ? 4 : 3), tags, sizes, values);
376     if (status != noErr)
377         LOG_ERROR("ATSUSetLayoutControls failed(%d)", status);
378
379     status = ATSUSetTransientFontMatching(layout, YES);
380     if (status != noErr)
381         LOG_ERROR("ATSUSetTransientFontMatching failed(%d)", status);
382
383     m_hasSyntheticBold = false;
384     ATSUFontID ATSUSubstituteFont;
385     UniCharArrayOffset substituteOffset = runFrom;
386     UniCharCount substituteLength;
387     UniCharArrayOffset lastOffset;
388     const FontData* substituteFontData = 0;
389
390     while (substituteOffset < runTo) {
391         lastOffset = substituteOffset;
392         status = ATSUMatchFontsToText(layout, substituteOffset, kATSUToTextEnd, &ATSUSubstituteFont, &substituteOffset, &substituteLength);
393         if (status == kATSUFontsMatched || status == kATSUFontsNotMatched) {
394             // FIXME: Should go through fallback list eventually.
395             substituteFontData = fontData->findSubstituteFontData(m_run.characters() + substituteOffset, substituteLength, m_font->fontDescription());
396             if (substituteFontData) {
397                 initializeATSUStyle(substituteFontData);
398                 if (substituteFontData->m_ATSUStyle)
399                     ATSUSetRunStyle(layout, substituteFontData->m_ATSUStyle, substituteOffset, substituteLength);
400             } else
401                 substituteFontData = fontData;
402         } else {
403             substituteOffset = runTo;
404             substituteLength = 0;
405         }
406
407         bool isSmallCap = false;
408         UniCharArrayOffset firstSmallCap = 0;
409         const FontData *r = fontData;
410         UniCharArrayOffset i;
411         for (i = lastOffset;  ; i++) {
412             if (i == substituteOffset || i == substituteOffset + substituteLength) {
413                 if (isSmallCap) {
414                     isSmallCap = false;
415                     initializeATSUStyle(r->smallCapsFontData());
416                     ATSUSetRunStyle(layout, r->smallCapsFontData()->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
417                 }
418                 if (i == substituteOffset && substituteLength > 0)
419                     r = substituteFontData;
420                 else
421                     break;
422             }
423             if (m_rtl && m_charBuffer && !r->m_ATSUMirrors)
424                 m_charBuffer[i] = u_charMirror(m_charBuffer[i]);
425             if (m_font->isSmallCaps()) {
426                 UChar c = m_charBuffer[i];
427                 UChar newC;
428                 if (U_GET_GC_MASK(c) & U_GC_M_MASK)
429                     m_fonts[i] = isSmallCap ? r->smallCapsFontData() : r;
430                 else if (!u_isUUppercase(c) && (newC = u_toupper(c)) != c) {
431                     m_charBuffer[i] = newC;
432                     if (!isSmallCap) {
433                         isSmallCap = true;
434                         firstSmallCap = i;
435                     }
436                     m_fonts[i] = r->smallCapsFontData();
437                 } else {
438                     if (isSmallCap) {
439                         isSmallCap = false;
440                         initializeATSUStyle(r->smallCapsFontData());
441                         ATSUSetRunStyle(layout, r->smallCapsFontData()->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
442                     }
443                     m_fonts[i] = r;
444                 }
445             } else
446                 m_fonts[i] = r;
447             if (m_fonts[i]->m_syntheticBoldOffset)
448                 m_hasSyntheticBold = true;
449         }
450         substituteOffset += substituteLength;
451     }
452     if (m_padding) {
453         float numSpaces = 0;
454         unsigned k;
455         for (k = 0; k < totalLength; k++)
456             if (Font::treatAsSpace(m_run[k]))
457                 numSpaces++;
458
459         m_padPerSpace = ceilf(m_padding / numSpaces);
460     } else
461         m_padPerSpace = 0;
462 }
463
464 static void disposeATSULayoutParameters(ATSULayoutParameters *params)
465 {
466     ATSUDisposeTextLayout(params->m_layout);
467     delete []params->m_charBuffer;
468     delete []params->m_fonts;
469 }
470
471 const FontPlatformData& Font::platformFont() const
472 {
473     return m_fontList->platformFont(fontDescription());
474 }
475
476 FloatRect Font::selectionRectForText(const TextRun& textRun, const IntPoint& point, int h, int tabWidth, int xpos, int toAdd, bool rtl, bool visuallyOrdered) const
477 {
478     assert(m_fontList);
479
480     CREATE_FAMILY_ARRAY(fontDescription(), families);
481
482     WebCoreTextRun run;
483     WebCoreInitializeTextRun(&run, textRun.characters(), textRun.length(), textRun.from(), textRun.to());
484     WebCoreTextStyle style;
485     WebCoreInitializeEmptyTextStyle(&style);
486     style.rtl = rtl;
487     style.directionalOverride = visuallyOrdered;
488     style.letterSpacing = letterSpacing();
489     style.wordSpacing = wordSpacing();
490     style.smallCaps = fontDescription().smallCaps();
491     style.families = families;    
492     style.padding = toAdd;
493     style.tabWidth = tabWidth;
494     style.xpos = xpos;
495     WebCoreTextGeometry geometry;
496     WebCoreInitializeEmptyTextGeometry(&geometry);
497     geometry.point = point;
498     geometry.selectionY = point.y();
499     geometry.selectionHeight = h;
500     geometry.useFontMetricsForSelectionYAndHeight = false;
501     return m_fontList->primaryFont(fontDescription())->selectionRectForRun(&run, &style, &geometry);
502 }
503
504 void Font::drawComplexText(GraphicsContext* graphicsContext, const TextRun& run, const IntPoint& point, int tabWidth, int xpos,
505                            int toAdd, TextDirection d, bool visuallyOrdered) const
506 {
507     OSStatus status;
508     
509     const UChar* characters = run.characters();
510     int runLength = run.length();
511
512     TextRun adjustedRun = visuallyOrdered ? addDirectionalOverride(run, d == RTL) : run;
513     ATSULayoutParameters params(adjustedRun, toAdd, d, true, true);
514     params.initialize(this);
515
516     [nsColor(graphicsContext->pen().color()) set];
517
518     // ATSUI can't draw beyond -32768 to +32767 so we translate the CTM and tell ATSUI to draw at (0, 0).
519     // FIXME: Cut the dependency on currentContext here.
520     NSGraphicsContext *gContext = [NSGraphicsContext currentContext];
521     CGContextRef context = (CGContextRef)[gContext graphicsPort];
522     CGContextTranslateCTM(context, point.x(), point.y());
523     bool flipped = [gContext isFlipped];
524     if (!flipped)
525         CGContextScaleCTM(context, 1.0, -1.0);
526     status = ATSUDrawText(params.m_layout, adjustedRun.from(), runLength, 0, 0);
527     if (status == noErr && params.m_hasSyntheticBold) {
528         // Force relayout for the bold pass
529         ATSUClearLayoutCache(params.m_layout, 0);
530         params.m_syntheticBoldPass = true;
531         status = ATSUDrawText(params.m_layout, adjustedRun.from(), runLength, 0, 0);
532     }
533     if (!flipped)
534         CGContextScaleCTM(context, 1.0, -1.0);
535     CGContextTranslateCTM(context, -point.x(), -point.y());
536
537     if (status != noErr) {
538         // Nothing to do but report the error (dev build only).
539         LOG_ERROR("ATSUDrawText() failed(%d)", status);
540     }
541
542     disposeATSULayoutParameters(&params);
543     
544     if (visuallyOrdered)
545         delete []characters;
546 }
547
548 void Font::drawLineForText(GraphicsContext* context, const IntPoint& point, int yOffset, int width) const
549 {
550     m_fontList->primaryFont(fontDescription())->drawLineForCharacters(point, yOffset, width, context->pen().color(), context->pen().width());
551 }
552
553 void Font::drawLineForMisspelling(GraphicsContext* context, const IntPoint& point, int width) const
554 {
555     m_fontList->primaryFont(fontDescription())->drawLineForMisspelling(point, width);
556 }
557
558 int Font::misspellingLineThickness(GraphicsContext* context) const
559 {
560     return m_fontList->primaryFont(fontDescription())->misspellingLineThickness();
561 }
562
563 float Font::floatWidthForComplexText(const TextRun& run, int tabWidth, int xpos, bool runRounding) const
564 {
565     ATSULayoutParameters params(run, 0, LTR, true, runRounding);
566     params.initialize(this);
567     
568     OSStatus status;
569     
570     ATSTrapezoid firstGlyphBounds;
571     ItemCount actualNumBounds;
572     status = ATSUGetGlyphBounds(params.m_layout, 0, 0, run.from(), run.to() - run.from(), kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds);    
573     if (status != noErr)
574         LOG_ERROR("ATSUGetGlyphBounds() failed(%d)", status);
575     if (actualNumBounds != 1)
576         LOG_ERROR("unexpected result from ATSUGetGlyphBounds(): actualNumBounds(%d) != 1", actualNumBounds);
577
578     disposeATSULayoutParameters(&params);
579
580     return MAX(FixedToFloat(firstGlyphBounds.upperRight.x), FixedToFloat(firstGlyphBounds.lowerRight.x)) -
581            MIN(FixedToFloat(firstGlyphBounds.upperLeft.x), FixedToFloat(firstGlyphBounds.lowerLeft.x));
582 }
583
584 int Font::checkSelectionPoint(const TextRun& textRun, int toAdd, int tabWidth, int xpos, int x, TextDirection d, bool visuallyOrdered, bool includePartialGlyphs) const
585 {
586     assert(m_fontList);
587     CREATE_FAMILY_ARRAY(fontDescription(), families);
588     
589     WebCoreTextRun run;
590     WebCoreInitializeTextRun(&run, textRun.characters(), textRun.length(), textRun.from(), textRun.to());
591     
592     WebCoreTextStyle style;
593     WebCoreInitializeEmptyTextStyle(&style);
594     style.letterSpacing = letterSpacing();
595     style.wordSpacing = wordSpacing();
596     style.smallCaps = fontDescription().smallCaps();
597     style.families = families;
598     style.padding = toAdd;
599     style.tabWidth = tabWidth;
600     style.xpos = xpos;
601     style.rtl =  d == RTL;
602     style.directionalOverride = visuallyOrdered;
603
604     return m_fontList->primaryFont(fontDescription())->pointToOffset(&run, &style, x, includePartialGlyphs);
605 }
606
607 void Font::drawGlyphs(GraphicsContext* context, const FontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
608 {
609     // FIXME: Grab the CGContext from the GraphicsContext eventually, when we have made sure to shield against flipping caused by calls into us
610     // from Safari.
611     NSGraphicsContext *gContext = [NSGraphicsContext currentContext];
612     CGContextRef cgContext = (CGContextRef)[gContext graphicsPort];
613
614     bool originalShouldUseFontSmoothing = wkCGContextGetShouldSmoothFonts(cgContext);
615     CGContextSetShouldSmoothFonts(cgContext, WebCoreShouldUseFontSmoothing());
616     
617     const FontPlatformData& platformData = font->platformData();
618     NSFont* drawFont;
619     if ([gContext isDrawingToScreen]) {
620         drawFont = [platformData.font screenFont];
621         if (drawFont != platformData.font)
622             // We are getting this in too many places (3406411); use ERROR so it only prints on debug versions for now. (We should debug this also, eventually).
623             LOG_ERROR("Attempting to set non-screen font (%@) when drawing to screen.  Using screen font anyway, may result in incorrect metrics.",
624                 [[[platformData.font fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]);
625     } else {
626         drawFont = [platformData.font printerFont];
627         if (drawFont != platformData.font)
628             NSLog(@"Attempting to set non-printer font (%@) when printing.  Using printer font anyway, may result in incorrect metrics.",
629                 [[[platformData.font fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]);
630     }
631     
632     CGContextSetFont(cgContext, wkGetCGFontFromNSFont(drawFont));
633
634     CGAffineTransform matrix;
635     memcpy(&matrix, [drawFont matrix], sizeof(matrix));
636     if ([gContext isFlipped]) {
637         matrix.b = -matrix.b;
638         matrix.d = -matrix.d;
639     }
640     if (platformData.syntheticOblique)
641         matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0)); 
642     CGContextSetTextMatrix(cgContext, matrix);
643
644     wkSetCGFontRenderingMode(cgContext, drawFont);
645     CGContextSetFontSize(cgContext, 1.0f);
646
647     [nsColor(context->pen().color()) set];
648
649     CGContextSetTextPosition(cgContext, point.x(), point.y());
650     CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
651     if (font->m_syntheticBoldOffset) {
652         CGContextSetTextPosition(cgContext, point.x() + font->m_syntheticBoldOffset, point.y());
653         CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
654     }
655
656     CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing);
657 }
658
659 }