1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 4 * (C) 2000 Dirk Mueller (mueller@kde.org) 5 * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 */ 22 23#import "config.h" 24#import "Font.h" 25 26#import "DashArray.h" 27#import "GlyphBuffer.h" 28#import "GraphicsContext.h" 29#import "Logging.h" 30#import "SimpleFontData.h" 31#import "WebCoreSystemInterface.h" 32#if USE(APPKIT) 33#import <AppKit/AppKit.h> 34#endif 35#import <wtf/MathExtras.h> 36 37#if __has_include(<CoreText/CTFontDescriptorPriv.h>) 38#import <CoreText/CTFontDescriptorPriv.h> 39#endif 40extern "C" bool CTFontDescriptorIsSystemUIFont(CTFontDescriptorRef); 41 42#if ENABLE(LETTERPRESS) 43#import "SoftLinking.h" 44#if __has_include(<CoreGraphics/CoreGraphicsPrivate.h>) 45#import <CoreGraphics/CoreGraphicsPrivate.h> 46#else 47extern CGColorRef CGContextGetFillColorAsColor(CGContextRef); 48#endif 49#import <CoreUI/CUICatalog.h> 50#import <CoreUI/CUIStyleEffectConfiguration.h> 51 52SOFT_LINK_PRIVATE_FRAMEWORK(CoreUI) 53SOFT_LINK_CLASS(CoreUI, CUICatalog) 54SOFT_LINK_CLASS(CoreUI, CUIStyleEffectConfiguration) 55 56SOFT_LINK_FRAMEWORK(UIKit) 57SOFT_LINK(UIKit, _UIKitGetTextEffectsCatalog, CUICatalog *, (void), ()) 58#endif 59 60#define SYNTHETIC_OBLIQUE_ANGLE 14 61 62#ifdef __LP64__ 63#define URefCon void* 64#else 65#define URefCon UInt32 66#endif 67 68namespace WebCore { 69 70bool Font::canReturnFallbackFontsForComplexText() 71{ 72 return true; 73} 74 75bool Font::canExpandAroundIdeographsInComplexText() 76{ 77 return true; 78} 79 80static inline void fillVectorWithHorizontalGlyphPositions(Vector<CGPoint, 256>& positions, CGContextRef context, const CGSize* advances, size_t count) 81{ 82 CGAffineTransform matrix = CGAffineTransformInvert(CGContextGetTextMatrix(context)); 83 positions[0] = CGPointZero; 84 for (size_t i = 1; i < count; ++i) { 85 CGSize advance = CGSizeApplyAffineTransform(advances[i - 1], matrix); 86 positions[i].x = positions[i - 1].x + advance.width; 87 positions[i].y = positions[i - 1].y + advance.height; 88 } 89} 90 91static inline bool shouldUseLetterpressEffect(const GraphicsContext& context) 92{ 93#if ENABLE(LETTERPRESS) 94 return context.textDrawingMode() & TextModeLetterpress; 95#else 96 UNUSED_PARAM(context); 97 return false; 98#endif 99} 100 101static void showLetterpressedGlyphsWithAdvances(const FloatPoint& point, const SimpleFontData* font, CGContextRef context, const CGGlyph* glyphs, const CGSize* advances, size_t count) 102{ 103#if ENABLE(LETTERPRESS) 104 if (!count) 105 return; 106 107 const FontPlatformData& platformData = font->platformData(); 108 if (platformData.orientation() == Vertical) { 109 // FIXME: Implement support for vertical text. See <rdar://problem/13737298>. 110 return; 111 } 112 113 CGContextSetTextPosition(context, point.x(), point.y()); 114 Vector<CGPoint, 256> positions(count); 115 fillVectorWithHorizontalGlyphPositions(positions, context, advances, count); 116 117 CTFontRef ctFont = platformData.ctFont(); 118 CGContextSetFontSize(context, CTFontGetSize(ctFont)); 119 120 static CUICatalog *catalog = _UIKitGetTextEffectsCatalog(); 121 if (!catalog) 122 return; 123 124 static CUIStyleEffectConfiguration *styleConfiguration; 125 if (!styleConfiguration) { 126 styleConfiguration = [[getCUIStyleEffectConfigurationClass() alloc] init]; 127 styleConfiguration.useSimplifiedEffect = YES; 128 } 129 130 [catalog drawGlyphs:glyphs atPositions:positions.data() inContext:context withFont:ctFont count:count stylePresetName:@"_UIKitNewLetterpressStyle" styleConfiguration:styleConfiguration foregroundColor:CGContextGetFillColorAsColor(context)]; 131#else 132 UNUSED_PARAM(point); 133 UNUSED_PARAM(font); 134 UNUSED_PARAM(context); 135 UNUSED_PARAM(glyphs); 136 UNUSED_PARAM(advances); 137 UNUSED_PARAM(count); 138#endif 139} 140 141static void showGlyphsWithAdvances(const FloatPoint& point, const SimpleFontData* font, CGContextRef context, const CGGlyph* glyphs, const CGSize* advances, size_t count) 142{ 143 if (!count) 144 return; 145 146 CGContextSetTextPosition(context, point.x(), point.y()); 147 148 const FontPlatformData& platformData = font->platformData(); 149 Vector<CGPoint, 256> positions(count); 150 if (platformData.isColorBitmapFont()) 151 fillVectorWithHorizontalGlyphPositions(positions, context, advances, count); 152 if (platformData.orientation() == Vertical) { 153 CGAffineTransform savedMatrix; 154 CGAffineTransform rotateLeftTransform = CGAffineTransformMake(0, -1, 1, 0, 0, 0); 155 savedMatrix = CGContextGetTextMatrix(context); 156 CGAffineTransform runMatrix = CGAffineTransformConcat(savedMatrix, rotateLeftTransform); 157 CGContextSetTextMatrix(context, runMatrix); 158 159 Vector<CGSize, 256> translations(count); 160 CTFontGetVerticalTranslationsForGlyphs(platformData.ctFont(), glyphs, translations.data(), count); 161 162 CGAffineTransform transform = CGAffineTransformInvert(CGContextGetTextMatrix(context)); 163 164 CGPoint position = FloatPoint(point.x(), point.y() + font->fontMetrics().floatAscent(IdeographicBaseline) - font->fontMetrics().floatAscent()); 165 for (size_t i = 0; i < count; ++i) { 166 CGSize translation = CGSizeApplyAffineTransform(translations[i], rotateLeftTransform); 167 positions[i] = CGPointApplyAffineTransform(CGPointMake(position.x - translation.width, position.y + translation.height), transform); 168 position.x += advances[i].width; 169 position.y += advances[i].height; 170 } 171 if (!platformData.isColorBitmapFont()) 172 CGContextShowGlyphsAtPositions(context, glyphs, positions.data(), count); 173 else 174 CTFontDrawGlyphs(platformData.ctFont(), glyphs, positions.data(), count, context); 175 CGContextSetTextMatrix(context, savedMatrix); 176 } else { 177 if (!platformData.isColorBitmapFont()) 178#pragma clang diagnostic push 179#pragma clang diagnostic ignored "-Wdeprecated-declarations" 180 CGContextShowGlyphsWithAdvances(context, glyphs, advances, count); 181#pragma clang diagnostic pop 182 else 183 CTFontDrawGlyphs(platformData.ctFont(), glyphs, positions.data(), count, context); 184 } 185} 186 187void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& anchorPoint) const 188{ 189 const FontPlatformData& platformData = font->platformData(); 190 if (!platformData.size()) 191 return; 192 193 CGContextRef cgContext = context->platformContext(); 194 195 bool shouldSmoothFonts; 196 bool changeFontSmoothing; 197 198 switch(fontDescription().fontSmoothing()) { 199 case Antialiased: { 200 context->setShouldAntialias(true); 201 shouldSmoothFonts = false; 202 changeFontSmoothing = true; 203 break; 204 } 205 case SubpixelAntialiased: { 206 context->setShouldAntialias(true); 207 shouldSmoothFonts = true; 208 changeFontSmoothing = true; 209 break; 210 } 211 case NoSmoothing: { 212 context->setShouldAntialias(false); 213 shouldSmoothFonts = false; 214 changeFontSmoothing = true; 215 break; 216 } 217 case AutoSmoothing: { 218 shouldSmoothFonts = true; 219 changeFontSmoothing = false; 220 break; 221 } 222 } 223 224 if (!shouldUseSmoothing()) { 225 shouldSmoothFonts = false; 226 changeFontSmoothing = true; 227 } 228 229#if !PLATFORM(IOS) 230 bool originalShouldUseFontSmoothing = false; 231 if (changeFontSmoothing) { 232 originalShouldUseFontSmoothing = wkCGContextGetShouldSmoothFonts(cgContext); 233 CGContextSetShouldSmoothFonts(cgContext, shouldSmoothFonts); 234 } 235#endif 236 237#if !PLATFORM(IOS) 238 NSFont* drawFont; 239 if (!isPrinterFont()) { 240 drawFont = [platformData.font() screenFont]; 241 if (drawFont != platformData.font()) 242 // 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). 243 LOG_ERROR("Attempting to set non-screen font (%@) when drawing to screen. Using screen font anyway, may result in incorrect metrics.", 244 [[[platformData.font() fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]); 245 } else { 246 drawFont = [platformData.font() printerFont]; 247 if (drawFont != platformData.font()) 248 NSLog(@"Attempting to set non-printer font (%@) when printing. Using printer font anyway, may result in incorrect metrics.", 249 [[[platformData.font() fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]); 250 } 251#endif 252 253 CGContextSetFont(cgContext, platformData.cgFont()); 254 255 bool useLetterpressEffect = shouldUseLetterpressEffect(*context); 256 FloatPoint point = anchorPoint; 257#if PLATFORM(IOS) 258 float fontSize = platformData.size(); 259 CGAffineTransform matrix = useLetterpressEffect || platformData.isColorBitmapFont() ? CGAffineTransformIdentity : CGAffineTransformMakeScale(fontSize, fontSize); 260 if (platformData.m_isEmoji) { 261 if (!context->emojiDrawingEnabled()) 262 return; 263 264 // Mimic the positioining of non-bitmap glyphs, which are not subpixel-positioned. 265 point.setY(ceilf(point.y())); 266 267 // Emoji glyphs snap to the CSS pixel grid. 268 point.setX(floorf(point.x())); 269 270 // Emoji glyphs are offset one CSS pixel to the right. 271 point.move(1, 0); 272 273 // Emoji glyphs are offset vertically based on font size. 274 float y = point.y(); 275 if (fontSize <= 15) { 276 // Undo Core Text's y adjustment. 277 static float yAdjustmentFactor = iosExecutableWasLinkedOnOrAfterVersion(wkIOSSystemVersion_6_0) ? .19 : .1; 278 point.setY(floorf(y - yAdjustmentFactor * (fontSize + 2) + 2)); 279 } else { 280 if (fontSize < 26) 281 y -= .35f * fontSize - 10; 282 283 // Undo Core Text's y adjustment. 284 static float yAdjustment = iosExecutableWasLinkedOnOrAfterVersion(wkIOSSystemVersion_6_0) ? 3.8 : 2; 285 point.setY(floorf(y - yAdjustment)); 286 } 287 } 288#else 289 CGAffineTransform matrix = CGAffineTransformIdentity; 290 if (drawFont && !platformData.isColorBitmapFont()) 291 memcpy(&matrix, [drawFont matrix], sizeof(matrix)); 292#endif 293 matrix.b = -matrix.b; 294 matrix.d = -matrix.d; 295 if (platformData.m_syntheticOblique) { 296 static float obliqueSkew = tanf(SYNTHETIC_OBLIQUE_ANGLE * piFloat / 180); 297 if (platformData.orientation() == Vertical) 298 matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, obliqueSkew, 0, 1, 0, 0)); 299 else 300 matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, -obliqueSkew, 1, 0, 0)); 301 } 302 CGContextSetTextMatrix(cgContext, matrix); 303 304#if PLATFORM(IOS) 305 CGContextSetFontSize(cgContext, 1); 306 CGContextSetShouldSubpixelQuantizeFonts(cgContext, context->shouldSubpixelQuantizeFonts()); 307#else 308 wkSetCGFontRenderingMode(cgContext, drawFont, context->shouldSubpixelQuantizeFonts()); 309 if (drawFont) 310 CGContextSetFontSize(cgContext, 1); 311 else 312 CGContextSetFontSize(cgContext, platformData.m_size); 313#endif 314 315 316 FloatSize shadowOffset; 317 float shadowBlur; 318 Color shadowColor; 319 ColorSpace shadowColorSpace; 320 ColorSpace fillColorSpace = context->fillColorSpace(); 321 context->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace); 322 323 AffineTransform contextCTM = context->getCTM(); 324 float syntheticBoldOffset = font->syntheticBoldOffset(); 325 if (syntheticBoldOffset && !contextCTM.isIdentityOrTranslationOrFlipped()) { 326 FloatSize horizontalUnitSizeInDevicePixels = contextCTM.mapSize(FloatSize(1, 0)); 327 float horizontalUnitLengthInDevicePixels = sqrtf(horizontalUnitSizeInDevicePixels.width() * horizontalUnitSizeInDevicePixels.width() + horizontalUnitSizeInDevicePixels.height() * horizontalUnitSizeInDevicePixels.height()); 328 if (horizontalUnitLengthInDevicePixels) 329 syntheticBoldOffset /= horizontalUnitLengthInDevicePixels; 330 }; 331 332 bool hasSimpleShadow = context->textDrawingMode() == TextModeFill && shadowColor.isValid() && !shadowBlur && !platformData.isColorBitmapFont() && (!context->shadowsIgnoreTransforms() || contextCTM.isIdentityOrTranslationOrFlipped()) && !context->isInTransparencyLayer(); 333 if (hasSimpleShadow) { 334 // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. 335 context->clearShadow(); 336 Color fillColor = context->fillColor(); 337 Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); 338 context->setFillColor(shadowFillColor, shadowColorSpace); 339 float shadowTextX = point.x() + shadowOffset.width(); 340 // If shadows are ignoring transforms, then we haven't applied the Y coordinate flip yet, so down is negative. 341 float shadowTextY = point.y() + shadowOffset.height() * (context->shadowsIgnoreTransforms() ? -1 : 1); 342 showGlyphsWithAdvances(FloatPoint(shadowTextX, shadowTextY), font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs); 343#if !PLATFORM(IOS) 344 if (syntheticBoldOffset) 345#else 346 if (syntheticBoldOffset && !platformData.m_isEmoji) 347#endif 348 showGlyphsWithAdvances(FloatPoint(shadowTextX + syntheticBoldOffset, shadowTextY), font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs); 349 context->setFillColor(fillColor, fillColorSpace); 350 } 351 352 if (useLetterpressEffect) 353 showLetterpressedGlyphsWithAdvances(point, font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs); 354 else 355 showGlyphsWithAdvances(point, font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs); 356#if !PLATFORM(IOS) 357 if (syntheticBoldOffset) 358#else 359 if (syntheticBoldOffset && !platformData.m_isEmoji) 360#endif 361 showGlyphsWithAdvances(FloatPoint(point.x() + syntheticBoldOffset, point.y()), font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs); 362 363 if (hasSimpleShadow) 364 context->setShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace); 365 366#if !PLATFORM(IOS) 367 if (changeFontSmoothing) 368 CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing); 369#endif 370} 371 372#if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK) 373struct GlyphIterationState { 374 GlyphIterationState(CGPoint startingPoint, CGPoint currentPoint, CGFloat y1, CGFloat y2, CGFloat minX, CGFloat maxX) 375 : startingPoint(startingPoint) 376 , currentPoint(currentPoint) 377 , y1(y1) 378 , y2(y2) 379 , minX(minX) 380 , maxX(maxX) 381 { 382 } 383 CGPoint startingPoint; 384 CGPoint currentPoint; 385 CGFloat y1; 386 CGFloat y2; 387 CGFloat minX; 388 CGFloat maxX; 389}; 390 391static bool findIntersectionPoint(float y, CGPoint p1, CGPoint p2, CGFloat& x) 392{ 393 x = p1.x + (y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y); 394 return (p1.y < y && p2.y > y) || (p1.y > y && p2.y < y); 395} 396 397static void updateX(GlyphIterationState& state, CGFloat x) 398{ 399 state.minX = std::min(state.minX, x); 400 state.maxX = std::max(state.maxX, x); 401} 402 403// This function is called by CGPathApply and is therefore invoked for each 404// contour in a glyph. This function models each contours as a straight line 405// and calculates the intersections between each pseudo-contour and 406// two horizontal lines (the upper and lower bounds of an underline) found in 407// GlyphIterationState::y1 and GlyphIterationState::y2. It keeps track of the 408// leftmost and rightmost intersection in GlyphIterationState::minX and 409// GlyphIterationState::maxX. 410static void findPathIntersections(void* stateAsVoidPointer, const CGPathElement* e) 411{ 412 auto& state = *static_cast<GlyphIterationState*>(stateAsVoidPointer); 413 bool doIntersection = false; 414 CGPoint point = CGPointZero; 415 switch (e->type) { 416 case kCGPathElementMoveToPoint: 417 state.startingPoint = e->points[0]; 418 state.currentPoint = e->points[0]; 419 break; 420 case kCGPathElementAddLineToPoint: 421 doIntersection = true; 422 point = e->points[0]; 423 break; 424 case kCGPathElementAddQuadCurveToPoint: 425 doIntersection = true; 426 point = e->points[1]; 427 break; 428 case kCGPathElementAddCurveToPoint: 429 doIntersection = true; 430 point = e->points[2]; 431 break; 432 case kCGPathElementCloseSubpath: 433 doIntersection = true; 434 point = state.startingPoint; 435 break; 436 } 437 if (!doIntersection) 438 return; 439 CGFloat x; 440 if (findIntersectionPoint(state.y1, state.currentPoint, point, x)) 441 updateX(state, x); 442 if (findIntersectionPoint(state.y2, state.currentPoint, point, x)) 443 updateX(state, x); 444 if ((state.currentPoint.y >= state.y1 && state.currentPoint.y <= state.y2) 445 || (state.currentPoint.y <= state.y1 && state.currentPoint.y >= state.y2)) 446 updateX(state, state.currentPoint.x); 447 state.currentPoint = point; 448} 449 450class MacGlyphToPathTranslator final : public GlyphToPathTranslator { 451public: 452 MacGlyphToPathTranslator(const TextRun& textRun, const GlyphBuffer& glyphBuffer, const FloatPoint& textOrigin) 453 : m_index(0) 454 , m_textRun(textRun) 455 , m_glyphBuffer(glyphBuffer) 456 , m_fontData(glyphBuffer.fontDataAt(m_index)) 457 , m_translation(CGAffineTransformScale(CGAffineTransformMakeTranslation(textOrigin.x(), textOrigin.y()), 1, -1)) 458 { 459 moveToNextValidGlyph(); 460 } 461private: 462 virtual bool containsMorePaths() override 463 { 464 return m_index != m_glyphBuffer.size(); 465 } 466 virtual Path path() override; 467 virtual std::pair<float, float> extents() override; 468 virtual GlyphUnderlineType underlineType() override; 469 virtual void advance() override; 470 void moveToNextValidGlyph(); 471 472 int m_index; 473 const TextRun& m_textRun; 474 const GlyphBuffer& m_glyphBuffer; 475 const SimpleFontData* m_fontData; 476 CGAffineTransform m_translation; 477}; 478 479Path MacGlyphToPathTranslator::path() 480{ 481 RetainPtr<CGPathRef> result = adoptCF(CTFontCreatePathForGlyph(m_fontData->platformData().ctFont(), m_glyphBuffer.glyphAt(m_index), &m_translation)); 482 return adoptCF(CGPathCreateMutableCopy(result.get())); 483} 484 485std::pair<float, float> MacGlyphToPathTranslator::extents() 486{ 487 CGPoint beginning = CGPointApplyAffineTransform(CGPointMake(0, 0), m_translation); 488 CGSize end = CGSizeApplyAffineTransform(m_glyphBuffer.advanceAt(m_index), m_translation); 489 return std::make_pair(static_cast<float>(beginning.x), static_cast<float>(beginning.x + end.width)); 490} 491 492auto MacGlyphToPathTranslator::underlineType() -> GlyphUnderlineType 493{ 494 return computeUnderlineType(m_textRun, m_glyphBuffer, m_index); 495} 496 497void MacGlyphToPathTranslator::moveToNextValidGlyph() 498{ 499 if (!m_fontData->isSVGFont()) 500 return; 501 advance(); 502} 503 504void MacGlyphToPathTranslator::advance() 505{ 506 do { 507 GlyphBufferAdvance advance = m_glyphBuffer.advanceAt(m_index); 508 m_translation = CGAffineTransformTranslate(m_translation, advance.width(), advance.height()); 509 ++m_index; 510 if (m_index >= m_glyphBuffer.size()) 511 break; 512 m_fontData = m_glyphBuffer.fontDataAt(m_index); 513 } while (m_fontData->isSVGFont() && m_index < m_glyphBuffer.size()); 514} 515 516DashArray Font::dashesForIntersectionsWithRect(const TextRun& run, const FloatPoint& textOrigin, const FloatRect& lineExtents) const 517{ 518 if (loadingCustomFonts()) 519 return DashArray(); 520 521 GlyphBuffer glyphBuffer; 522 glyphBuffer.saveOffsetsInString(); 523 float deltaX; 524 if (codePath(run) != Font::Complex) 525 deltaX = getGlyphsAndAdvancesForSimpleText(run, 0, run.length(), glyphBuffer); 526 else 527 deltaX = getGlyphsAndAdvancesForComplexText(run, 0, run.length(), glyphBuffer); 528 529 if (!glyphBuffer.size()) 530 return DashArray(); 531 532 // FIXME: Handle SVG + non-SVG interleaved runs. https://bugs.webkit.org/show_bug.cgi?id=133778 533 const SimpleFontData* fontData = glyphBuffer.fontDataAt(0); 534 std::unique_ptr<GlyphToPathTranslator> translator; 535 bool isSVG = false; 536 FloatPoint origin = FloatPoint(textOrigin.x() + deltaX, textOrigin.y()); 537 if (!fontData->isSVGFont()) 538 translator = std::make_unique<MacGlyphToPathTranslator>(run, glyphBuffer, origin); 539 else { 540 translator = run.renderingContext()->createGlyphToPathTranslator(*fontData, &run, glyphBuffer, 0, glyphBuffer.size(), origin); 541 isSVG = true; 542 } 543 DashArray result; 544 for (int index = 0; translator->containsMorePaths(); ++index, translator->advance()) { 545 GlyphIterationState info = GlyphIterationState(CGPointMake(0, 0), CGPointMake(0, 0), lineExtents.y(), lineExtents.y() + lineExtents.height(), lineExtents.x() + lineExtents.width(), lineExtents.x()); 546 const SimpleFontData* localFontData = glyphBuffer.fontDataAt(index); 547 if (!localFontData || (!isSVG && localFontData->isSVGFont()) || (isSVG && localFontData != fontData)) { 548 // The advances will get all messed up if we do anything other than bail here. 549 result.clear(); 550 break; 551 } 552 switch (translator->underlineType()) { 553 case GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders: { 554 Path path = translator->path(); 555 CGPathApply(path.platformPath(), &info, &findPathIntersections); 556 if (info.minX < info.maxX) { 557 result.append(info.minX - lineExtents.x()); 558 result.append(info.maxX - lineExtents.x()); 559 } 560 break; 561 } 562 case GlyphToPathTranslator::GlyphUnderlineType::SkipGlyph: { 563 std::pair<float, float> extents = translator->extents(); 564 result.append(extents.first - lineExtents.x()); 565 result.append(extents.second - lineExtents.x()); 566 break; 567 } 568 case GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph: 569 // Nothing to do 570 break; 571 } 572 } 573 return result; 574} 575#endif 576 577bool Font::primaryFontDataIsSystemFont() const 578{ 579#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED > 1090 580 const auto* fontData = primaryFont(); 581 return !fontData->isSVGFont() && CTFontDescriptorIsSystemUIFont(adoptCF(CTFontCopyFontDescriptor(fontData->platformData().ctFont())).get()); 582#else 583 // System fonts are hidden by having a name that begins with a period, so simply search 584 // for that here rather than try to keep the list up to date. 585 return firstFamily().startsWith('.'); 586#endif 587} 588 589} 590