2 * This file is part of the html renderer for KDE.
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.
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.
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.
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.
29 #import "BlockExceptions.h"
30 #import "CharacterNames.h"
32 #import "FontFallbackList.h"
33 #import "GlyphBuffer.h"
34 #import "GraphicsContext.h"
37 #import "WebCoreSystemInterface.h"
38 #import "WebCoreTextRenderer.h"
39 #import "ShapeArabic.h"
41 #define SYNTHETIC_OBLIQUE_ANGLE 14
46 #define URefCon UInt32
53 // =================================================================
54 // Font Class (Platform-Specific Portion)
55 // =================================================================
57 struct ATSULayoutParameters
59 ATSULayoutParameters(const TextRun& run)
64 , m_hasSyntheticBold(false)
65 , m_syntheticBoldPass(false)
69 void initialize(const Font*, const GraphicsContext* = 0);
75 ATSUTextLayout m_layout;
76 const FontData **m_fonts;
79 bool m_hasSyntheticBold;
80 bool m_syntheticBoldPass;
84 // Be sure to free the array allocated by this function.
85 static TextRun addDirectionalOverride(const TextRun& run, bool rtl)
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;
93 result.setText(charactersWithOverride, run.length() + 2);
97 static void initializeATSUStyle(const FontData* fontData)
99 // The two NSFont calls in this method (pointSize and _atsFontID) do not raise exceptions.
101 if (!fontData->m_ATSUStyleInitialized) {
103 ByteCount propTableSize;
105 status = ATSUCreateStyle(&fontData->m_ATSUStyle);
107 LOG_ERROR("ATSUCreateStyle failed (%d)", status);
109 ATSUFontID fontID = wkGetNSFontATSUFontId(fontData->m_font.font());
111 ATSUDisposeStyle(fontData->m_ATSUStyle);
112 LOG_ERROR("unable to get ATSUFontID for %@", fontData->m_font.font());
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);
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;
134 LOG_ERROR("ATSFontGetTable failed (%d)", status);
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);
146 fontData->m_ATSUStyleInitialized = true;
150 static OSStatus overrideLayoutOperation(ATSULayoutOperationSelector iCurrentOperation, ATSULineRef iLineRef, URefCon iRefCon,
151 void *iOperationCallbackParameterPtr, ATSULayoutOperationCallbackStatus *oCallbackStatus)
153 ATSULayoutParameters *params = (ATSULayoutParameters *)iRefCon;
156 ATSLayoutRecord *layoutRecords;
158 if (params->m_run.applyWordRounding()) {
159 status = ATSUDirectGetLayoutDataArrayPtrFromLineRef(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, true, (void **)&layoutRecords, &count);
160 if (status != noErr) {
161 *oCallbackStatus = kATSULayoutOperationCallbackStatusContinue;
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;
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;
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);
198 if (nextCh == zeroWidthSpace || Font::treatAsZeroWidthSpace(nextCh) && !Font::treatAsSpace(nextCh)) {
200 layoutRecords[i-1].glyphID = spaceGlyph;
202 width = FixedToFloat(layoutRecords[i].realPos - lastNativePos);
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;
209 lastNativePos = layoutRecords[i].realPos;
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) {
220 width += params->m_padPerSpace;
221 padding -= params->m_padPerSpace;
224 if (offset != 0 && !Font::treatAsSpace(*((UChar *)(((char *)characters)+offset) - 1)) && params->m_font->wordSpacing())
225 width += params->m_font->wordSpacing();
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);
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);
242 float roundingWidth = ceilf(lastAdjustedPos) - lastAdjustedPos;
243 Fixed rw = FloatToFixed(roundingWidth);
245 for (j = lastRoundingChar; j < i; j++)
246 layoutRecords[j].realPos += rw;
247 lastRoundingChar = i;
248 lastAdjustedPos += roundingWidth;
251 if (syntheticBoldPass) {
252 if (syntheticBoldOffset)
253 layoutRecords[i-1].realPos += syntheticBoldOffset;
255 layoutRecords[i-1].glyphID = spaceGlyph;
257 layoutRecords[i].realPos = FloatToFixed(lastAdjustedPos);
260 status = ATSUDirectReleaseLayoutDataArrayPtr(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&layoutRecords);
262 *oCallbackStatus = kATSULayoutOperationCallbackStatusHandled;
266 static inline bool isArabicLamWithAlefLigature(UChar c)
268 return c >= 0xfef5 && c <= 0xfefc;
271 static void shapeArabic(const UChar* source, UChar* dest, unsigned totalLength, unsigned shapingStart)
273 while (shapingStart < totalLength) {
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] == ' ';
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);
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;
291 if (foundLigatureSpace) {
292 dest[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;
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));
306 shapingStart = shapingEnd;
310 void ATSULayoutParameters::initialize(const Font* font, const GraphicsContext* graphicsContext)
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;
318 ATSUTextLayout layout;
320 ATSULayoutOperationOverrideSpecifier overrideSpecifier;
322 initializeATSUStyle(fontData);
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.
327 UniCharCount runLength = m_run.length();
330 memcpy(m_charBuffer, m_run.characters(), runLength * sizeof(UChar));
332 status = ATSUCreateTextLayoutWithTextPtr(
333 (m_charBuffer ? m_charBuffer : m_run.characters()),
336 runLength, // total length
338 &runLength, // length of style run
339 &fontData->m_ATSUStyle,
342 LOG_ERROR("ATSUCreateTextLayoutWithTextPtr failed(%d)", status);
344 ATSUSetTextLayoutRefCon(m_layout, (URefCon)this);
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];
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 };
361 status = ATSUSetLayoutControls(layout, (m_run.applyWordRounding() ? 4 : 3), tags, sizes, values);
363 LOG_ERROR("ATSUSetLayoutControls failed(%d)", status);
365 status = ATSUSetTransientFontMatching(layout, YES);
367 LOG_ERROR("ATSUSetTransientFontMatching failed(%d)", status);
369 m_hasSyntheticBold = false;
370 ATSUFontID ATSUSubstituteFont;
371 UniCharArrayOffset substituteOffset = 0;
372 UniCharCount substituteLength;
373 UniCharArrayOffset lastOffset;
374 const FontData* substituteFontData = 0;
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);
386 substituteFontData = fontData;
388 substituteOffset = runLength;
389 substituteLength = 0;
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) {
401 initializeATSUStyle(r->smallCapsFontData(m_font->fontDescription()));
402 ATSUSetRunStyle(layout, r->smallCapsFontData(m_font->fontDescription())->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
404 if (i == substituteOffset && substituteLength > 0)
405 r = substituteFontData;
409 if (!shapedArabic && ublock_getCode(m_run[i]) == UBLOCK_ARABIC && !r->shapesArabic()) {
412 m_charBuffer = new UChar[runLength];
413 memcpy(m_charBuffer, m_run.characters(), i * sizeof(UChar));
414 ATSUTextMoved(layout, m_charBuffer);
416 shapeArabic(m_run.characters(), m_charBuffer, runLength, i);
418 if (m_run.rtl() && !r->m_ATSUMirrors) {
419 UChar mirroredChar = u_charMirror(m_run[i]);
420 if (mirroredChar != m_run[i]) {
422 m_charBuffer = new UChar[runLength];
423 memcpy(m_charBuffer, m_run.characters(), runLength * sizeof(UChar));
424 ATSUTextMoved(layout, m_charBuffer);
426 m_charBuffer[i] = mirroredChar;
429 if (m_font->isSmallCaps()) {
430 const FontData* smallCapsData = r->smallCapsFontData(m_font->fontDescription());
431 UChar c = m_charBuffer[i];
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;
441 m_fonts[i] = smallCapsData;
445 initializeATSUStyle(smallCapsData);
446 ATSUSetRunStyle(layout, smallCapsData->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
452 if (m_fonts[i]->m_syntheticBoldOffset)
453 m_hasSyntheticBold = true;
455 substituteOffset += substituteLength;
457 if (m_run.padding()) {
460 for (k = 0; k < runLength; k++)
461 if (Font::treatAsSpace(m_run[k]))
467 m_padPerSpace = ceilf(m_run.padding() / numSpaces);
472 static void disposeATSULayoutParameters(ATSULayoutParameters *params)
474 ATSUDisposeTextLayout(params->m_layout);
475 delete []params->m_charBuffer;
476 delete []params->m_fonts;
479 FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const
481 TextRun adjustedRun = run.directionalOverride() ? addDirectionalOverride(run, run.rtl()) : run;
482 if (run.directionalOverride()) {
487 ATSULayoutParameters params(adjustedRun);
488 params.initialize(this);
490 ATSTrapezoid firstGlyphBounds;
491 ItemCount actualNumBounds;
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;
498 disposeATSULayoutParameters(¶ms);
500 float beforeWidth = MIN(FixedToFloat(firstGlyphBounds.lowerLeft.x), FixedToFloat(firstGlyphBounds.upperLeft.x));
501 float afterWidth = MAX(FixedToFloat(firstGlyphBounds.lowerRight.x), FixedToFloat(firstGlyphBounds.upperRight.x));
503 FloatRect rect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h);
505 if (run.directionalOverride())
506 delete []adjustedRun.characters();
511 void Font::drawComplexText(GraphicsContext* graphicsContext, const TextRun& run, const FloatPoint& point, int from, int to) const
515 int drawPortionLength = to - from;
516 TextRun adjustedRun = run.directionalOverride() ? addDirectionalOverride(run, run.rtl()) : run;
517 if (run.directionalOverride())
520 ATSULayoutParameters params(TextRun(adjustedRun.characters(), adjustedRun.length()));
521 params.initialize(this, graphicsContext);
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();
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);
534 CGContextTranslateCTM(context, -point.x(), -point.y());
537 // Nothing to do but report the error (dev build only).
538 LOG_ERROR("ATSUDrawText() failed(%d)", status);
540 disposeATSULayoutParameters(¶ms);
542 if (run.directionalOverride())
543 delete []adjustedRun.characters();
546 float Font::floatWidthForComplexText(const TextRun& run) const
548 if (run.length() == 0)
551 ATSULayoutParameters params(run);
552 params.initialize(this);
556 ATSTrapezoid firstGlyphBounds;
557 ItemCount actualNumBounds;
558 status = ATSUGetGlyphBounds(params.m_layout, 0, 0, 0, run.length(), kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds);
560 LOG_ERROR("ATSUGetGlyphBounds() failed(%d)", status);
561 if (actualNumBounds != 1)
562 LOG_ERROR("unexpected result from ATSUGetGlyphBounds(): actualNumBounds(%d) != 1", actualNumBounds);
564 disposeATSULayoutParameters(¶ms);
566 return MAX(FixedToFloat(firstGlyphBounds.upperRight.x), FixedToFloat(firstGlyphBounds.lowerRight.x)) -
567 MIN(FixedToFloat(firstGlyphBounds.upperLeft.x), FixedToFloat(firstGlyphBounds.lowerLeft.x));
570 int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const
572 TextRun adjustedRun = run.directionalOverride() ? addDirectionalOverride(run, run.rtl()) : run;
574 ATSULayoutParameters params(adjustedRun);
575 params.initialize(this);
577 UniCharArrayOffset primaryOffset = 0;
579 // FIXME: No idea how to avoid including partial glyphs.
580 // Not even sure if that's the behavior this yields now.
582 UniCharArrayOffset secondaryOffset = 0;
583 OSStatus status = ATSUPositionToOffset(params.m_layout, FloatToFixed(x), FloatToFixed(-1), &primaryOffset, &isLeading, &secondaryOffset);
585 if (status == noErr) {
586 offset = (unsigned)primaryOffset;
587 if (run.directionalOverride() && offset > 0)
590 // Failed to find offset! Return 0 offset.
593 disposeATSULayoutParameters(¶ms);
595 if (run.directionalOverride())
596 delete []adjustedRun.characters();
601 void Font::drawGlyphs(GraphicsContext* context, const FontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
603 CGContextRef cgContext = context->platformContext();
605 bool originalShouldUseFontSmoothing = wkCGContextGetShouldSmoothFonts(cgContext);
606 bool newShouldUseFontSmoothing = WebCoreShouldUseFontSmoothing();
608 if (originalShouldUseFontSmoothing != newShouldUseFontSmoothing)
609 CGContextSetShouldSmoothFonts(cgContext, newShouldUseFontSmoothing);
611 const FontPlatformData& platformData = font->platformData();
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]);
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]);
626 CGContextSetFont(cgContext, platformData.m_cgFont);
628 CGAffineTransform matrix = CGAffineTransformIdentity;
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);
638 wkSetCGFontRenderingMode(cgContext, drawFont);
639 CGContextSetFontSize(cgContext, 1.0f);
641 CGContextSetFontSize(cgContext, platformData.m_size);
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);
650 if (originalShouldUseFontSmoothing != newShouldUseFontSmoothing)
651 CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing);