Migrate from ints to unsigneds when referring to indices into strings
[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, unsigned count)
74 {
75     CGAffineTransform matrix = CGAffineTransformInvert(CGContextGetTextMatrix(context));
76     positions[0] = CGPointZero;
77     for (unsigned 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, unsigned 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     RenderingStyleSaver(CTFontRef, CGContextRef) { }
138 #else
139     RenderingStyleSaver(CTFontRef font, CGContextRef context)
140         : m_context(context)
141     {
142         m_changed = CTFontSetRenderingStyle(font, context, &m_originalStyle, &m_originalDilation);
143     }
144
145     ~RenderingStyleSaver()
146     {
147         if (!m_changed)
148             return;
149         CGContextSetFontRenderingStyle(m_context, m_originalStyle);
150         CGContextSetFontDilation(m_context, m_originalDilation);
151     }
152
153 private:
154     bool m_changed;
155     CGContextRef m_context;
156     CGFontRenderingStyle m_originalStyle;
157     CGSize m_originalDilation;
158 #endif
159 };
160
161 static void showGlyphsWithAdvances(const FloatPoint& point, const Font& font, CGContextRef context, const CGGlyph* glyphs, const CGSize* advances, unsigned count)
162 {
163     if (!count)
164         return;
165
166     CGContextSetTextPosition(context, point.x(), point.y());
167
168     const FontPlatformData& platformData = font.platformData();
169     Vector<CGPoint, 256> positions(count);
170     if (platformData.isColorBitmapFont())
171         fillVectorWithHorizontalGlyphPositions(positions, context, advances, count);
172     if (platformData.orientation() == Vertical) {
173         CGAffineTransform savedMatrix;
174         CGAffineTransform rotateLeftTransform = CGAffineTransformMake(0, -1, 1, 0, 0, 0);
175         savedMatrix = CGContextGetTextMatrix(context);
176         CGAffineTransform runMatrix = CGAffineTransformConcat(savedMatrix, rotateLeftTransform);
177         CGContextSetTextMatrix(context, runMatrix);
178
179         Vector<CGSize, 256> translations(count);
180         CTFontGetVerticalTranslationsForGlyphs(platformData.ctFont(), glyphs, translations.data(), count);
181
182         CGAffineTransform transform = CGAffineTransformInvert(CGContextGetTextMatrix(context));
183
184         CGPoint position = FloatPoint(point.x(), point.y() + font.fontMetrics().floatAscent(IdeographicBaseline) - font.fontMetrics().floatAscent());
185         for (unsigned i = 0; i < count; ++i) {
186             CGSize translation = CGSizeApplyAffineTransform(translations[i], rotateLeftTransform);
187             positions[i] = CGPointApplyAffineTransform(CGPointMake(position.x - translation.width, position.y + translation.height), transform);
188             position.x += advances[i].width;
189             position.y += advances[i].height;
190         }
191         if (!platformData.isColorBitmapFont()) {
192             RenderingStyleSaver saver(platformData.ctFont(), context);
193             CGContextShowGlyphsAtPositions(context, glyphs, positions.data(), count);
194         } else
195             CTFontDrawGlyphs(platformData.ctFont(), glyphs, positions.data(), count, context);
196         CGContextSetTextMatrix(context, savedMatrix);
197     } else {
198         if (!platformData.isColorBitmapFont()) {
199             RenderingStyleSaver saver(platformData.ctFont(), context);
200 #pragma clang diagnostic push
201 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
202             CGContextShowGlyphsWithAdvances(context, glyphs, advances, count);
203 #pragma clang diagnostic pop
204         } else
205             CTFontDrawGlyphs(platformData.ctFont(), glyphs, positions.data(), count, context);
206     }
207 }
208
209 static void setCGFontRenderingMode(GraphicsContext& context)
210 {
211     CGContextRef cgContext = context.platformContext();
212     CGContextSetShouldAntialiasFonts(cgContext, true);
213
214     CGAffineTransform contextTransform = CGContextGetCTM(cgContext);
215     bool isTranslationOrIntegralScale = WTF::isIntegral(contextTransform.a) && WTF::isIntegral(contextTransform.d) && contextTransform.b == 0.f && contextTransform.c == 0.f;
216     bool isRotated = ((contextTransform.b || contextTransform.c) && (contextTransform.a || contextTransform.d));
217     bool doSubpixelQuantization = isTranslationOrIntegralScale || (!isRotated && context.shouldSubpixelQuantizeFonts());
218
219     CGContextSetShouldSubpixelPositionFonts(cgContext, true);
220     CGContextSetShouldSubpixelQuantizeFonts(cgContext, doSubpixelQuantization);
221 }
222
223 void FontCascade::drawGlyphs(GraphicsContext& context, const Font& font, const GlyphBuffer& glyphBuffer, unsigned from, unsigned numGlyphs, const FloatPoint& anchorPoint, FontSmoothingMode smoothingMode)
224 {
225     const FontPlatformData& platformData = font.platformData();
226     if (!platformData.size())
227         return;
228
229     CGContextRef cgContext = context.platformContext();
230
231     bool shouldSmoothFonts;
232     bool changeFontSmoothing;
233     
234     switch (smoothingMode) {
235     case Antialiased: {
236         context.setShouldAntialias(true);
237         shouldSmoothFonts = false;
238         changeFontSmoothing = true;
239         break;
240     }
241     case SubpixelAntialiased: {
242         context.setShouldAntialias(true);
243         shouldSmoothFonts = true;
244         changeFontSmoothing = true;
245         break;
246     }
247     case NoSmoothing: {
248         context.setShouldAntialias(false);
249         shouldSmoothFonts = false;
250         changeFontSmoothing = true;
251         break;
252     }
253     case AutoSmoothing: {
254         shouldSmoothFonts = true;
255         changeFontSmoothing = false;
256         break;
257     }
258     }
259     
260     if (!shouldUseSmoothing()) {
261         shouldSmoothFonts = false;
262         changeFontSmoothing = true;
263     }
264
265 #if !PLATFORM(IOS)
266     bool originalShouldUseFontSmoothing = false;
267     if (changeFontSmoothing) {
268         originalShouldUseFontSmoothing = CGContextGetShouldSmoothFonts(cgContext);
269         CGContextSetShouldSmoothFonts(cgContext, shouldSmoothFonts);
270     }
271 #endif
272
273     CGContextSetFont(cgContext, platformData.cgFont());
274
275     bool useLetterpressEffect = shouldUseLetterpressEffect(context);
276     FloatPoint point = anchorPoint;
277
278     CGAffineTransform matrix = CGAffineTransformIdentity;
279     if (!platformData.isColorBitmapFont())
280         matrix = CTFontGetMatrix(platformData.font());
281     matrix.b = -matrix.b;
282     matrix.d = -matrix.d;
283     if (platformData.syntheticOblique()) {
284         static float obliqueSkew = tanf(SYNTHETIC_OBLIQUE_ANGLE * piFloat / 180);
285         if (platformData.orientation() == Vertical)
286             matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, obliqueSkew, 0, 1, 0, 0));
287         else
288             matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, -obliqueSkew, 1, 0, 0));
289     }
290     CGContextSetTextMatrix(cgContext, matrix);
291
292     setCGFontRenderingMode(context);
293     CGContextSetFontSize(cgContext, platformData.size());
294
295
296     FloatSize shadowOffset;
297     float shadowBlur;
298     Color shadowColor;
299     context.getShadow(shadowOffset, shadowBlur, shadowColor);
300
301     AffineTransform contextCTM = context.getCTM();
302     float syntheticBoldOffset = font.syntheticBoldOffset();
303     if (syntheticBoldOffset && !contextCTM.isIdentityOrTranslationOrFlipped()) {
304         FloatSize horizontalUnitSizeInDevicePixels = contextCTM.mapSize(FloatSize(1, 0));
305         float horizontalUnitLengthInDevicePixels = sqrtf(horizontalUnitSizeInDevicePixels.width() * horizontalUnitSizeInDevicePixels.width() + horizontalUnitSizeInDevicePixels.height() * horizontalUnitSizeInDevicePixels.height());
306         if (horizontalUnitLengthInDevicePixels)
307             syntheticBoldOffset /= horizontalUnitLengthInDevicePixels;
308     };
309
310     bool hasSimpleShadow = context.textDrawingMode() == TextModeFill && shadowColor.isValid() && !shadowBlur && !platformData.isColorBitmapFont() && (!context.shadowsIgnoreTransforms() || contextCTM.isIdentityOrTranslationOrFlipped()) && !context.isInTransparencyLayer();
311     if (hasSimpleShadow) {
312         // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing.
313         context.clearShadow();
314         Color fillColor = context.fillColor();
315         Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255);
316         context.setFillColor(shadowFillColor);
317         float shadowTextX = point.x() + shadowOffset.width();
318         // If shadows are ignoring transforms, then we haven't applied the Y coordinate flip yet, so down is negative.
319         float shadowTextY = point.y() + shadowOffset.height() * (context.shadowsIgnoreTransforms() ? -1 : 1);
320         showGlyphsWithAdvances(FloatPoint(shadowTextX, shadowTextY), font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
321         if (syntheticBoldOffset)
322             showGlyphsWithAdvances(FloatPoint(shadowTextX + syntheticBoldOffset, shadowTextY), font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
323         context.setFillColor(fillColor);
324     }
325
326     if (useLetterpressEffect)
327         showLetterpressedGlyphsWithAdvances(point, font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
328     else
329         showGlyphsWithAdvances(point, font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
330     if (syntheticBoldOffset)
331         showGlyphsWithAdvances(FloatPoint(point.x() + syntheticBoldOffset, point.y()), font, cgContext, glyphBuffer.glyphs(from), static_cast<const CGSize*>(glyphBuffer.advances(from)), numGlyphs);
332
333     if (hasSimpleShadow)
334         context.setShadow(shadowOffset, shadowBlur, shadowColor);
335
336 #if !PLATFORM(IOS)
337     if (changeFontSmoothing)
338         CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing);
339 #endif
340 }
341
342 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
343 struct GlyphIterationState {
344     GlyphIterationState(CGPoint startingPoint, CGPoint currentPoint, CGFloat y1, CGFloat y2, CGFloat minX, CGFloat maxX)
345         : startingPoint(startingPoint)
346         , currentPoint(currentPoint)
347         , y1(y1)
348         , y2(y2)
349         , minX(minX)
350         , maxX(maxX)
351     {
352     }
353     CGPoint startingPoint;
354     CGPoint currentPoint;
355     CGFloat y1;
356     CGFloat y2;
357     CGFloat minX;
358     CGFloat maxX;
359 };
360
361 static bool findIntersectionPoint(float y, CGPoint p1, CGPoint p2, CGFloat& x)
362 {
363     x = p1.x + (y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y);
364     return (p1.y < y && p2.y > y) || (p1.y > y && p2.y < y);
365 }
366
367 static void updateX(GlyphIterationState& state, CGFloat x)
368 {
369     state.minX = std::min(state.minX, x);
370     state.maxX = std::max(state.maxX, x);
371 }
372
373 // This function is called by CGPathApply and is therefore invoked for each
374 // contour in a glyph. This function models each contours as a straight line
375 // and calculates the intersections between each pseudo-contour and
376 // two horizontal lines (the upper and lower bounds of an underline) found in
377 // GlyphIterationState::y1 and GlyphIterationState::y2. It keeps track of the
378 // leftmost and rightmost intersection in GlyphIterationState::minX and
379 // GlyphIterationState::maxX.
380 static void findPathIntersections(void* stateAsVoidPointer, const CGPathElement* e)
381 {
382     auto& state = *static_cast<GlyphIterationState*>(stateAsVoidPointer);
383     bool doIntersection = false;
384     CGPoint point = CGPointZero;
385     switch (e->type) {
386     case kCGPathElementMoveToPoint:
387         state.startingPoint = e->points[0];
388         state.currentPoint = e->points[0];
389         break;
390     case kCGPathElementAddLineToPoint:
391         doIntersection = true;
392         point = e->points[0];
393         break;
394     case kCGPathElementAddQuadCurveToPoint:
395         doIntersection = true;
396         point = e->points[1];
397         break;
398     case kCGPathElementAddCurveToPoint:
399         doIntersection = true;
400         point = e->points[2];
401         break;
402     case kCGPathElementCloseSubpath:
403         doIntersection = true;
404         point = state.startingPoint;
405         break;
406     }
407     if (!doIntersection)
408         return;
409     CGFloat x;
410     if (findIntersectionPoint(state.y1, state.currentPoint, point, x))
411         updateX(state, x);
412     if (findIntersectionPoint(state.y2, state.currentPoint, point, x))
413         updateX(state, x);
414     if ((state.currentPoint.y >= state.y1 && state.currentPoint.y <= state.y2)
415         || (state.currentPoint.y <= state.y1 && state.currentPoint.y >= state.y2))
416         updateX(state, state.currentPoint.x);
417     state.currentPoint = point;
418 }
419
420 class MacGlyphToPathTranslator final : public GlyphToPathTranslator {
421 public:
422     MacGlyphToPathTranslator(const TextRun& textRun, const GlyphBuffer& glyphBuffer, const FloatPoint& textOrigin)
423         : m_index(0)
424         , m_textRun(textRun)
425         , m_glyphBuffer(glyphBuffer)
426         , m_fontData(glyphBuffer.fontAt(m_index))
427         , m_translation(CGAffineTransformScale(CGAffineTransformMakeTranslation(textOrigin.x(), textOrigin.y()), 1, -1))
428     {
429     }
430     bool containsMorePaths() final { return m_index != m_glyphBuffer.size(); }
431     Path path() final;
432     std::pair<float, float> extents() final;
433     GlyphUnderlineType underlineType() final;
434     void advance() final;
435
436 private:
437     unsigned m_index;
438     const TextRun& m_textRun;
439     const GlyphBuffer& m_glyphBuffer;
440     const Font* m_fontData;
441     CGAffineTransform m_translation;
442 };
443
444 Path MacGlyphToPathTranslator::path()
445 {
446     RetainPtr<CGPathRef> result = adoptCF(CTFontCreatePathForGlyph(m_fontData->platformData().ctFont(), m_glyphBuffer.glyphAt(m_index), &m_translation));
447     return adoptCF(CGPathCreateMutableCopy(result.get()));
448 }
449
450 std::pair<float, float> MacGlyphToPathTranslator::extents()
451 {
452     CGPoint beginning = CGPointApplyAffineTransform(CGPointMake(0, 0), m_translation);
453     CGSize end = CGSizeApplyAffineTransform(m_glyphBuffer.advanceAt(m_index), m_translation);
454     return std::make_pair(static_cast<float>(beginning.x), static_cast<float>(beginning.x + end.width));
455 }
456
457 auto MacGlyphToPathTranslator::underlineType() -> GlyphUnderlineType
458 {
459     return computeUnderlineType(m_textRun, m_glyphBuffer, m_index);
460 }
461
462 void MacGlyphToPathTranslator::advance()
463 {
464     GlyphBufferAdvance advance = m_glyphBuffer.advanceAt(m_index);
465     m_translation = CGAffineTransformTranslate(m_translation, advance.width(), advance.height());
466     ++m_index;
467     if (m_index < m_glyphBuffer.size())
468         m_fontData = m_glyphBuffer.fontAt(m_index);
469 }
470
471 DashArray FontCascade::dashesForIntersectionsWithRect(const TextRun& run, const FloatPoint& textOrigin, const FloatRect& lineExtents) const
472 {
473     if (isLoadingCustomFonts())
474         return DashArray();
475
476     GlyphBuffer glyphBuffer;
477     glyphBuffer.saveOffsetsInString();
478     float deltaX;
479     if (codePath(run) != FontCascade::Complex)
480         deltaX = getGlyphsAndAdvancesForSimpleText(run, 0, run.length(), glyphBuffer);
481     else
482         deltaX = getGlyphsAndAdvancesForComplexText(run, 0, run.length(), glyphBuffer);
483
484     if (!glyphBuffer.size())
485         return DashArray();
486     
487     // FIXME: Handle SVG + non-SVG interleaved runs. https://bugs.webkit.org/show_bug.cgi?id=133778
488     FloatPoint origin = FloatPoint(textOrigin.x() + deltaX, textOrigin.y());
489     MacGlyphToPathTranslator translator(run, glyphBuffer, origin);
490     DashArray result;
491     for (unsigned index = 0; translator.containsMorePaths(); ++index, translator.advance()) {
492         GlyphIterationState info = GlyphIterationState(CGPointMake(0, 0), CGPointMake(0, 0), lineExtents.y(), lineExtents.y() + lineExtents.height(), lineExtents.x() + lineExtents.width(), lineExtents.x());
493         const Font* localFont = glyphBuffer.fontAt(index);
494         if (!localFont) {
495             // The advances will get all messed up if we do anything other than bail here.
496             result.clear();
497             break;
498         }
499         switch (translator.underlineType()) {
500         case GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders: {
501             Path path = translator.path();
502             CGPathApply(path.platformPath(), &info, &findPathIntersections);
503             if (info.minX < info.maxX) {
504                 result.append(info.minX - lineExtents.x());
505                 result.append(info.maxX - lineExtents.x());
506             }
507             break;
508         }
509         case GlyphToPathTranslator::GlyphUnderlineType::SkipGlyph: {
510             std::pair<float, float> extents = translator.extents();
511             result.append(extents.first - lineExtents.x());
512             result.append(extents.second - lineExtents.x());
513             break;
514         }
515         case GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph:
516             // Nothing to do
517             break;
518         }
519     }
520     return result;
521 }
522 #endif
523
524 bool FontCascade::primaryFontIsSystemFont() const
525 {
526     const auto& fontData = primaryFont();
527     return CTFontDescriptorIsSystemUIFont(adoptCF(CTFontCopyFontDescriptor(fontData.platformData().ctFont())).get());
528 }
529
530 void FontCascade::adjustSelectionRectForComplexText(const TextRun& run, LayoutRect& selectionRect, unsigned from, unsigned to) const
531 {
532     ComplexTextController controller(*this, run);
533     controller.advance(from);
534     float beforeWidth = controller.runWidthSoFar();
535     controller.advance(to);
536     float afterWidth = controller.runWidthSoFar();
537
538     if (run.rtl())
539         selectionRect.move(controller.totalWidth() - afterWidth + controller.leadingExpansion(), 0);
540     else
541         selectionRect.move(beforeWidth, 0);
542     selectionRect.setWidth(LayoutUnit::fromFloatCeil(afterWidth - beforeWidth));
543 }
544
545 float FontCascade::getGlyphsAndAdvancesForComplexText(const TextRun& run, unsigned from, unsigned to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const
546 {
547     float initialAdvance;
548
549     ComplexTextController controller(*this, run, false, 0, forTextEmphasis);
550     controller.advance(from);
551     float beforeWidth = controller.runWidthSoFar();
552     controller.advance(to, &glyphBuffer);
553
554     if (glyphBuffer.isEmpty())
555         return 0;
556
557     float afterWidth = controller.runWidthSoFar();
558
559     if (run.rtl()) {
560         initialAdvance = controller.totalWidth() + controller.finalRoundingWidth() - afterWidth + controller.leadingExpansion();
561         glyphBuffer.reverse(0, glyphBuffer.size());
562     } else
563         initialAdvance = beforeWidth;
564
565     return initialAdvance;
566 }
567
568 void FontCascade::drawEmphasisMarksForComplexText(GraphicsContext& context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, unsigned from, unsigned to) const
569 {
570     GlyphBuffer glyphBuffer;
571     float initialAdvance = getGlyphsAndAdvancesForComplexText(run, from, to, glyphBuffer, ForTextEmphasis);
572
573     if (glyphBuffer.isEmpty())
574         return;
575
576     drawEmphasisMarks(context, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y()));
577 }
578
579 float FontCascade::floatWidthForComplexText(const TextRun& run, HashSet<const Font*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
580 {
581     ComplexTextController controller(*this, run, true, fallbackFonts);
582     if (glyphOverflow) {
583         glyphOverflow->top = std::max<int>(glyphOverflow->top, ceilf(-controller.minGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().ascent()));
584         glyphOverflow->bottom = std::max<int>(glyphOverflow->bottom, ceilf(controller.maxGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().descent()));
585         glyphOverflow->left = std::max<int>(0, ceilf(-controller.minGlyphBoundingBoxX()));
586         glyphOverflow->right = std::max<int>(0, ceilf(controller.maxGlyphBoundingBoxX() - controller.totalWidth()));
587     }
588     return controller.totalWidth();
589 }
590
591 int FontCascade::offsetForPositionForComplexText(const TextRun& run, float x, bool includePartialGlyphs) const
592 {
593     ComplexTextController controller(*this, run);
594     return controller.offsetForPosition(x, includePartialGlyphs);
595 }
596
597 const Font* FontCascade::fontForCombiningCharacterSequence(const UChar* characters, size_t length) const
598 {
599     UChar32 baseCharacter;
600     size_t baseCharacterLength = 0;
601     U16_NEXT(characters, baseCharacterLength, length, baseCharacter);
602
603     GlyphData baseCharacterGlyphData = glyphDataForCharacter(baseCharacter, false, NormalVariant);
604
605     if (!baseCharacterGlyphData.glyph)
606         return nullptr;
607
608     if (length == baseCharacterLength)
609         return baseCharacterGlyphData.font;
610
611     bool triedBaseCharacterFont = false;
612
613     for (unsigned i = 0; !fallbackRangesAt(i).isNull(); ++i) {
614         const Font* font = fallbackRangesAt(i).fontForCharacter(baseCharacter);
615         if (!font)
616             continue;
617 #if PLATFORM(IOS)
618         if (baseCharacter >= 0x0600 && baseCharacter <= 0x06ff && font->shouldNotBeUsedForArabic())
619             continue;
620 #endif
621         if (font->platformData().orientation() == Vertical) {
622             if (isCJKIdeographOrSymbol(baseCharacter) && !font->hasVerticalGlyphs())
623                 font = &font->brokenIdeographFont();
624             else if (m_fontDescription.nonCJKGlyphOrientation() == NonCJKGlyphOrientation::Mixed) {
625                 const Font& verticalRightFont = font->verticalRightOrientationFont();
626                 Glyph verticalRightGlyph = verticalRightFont.glyphForCharacter(baseCharacter);
627                 if (verticalRightGlyph == baseCharacterGlyphData.glyph)
628                     font = &verticalRightFont;
629             } else {
630                 const Font& uprightFont = font->uprightOrientationFont();
631                 Glyph uprightGlyph = uprightFont.glyphForCharacter(baseCharacter);
632                 if (uprightGlyph != baseCharacterGlyphData.glyph)
633                     font = &uprightFont;
634             }
635         }
636
637         if (font == baseCharacterGlyphData.font)
638             triedBaseCharacterFont = true;
639
640         if (font->canRenderCombiningCharacterSequence(characters, length))
641             return font;
642     }
643
644     if (!triedBaseCharacterFont && baseCharacterGlyphData.font && baseCharacterGlyphData.font->canRenderCombiningCharacterSequence(characters, length))
645         return baseCharacterGlyphData.font;
646
647     return Font::systemFallback();
648 }
649
650 }