Revert r170413 and r170390
[WebKit-https.git] / Source / WebCore / platform / graphics / mac / FontMac.mm
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
40 extern "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
47 extern CGColorRef CGContextGetFillColorAsColor(CGContextRef);
48 #endif
49 #import <CoreUI/CUICatalog.h>
50 #import <CoreUI/CUIStyleEffectConfiguration.h>
51
52 SOFT_LINK_PRIVATE_FRAMEWORK(CoreUI)
53 SOFT_LINK_CLASS(CoreUI, CUICatalog)
54 SOFT_LINK_CLASS(CoreUI, CUIStyleEffectConfiguration)
55
56 SOFT_LINK_FRAMEWORK(UIKit)
57 SOFT_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
68 namespace WebCore {
69
70 bool Font::canReturnFallbackFontsForComplexText()
71 {
72     return true;
73 }
74
75 bool Font::canExpandAroundIdeographsInComplexText()
76 {
77     return true;
78 }
79
80 static 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
91 static 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
101 static 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
141 static 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
187 void 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 #else
307     wkSetCGFontRenderingMode(cgContext, drawFont, context->shouldSubpixelQuantizeFonts());
308     if (drawFont)
309         CGContextSetFontSize(cgContext, 1);
310     else
311         CGContextSetFontSize(cgContext, platformData.m_size);
312 #endif
313
314
315     FloatSize shadowOffset;
316     float shadowBlur;
317     Color shadowColor;
318     ColorSpace shadowColorSpace;
319     ColorSpace fillColorSpace = context->fillColorSpace();
320     context->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace);
321
322     AffineTransform contextCTM = context->getCTM();
323     float syntheticBoldOffset = font->syntheticBoldOffset();
324     if (syntheticBoldOffset && !contextCTM.isIdentityOrTranslationOrFlipped()) {
325         FloatSize horizontalUnitSizeInDevicePixels = contextCTM.mapSize(FloatSize(1, 0));
326         float horizontalUnitLengthInDevicePixels = sqrtf(horizontalUnitSizeInDevicePixels.width() * horizontalUnitSizeInDevicePixels.width() + horizontalUnitSizeInDevicePixels.height() * horizontalUnitSizeInDevicePixels.height());
327         if (horizontalUnitLengthInDevicePixels)
328             syntheticBoldOffset /= horizontalUnitLengthInDevicePixels;
329     };
330
331     bool hasSimpleShadow = context->textDrawingMode() == TextModeFill && shadowColor.isValid() && !shadowBlur && !platformData.isColorBitmapFont() && (!context->shadowsIgnoreTransforms() || contextCTM.isIdentityOrTranslationOrFlipped()) && !context->isInTransparencyLayer();
332     if (hasSimpleShadow) {
333         // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing.
334         context->clearShadow();
335         Color fillColor = context->fillColor();
336         Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255);
337         context->setFillColor(shadowFillColor, shadowColorSpace);
338         float shadowTextX = point.x() + shadowOffset.width();
339         // If shadows are ignoring transforms, then we haven't applied the Y coordinate flip yet, so down is negative.
340         float shadowTextY = point.y() + shadowOffset.height() * (context->shadowsIgnoreTransforms() ? -1 : 1);
341         showGlyphsWithAdvances(FloatPoint(shadowTextX, shadowTextY), font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
342 #if !PLATFORM(IOS)
343         if (syntheticBoldOffset)
344 #else
345         if (syntheticBoldOffset && !platformData.m_isEmoji)
346 #endif
347             showGlyphsWithAdvances(FloatPoint(shadowTextX + syntheticBoldOffset, shadowTextY), font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
348         context->setFillColor(fillColor, fillColorSpace);
349     }
350
351     if (useLetterpressEffect)
352         showLetterpressedGlyphsWithAdvances(point, font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
353     else
354         showGlyphsWithAdvances(point, font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
355 #if !PLATFORM(IOS)
356     if (syntheticBoldOffset)
357 #else
358     if (syntheticBoldOffset && !platformData.m_isEmoji)
359 #endif
360         showGlyphsWithAdvances(FloatPoint(point.x() + syntheticBoldOffset, point.y()), font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
361
362     if (hasSimpleShadow)
363         context->setShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace);
364
365 #if !PLATFORM(IOS)
366     if (changeFontSmoothing)
367         CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing);
368 #endif
369 }
370
371 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
372 struct GlyphIterationState {
373     GlyphIterationState(CGPoint startingPoint, CGPoint currentPoint, CGFloat y1, CGFloat y2, CGFloat minX, CGFloat maxX)
374         : startingPoint(startingPoint)
375         , currentPoint(currentPoint)
376         , y1(y1)
377         , y2(y2)
378         , minX(minX)
379         , maxX(maxX)
380     {
381     }
382     CGPoint startingPoint;
383     CGPoint currentPoint;
384     CGFloat y1;
385     CGFloat y2;
386     CGFloat minX;
387     CGFloat maxX;
388 };
389
390 static bool findIntersectionPoint(float y, CGPoint p1, CGPoint p2, CGFloat& x)
391 {
392     x = p1.x + (y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y);
393     return (p1.y < y && p2.y > y) || (p1.y > y && p2.y < y);
394 }
395
396 static void updateX(GlyphIterationState& state, CGFloat x)
397 {
398     state.minX = std::min(state.minX, x);
399     state.maxX = std::max(state.maxX, x);
400 }
401
402 // This function is called by CGPathApply and is therefore invoked for each
403 // contour in a glyph. This function models each contours as a straight line
404 // and calculates the intersections between each pseudo-contour and
405 // two horizontal lines (the upper and lower bounds of an underline) found in
406 // GlyphIterationState::y1 and GlyphIterationState::y2. It keeps track of the
407 // leftmost and rightmost intersection in GlyphIterationState::minX and
408 // GlyphIterationState::maxX.
409 static void findPathIntersections(void* stateAsVoidPointer, const CGPathElement* e)
410 {
411     auto& state = *static_cast<GlyphIterationState*>(stateAsVoidPointer);
412     bool doIntersection = false;
413     CGPoint point = CGPointZero;
414     switch (e->type) {
415     case kCGPathElementMoveToPoint:
416         state.startingPoint = e->points[0];
417         state.currentPoint = e->points[0];
418         break;
419     case kCGPathElementAddLineToPoint:
420         doIntersection = true;
421         point = e->points[0];
422         break;
423     case kCGPathElementAddQuadCurveToPoint:
424         doIntersection = true;
425         point = e->points[1];
426         break;
427     case kCGPathElementAddCurveToPoint:
428         doIntersection = true;
429         point = e->points[2];
430         break;
431     case kCGPathElementCloseSubpath:
432         doIntersection = true;
433         point = state.startingPoint;
434         break;
435     }
436     if (!doIntersection)
437         return;
438     CGFloat x;
439     if (findIntersectionPoint(state.y1, state.currentPoint, point, x))
440         updateX(state, x);
441     if (findIntersectionPoint(state.y2, state.currentPoint, point, x))
442         updateX(state, x);
443     if ((state.currentPoint.y >= state.y1 && state.currentPoint.y <= state.y2)
444         || (state.currentPoint.y <= state.y1 && state.currentPoint.y >= state.y2))
445         updateX(state, state.currentPoint.x);
446     state.currentPoint = point;
447 }
448
449 class MacGlyphToPathTranslator final : public GlyphToPathTranslator {
450 public:
451     MacGlyphToPathTranslator(const TextRun& textRun, const GlyphBuffer& glyphBuffer, const FloatPoint& textOrigin)
452         : m_index(0)
453         , m_textRun(textRun)
454         , m_glyphBuffer(glyphBuffer)
455         , m_fontData(glyphBuffer.fontDataAt(m_index))
456         , m_translation(CGAffineTransformScale(CGAffineTransformMakeTranslation(textOrigin.x(), textOrigin.y()), 1, -1))
457     {
458         moveToNextValidGlyph();
459     }
460 private:
461     virtual bool containsMorePaths() override
462     {
463         return m_index != m_glyphBuffer.size();
464     }
465     virtual Path path() override;
466     virtual std::pair<float, float> extents() override;
467     virtual GlyphUnderlineType underlineType() override;
468     virtual void advance() override;
469     void moveToNextValidGlyph();
470
471     int m_index;
472     const TextRun& m_textRun;
473     const GlyphBuffer& m_glyphBuffer;
474     const SimpleFontData* m_fontData;
475     CGAffineTransform m_translation;
476 };
477
478 Path MacGlyphToPathTranslator::path()
479 {
480     RetainPtr<CGPathRef> result = adoptCF(CTFontCreatePathForGlyph(m_fontData->platformData().ctFont(), m_glyphBuffer.glyphAt(m_index), &m_translation));
481     return adoptCF(CGPathCreateMutableCopy(result.get()));
482 }
483
484 std::pair<float, float> MacGlyphToPathTranslator::extents()
485 {
486     CGPoint beginning = CGPointApplyAffineTransform(CGPointMake(0, 0), m_translation);
487     CGSize end = CGSizeApplyAffineTransform(m_glyphBuffer.advanceAt(m_index), m_translation);
488     return std::make_pair(static_cast<float>(beginning.x), static_cast<float>(beginning.x + end.width));
489 }
490
491 auto MacGlyphToPathTranslator::underlineType() -> GlyphUnderlineType
492 {
493     return computeUnderlineType(m_textRun, m_glyphBuffer, m_index);
494 }
495
496 void MacGlyphToPathTranslator::moveToNextValidGlyph()
497 {
498     if (!m_fontData->isSVGFont())
499         return;
500     advance();
501 }
502
503 void MacGlyphToPathTranslator::advance()
504 {
505     do {
506         GlyphBufferAdvance advance = m_glyphBuffer.advanceAt(m_index);
507         m_translation = CGAffineTransformTranslate(m_translation, advance.width(), advance.height());
508         ++m_index;
509         if (m_index >= m_glyphBuffer.size())
510             break;
511         m_fontData = m_glyphBuffer.fontDataAt(m_index);
512     } while (m_fontData->isSVGFont() && m_index < m_glyphBuffer.size());
513 }
514
515 DashArray Font::dashesForIntersectionsWithRect(const TextRun& run, const FloatPoint& textOrigin, const FloatRect& lineExtents) const
516 {
517     if (loadingCustomFonts())
518         return DashArray();
519
520     GlyphBuffer glyphBuffer;
521     glyphBuffer.saveOffsetsInString();
522     float deltaX;
523     if (codePath(run) != Font::Complex)
524         deltaX = getGlyphsAndAdvancesForSimpleText(run, 0, run.length(), glyphBuffer);
525     else
526         deltaX = getGlyphsAndAdvancesForComplexText(run, 0, run.length(), glyphBuffer);
527
528     if (!glyphBuffer.size())
529         return DashArray();
530     
531     // FIXME: Handle SVG + non-SVG interleaved runs. https://bugs.webkit.org/show_bug.cgi?id=133778
532     const SimpleFontData* fontData = glyphBuffer.fontDataAt(0);
533     std::unique_ptr<GlyphToPathTranslator> translator;
534     bool isSVG = false;
535     FloatPoint origin = FloatPoint(textOrigin.x() + deltaX, textOrigin.y());
536     if (!fontData->isSVGFont())
537         translator = std::make_unique<MacGlyphToPathTranslator>(run, glyphBuffer, origin);
538     else {
539         translator = run.renderingContext()->createGlyphToPathTranslator(*fontData, &run, glyphBuffer, 0, glyphBuffer.size(), origin);
540         isSVG = true;
541     }
542     DashArray result;
543     for (int index = 0; translator->containsMorePaths(); ++index, translator->advance()) {
544         GlyphIterationState info = GlyphIterationState(CGPointMake(0, 0), CGPointMake(0, 0), lineExtents.y(), lineExtents.y() + lineExtents.height(), lineExtents.x() + lineExtents.width(), lineExtents.x());
545         const SimpleFontData* localFontData = glyphBuffer.fontDataAt(index);
546         if (!localFontData || (!isSVG && localFontData->isSVGFont()) || (isSVG && localFontData != fontData)) {
547             // The advances will get all messed up if we do anything other than bail here.
548             result.clear();
549             break;
550         }
551         switch (translator->underlineType()) {
552         case GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders: {
553             Path path = translator->path();
554             CGPathApply(path.platformPath(), &info, &findPathIntersections);
555             if (info.minX < info.maxX) {
556                 result.append(info.minX - lineExtents.x());
557                 result.append(info.maxX - lineExtents.x());
558             }
559             break;
560         }
561         case GlyphToPathTranslator::GlyphUnderlineType::SkipGlyph: {
562             std::pair<float, float> extents = translator->extents();
563             result.append(extents.first - lineExtents.x());
564             result.append(extents.second - lineExtents.x());
565             break;
566         }
567         case GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph:
568             // Nothing to do
569             break;
570         }
571     }
572     return result;
573 }
574 #endif
575
576 bool Font::primaryFontDataIsSystemFont() const
577 {
578 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED > 1090
579     const auto* fontData = primaryFont();
580     return !fontData->isSVGFont() && CTFontDescriptorIsSystemUIFont(adoptCF(CTFontCopyFontDescriptor(fontData->platformData().ctFont())).get());
581 #else
582     // System fonts are hidden by having a name that begins with a period, so simply search
583     // for that here rather than try to keep the list up to date.
584     return firstFamily().startsWith('.');
585 #endif
586 }
587
588 }