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