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