[OS X] Text dilation parameters are not restored after being set
[WebKit-https.git] / Source / WebCore / platform / graphics / cocoa / FontCascadeCocoa.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 "FontCascade.h"
25
26 #import "ComplexTextController.h"
27 #import "CoreGraphicsSPI.h"
28 #import "CoreTextSPI.h"
29 #import "DashArray.h"
30 #import "Font.h"
31 #import "GlyphBuffer.h"
32 #import "GraphicsContext.h"
33 #import "LayoutRect.h"
34 #import "Logging.h"
35 #import "WebCoreSystemInterface.h"
36 #if USE(APPKIT)
37 #import <AppKit/AppKit.h>
38 #endif
39 #import <wtf/MathExtras.h>
40
41 #if ENABLE(LETTERPRESS)
42 #import "CoreUISPI.h"
43 #import "SoftLinking.h"
44
45 SOFT_LINK_PRIVATE_FRAMEWORK(CoreUI)
46 SOFT_LINK_CLASS(CoreUI, CUICatalog)
47 SOFT_LINK_CLASS(CoreUI, CUIStyleEffectConfiguration)
48
49 SOFT_LINK_FRAMEWORK(UIKit)
50 SOFT_LINK(UIKit, _UIKitGetTextEffectsCatalog, CUICatalog *, (void), ())
51 #endif
52
53 #define SYNTHETIC_OBLIQUE_ANGLE 14
54
55 #ifdef __LP64__
56 #define URefCon void*
57 #else
58 #define URefCon UInt32
59 #endif
60
61 namespace WebCore {
62
63 bool FontCascade::canReturnFallbackFontsForComplexText()
64 {
65     return true;
66 }
67
68 bool FontCascade::canExpandAroundIdeographsInComplexText()
69 {
70     return true;
71 }
72
73 static inline void fillVectorWithHorizontalGlyphPositions(Vector<CGPoint, 256>& positions, CGContextRef context, const CGSize* advances, size_t count)
74 {
75     CGAffineTransform matrix = CGAffineTransformInvert(CGContextGetTextMatrix(context));
76     positions[0] = CGPointZero;
77     for (size_t i = 1; i < count; ++i) {
78         CGSize advance = CGSizeApplyAffineTransform(advances[i - 1], matrix);
79         positions[i].x = positions[i - 1].x + advance.width;
80         positions[i].y = positions[i - 1].y + advance.height;
81     }
82 }
83
84 static inline bool shouldUseLetterpressEffect(const GraphicsContext& context)
85 {
86 #if ENABLE(LETTERPRESS)
87     return context.textDrawingMode() & TextModeLetterpress;
88 #else
89     UNUSED_PARAM(context);
90     return false;
91 #endif
92 }
93
94 static void showLetterpressedGlyphsWithAdvances(const FloatPoint& point, const Font* font, CGContextRef context, const CGGlyph* glyphs, const CGSize* advances, size_t count)
95 {
96 #if ENABLE(LETTERPRESS)
97     if (!count)
98         return;
99
100     const FontPlatformData& platformData = font->platformData();
101     if (platformData.orientation() == Vertical) {
102         // FIXME: Implement support for vertical text. See <rdar://problem/13737298>.
103         return;
104     }
105
106     CGContextSetTextPosition(context, point.x(), point.y());
107     Vector<CGPoint, 256> positions(count);
108     fillVectorWithHorizontalGlyphPositions(positions, context, advances, count);
109
110     CTFontRef ctFont = platformData.ctFont();
111     CGContextSetFontSize(context, CTFontGetSize(ctFont));
112
113     static CUICatalog *catalog = _UIKitGetTextEffectsCatalog();
114     if (!catalog)
115         return;
116
117     static CUIStyleEffectConfiguration *styleConfiguration;
118     if (!styleConfiguration) {
119         styleConfiguration = [allocCUIStyleEffectConfigurationInstance() init];
120         styleConfiguration.useSimplifiedEffect = YES;
121     }
122
123     [catalog drawGlyphs:glyphs atPositions:positions.data() inContext:context withFont:ctFont count:count stylePresetName:@"_UIKitNewLetterpressStyle" styleConfiguration:styleConfiguration foregroundColor:CGContextGetFillColorAsColor(context)];
124 #else
125     UNUSED_PARAM(point);
126     UNUSED_PARAM(font);
127     UNUSED_PARAM(context);
128     UNUSED_PARAM(glyphs);
129     UNUSED_PARAM(advances);
130     UNUSED_PARAM(count);
131 #endif
132 }
133
134 class RenderingStyleSaver {
135 public:
136 #if !PLATFORM(MAC) || __MAC_OS_X_VERSION_MIN_REQUIRED <= 101000
137
138     RenderingStyleSaver(CTFontRef, CGContextRef) { }
139
140 #elif !defined(CORETEXT_HAS_CTFontSetRenderingStyle) || CORETEXT_HAS_CTFontSetRenderingStyle != 1
141
142     // This is very slow, but it's just a holdover until everyone migrates to CTFontSetRenderingStyle()
143     // FIXME: Delete this implementation when everyone has migrated off
144     RenderingStyleSaver(CTFontRef font, CGContextRef context)
145         : m_context(context)
146     {
147         CGContextSaveGState(context);
148         CTFontSetRenderingParameters(font, context);
149     }
150
151     ~RenderingStyleSaver()
152     {
153         CGContextRestoreGState(m_context);
154     }
155
156 private:
157     CGContextRef m_context;
158
159 #else
160
161     RenderingStyleSaver(CTFontRef font, CGContextRef context)
162         : m_context(context)
163     {
164         m_changed = CTFontSetRenderingStyle(font, context, &m_originalStyle, &m_originalDilation);
165     }
166
167     ~RenderingStyleSaver()
168     {
169         if (!m_changed)
170             return;
171         CGContextSetFontRenderingStyle(m_context, m_originalStyle);
172         CGContextSetFontDilation(m_context, m_originalDilation);
173     }
174
175 private:
176     bool m_changed;
177     CGContextRef m_context;
178     CGFontRenderingStyle m_originalStyle;
179     CGSize m_originalDilation;
180
181 #endif
182 };
183
184 static void showGlyphsWithAdvances(const FloatPoint& point, const Font* font, CGContextRef context, const CGGlyph* glyphs, const CGSize* advances, size_t count)
185 {
186     if (!count)
187         return;
188
189     CGContextSetTextPosition(context, point.x(), point.y());
190
191     const FontPlatformData& platformData = font->platformData();
192     Vector<CGPoint, 256> positions(count);
193     if (platformData.isColorBitmapFont())
194         fillVectorWithHorizontalGlyphPositions(positions, context, advances, count);
195     if (platformData.orientation() == Vertical) {
196         CGAffineTransform savedMatrix;
197         CGAffineTransform rotateLeftTransform = CGAffineTransformMake(0, -1, 1, 0, 0, 0);
198         savedMatrix = CGContextGetTextMatrix(context);
199         CGAffineTransform runMatrix = CGAffineTransformConcat(savedMatrix, rotateLeftTransform);
200         CGContextSetTextMatrix(context, runMatrix);
201
202         Vector<CGSize, 256> translations(count);
203         CTFontGetVerticalTranslationsForGlyphs(platformData.ctFont(), glyphs, translations.data(), count);
204
205         CGAffineTransform transform = CGAffineTransformInvert(CGContextGetTextMatrix(context));
206
207         CGPoint position = FloatPoint(point.x(), point.y() + font->fontMetrics().floatAscent(IdeographicBaseline) - font->fontMetrics().floatAscent());
208         for (size_t i = 0; i < count; ++i) {
209             CGSize translation = CGSizeApplyAffineTransform(translations[i], rotateLeftTransform);
210             positions[i] = CGPointApplyAffineTransform(CGPointMake(position.x - translation.width, position.y + translation.height), transform);
211             position.x += advances[i].width;
212             position.y += advances[i].height;
213         }
214         if (!platformData.isColorBitmapFont()) {
215             RenderingStyleSaver saver(platformData.ctFont(), context);
216             CGContextShowGlyphsAtPositions(context, glyphs, positions.data(), count);
217         } else
218             CTFontDrawGlyphs(platformData.ctFont(), glyphs, positions.data(), count, context);
219         CGContextSetTextMatrix(context, savedMatrix);
220     } else {
221         if (!platformData.isColorBitmapFont()) {
222             RenderingStyleSaver saver(platformData.ctFont(), context);
223 #pragma clang diagnostic push
224 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
225             CGContextShowGlyphsWithAdvances(context, glyphs, advances, count);
226 #pragma clang diagnostic pop
227         } else
228             CTFontDrawGlyphs(platformData.ctFont(), glyphs, positions.data(), count, context);
229     }
230 }
231
232 #if PLATFORM(MAC)
233 static void setCGFontRenderingMode(CGContextRef cgContext, NSFontRenderingMode renderingMode, BOOL shouldSubpixelQuantize)
234 {
235     if (renderingMode == NSFontIntegerAdvancementsRenderingMode) {
236         CGContextSetShouldAntialiasFonts(cgContext, false);
237         return;
238     }
239
240     CGContextSetShouldAntialiasFonts(cgContext, true);
241
242     CGAffineTransform contextTransform = CGContextGetCTM(cgContext);
243     BOOL isTranslationOrIntegralScale = WTF::isIntegral(contextTransform.a) && WTF::isIntegral(contextTransform.d) && contextTransform.b == 0.f && contextTransform.c == 0.f;
244     BOOL isRotated = ((contextTransform.b || contextTransform.c) && (contextTransform.a || contextTransform.d));
245     BOOL doSubpixelQuantization = isTranslationOrIntegralScale || (!isRotated && shouldSubpixelQuantize);
246
247     CGContextSetShouldSubpixelPositionFonts(cgContext, renderingMode != NSFontAntialiasedIntegerAdvancementsRenderingMode || !isTranslationOrIntegralScale);
248     CGContextSetShouldSubpixelQuantizeFonts(cgContext, doSubpixelQuantization);
249 }
250 #endif
251
252 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100
253 static CGSize dilationSizeForTextColor(const Color& color)
254 {
255     double hue;
256     double saturation;
257     double lightness;
258     color.getHSL(hue, saturation, lightness);
259     
260     // These values were derived empirically, and are only experimental.
261     if (lightness < 0.3333) // Dark
262         return CGSizeMake(0.007, 0.019);
263
264     if (lightness < 0.6667) // Medium
265         return CGSizeMake(0.032, 0.032);
266
267     // Light
268     return CGSizeMake(0.0475, 0.039);
269 }
270 #endif
271
272 static FloatPoint pointAdjustedForEmoji(const FontPlatformData& platformData, FloatPoint point)
273 {
274 #if PLATFORM(IOS)
275     if (!platformData.m_isEmoji)
276         return point;
277
278     // Mimic the positioining of non-bitmap glyphs, which are not subpixel-positioned.
279     point.setY(ceilf(point.y()));
280
281     // Emoji glyphs snap to the CSS pixel grid.
282     point.setX(floorf(point.x()));
283
284     // Emoji glyphs are offset one CSS pixel to the right.
285     point.move(1, 0);
286
287     // Emoji glyphs are offset vertically based on font size.
288     float fontSize = platformData.size();
289     float y = point.y();
290     if (fontSize <= 15) {
291         // Undo Core Text's y adjustment.
292         static float yAdjustmentFactor = iosExecutableWasLinkedOnOrAfterVersion(wkIOSSystemVersion_6_0) ? .19 : .1;
293         point.setY(floorf(y - yAdjustmentFactor * (fontSize + 2) + 2));
294     } else {
295         if (fontSize < 26)
296             y -= .35f * fontSize - 10;
297
298         // Undo Core Text's y adjustment.
299         static float yAdjustment = iosExecutableWasLinkedOnOrAfterVersion(wkIOSSystemVersion_6_0) ? 3.8 : 2;
300         point.setY(floorf(y - yAdjustment));
301     }
302 #else
303     UNUSED_PARAM(platformData);
304 #endif
305     return point;
306 }
307
308 void FontCascade::drawGlyphs(GraphicsContext* context, const Font* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& anchorPoint) const
309 {
310     const FontPlatformData& platformData = font->platformData();
311     if (!platformData.size())
312         return;
313
314     CGContextRef cgContext = context->platformContext();
315
316     bool shouldSmoothFonts;
317     bool changeFontSmoothing;
318     bool matchAntialiasedAndSmoothedFonts = context->antialiasedFontDilationEnabled();
319     
320     switch (fontDescription().fontSmoothing()) {
321     case Antialiased: {
322         context->setShouldAntialias(true);
323         shouldSmoothFonts = false;
324         changeFontSmoothing = true;
325         matchAntialiasedAndSmoothedFonts = false; // CSS has opted into strictly antialiased fonts.
326         break;
327     }
328     case SubpixelAntialiased: {
329         context->setShouldAntialias(true);
330         shouldSmoothFonts = true;
331         changeFontSmoothing = true;
332         matchAntialiasedAndSmoothedFonts = true;
333         break;
334     }
335     case NoSmoothing: {
336         context->setShouldAntialias(false);
337         shouldSmoothFonts = false;
338         changeFontSmoothing = true;
339         matchAntialiasedAndSmoothedFonts = false;
340         break;
341     }
342     case AutoSmoothing: {
343         shouldSmoothFonts = true;
344         changeFontSmoothing = false;
345         matchAntialiasedAndSmoothedFonts = true;
346         break;
347     }
348     }
349     
350     if (!shouldUseSmoothing()) {
351         shouldSmoothFonts = false;
352         changeFontSmoothing = true;
353         matchAntialiasedAndSmoothedFonts = true;
354     }
355
356 #if !PLATFORM(IOS)
357     bool originalShouldUseFontSmoothing = false;
358     if (changeFontSmoothing) {
359         originalShouldUseFontSmoothing = CGContextGetShouldSmoothFonts(cgContext);
360         CGContextSetShouldSmoothFonts(cgContext, shouldSmoothFonts);
361     }
362     
363 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100
364     CGFontAntialiasingStyle oldAntialiasingStyle;
365     bool resetAntialiasingStyle = false;
366     if (antialiasedFontDilationEnabled() && !CGContextGetShouldSmoothFonts(cgContext) && matchAntialiasedAndSmoothedFonts) {
367         resetAntialiasingStyle = true;
368         oldAntialiasingStyle = CGContextGetFontAntialiasingStyle(cgContext);
369         CGContextSetFontAntialiasingStyle(cgContext, kCGFontAntialiasingStyleUnfilteredCustomDilation);
370         CGContextSetFontDilation(cgContext, dilationSizeForTextColor(context->fillColor()));
371     }
372 #endif
373 #endif
374
375 #if !PLATFORM(IOS)
376     NSFont* drawFont = [platformData.nsFont() printerFont];
377 #endif
378     
379     CGContextSetFont(cgContext, platformData.cgFont());
380
381     bool useLetterpressEffect = shouldUseLetterpressEffect(*context);
382     FloatPoint point = pointAdjustedForEmoji(platformData, anchorPoint);
383
384 #if PLATFORM(IOS)
385     float fontSize = platformData.size();
386     CGAffineTransform matrix = useLetterpressEffect || platformData.isColorBitmapFont() ? CGAffineTransformIdentity : CGAffineTransformMakeScale(fontSize, fontSize);
387 #else
388     CGAffineTransform matrix = CGAffineTransformIdentity;
389     if (drawFont && !platformData.isColorBitmapFont())
390         memcpy(&matrix, [drawFont matrix], sizeof(matrix));
391 #endif
392     matrix.b = -matrix.b;
393     matrix.d = -matrix.d;
394     if (platformData.m_syntheticOblique) {
395         static float obliqueSkew = tanf(SYNTHETIC_OBLIQUE_ANGLE * piFloat / 180);
396         if (platformData.orientation() == Vertical)
397             matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, obliqueSkew, 0, 1, 0, 0));
398         else
399             matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, -obliqueSkew, 1, 0, 0));
400     }
401     CGContextSetTextMatrix(cgContext, matrix);
402
403 #if PLATFORM(IOS)
404     CGContextSetFontSize(cgContext, 1);
405     CGContextSetShouldSubpixelQuantizeFonts(cgContext, context->shouldSubpixelQuantizeFonts());
406 #else
407     setCGFontRenderingMode(cgContext, [drawFont renderingMode], context->shouldSubpixelQuantizeFonts());
408     if (drawFont)
409         CGContextSetFontSize(cgContext, 1);
410     else
411         CGContextSetFontSize(cgContext, platformData.m_size);
412 #endif
413
414
415     FloatSize shadowOffset;
416     float shadowBlur;
417     Color shadowColor;
418     ColorSpace shadowColorSpace;
419     ColorSpace fillColorSpace = context->fillColorSpace();
420     context->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace);
421
422     AffineTransform contextCTM = context->getCTM();
423     float syntheticBoldOffset = font->syntheticBoldOffset();
424     if (syntheticBoldOffset && !contextCTM.isIdentityOrTranslationOrFlipped()) {
425         FloatSize horizontalUnitSizeInDevicePixels = contextCTM.mapSize(FloatSize(1, 0));
426         float horizontalUnitLengthInDevicePixels = sqrtf(horizontalUnitSizeInDevicePixels.width() * horizontalUnitSizeInDevicePixels.width() + horizontalUnitSizeInDevicePixels.height() * horizontalUnitSizeInDevicePixels.height());
427         if (horizontalUnitLengthInDevicePixels)
428             syntheticBoldOffset /= horizontalUnitLengthInDevicePixels;
429     };
430
431     bool hasSimpleShadow = context->textDrawingMode() == TextModeFill && shadowColor.isValid() && !shadowBlur && !platformData.isColorBitmapFont() && (!context->shadowsIgnoreTransforms() || contextCTM.isIdentityOrTranslationOrFlipped()) && !context->isInTransparencyLayer();
432     if (hasSimpleShadow) {
433         // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing.
434         context->clearShadow();
435         Color fillColor = context->fillColor();
436         Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255);
437         context->setFillColor(shadowFillColor, shadowColorSpace);
438         float shadowTextX = point.x() + shadowOffset.width();
439         // If shadows are ignoring transforms, then we haven't applied the Y coordinate flip yet, so down is negative.
440         float shadowTextY = point.y() + shadowOffset.height() * (context->shadowsIgnoreTransforms() ? -1 : 1);
441         showGlyphsWithAdvances(FloatPoint(shadowTextX, shadowTextY), font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
442 #if !PLATFORM(IOS)
443         if (syntheticBoldOffset)
444 #else
445         if (syntheticBoldOffset && !platformData.m_isEmoji)
446 #endif
447             showGlyphsWithAdvances(FloatPoint(shadowTextX + syntheticBoldOffset, shadowTextY), font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
448         context->setFillColor(fillColor, fillColorSpace);
449     }
450
451     if (useLetterpressEffect)
452         showLetterpressedGlyphsWithAdvances(point, font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
453     else
454         showGlyphsWithAdvances(point, font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
455 #if !PLATFORM(IOS)
456     if (syntheticBoldOffset)
457 #else
458     if (syntheticBoldOffset && !platformData.m_isEmoji)
459 #endif
460         showGlyphsWithAdvances(FloatPoint(point.x() + syntheticBoldOffset, point.y()), font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
461
462     if (hasSimpleShadow)
463         context->setShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace);
464
465 #if !PLATFORM(IOS)
466 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100
467     if (resetAntialiasingStyle)
468         CGContextSetFontAntialiasingStyle(cgContext, oldAntialiasingStyle);
469 #endif
470     
471     if (changeFontSmoothing)
472         CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing);
473 #endif
474 }
475
476 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
477 struct GlyphIterationState {
478     GlyphIterationState(CGPoint startingPoint, CGPoint currentPoint, CGFloat y1, CGFloat y2, CGFloat minX, CGFloat maxX)
479         : startingPoint(startingPoint)
480         , currentPoint(currentPoint)
481         , y1(y1)
482         , y2(y2)
483         , minX(minX)
484         , maxX(maxX)
485     {
486     }
487     CGPoint startingPoint;
488     CGPoint currentPoint;
489     CGFloat y1;
490     CGFloat y2;
491     CGFloat minX;
492     CGFloat maxX;
493 };
494
495 static bool findIntersectionPoint(float y, CGPoint p1, CGPoint p2, CGFloat& x)
496 {
497     x = p1.x + (y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y);
498     return (p1.y < y && p2.y > y) || (p1.y > y && p2.y < y);
499 }
500
501 static void updateX(GlyphIterationState& state, CGFloat x)
502 {
503     state.minX = std::min(state.minX, x);
504     state.maxX = std::max(state.maxX, x);
505 }
506
507 // This function is called by CGPathApply and is therefore invoked for each
508 // contour in a glyph. This function models each contours as a straight line
509 // and calculates the intersections between each pseudo-contour and
510 // two horizontal lines (the upper and lower bounds of an underline) found in
511 // GlyphIterationState::y1 and GlyphIterationState::y2. It keeps track of the
512 // leftmost and rightmost intersection in GlyphIterationState::minX and
513 // GlyphIterationState::maxX.
514 static void findPathIntersections(void* stateAsVoidPointer, const CGPathElement* e)
515 {
516     auto& state = *static_cast<GlyphIterationState*>(stateAsVoidPointer);
517     bool doIntersection = false;
518     CGPoint point = CGPointZero;
519     switch (e->type) {
520     case kCGPathElementMoveToPoint:
521         state.startingPoint = e->points[0];
522         state.currentPoint = e->points[0];
523         break;
524     case kCGPathElementAddLineToPoint:
525         doIntersection = true;
526         point = e->points[0];
527         break;
528     case kCGPathElementAddQuadCurveToPoint:
529         doIntersection = true;
530         point = e->points[1];
531         break;
532     case kCGPathElementAddCurveToPoint:
533         doIntersection = true;
534         point = e->points[2];
535         break;
536     case kCGPathElementCloseSubpath:
537         doIntersection = true;
538         point = state.startingPoint;
539         break;
540     }
541     if (!doIntersection)
542         return;
543     CGFloat x;
544     if (findIntersectionPoint(state.y1, state.currentPoint, point, x))
545         updateX(state, x);
546     if (findIntersectionPoint(state.y2, state.currentPoint, point, x))
547         updateX(state, x);
548     if ((state.currentPoint.y >= state.y1 && state.currentPoint.y <= state.y2)
549         || (state.currentPoint.y <= state.y1 && state.currentPoint.y >= state.y2))
550         updateX(state, state.currentPoint.x);
551     state.currentPoint = point;
552 }
553
554 class MacGlyphToPathTranslator final : public GlyphToPathTranslator {
555 public:
556     MacGlyphToPathTranslator(const TextRun& textRun, const GlyphBuffer& glyphBuffer, const FloatPoint& textOrigin)
557         : m_index(0)
558         , m_textRun(textRun)
559         , m_glyphBuffer(glyphBuffer)
560         , m_fontData(glyphBuffer.fontAt(m_index))
561         , m_translation(CGAffineTransformScale(CGAffineTransformMakeTranslation(textOrigin.x(), textOrigin.y()), 1, -1))
562     {
563         moveToNextValidGlyph();
564     }
565 private:
566     virtual bool containsMorePaths() override
567     {
568         return m_index != m_glyphBuffer.size();
569     }
570     virtual Path path() override;
571     virtual std::pair<float, float> extents() override;
572     virtual GlyphUnderlineType underlineType() override;
573     virtual void advance() override;
574     void moveToNextValidGlyph();
575
576     int m_index;
577     const TextRun& m_textRun;
578     const GlyphBuffer& m_glyphBuffer;
579     const Font* m_fontData;
580     CGAffineTransform m_translation;
581 };
582
583 Path MacGlyphToPathTranslator::path()
584 {
585     RetainPtr<CGPathRef> result = adoptCF(CTFontCreatePathForGlyph(m_fontData->platformData().ctFont(), m_glyphBuffer.glyphAt(m_index), &m_translation));
586     return adoptCF(CGPathCreateMutableCopy(result.get()));
587 }
588
589 std::pair<float, float> MacGlyphToPathTranslator::extents()
590 {
591     CGPoint beginning = CGPointApplyAffineTransform(CGPointMake(0, 0), m_translation);
592     CGSize end = CGSizeApplyAffineTransform(m_glyphBuffer.advanceAt(m_index), m_translation);
593     return std::make_pair(static_cast<float>(beginning.x), static_cast<float>(beginning.x + end.width));
594 }
595
596 auto MacGlyphToPathTranslator::underlineType() -> GlyphUnderlineType
597 {
598     return computeUnderlineType(m_textRun, m_glyphBuffer, m_index);
599 }
600
601 void MacGlyphToPathTranslator::moveToNextValidGlyph()
602 {
603     if (!m_fontData->isSVGFont())
604         return;
605     advance();
606 }
607
608 void MacGlyphToPathTranslator::advance()
609 {
610     do {
611         GlyphBufferAdvance advance = m_glyphBuffer.advanceAt(m_index);
612         m_translation = CGAffineTransformTranslate(m_translation, advance.width(), advance.height());
613         ++m_index;
614         if (m_index >= m_glyphBuffer.size())
615             break;
616         m_fontData = m_glyphBuffer.fontAt(m_index);
617     } while (m_fontData->isSVGFont() && m_index < m_glyphBuffer.size());
618 }
619
620 DashArray FontCascade::dashesForIntersectionsWithRect(const TextRun& run, const FloatPoint& textOrigin, const FloatRect& lineExtents) const
621 {
622     if (isLoadingCustomFonts())
623         return DashArray();
624
625     GlyphBuffer glyphBuffer;
626     glyphBuffer.saveOffsetsInString();
627     float deltaX;
628     if (codePath(run) != FontCascade::Complex)
629         deltaX = getGlyphsAndAdvancesForSimpleText(run, 0, run.length(), glyphBuffer);
630     else
631         deltaX = getGlyphsAndAdvancesForComplexText(run, 0, run.length(), glyphBuffer);
632
633     if (!glyphBuffer.size())
634         return DashArray();
635     
636     // FIXME: Handle SVG + non-SVG interleaved runs. https://bugs.webkit.org/show_bug.cgi?id=133778
637     const Font* fontData = glyphBuffer.fontAt(0);
638     std::unique_ptr<GlyphToPathTranslator> translator;
639     bool isSVG = false;
640     FloatPoint origin = FloatPoint(textOrigin.x() + deltaX, textOrigin.y());
641     if (!fontData->isSVGFont())
642         translator = std::make_unique<MacGlyphToPathTranslator>(run, glyphBuffer, origin);
643     else {
644         TextRun::RenderingContext* renderingContext = run.renderingContext();
645         if (!renderingContext)
646             return DashArray();
647         translator = renderingContext->createGlyphToPathTranslator(*fontData, &run, glyphBuffer, 0, glyphBuffer.size(), origin);
648         isSVG = true;
649     }
650     DashArray result;
651     for (int index = 0; translator->containsMorePaths(); ++index, translator->advance()) {
652         GlyphIterationState info = GlyphIterationState(CGPointMake(0, 0), CGPointMake(0, 0), lineExtents.y(), lineExtents.y() + lineExtents.height(), lineExtents.x() + lineExtents.width(), lineExtents.x());
653         const Font* localFont = glyphBuffer.fontAt(index);
654         if (!localFont || (!isSVG && localFont->isSVGFont()) || (isSVG && localFont != fontData)) {
655             // The advances will get all messed up if we do anything other than bail here.
656             result.clear();
657             break;
658         }
659         switch (translator->underlineType()) {
660         case GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders: {
661             Path path = translator->path();
662             CGPathApply(path.platformPath(), &info, &findPathIntersections);
663             if (info.minX < info.maxX) {
664                 result.append(info.minX - lineExtents.x());
665                 result.append(info.maxX - lineExtents.x());
666             }
667             break;
668         }
669         case GlyphToPathTranslator::GlyphUnderlineType::SkipGlyph: {
670             std::pair<float, float> extents = translator->extents();
671             result.append(extents.first - lineExtents.x());
672             result.append(extents.second - lineExtents.x());
673             break;
674         }
675         case GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph:
676             // Nothing to do
677             break;
678         }
679     }
680     return result;
681 }
682 #endif
683
684 bool FontCascade::primaryFontIsSystemFont() const
685 {
686 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED > 1090
687     const auto& fontData = primaryFont();
688     return !fontData.isSVGFont() && CTFontDescriptorIsSystemUIFont(adoptCF(CTFontCopyFontDescriptor(fontData.platformData().ctFont())).get());
689 #else
690     // System fonts are hidden by having a name that begins with a period, so simply search
691     // for that here rather than try to keep the list up to date.
692     return firstFamily().startsWith('.');
693 #endif
694 }
695
696 void FontCascade::adjustSelectionRectForComplexText(const TextRun& run, LayoutRect& selectionRect, int from, int to) const
697 {
698     ComplexTextController controller(*this, run);
699     controller.advance(from);
700     float beforeWidth = controller.runWidthSoFar();
701     controller.advance(to);
702     float afterWidth = controller.runWidthSoFar();
703
704     if (run.rtl())
705         selectionRect.move(controller.totalWidth() - afterWidth + controller.leadingExpansion(), 0);
706     else
707         selectionRect.move(beforeWidth, 0);
708     selectionRect.setWidth(afterWidth - beforeWidth);
709 }
710
711 float FontCascade::getGlyphsAndAdvancesForComplexText(const TextRun& run, int from, int to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const
712 {
713     float initialAdvance;
714
715     ComplexTextController controller(*this, run, false, 0, forTextEmphasis);
716     controller.advance(from);
717     float beforeWidth = controller.runWidthSoFar();
718     controller.advance(to, &glyphBuffer);
719
720     if (glyphBuffer.isEmpty())
721         return 0;
722
723     float afterWidth = controller.runWidthSoFar();
724
725     if (run.rtl()) {
726         initialAdvance = controller.totalWidth() + controller.finalRoundingWidth() - afterWidth + controller.leadingExpansion();
727         glyphBuffer.reverse(0, glyphBuffer.size());
728     } else
729         initialAdvance = beforeWidth;
730
731     return initialAdvance;
732 }
733
734 float FontCascade::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const
735 {
736     // This glyph buffer holds our glyphs + advances + font data for each glyph.
737     GlyphBuffer glyphBuffer;
738
739     float startX = point.x() + getGlyphsAndAdvancesForComplexText(run, from, to, glyphBuffer);
740
741     // We couldn't generate any glyphs for the run. Give up.
742     if (glyphBuffer.isEmpty())
743         return 0;
744
745     // Draw the glyph buffer now at the starting point returned in startX.
746     FloatPoint startPoint(startX, point.y());
747     drawGlyphBuffer(context, run, glyphBuffer, startPoint);
748
749     return startPoint.x() - startX;
750 }
751
752 void FontCascade::drawEmphasisMarksForComplexText(GraphicsContext* context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to) const
753 {
754     GlyphBuffer glyphBuffer;
755     float initialAdvance = getGlyphsAndAdvancesForComplexText(run, from, to, glyphBuffer, ForTextEmphasis);
756
757     if (glyphBuffer.isEmpty())
758         return;
759
760     drawEmphasisMarks(context, run, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y()));
761 }
762
763 float FontCascade::floatWidthForComplexText(const TextRun& run, HashSet<const Font*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
764 {
765     ComplexTextController controller(*this, run, true, fallbackFonts);
766     if (glyphOverflow) {
767         glyphOverflow->top = std::max<int>(glyphOverflow->top, ceilf(-controller.minGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().ascent()));
768         glyphOverflow->bottom = std::max<int>(glyphOverflow->bottom, ceilf(controller.maxGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().descent()));
769         glyphOverflow->left = std::max<int>(0, ceilf(-controller.minGlyphBoundingBoxX()));
770         glyphOverflow->right = std::max<int>(0, ceilf(controller.maxGlyphBoundingBoxX() - controller.totalWidth()));
771     }
772     return controller.totalWidth();
773 }
774
775 int FontCascade::offsetForPositionForComplexText(const TextRun& run, float x, bool includePartialGlyphs) const
776 {
777     ComplexTextController controller(*this, run);
778     return controller.offsetForPosition(x, includePartialGlyphs);
779 }
780
781 const Font* FontCascade::fontForCombiningCharacterSequence(const UChar* characters, size_t length, FontVariant variant) const
782 {
783     UChar32 baseCharacter;
784     size_t baseCharacterLength = 0;
785     U16_NEXT(characters, baseCharacterLength, length, baseCharacter);
786
787     GlyphData baseCharacterGlyphData = glyphDataForCharacter(baseCharacter, false, variant);
788
789     if (!baseCharacterGlyphData.glyph)
790         return 0;
791
792     if (length == baseCharacterLength)
793         return baseCharacterGlyphData.font;
794
795     bool triedBaseCharacterFont = false;
796
797     for (unsigned i = 0; !fallbackRangesAt(i).isNull(); ++i) {
798         const Font* font = fallbackRangesAt(i).fontForCharacter(baseCharacter);
799         if (!font)
800             continue;
801 #if PLATFORM(IOS)
802         if (baseCharacter >= 0x0600 && baseCharacter <= 0x06ff && font->shouldNotBeUsedForArabic())
803             continue;
804 #endif
805         if (variant == NormalVariant) {
806             if (font->platformData().orientation() == Vertical) {
807                 if (isCJKIdeographOrSymbol(baseCharacter) && !font->hasVerticalGlyphs()) {
808                     variant = BrokenIdeographVariant;
809                     font = font->brokenIdeographFont().get();
810                 } else if (m_fontDescription.nonCJKGlyphOrientation() == NonCJKGlyphOrientationVerticalRight) {
811                     Font* verticalRightFont = font->verticalRightOrientationFont().get();
812                     Glyph verticalRightGlyph = verticalRightFont->glyphForCharacter(baseCharacter);
813                     if (verticalRightGlyph == baseCharacterGlyphData.glyph)
814                         font = verticalRightFont;
815                 } else {
816                     Font* uprightFont = font->uprightOrientationFont().get();
817                     Glyph uprightGlyph = uprightFont->glyphForCharacter(baseCharacter);
818                     if (uprightGlyph != baseCharacterGlyphData.glyph)
819                         font = uprightFont;
820                 }
821             }
822         } else {
823             if (const Font* variantFont = font->variantFont(m_fontDescription, variant).get())
824                 font = variantFont;
825         }
826
827         if (font == baseCharacterGlyphData.font)
828             triedBaseCharacterFont = true;
829
830         if (font->canRenderCombiningCharacterSequence(characters, length))
831             return font;
832     }
833
834     if (!triedBaseCharacterFont && baseCharacterGlyphData.font && baseCharacterGlyphData.font->canRenderCombiningCharacterSequence(characters, length))
835         return baseCharacterGlyphData.font;
836
837     return Font::systemFallback();
838 }
839
840 }