Reviewed by Darin Adler.
[WebKit-https.git] / WebCore / platform / graphics / 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., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  *
24  */
25
26 #import "config.h"
27 #import "Font.h"
28
29 #import "BlockExceptions.h"
30 #import "CharacterNames.h"
31 #import "FontData.h"
32 #import "FontFallbackList.h"
33 #import "GlyphBuffer.h"
34 #import "GraphicsContext.h"
35 #import "IntRect.h"
36 #import "Logging.h"
37 #import "WebCoreSystemInterface.h"
38 #import "WebCoreTextRenderer.h"
39 #import "ShapeArabic.h"
40
41 #define SYNTHETIC_OBLIQUE_ANGLE 14
42
43 #ifdef __LP64__
44 #define URefCon void*
45 #else
46 #define URefCon UInt32
47 #endif
48
49 using namespace std;
50
51 namespace WebCore {
52
53 // =================================================================
54 // Font Class (Platform-Specific Portion)
55 // =================================================================
56
57 struct ATSULayoutParameters
58 {
59     ATSULayoutParameters(const TextRun& run)
60         : m_run(run)
61         , m_font(0)
62         , m_fonts(0)
63         , m_charBuffer(0)
64         , m_hasSyntheticBold(false)
65         , m_syntheticBoldPass(false)
66         , m_padPerSpace(0)
67     {}
68
69     void initialize(const Font*, const GraphicsContext* = 0);
70
71     const TextRun& m_run;
72     
73     const Font* m_font;
74     
75     ATSUTextLayout m_layout;
76     const FontData **m_fonts;
77     
78     UChar *m_charBuffer;
79     bool m_hasSyntheticBold;
80     bool m_syntheticBoldPass;
81     float m_padPerSpace;
82 };
83
84 // Be sure to free the array allocated by this function.
85 static TextRun addDirectionalOverride(const TextRun& run, bool rtl)
86 {
87     UChar* charactersWithOverride = new UChar[run.length() + 2];
88     charactersWithOverride[0] = rtl ? rightToLeftOverride : leftToRightOverride;
89     memcpy(&charactersWithOverride[1], run.data(0), sizeof(UChar) * run.length());
90     charactersWithOverride[run.length() + 1] = popDirectionalFormatting;
91
92     TextRun result = run;
93     result.setText(charactersWithOverride, run.length() + 2);
94     return result;
95 }
96
97 static void initializeATSUStyle(const FontData* fontData)
98 {
99     // The two NSFont calls in this method (pointSize and _atsFontID) do not raise exceptions.
100
101     if (!fontData->m_ATSUStyleInitialized) {
102         OSStatus status;
103         ByteCount propTableSize;
104         
105         status = ATSUCreateStyle(&fontData->m_ATSUStyle);
106         if (status != noErr)
107             LOG_ERROR("ATSUCreateStyle failed (%d)", status);
108     
109         ATSUFontID fontID = wkGetNSFontATSUFontId(fontData->m_font.font());
110         if (fontID == 0) {
111             ATSUDisposeStyle(fontData->m_ATSUStyle);
112             LOG_ERROR("unable to get ATSUFontID for %@", fontData->m_font.font());
113             return;
114         }
115         
116         CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
117         if (fontData->m_font.m_syntheticOblique)
118             transform = CGAffineTransformConcat(transform, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0)); 
119         Fixed fontSize = FloatToFixed([fontData->m_font.font() pointSize]);
120         // Turn off automatic kerning until it is supported in the CG code path (6136 in bugzilla)
121         Fract kerningInhibitFactor = FloatToFract(1.0);
122         ATSUAttributeTag styleTags[4] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag, kATSUKerningInhibitFactorTag };
123         ByteCount styleSizes[4] = { sizeof(Fixed), sizeof(ATSUFontID), sizeof(CGAffineTransform), sizeof(Fract) };
124         ATSUAttributeValuePtr styleValues[4] = { &fontSize, &fontID, &transform, &kerningInhibitFactor };
125         status = ATSUSetAttributes(fontData->m_ATSUStyle, 4, styleTags, styleSizes, styleValues);
126         if (status != noErr)
127             LOG_ERROR("ATSUSetAttributes failed (%d)", status);
128         status = ATSFontGetTable(fontID, 'prop', 0, 0, 0, &propTableSize);
129         if (status == noErr)    // naively assume that if a 'prop' table exists then it contains mirroring info
130             fontData->m_ATSUMirrors = true;
131         else if (status == kATSInvalidFontTableAccess)
132             fontData->m_ATSUMirrors = false;
133         else
134             LOG_ERROR("ATSFontGetTable failed (%d)", status);
135
136         // Turn off ligatures such as 'fi' to match the CG code path's behavior, until bugzilla 6135 is fixed.
137         // Don't be too aggressive: if the font doesn't contain 'a', then assume that any ligatures it contains are
138         // in characters that always go through ATSUI, and therefore allow them. Geeza Pro is an example.
139         // See bugzilla 5166.
140         if ([[fontData->m_font.font() coveredCharacterSet] characterIsMember:'a']) {
141             ATSUFontFeatureType featureTypes[] = { kLigaturesType };
142             ATSUFontFeatureSelector featureSelectors[] = { kCommonLigaturesOffSelector };
143             status = ATSUSetFontFeatures(fontData->m_ATSUStyle, 1, featureTypes, featureSelectors);
144         }
145
146         fontData->m_ATSUStyleInitialized = true;
147     }
148 }
149
150 static OSStatus overrideLayoutOperation(ATSULayoutOperationSelector iCurrentOperation, ATSULineRef iLineRef, URefCon iRefCon,
151                                         void *iOperationCallbackParameterPtr, ATSULayoutOperationCallbackStatus *oCallbackStatus)
152 {
153     ATSULayoutParameters *params = (ATSULayoutParameters *)iRefCon;
154     OSStatus status;
155     ItemCount count;
156     ATSLayoutRecord *layoutRecords;
157
158     if (params->m_run.applyWordRounding()) {
159         status = ATSUDirectGetLayoutDataArrayPtrFromLineRef(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, true, (void **)&layoutRecords, &count);
160         if (status != noErr) {
161             *oCallbackStatus = kATSULayoutOperationCallbackStatusContinue;
162             return status;
163         }
164         
165         Fixed lastNativePos = 0;
166         float lastAdjustedPos = 0;
167         const UChar* characters = params->m_charBuffer ? params->m_charBuffer : params->m_run.characters();
168         const FontData **renderers = params->m_fonts;
169         const FontData *renderer;
170         const FontData *lastRenderer = 0;
171         UChar ch, nextCh;
172         ByteCount offset = layoutRecords[0].originalOffset;
173         nextCh = *(UChar *)(((char *)characters)+offset);
174         bool shouldRound = false;
175         bool syntheticBoldPass = params->m_syntheticBoldPass;
176         Fixed syntheticBoldOffset = 0;
177         ATSGlyphRef spaceGlyph = 0;
178         bool hasExtraSpacing = params->m_font->letterSpacing() || params->m_font->wordSpacing() | params->m_run.padding();
179         float padding = params->m_run.padding();
180         // In the CoreGraphics code path, the rounding hack is applied in logical order.
181         // Here it is applied in visual left-to-right order, which may be better.
182         ItemCount lastRoundingChar = 0;
183         ItemCount i;
184         for (i = 1; i < count; i++) {
185             bool isLastChar = i == count - 1;
186             renderer = renderers[offset / 2];
187             if (renderer != lastRenderer) {
188                 lastRenderer = renderer;
189                 spaceGlyph = renderer->m_spaceGlyph;
190                 // The CoreGraphics interpretation of NSFontAntialiasedIntegerAdvancementsRenderingMode seems
191                 // to be "round each glyph's width to the nearest integer". This is not the same as ATSUI
192                 // does in any of its device-metrics modes.
193                 shouldRound = [renderer->m_font.font() renderingMode] == NSFontAntialiasedIntegerAdvancementsRenderingMode;
194                 if (syntheticBoldPass)
195                     syntheticBoldOffset = FloatToFixed(renderer->m_syntheticBoldOffset);
196             }
197             float width;
198             if (nextCh == zeroWidthSpace || Font::treatAsZeroWidthSpace(nextCh) && !Font::treatAsSpace(nextCh)) {
199                 width = 0;
200                 layoutRecords[i-1].glyphID = spaceGlyph;
201             } else {
202                 width = FixedToFloat(layoutRecords[i].realPos - lastNativePos);
203                 if (shouldRound)
204                     width = roundf(width);
205                 width += renderer->m_syntheticBoldOffset;
206                 if (renderer->m_treatAsFixedPitch ? width == renderer->m_spaceWidth : (layoutRecords[i-1].flags & kATSGlyphInfoIsWhiteSpace))
207                     width = renderer->m_adjustedSpaceWidth;
208             }
209             lastNativePos = layoutRecords[i].realPos;
210
211             if (hasExtraSpacing) {
212                 if (width && params->m_font->letterSpacing())
213                     width +=params->m_font->letterSpacing();
214                 if (Font::treatAsSpace(nextCh)) {
215                     if (params->m_run.padding()) {
216                         if (padding < params->m_padPerSpace) {
217                             width += padding;
218                             padding = 0;
219                         } else {
220                             width += params->m_padPerSpace;
221                             padding -= params->m_padPerSpace;
222                         }
223                     }
224                     if (offset != 0 && !Font::treatAsSpace(*((UChar *)(((char *)characters)+offset) - 1)) && params->m_font->wordSpacing())
225                         width += params->m_font->wordSpacing();
226                 }
227             }
228
229             ch = nextCh;
230             offset = layoutRecords[i].originalOffset;
231             // Use space for nextCh at the end of the loop so that we get inside the rounding hack code.
232             // We won't actually round unless the other conditions are satisfied.
233             nextCh = isLastChar ? ' ' : *(UChar *)(((char *)characters)+offset);
234
235             if (Font::isRoundingHackCharacter(ch))
236                 width = ceilf(width);
237             lastAdjustedPos = lastAdjustedPos + width;
238             if (Font::isRoundingHackCharacter(nextCh) && (!isLastChar || params->m_run.applyRunRounding())){
239                 if (params->m_run.ltr())
240                     lastAdjustedPos = ceilf(lastAdjustedPos);
241                 else {
242                     float roundingWidth = ceilf(lastAdjustedPos) - lastAdjustedPos;
243                     Fixed rw = FloatToFixed(roundingWidth);
244                     ItemCount j;
245                     for (j = lastRoundingChar; j < i; j++)
246                         layoutRecords[j].realPos += rw;
247                     lastRoundingChar = i;
248                     lastAdjustedPos += roundingWidth;
249                 }
250             }
251             if (syntheticBoldPass) {
252                 if (syntheticBoldOffset)
253                     layoutRecords[i-1].realPos += syntheticBoldOffset;
254                 else
255                     layoutRecords[i-1].glyphID = spaceGlyph;
256             }
257             layoutRecords[i].realPos = FloatToFixed(lastAdjustedPos);
258         }
259         
260         status = ATSUDirectReleaseLayoutDataArrayPtr(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&layoutRecords);
261     }
262     *oCallbackStatus = kATSULayoutOperationCallbackStatusHandled;
263     return noErr;
264 }
265
266 static inline bool isArabicLamWithAlefLigature(UChar c)
267 {
268     return c >= 0xfef5 && c <= 0xfefc;
269 }
270
271 static void shapeArabic(const UChar* source, UChar* dest, unsigned totalLength, unsigned shapingStart)
272 {
273     while (shapingStart < totalLength) {
274         unsigned shapingEnd;
275         // We do not want to pass a Lam with Alef ligature followed by a space to the shaper,
276         // since we want to be able to identify this sequence as the result of shaping a Lam
277         // followed by an Alef and padding with a space.
278         bool foundLigatureSpace = false;
279         for (shapingEnd = shapingStart; !foundLigatureSpace && shapingEnd < totalLength - 1; ++shapingEnd)
280             foundLigatureSpace = isArabicLamWithAlefLigature(source[shapingEnd]) && source[shapingEnd + 1] == ' ';
281         shapingEnd++;
282
283         UErrorCode shapingError = U_ZERO_ERROR;
284         unsigned charsWritten = shapeArabic(source + shapingStart, shapingEnd - shapingStart, dest + shapingStart, shapingEnd - shapingStart, U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_NEAR, &shapingError);
285
286         if (U_SUCCESS(shapingError) && charsWritten == shapingEnd - shapingStart) {
287             for (unsigned j = shapingStart; j < shapingEnd - 1; ++j) {
288                 if (isArabicLamWithAlefLigature(dest[j]) && dest[j + 1] == ' ')
289                     dest[++j] = zeroWidthSpace;
290             }
291             if (foundLigatureSpace) {
292                 dest[shapingEnd] = ' ';
293                 shapingEnd++;
294             } else if (isArabicLamWithAlefLigature(dest[shapingEnd - 1])) {
295                 // u_shapeArabic quirk: if the last two characters in the source string are a Lam and an Alef,
296                 // the space is put at the beginning of the string, despite U_SHAPE_LENGTH_FIXED_SPACES_NEAR.
297                 ASSERT(dest[shapingStart] == ' ');
298                 dest[shapingStart] = zeroWidthSpace;
299             }
300         } else {
301             // Something went wrong. Abandon shaping and just copy the rest of the buffer.
302             LOG_ERROR("u_shapeArabic failed(%d)", shapingError);
303             shapingEnd = totalLength;
304             memcpy(dest + shapingStart, source + shapingStart, (shapingEnd - shapingStart) * sizeof(UChar));
305         }
306         shapingStart = shapingEnd;
307     }
308 }
309
310 void ATSULayoutParameters::initialize(const Font* font, const GraphicsContext* graphicsContext)
311 {
312     m_font = font;
313     
314     const FontData* fontData = font->primaryFont();
315     m_fonts = new const FontData*[m_run.length()];
316     m_charBuffer = font->isSmallCaps() ? new UChar[m_run.length()] : 0;
317     
318     ATSUTextLayout layout;
319     OSStatus status;
320     ATSULayoutOperationOverrideSpecifier overrideSpecifier;
321     
322     initializeATSUStyle(fontData);
323     
324     // FIXME: This is currently missing the following required features that the CoreGraphics code path has:
325     // - \n, \t, and nonbreaking space render as a space.
326
327     UniCharCount runLength = m_run.length();
328      
329     if (m_charBuffer)
330         memcpy(m_charBuffer, m_run.characters(), runLength * sizeof(UChar));
331     
332     status = ATSUCreateTextLayoutWithTextPtr(
333             (m_charBuffer ? m_charBuffer : m_run.characters()),
334             0,        // offset
335             runLength,      // length
336             runLength,    // total length
337             1,              // styleRunCount
338             &runLength,     // length of style run
339             &fontData->m_ATSUStyle, 
340             &layout);
341     if (status != noErr)
342         LOG_ERROR("ATSUCreateTextLayoutWithTextPtr failed(%d)", status);
343     m_layout = layout;
344     ATSUSetTextLayoutRefCon(m_layout, (URefCon)this);
345
346     // FIXME: There are certain times when this method is called, when we don't have access to a GraphicsContext
347     // measuring text runs with floatWidthForComplexText is one example.
348     // ATSUI requires that we pass a valid CGContextRef to it when specifying kATSUCGContextTag (crashes when passed 0)
349     // ATSUI disables sub-pixel rendering if kATSUCGContextTag is not specified!  So we're in a bind.
350     // Sometimes [[NSGraphicsContext currentContext] graphicsPort] may return the wrong (or no!) context.  Nothing we can do about it (yet).
351     CGContextRef cgContext = graphicsContext ? graphicsContext->platformContext() : (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
352     
353     ATSLineLayoutOptions lineLayoutOptions = kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers;
354     Boolean rtl = m_run.rtl();
355     overrideSpecifier.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
356     overrideSpecifier.overrideUPP = overrideLayoutOperation;
357     ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineLayoutOptionsTag, kATSULineDirectionTag, kATSULayoutOperationOverrideTag };
358     ByteCount sizes[] = { sizeof(CGContextRef), sizeof(ATSLineLayoutOptions), sizeof(Boolean), sizeof(ATSULayoutOperationOverrideSpecifier) };
359     ATSUAttributeValuePtr values[] = { &cgContext, &lineLayoutOptions, &rtl, &overrideSpecifier };
360     
361     status = ATSUSetLayoutControls(layout, (m_run.applyWordRounding() ? 4 : 3), tags, sizes, values);
362     if (status != noErr)
363         LOG_ERROR("ATSUSetLayoutControls failed(%d)", status);
364
365     status = ATSUSetTransientFontMatching(layout, YES);
366     if (status != noErr)
367         LOG_ERROR("ATSUSetTransientFontMatching failed(%d)", status);
368
369     m_hasSyntheticBold = false;
370     ATSUFontID ATSUSubstituteFont;
371     UniCharArrayOffset substituteOffset = 0;
372     UniCharCount substituteLength;
373     UniCharArrayOffset lastOffset;
374     const FontData* substituteFontData = 0;
375
376     while (substituteOffset < runLength) {
377         lastOffset = substituteOffset;
378         status = ATSUMatchFontsToText(layout, substituteOffset, kATSUToTextEnd, &ATSUSubstituteFont, &substituteOffset, &substituteLength);
379         if (status == kATSUFontsMatched || status == kATSUFontsNotMatched) {
380             substituteFontData = m_font->fontDataForCharacters(m_run.characters() + substituteOffset, substituteLength);
381             if (substituteFontData) {
382                 initializeATSUStyle(substituteFontData);
383                 if (substituteFontData->m_ATSUStyle)
384                     ATSUSetRunStyle(layout, substituteFontData->m_ATSUStyle, substituteOffset, substituteLength);
385             } else
386                 substituteFontData = fontData;
387         } else {
388             substituteOffset = runLength;
389             substituteLength = 0;
390         }
391
392         bool shapedArabic = false;
393         bool isSmallCap = false;
394         UniCharArrayOffset firstSmallCap = 0;
395         const FontData *r = fontData;
396         UniCharArrayOffset i;
397         for (i = lastOffset;  ; i++) {
398             if (i == substituteOffset || i == substituteOffset + substituteLength) {
399                 if (isSmallCap) {
400                     isSmallCap = false;
401                     initializeATSUStyle(r->smallCapsFontData(m_font->fontDescription()));
402                     ATSUSetRunStyle(layout, r->smallCapsFontData(m_font->fontDescription())->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
403                 }
404                 if (i == substituteOffset && substituteLength > 0)
405                     r = substituteFontData;
406                 else
407                     break;
408             }
409             if (!shapedArabic && ublock_getCode(m_run[i]) == UBLOCK_ARABIC && !r->shapesArabic()) {
410                 shapedArabic = true;
411                 if (!m_charBuffer) {
412                     m_charBuffer = new UChar[runLength];
413                     memcpy(m_charBuffer, m_run.characters(), i * sizeof(UChar));
414                     ATSUTextMoved(layout, m_charBuffer);
415                 }
416                 shapeArabic(m_run.characters(), m_charBuffer, runLength, i);
417             }
418             if (m_run.rtl() && !r->m_ATSUMirrors) {
419                 UChar mirroredChar = u_charMirror(m_run[i]);
420                 if (mirroredChar != m_run[i]) {
421                     if (!m_charBuffer) {
422                         m_charBuffer = new UChar[runLength];
423                         memcpy(m_charBuffer, m_run.characters(), runLength * sizeof(UChar));
424                         ATSUTextMoved(layout, m_charBuffer);
425                     }
426                     m_charBuffer[i] = mirroredChar;
427                 }
428             }
429             if (m_font->isSmallCaps()) {
430                 const FontData* smallCapsData = r->smallCapsFontData(m_font->fontDescription());
431                 UChar c = m_charBuffer[i];
432                 UChar newC;
433                 if (U_GET_GC_MASK(c) & U_GC_M_MASK)
434                     m_fonts[i] = isSmallCap ? smallCapsData : r;
435                 else if (!u_isUUppercase(c) && (newC = u_toupper(c)) != c) {
436                     m_charBuffer[i] = newC;
437                     if (!isSmallCap) {
438                         isSmallCap = true;
439                         firstSmallCap = i;
440                     }
441                     m_fonts[i] = smallCapsData;
442                 } else {
443                     if (isSmallCap) {
444                         isSmallCap = false;
445                         initializeATSUStyle(smallCapsData);
446                         ATSUSetRunStyle(layout, smallCapsData->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
447                     }
448                     m_fonts[i] = r;
449                 }
450             } else
451                 m_fonts[i] = r;
452             if (m_fonts[i]->m_syntheticBoldOffset)
453                 m_hasSyntheticBold = true;
454         }
455         substituteOffset += substituteLength;
456     }
457     if (m_run.padding()) {
458         float numSpaces = 0;
459         unsigned k;
460         for (k = 0; k < runLength; k++)
461             if (Font::treatAsSpace(m_run[k]))
462                 numSpaces++;
463
464         if (numSpaces == 0)
465             m_padPerSpace = 0;
466         else
467             m_padPerSpace = ceilf(m_run.padding() / numSpaces);
468     } else
469         m_padPerSpace = 0;
470 }
471
472 static void disposeATSULayoutParameters(ATSULayoutParameters *params)
473 {
474     ATSUDisposeTextLayout(params->m_layout);
475     delete []params->m_charBuffer;
476     delete []params->m_fonts;
477 }
478
479 FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const
480 {        
481     TextRun adjustedRun = run.directionalOverride() ? addDirectionalOverride(run, run.rtl()) : run;
482     if (run.directionalOverride()) {
483         from++;
484         to++;
485     }
486
487     ATSULayoutParameters params(adjustedRun);
488     params.initialize(this);
489
490     ATSTrapezoid firstGlyphBounds;
491     ItemCount actualNumBounds;
492     
493     OSStatus status = ATSUGetGlyphBounds(params.m_layout, 0, 0, from, to - from, kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds);
494     if (status != noErr || actualNumBounds != 1) {
495         static ATSTrapezoid zeroTrapezoid = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
496         firstGlyphBounds = zeroTrapezoid;
497     }
498     disposeATSULayoutParameters(&params);
499     
500     float beforeWidth = MIN(FixedToFloat(firstGlyphBounds.lowerLeft.x), FixedToFloat(firstGlyphBounds.upperLeft.x));
501     float afterWidth = MAX(FixedToFloat(firstGlyphBounds.lowerRight.x), FixedToFloat(firstGlyphBounds.upperRight.x));
502     
503     FloatRect rect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h);
504
505     if (run.directionalOverride())
506         delete []adjustedRun.characters();
507
508     return rect;
509 }
510
511 void Font::drawComplexText(GraphicsContext* graphicsContext, const TextRun& run, const FloatPoint& point, int from, int to) const
512 {
513     OSStatus status;
514     
515     int drawPortionLength = to - from;
516     TextRun adjustedRun = run.directionalOverride() ? addDirectionalOverride(run, run.rtl()) : run;
517     if (run.directionalOverride())
518         from++;
519
520     ATSULayoutParameters params(TextRun(adjustedRun.characters(), adjustedRun.length()));
521     params.initialize(this, graphicsContext);
522     
523     // ATSUI can't draw beyond -32768 to +32767 so we translate the CTM and tell ATSUI to draw at (0, 0).
524     CGContextRef context = graphicsContext->platformContext();
525
526     CGContextTranslateCTM(context, point.x(), point.y());
527     status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0);
528     if (status == noErr && params.m_hasSyntheticBold) {
529         // Force relayout for the bold pass
530         ATSUClearLayoutCache(params.m_layout, 0);
531         params.m_syntheticBoldPass = true;
532         status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0);
533     }
534     CGContextTranslateCTM(context, -point.x(), -point.y());
535
536     if (status != noErr)
537         // Nothing to do but report the error (dev build only).
538         LOG_ERROR("ATSUDrawText() failed(%d)", status);
539
540     disposeATSULayoutParameters(&params);
541     
542     if (run.directionalOverride())
543         delete []adjustedRun.characters();
544 }
545
546 float Font::floatWidthForComplexText(const TextRun& run) const
547 {
548     if (run.length() == 0)
549         return 0;
550
551     ATSULayoutParameters params(run);
552     params.initialize(this);
553     
554     OSStatus status;
555     
556     ATSTrapezoid firstGlyphBounds;
557     ItemCount actualNumBounds;
558     status = ATSUGetGlyphBounds(params.m_layout, 0, 0, 0, run.length(), kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds);    
559     if (status != noErr)
560         LOG_ERROR("ATSUGetGlyphBounds() failed(%d)", status);
561     if (actualNumBounds != 1)
562         LOG_ERROR("unexpected result from ATSUGetGlyphBounds(): actualNumBounds(%d) != 1", actualNumBounds);
563
564     disposeATSULayoutParameters(&params);
565
566     return MAX(FixedToFloat(firstGlyphBounds.upperRight.x), FixedToFloat(firstGlyphBounds.lowerRight.x)) -
567            MIN(FixedToFloat(firstGlyphBounds.upperLeft.x), FixedToFloat(firstGlyphBounds.lowerLeft.x));
568 }
569
570 int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const
571 {
572     TextRun adjustedRun = run.directionalOverride() ? addDirectionalOverride(run, run.rtl()) : run;
573     
574     ATSULayoutParameters params(adjustedRun);
575     params.initialize(this);
576
577     UniCharArrayOffset primaryOffset = 0;
578     
579     // FIXME: No idea how to avoid including partial glyphs.
580     // Not even sure if that's the behavior this yields now.
581     Boolean isLeading;
582     UniCharArrayOffset secondaryOffset = 0;
583     OSStatus status = ATSUPositionToOffset(params.m_layout, FloatToFixed(x), FloatToFixed(-1), &primaryOffset, &isLeading, &secondaryOffset);
584     unsigned offset;
585     if (status == noErr) {
586         offset = (unsigned)primaryOffset;
587         if (run.directionalOverride() && offset > 0)
588             offset--;
589     } else
590         // Failed to find offset!  Return 0 offset.
591         offset = 0;
592
593     disposeATSULayoutParameters(&params);
594     
595     if (run.directionalOverride())
596         delete []adjustedRun.characters();
597
598     return offset;
599 }
600
601 void Font::drawGlyphs(GraphicsContext* context, const FontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
602 {
603     CGContextRef cgContext = context->platformContext();
604
605     bool originalShouldUseFontSmoothing = wkCGContextGetShouldSmoothFonts(cgContext);
606     bool newShouldUseFontSmoothing = WebCoreShouldUseFontSmoothing();
607     
608     if (originalShouldUseFontSmoothing != newShouldUseFontSmoothing)
609         CGContextSetShouldSmoothFonts(cgContext, newShouldUseFontSmoothing);
610     
611     const FontPlatformData& platformData = font->platformData();
612     NSFont* drawFont;
613     if (!isPrinterFont()) {
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, platformData.m_cgFont);
627
628     CGAffineTransform matrix = CGAffineTransformIdentity;
629     if (drawFont)
630         memcpy(&matrix, [drawFont matrix], sizeof(matrix));
631     matrix.b = -matrix.b;
632     matrix.d = -matrix.d;
633     if (platformData.m_syntheticOblique)
634         matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0)); 
635     CGContextSetTextMatrix(cgContext, matrix);
636
637     if (drawFont) {
638         wkSetCGFontRenderingMode(cgContext, drawFont);
639         CGContextSetFontSize(cgContext, 1.0f);
640     } else
641         CGContextSetFontSize(cgContext, platformData.m_size);
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     if (originalShouldUseFontSmoothing != newShouldUseFontSmoothing)
651         CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing);
652 }
653
654 }