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