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., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
30 #import "BlockExceptions.h"
31 #import "FoundationExtras.h"
33 #import "FontFallbackList.h"
34 #import "GraphicsContext.h"
35 #import "KWQKHTMLSettings.h"
38 #import "WebTextRendererFactory.h"
42 #import "WebCoreSystemInterface.h"
43 #import "WebCoreTextRenderer.h"
45 #define SYNTHETIC_OBLIQUE_ANGLE 14
47 #define POP_DIRECTIONAL_FORMATTING 0x202C
48 #define LEFT_TO_RIGHT_OVERRIDE 0x202D
49 #define RIGHT_TO_LEFT_OVERRIDE 0x202E
55 FontFallbackList::FontFallbackList()
56 :m_pitch(UnknownPitch), m_font(nil)
58 m_platformFont.font = nil;
61 FontFallbackList::~FontFallbackList()
63 KWQRelease(m_platformFont.font);
66 const FontPlatformData& FontFallbackList::platformFont(const FontDescription& fontDescription) const
68 if (!m_platformFont.font) {
69 CREATE_FAMILY_ARRAY(fontDescription, families);
70 BEGIN_BLOCK_OBJC_EXCEPTIONS;
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;
82 return m_platformFont;
85 FontData* FontFallbackList::primaryFont(const FontDescription& fontDescription) const
88 m_font = [[WebTextRendererFactory sharedFactory] rendererWithFont:platformFont(fontDescription)];
92 void FontFallbackList::determinePitch(const FontDescription& fontDescription) const
94 BEGIN_BLOCK_OBJC_EXCEPTIONS;
95 if ([[WebTextRendererFactory sharedFactory] isFontFixedPitch:platformFont(fontDescription)])
98 m_pitch = VariablePitch;
99 END_BLOCK_OBJC_EXCEPTIONS;
102 void FontFallbackList::invalidate()
105 KWQRelease(m_platformFont.font);
106 m_platformFont.font = nil;
107 m_pitch = UnknownPitch;
110 // =================================================================
111 // Font Class (Platform-Specific Portion)
112 // =================================================================
114 struct ATSULayoutParameters
116 ATSULayoutParameters(const TextRun& run, int toAdd, TextDirection dir, bool applyWordRounding, bool applyRunRounding)
117 :m_run(run), 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)
122 void initialize(const Font* font);
124 const TextRun& m_run;
128 bool m_applyWordRounding;
129 bool m_applyRunRounding;
133 ATSUTextLayout m_layout;
134 const FontData **m_fonts;
137 bool m_hasSyntheticBold;
138 bool m_syntheticBoldPass;
142 // Be sure to free the array allocated by this function.
143 static TextRun addDirectionalOverride(const TextRun& run, bool rtl)
145 UChar* charactersWithOverride = new UChar[run.length() + 2];
146 charactersWithOverride[0] = rtl ? RIGHT_TO_LEFT_OVERRIDE : LEFT_TO_RIGHT_OVERRIDE;
147 memcpy(&charactersWithOverride[1], run.data(0), sizeof(UChar) * run.length());
148 charactersWithOverride[run.length() + 1] = POP_DIRECTIONAL_FORMATTING;
150 return TextRun(charactersWithOverride, run.length() + 2, run.from() + 1, run.to() + 1);
153 static void initializeATSUStyle(const FontData* fontData)
155 // The two NSFont calls in this method (pointSize and _atsFontID) do not raise exceptions.
157 if (!fontData->m_ATSUStyleInitialized) {
159 ByteCount propTableSize;
161 status = ATSUCreateStyle(&fontData->m_ATSUStyle);
163 LOG_ERROR("ATSUCreateStyle failed (%d)", status);
165 ATSUFontID fontID = wkGetNSFontATSUFontId(fontData->m_font.font);
167 ATSUDisposeStyle(fontData->m_ATSUStyle);
168 LOG_ERROR("unable to get ATSUFontID for %@", fontData->m_font.font);
172 CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
173 if (fontData->m_font.syntheticOblique)
174 transform = CGAffineTransformConcat(transform, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0));
175 Fixed fontSize = FloatToFixed([fontData->m_font.font pointSize]);
176 // Turn off automatic kerning until it is supported in the CG code path (6136 in bugzilla)
177 Fract kerningInhibitFactor = FloatToFract(1.0);
178 ATSUAttributeTag styleTags[4] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag, kATSUKerningInhibitFactorTag };
179 ByteCount styleSizes[4] = { sizeof(Fixed), sizeof(ATSUFontID), sizeof(CGAffineTransform), sizeof(Fract) };
180 ATSUAttributeValuePtr styleValues[4] = { &fontSize, &fontID, &transform, &kerningInhibitFactor };
181 status = ATSUSetAttributes(fontData->m_ATSUStyle, 4, styleTags, styleSizes, styleValues);
183 LOG_ERROR("ATSUSetAttributes failed (%d)", status);
184 status = ATSFontGetTable(fontID, 'prop', 0, 0, 0, &propTableSize);
185 if (status == noErr) // naively assume that if a 'prop' table exists then it contains mirroring info
186 fontData->m_ATSUMirrors = true;
187 else if (status == kATSInvalidFontTableAccess)
188 fontData->m_ATSUMirrors = false;
190 LOG_ERROR("ATSFontGetTable failed (%d)", status);
192 // Turn off ligatures such as 'fi' to match the CG code path's behavior, until bugzilla 6135 is fixed.
193 // Don't be too aggressive: if the font doesn't contain 'a', then assume that any ligatures it contains are
194 // in characters that always go through ATSUI, and therefore allow them. Geeza Pro is an example.
195 // See bugzilla 5166.
196 if ([[fontData->m_font.font coveredCharacterSet] characterIsMember:'a']) {
197 ATSUFontFeatureType featureTypes[] = { kLigaturesType };
198 ATSUFontFeatureSelector featureSelectors[] = { kCommonLigaturesOffSelector };
199 status = ATSUSetFontFeatures(fontData->m_ATSUStyle, 1, featureTypes, featureSelectors);
202 fontData->m_ATSUStyleInitialized = true;
206 static OSStatus overrideLayoutOperation(ATSULayoutOperationSelector iCurrentOperation, ATSULineRef iLineRef, UInt32 iRefCon,
207 void *iOperationCallbackParameterPtr, ATSULayoutOperationCallbackStatus *oCallbackStatus)
209 ATSULayoutParameters *params = (ATSULayoutParameters *)iRefCon;
212 ATSLayoutRecord *layoutRecords;
214 if (params->m_applyWordRounding) {
215 status = ATSUDirectGetLayoutDataArrayPtrFromLineRef(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, true, (void **)&layoutRecords, &count);
216 if (status != noErr) {
217 *oCallbackStatus = kATSULayoutOperationCallbackStatusContinue;
221 Fixed lastNativePos = 0;
222 float lastAdjustedPos = 0;
223 const UChar *characters = params->m_run.data(params->m_run.from());
224 const FontData **renderers = params->m_fonts + params->m_run.from();
225 const FontData *renderer;
226 const FontData *lastRenderer = 0;
228 ByteCount offset = layoutRecords[0].originalOffset;
229 nextCh = *(UChar *)(((char *)characters)+offset);
230 bool shouldRound = false;
231 bool syntheticBoldPass = params->m_syntheticBoldPass;
232 Fixed syntheticBoldOffset = 0;
233 ATSGlyphRef spaceGlyph = 0;
234 bool hasExtraSpacing = params->m_font->letterSpacing() || params->m_font->wordSpacing() | params->m_padding;
235 float padding = params->m_padding;
236 // In the CoreGraphics code path, the rounding hack is applied in logical order.
237 // Here it is applied in visual left-to-right order, which may be better.
238 ItemCount lastRoundingChar = 0;
240 for (i = 1; i < count; i++) {
241 bool isLastChar = i == count - 1;
242 renderer = renderers[offset / 2];
243 if (renderer != lastRenderer) {
244 lastRenderer = renderer;
245 // The CoreGraphics interpretation of NSFontAntialiasedIntegerAdvancementsRenderingMode seems
246 // to be "round each glyph's width to the nearest integer". This is not the same as ATSUI
247 // does in any of its device-metrics modes.
248 shouldRound = [renderer->m_font.font renderingMode] == NSFontAntialiasedIntegerAdvancementsRenderingMode;
249 if (syntheticBoldPass) {
250 syntheticBoldOffset = FloatToFixed(renderer->m_syntheticBoldOffset);
251 spaceGlyph = renderer->m_spaceGlyph;
254 float width = FixedToFloat(layoutRecords[i].realPos - lastNativePos);
255 lastNativePos = layoutRecords[i].realPos;
257 width = roundf(width);
258 width += renderer->m_syntheticBoldOffset;
259 if (renderer->m_treatAsFixedPitch ? width == renderer->m_spaceWidth : (layoutRecords[i-1].flags & kATSGlyphInfoIsWhiteSpace))
260 width = renderer->m_adjustedSpaceWidth;
262 if (hasExtraSpacing) {
263 if (width && params->m_font->letterSpacing())
264 width +=params->m_font->letterSpacing();
265 if (Font::treatAsSpace(nextCh)) {
266 if (params->m_padding) {
267 if (padding < params->m_padPerSpace) {
271 width += params->m_padPerSpace;
272 padding -= params->m_padPerSpace;
275 if (offset != 0 && !Font::treatAsSpace(*((UChar *)(((char *)characters)+offset) - 1)) && params->m_font->wordSpacing())
276 width += params->m_font->wordSpacing();
281 offset = layoutRecords[i].originalOffset;
282 // Use space for nextCh at the end of the loop so that we get inside the rounding hack code.
283 // We won't actually round unless the other conditions are satisfied.
284 nextCh = isLastChar ? ' ' : *(UChar *)(((char *)characters)+offset);
286 if (Font::isRoundingHackCharacter(ch))
287 width = ceilf(width);
288 lastAdjustedPos = lastAdjustedPos + width;
289 if (Font::isRoundingHackCharacter(nextCh)
291 || params->m_applyRunRounding
292 || (params->m_run.to() < (int)params->m_run.length() &&
293 Font::isRoundingHackCharacter(characters[params->m_run.to() - params->m_run.from()])))) {
295 lastAdjustedPos = ceilf(lastAdjustedPos);
297 float roundingWidth = ceilf(lastAdjustedPos) - lastAdjustedPos;
298 Fixed rw = FloatToFixed(roundingWidth);
300 for (j = lastRoundingChar; j < i; j++)
301 layoutRecords[j].realPos += rw;
302 lastRoundingChar = i;
303 lastAdjustedPos += roundingWidth;
306 if (syntheticBoldPass) {
307 if (syntheticBoldOffset)
308 layoutRecords[i-1].realPos += syntheticBoldOffset;
310 layoutRecords[i-1].glyphID = spaceGlyph;
312 layoutRecords[i].realPos = FloatToFixed(lastAdjustedPos);
315 status = ATSUDirectReleaseLayoutDataArrayPtr(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&layoutRecords);
317 *oCallbackStatus = kATSULayoutOperationCallbackStatusHandled;
321 void ATSULayoutParameters::initialize(const Font* font)
325 // FIXME: It is probably best to always allocate a buffer for RTL, since even if for this
326 // fontData ATSUMirrors is true, for a substitute fontData it might be false.
327 const FontData* fontData = font->primaryFont();
328 m_fonts = new const FontData*[m_run.length()];
329 m_charBuffer = (UChar*)((font->isSmallCaps() || (m_rtl && !fontData->m_ATSUMirrors)) ? new UChar[m_run.length()] : 0);
331 // The only Cocoa calls here are to NSGraphicsContext, which does not raise exceptions.
333 ATSUTextLayout layout;
335 ATSULayoutOperationOverrideSpecifier overrideSpecifier;
337 initializeATSUStyle(fontData);
339 // FIXME: This is currently missing the following required features that the CoreGraphics code path has:
340 // - \n, \t, and nonbreaking space render as a space.
341 // - Other control characters do not render (other code path uses zero-width spaces).
343 UniCharCount totalLength = m_run.length();
344 UniCharArrayOffset runTo = m_run.to();
345 UniCharArrayOffset runFrom = m_run.from();
348 memcpy(m_charBuffer, m_run.characters(), totalLength * sizeof(UChar));
350 UniCharCount runLength = runTo - runFrom;
352 status = ATSUCreateTextLayoutWithTextPtr(
353 (m_charBuffer ? m_charBuffer : m_run.characters()),
356 totalLength, // total length
358 &runLength, // length of style run
359 &fontData->m_ATSUStyle,
362 LOG_ERROR("ATSUCreateTextLayoutWithTextPtr failed(%d)", status);
364 ATSUSetTextLayoutRefCon(m_layout, (UInt32)this);
366 CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
367 ATSLineLayoutOptions lineLayoutOptions = kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers;
369 overrideSpecifier.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
370 overrideSpecifier.overrideUPP = overrideLayoutOperation;
371 ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineLayoutOptionsTag, kATSULineDirectionTag, kATSULayoutOperationOverrideTag };
372 ByteCount sizes[] = { sizeof(CGContextRef), sizeof(ATSLineLayoutOptions), sizeof(Boolean), sizeof(ATSULayoutOperationOverrideSpecifier) };
373 ATSUAttributeValuePtr values[] = { &cgContext, &lineLayoutOptions, &rtl, &overrideSpecifier };
375 status = ATSUSetLayoutControls(layout, (m_applyWordRounding ? 4 : 3), tags, sizes, values);
377 LOG_ERROR("ATSUSetLayoutControls failed(%d)", status);
379 status = ATSUSetTransientFontMatching(layout, YES);
381 LOG_ERROR("ATSUSetTransientFontMatching failed(%d)", status);
383 m_hasSyntheticBold = false;
384 ATSUFontID ATSUSubstituteFont;
385 UniCharArrayOffset substituteOffset = runFrom;
386 UniCharCount substituteLength;
387 UniCharArrayOffset lastOffset;
388 const FontData* substituteFontData = 0;
390 while (substituteOffset < runTo) {
391 lastOffset = substituteOffset;
392 status = ATSUMatchFontsToText(layout, substituteOffset, kATSUToTextEnd, &ATSUSubstituteFont, &substituteOffset, &substituteLength);
393 if (status == kATSUFontsMatched || status == kATSUFontsNotMatched) {
394 // FIXME: Should go through fallback list eventually.
395 substituteFontData = fontData->findSubstituteFontData(m_run.characters() + substituteOffset, substituteLength, m_font->fontDescription());
396 if (substituteFontData) {
397 initializeATSUStyle(substituteFontData);
398 if (substituteFontData->m_ATSUStyle)
399 ATSUSetRunStyle(layout, substituteFontData->m_ATSUStyle, substituteOffset, substituteLength);
401 substituteFontData = fontData;
403 substituteOffset = runTo;
404 substituteLength = 0;
407 bool isSmallCap = false;
408 UniCharArrayOffset firstSmallCap = 0;
409 const FontData *r = fontData;
410 UniCharArrayOffset i;
411 for (i = lastOffset; ; i++) {
412 if (i == substituteOffset || i == substituteOffset + substituteLength) {
415 initializeATSUStyle(r->smallCapsFontData());
416 ATSUSetRunStyle(layout, r->smallCapsFontData()->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
418 if (i == substituteOffset && substituteLength > 0)
419 r = substituteFontData;
423 if (m_rtl && m_charBuffer && !r->m_ATSUMirrors)
424 m_charBuffer[i] = u_charMirror(m_charBuffer[i]);
425 if (m_font->isSmallCaps()) {
426 UChar c = m_charBuffer[i];
428 if (U_GET_GC_MASK(c) & U_GC_M_MASK)
429 m_fonts[i] = isSmallCap ? r->smallCapsFontData() : r;
430 else if (!u_isUUppercase(c) && (newC = u_toupper(c)) != c) {
431 m_charBuffer[i] = newC;
436 m_fonts[i] = r->smallCapsFontData();
440 initializeATSUStyle(r->smallCapsFontData());
441 ATSUSetRunStyle(layout, r->smallCapsFontData()->m_ATSUStyle, firstSmallCap, i - firstSmallCap);
447 if (m_fonts[i]->m_syntheticBoldOffset)
448 m_hasSyntheticBold = true;
450 substituteOffset += substituteLength;
455 for (k = 0; k < totalLength; k++)
456 if (Font::treatAsSpace(m_run[k]))
459 m_padPerSpace = ceilf(m_padding / numSpaces);
464 static void disposeATSULayoutParameters(ATSULayoutParameters *params)
466 ATSUDisposeTextLayout(params->m_layout);
467 delete []params->m_charBuffer;
468 delete []params->m_fonts;
471 const FontPlatformData& Font::platformFont() const
473 return m_fontList->platformFont(fontDescription());
476 IntRect Font::selectionRectForText(const TextRun& textRun, const IntPoint& point, int h, int tabWidth, int xpos, int toAdd, bool rtl, bool visuallyOrdered) const
480 CREATE_FAMILY_ARRAY(fontDescription(), families);
483 WebCoreInitializeTextRun(&run, textRun.characters(), textRun.length(), textRun.from(), textRun.to());
484 WebCoreTextStyle style;
485 WebCoreInitializeEmptyTextStyle(&style);
487 style.directionalOverride = visuallyOrdered;
488 style.letterSpacing = letterSpacing();
489 style.wordSpacing = wordSpacing();
490 style.smallCaps = fontDescription().smallCaps();
491 style.families = families;
492 style.padding = toAdd;
493 style.tabWidth = tabWidth;
495 WebCoreTextGeometry geometry;
496 WebCoreInitializeEmptyTextGeometry(&geometry);
497 geometry.point = point;
498 geometry.selectionY = point.y();
499 geometry.selectionHeight = h;
500 geometry.useFontMetricsForSelectionYAndHeight = false;
501 return enclosingIntRect(m_fontList->primaryFont(fontDescription())->selectionRectForRun(&run, &style, &geometry));
504 void Font::drawComplexText(GraphicsContext* graphicsContext, const TextRun& run, const IntPoint& point, int tabWidth, int xpos,
505 int toAdd, TextDirection d, bool visuallyOrdered) const
509 const UChar* characters = run.characters();
510 int runLength = run.length();
512 TextRun adjustedRun = visuallyOrdered ? addDirectionalOverride(run, d == RTL) : run;
513 ATSULayoutParameters params(adjustedRun, toAdd, d, true, true);
514 params.initialize(this);
516 [nsColor(graphicsContext->pen().color()) set];
518 // ATSUI can't draw beyond -32768 to +32767 so we translate the CTM and tell ATSUI to draw at (0, 0).
519 // FIXME: Cut the dependency on currentContext here.
520 NSGraphicsContext *gContext = [NSGraphicsContext currentContext];
521 CGContextRef context = (CGContextRef)[gContext graphicsPort];
522 CGContextTranslateCTM(context, point.x(), point.y());
523 bool flipped = [gContext isFlipped];
525 CGContextScaleCTM(context, 1.0, -1.0);
526 status = ATSUDrawText(params.m_layout, adjustedRun.from(), runLength, 0, 0);
527 if (status == noErr && params.m_hasSyntheticBold) {
528 // Force relayout for the bold pass
529 ATSUClearLayoutCache(params.m_layout, 0);
530 params.m_syntheticBoldPass = true;
531 status = ATSUDrawText(params.m_layout, adjustedRun.from(), runLength, 0, 0);
534 CGContextScaleCTM(context, 1.0, -1.0);
535 CGContextTranslateCTM(context, -point.x(), -point.y());
537 if (status != noErr) {
538 // Nothing to do but report the error (dev build only).
539 LOG_ERROR("ATSUDrawText() failed(%d)", status);
542 disposeATSULayoutParameters(¶ms);
548 void Font::drawHighlightForText(GraphicsContext* context, const TextRun& textRun, const IntPoint& point, int h, int tabWidth, int xpos,
549 int toAdd, TextDirection d, bool visuallyOrdered, const Color& backgroundColor) const
551 // Avoid allocations, use stack array to pass font families. Normally these
552 // css fallback lists are small <= 3.
553 CREATE_FAMILY_ARRAY(*this, families);
556 WebCoreInitializeTextRun(&run, textRun.characters(), textRun.length(), textRun.from(), textRun.to());
557 WebCoreTextStyle style;
558 WebCoreInitializeEmptyTextStyle(&style);
559 style.textColor = nsColor(context->pen().color());
560 style.backgroundColor = backgroundColor.isValid() ? nsColor(backgroundColor) : nil;
561 style.rtl = d == RTL;
562 style.directionalOverride = visuallyOrdered;
563 style.letterSpacing = letterSpacing();
564 style.wordSpacing = wordSpacing();
565 style.smallCaps = isSmallCaps();
566 style.families = families;
567 style.padding = toAdd;
568 style.tabWidth = tabWidth;
570 WebCoreTextGeometry geometry;
571 WebCoreInitializeEmptyTextGeometry(&geometry);
572 geometry.point = point;
573 geometry.selectionY = point.y();
574 geometry.selectionHeight = h;
575 geometry.useFontMetricsForSelectionYAndHeight = false;
576 m_fontList->primaryFont(fontDescription())->drawHighlightForRun(&run, &style, &geometry);
579 void Font::drawLineForText(GraphicsContext* context, const IntPoint& point, int yOffset, int width) const
581 m_fontList->primaryFont(fontDescription())->drawLineForCharacters(point, yOffset, width, context->pen().color(), context->pen().width());
584 void Font::drawLineForMisspelling(GraphicsContext* context, const IntPoint& point, int width) const
586 m_fontList->primaryFont(fontDescription())->drawLineForMisspelling(point, width);
589 int Font::misspellingLineThickness(GraphicsContext* context) const
591 return m_fontList->primaryFont(fontDescription())->misspellingLineThickness();
594 float Font::floatWidthForComplexText(const TextRun& run, int tabWidth, int xpos, bool runRounding) const
596 ATSULayoutParameters params(run, 0, LTR, true, runRounding);
597 params.initialize(this);
601 ATSTrapezoid firstGlyphBounds;
602 ItemCount actualNumBounds;
603 status = ATSUGetGlyphBounds(params.m_layout, 0, 0, run.from(), run.to() - run.from(), kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds);
605 LOG_ERROR("ATSUGetGlyphBounds() failed(%d)", status);
606 if (actualNumBounds != 1)
607 LOG_ERROR("unexpected result from ATSUGetGlyphBounds(): actualNumBounds(%d) != 1", actualNumBounds);
609 disposeATSULayoutParameters(¶ms);
611 return MAX(FixedToFloat(firstGlyphBounds.upperRight.x), FixedToFloat(firstGlyphBounds.lowerRight.x)) -
612 MIN(FixedToFloat(firstGlyphBounds.upperLeft.x), FixedToFloat(firstGlyphBounds.lowerLeft.x));
615 int Font::checkSelectionPoint(const TextRun& textRun, int toAdd, int tabWidth, int xpos, int x, TextDirection d, bool visuallyOrdered, bool includePartialGlyphs) const
618 CREATE_FAMILY_ARRAY(fontDescription(), families);
621 WebCoreInitializeTextRun(&run, textRun.characters(), textRun.length(), textRun.from(), textRun.to());
623 WebCoreTextStyle style;
624 WebCoreInitializeEmptyTextStyle(&style);
625 style.letterSpacing = letterSpacing();
626 style.wordSpacing = wordSpacing();
627 style.smallCaps = fontDescription().smallCaps();
628 style.families = families;
629 style.padding = toAdd;
630 style.tabWidth = tabWidth;
632 style.rtl = d == RTL;
633 style.directionalOverride = visuallyOrdered;
635 return m_fontList->primaryFont(fontDescription())->pointToOffset(&run, &style, x, includePartialGlyphs);
638 void Font::drawGlyphs(GraphicsContext* context, const FontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
640 // FIXME: Grab the CGContext from the GraphicsContext eventually, when we have made sure to shield against flipping caused by calls into us
642 NSGraphicsContext *gContext = [NSGraphicsContext currentContext];
643 CGContextRef cgContext = (CGContextRef)[gContext graphicsPort];
645 bool originalShouldUseFontSmoothing = wkCGContextGetShouldSmoothFonts(cgContext);
646 CGContextSetShouldSmoothFonts(cgContext, WebCoreShouldUseFontSmoothing());
648 const FontPlatformData& platformData = font->platformData();
650 if ([gContext isDrawingToScreen]) {
651 drawFont = [platformData.font screenFont];
652 if (drawFont != platformData.font)
653 // 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).
654 LOG_ERROR("Attempting to set non-screen font (%@) when drawing to screen. Using screen font anyway, may result in incorrect metrics.",
655 [[[platformData.font fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]);
657 drawFont = [platformData.font printerFont];
658 if (drawFont != platformData.font)
659 NSLog(@"Attempting to set non-printer font (%@) when printing. Using printer font anyway, may result in incorrect metrics.",
660 [[[platformData.font fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]);
663 CGContextSetFont(cgContext, wkGetCGFontFromNSFont(drawFont));
665 CGAffineTransform matrix;
666 memcpy(&matrix, [drawFont matrix], sizeof(matrix));
667 if ([gContext isFlipped]) {
668 matrix.b = -matrix.b;
669 matrix.d = -matrix.d;
671 if (platformData.syntheticOblique)
672 matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0));
673 CGContextSetTextMatrix(cgContext, matrix);
675 wkSetCGFontRenderingMode(cgContext, drawFont);
676 CGContextSetFontSize(cgContext, 1.0f);
678 [nsColor(context->pen().color()) set];
680 CGContextSetTextPosition(cgContext, point.x(), point.y());
681 CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
682 if (font->m_syntheticBoldOffset) {
683 CGContextSetTextPosition(cgContext, point.x() + font->m_syntheticBoldOffset, point.y());
684 CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs);
687 CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing);