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