Diff
Modified: trunk/Source/WebCore/ChangeLog (211775 => 211776)
--- trunk/Source/WebCore/ChangeLog 2017-02-07 06:45:43 UTC (rev 211775)
+++ trunk/Source/WebCore/ChangeLog 2017-02-07 08:14:06 UTC (rev 211776)
@@ -1,3 +1,18 @@
+2017-02-07 Myles C. Maxfield <mmaxfi...@apple.com>
+
+ Move platform-independent parts of ComplexTextController out of mac/ subfolder
+ https://bugs.webkit.org/show_bug.cgi?id=167922
+
+ Reviewed by Jon Lee.
+
+ Just a mechanical git mv, and updating the Xcode projects.
+
+ No new tests because there is no behavior change.
+
+ * WebCore.xcodeproj/project.pbxproj:
+ * platform/graphics/ComplexTextController.cpp: Renamed from Source/WebCore/platform/graphics/mac/ComplexTextController.cpp.
+ * platform/graphics/ComplexTextController.h: Renamed from Source/WebCore/platform/graphics/mac/ComplexTextController.h.
+
2017-02-06 Carlos Garcia Campos <cgar...@igalia.com>
Overlay scrolling with iframe-s broken
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (211775 => 211776)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2017-02-07 06:45:43 UTC (rev 211775)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2017-02-07 08:14:06 UTC (rev 211776)
@@ -1464,8 +1464,6 @@
37ACCF690DA414E70089E602 /* FontDescription.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37ACCE620DA2AA960089E602 /* FontDescription.cpp */; };
37B327D31D17096A005737FA /* PIPSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 37B327D21D17096A005737FA /* PIPSPI.h */; };
37BAAE581980D1DD005DFE71 /* ProtectionSpace.h in Headers */ = {isa = PBXBuildFile; fileRef = 37BAAE571980D1DD005DFE71 /* ProtectionSpace.h */; settings = {ATTRIBUTES = (Private, ); }; };
- 37C236101097EE7700EF9F72 /* ComplexTextController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37C2360E1097EE7700EF9F72 /* ComplexTextController.cpp */; };
- 37C236111097EE7700EF9F72 /* ComplexTextController.h in Headers */ = {isa = PBXBuildFile; fileRef = 37C2360F1097EE7700EF9F72 /* ComplexTextController.h */; settings = {ATTRIBUTES = (Private, ); }; };
37C238221098C84200EF9F72 /* ComplexTextControllerCoreText.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C238201098C84200EF9F72 /* ComplexTextControllerCoreText.mm */; };
37D456FD1A9A50D8003330A1 /* LocalizableStrings.pm in Copy Scripts */ = {isa = PBXBuildFile; fileRef = 37D456FB1A9A50B6003330A1 /* LocalizableStrings.pm */; };
37DDCD9413844FD50008B793 /* MIMEHeader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37DDCD9213844FD50008B793 /* MIMEHeader.cpp */; };
@@ -5682,6 +5680,8 @@
C28083401C6DC275001451B6 /* JSFontFace.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C280833D1C6DC22C001451B6 /* JSFontFace.cpp */; };
C28083421C6DC96A001451B6 /* JSFontFaceCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C28083411C6DC96A001451B6 /* JSFontFaceCustom.cpp */; };
C2E1F43F1D6254E10094625C /* BreakLines.h in Headers */ = {isa = PBXBuildFile; fileRef = BCEA4816097D93020094C9E4 /* BreakLines.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ C2F4E78A1E45BEA1006D7105 /* ComplexTextController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2F4E7881E45AEDF006D7105 /* ComplexTextController.cpp */; };
+ C2F4E78C1E45C3EF006D7105 /* ComplexTextController.h in Headers */ = {isa = PBXBuildFile; fileRef = C2F4E7891E45AEDF006D7105 /* ComplexTextController.h */; settings = {ATTRIBUTES = (Private, ); }; };
C330A22313EC196B0000B45B /* ColorChooser.h in Headers */ = {isa = PBXBuildFile; fileRef = C330A22113EC196B0000B45B /* ColorChooser.h */; settings = {ATTRIBUTES = (Private, ); }; };
C33EE5C414FB49610002095A /* BaseClickableWithKeyInputType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C33EE5C214FB49610002095A /* BaseClickableWithKeyInputType.cpp */; };
C33EE5C514FB49610002095A /* BaseClickableWithKeyInputType.h in Headers */ = {isa = PBXBuildFile; fileRef = C33EE5C314FB49610002095A /* BaseClickableWithKeyInputType.h */; };
@@ -8707,8 +8707,6 @@
37ACCE620DA2AA960089E602 /* FontDescription.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FontDescription.cpp; sourceTree = "<group>"; };
37B327D21D17096A005737FA /* PIPSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PIPSPI.h; sourceTree = "<group>"; };
37BAAE571980D1DD005DFE71 /* ProtectionSpace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProtectionSpace.h; sourceTree = "<group>"; };
- 37C2360E1097EE7700EF9F72 /* ComplexTextController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ComplexTextController.cpp; sourceTree = "<group>"; };
- 37C2360F1097EE7700EF9F72 /* ComplexTextController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComplexTextController.h; sourceTree = "<group>"; };
37C238201098C84200EF9F72 /* ComplexTextControllerCoreText.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ComplexTextControllerCoreText.mm; sourceTree = "<group>"; };
37D456FB1A9A50B6003330A1 /* LocalizableStrings.pm */ = {isa = PBXFileReference; lastKnownFileType = text.script.perl; path = LocalizableStrings.pm; sourceTree = "<group>"; };
37DDCD9213844FD50008B793 /* MIMEHeader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MIMEHeader.cpp; sourceTree = "<group>"; };
@@ -13714,6 +13712,8 @@
C280833D1C6DC22C001451B6 /* JSFontFace.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFontFace.cpp; sourceTree = "<group>"; };
C280833E1C6DC22C001451B6 /* JSFontFace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSFontFace.h; sourceTree = "<group>"; };
C28083411C6DC96A001451B6 /* JSFontFaceCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFontFaceCustom.cpp; sourceTree = "<group>"; };
+ C2F4E7881E45AEDF006D7105 /* ComplexTextController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ComplexTextController.cpp; sourceTree = "<group>"; };
+ C2F4E7891E45AEDF006D7105 /* ComplexTextController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComplexTextController.h; sourceTree = "<group>"; };
C330A22113EC196B0000B45B /* ColorChooser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorChooser.h; sourceTree = "<group>"; };
C33EE5C214FB49610002095A /* BaseClickableWithKeyInputType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BaseClickableWithKeyInputType.cpp; sourceTree = "<group>"; };
C33EE5C314FB49610002095A /* BaseClickableWithKeyInputType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseClickableWithKeyInputType.h; sourceTree = "<group>"; };
@@ -21887,8 +21887,6 @@
children = (
EDE3A4FF0C7A430600956A37 /* ColorMac.h */,
B275354A0B053814002CE64F /* ColorMac.mm */,
- 37C2360E1097EE7700EF9F72 /* ComplexTextController.cpp */,
- 37C2360F1097EE7700EF9F72 /* ComplexTextController.h */,
37C238201098C84200EF9F72 /* ComplexTextControllerCoreText.mm */,
49AF2D6B14435D210016A784 /* DisplayRefreshMonitorMac.cpp */,
2DE70022192FE82A00B0975C /* DisplayRefreshMonitorMac.h */,
@@ -21923,6 +21921,8 @@
B2A015910AF6CD53006BCE0E /* graphics */ = {
isa = PBXGroup;
children = (
+ C2F4E7881E45AEDF006D7105 /* ComplexTextController.cpp */,
+ C2F4E7891E45AEDF006D7105 /* ComplexTextController.h */,
076F0D0812B8192700C26AA4 /* avfoundation */,
499B3EC0128CCC1800E726C2 /* ca */,
B27535290B053814002CE64F /* cg */,
@@ -25172,6 +25172,7 @@
FD31603E12B0267600C1A359 /* AnalyserNode.h in Headers */,
31A795C71888BCB200382F90 /* ANGLEInstancedArrays.h in Headers */,
490707E71219C04300D90E51 /* ANGLEWebKitBridge.h in Headers */,
+ C2F4E78C1E45C3EF006D7105 /* ComplexTextController.h in Headers */,
49E912AB0EFAC906009D0CAF /* Animation.h in Headers */,
316FE1120E6E1DA700BF6088 /* AnimationBase.h in Headers */,
319848011A1D817B00A13318 /* AnimationEvent.h in Headers */,
@@ -25434,7 +25435,6 @@
E1FE137518402A6700892F13 /* CommonCryptoUtilities.h in Headers */,
0F60F32B1DFBB10700416D6C /* CommonVM.h in Headers */,
7C93F34A1AA6BA5E00A98BAB /* CompiledContentExtension.h in Headers */,
- 37C236111097EE7700EF9F72 /* ComplexTextController.h in Headers */,
E4BA50901BCFBD9500E34EF7 /* ComposedTreeAncestorIterator.h in Headers */,
E44FA1851BCA6B5A0091B6EF /* ComposedTreeIterator.h in Headers */,
316FE1160E6E1DA700BF6088 /* CompositeAnimation.h in Headers */,
@@ -29338,7 +29338,6 @@
E1FE137418402A6700892F13 /* CommonCryptoUtilities.cpp in Sources */,
0F60F32C1DFBB10B00416D6C /* CommonVM.cpp in Sources */,
7C93F3491AA6BA5E00A98BAB /* CompiledContentExtension.cpp in Sources */,
- 37C236101097EE7700EF9F72 /* ComplexTextController.cpp in Sources */,
37C238221098C84200EF9F72 /* ComplexTextControllerCoreText.mm in Sources */,
E44FA1871BCA91560091B6EF /* ComposedTreeIterator.cpp in Sources */,
316FE1150E6E1DA700BF6088 /* CompositeAnimation.cpp in Sources */,
@@ -31308,6 +31307,7 @@
D06C0D900CFD11460065F43F /* RemoveFormatCommand.cpp in Sources */,
93309E04099E64920056E581 /* RemoveNodeCommand.cpp in Sources */,
93309E06099E64920056E581 /* RemoveNodePreservingChildrenCommand.cpp in Sources */,
+ C2F4E78A1E45BEA1006D7105 /* ComplexTextController.cpp in Sources */,
7CD494CC1A86EB1D000A87EC /* RenderAttachment.cpp in Sources */,
BCEA485F097D93020094C9E4 /* RenderBlock.cpp in Sources */,
BC10D76717D8EE6E005E2626 /* RenderBlockFlow.cpp in Sources */,
Copied: trunk/Source/WebCore/platform/graphics/ComplexTextController.cpp (from rev 211775, trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp) (0 => 211776)
--- trunk/Source/WebCore/platform/graphics/ComplexTextController.cpp (rev 0)
+++ trunk/Source/WebCore/platform/graphics/ComplexTextController.cpp 2017-02-07 08:14:06 UTC (rev 211776)
@@ -0,0 +1,833 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ComplexTextController.h"
+
+#include "CharacterProperties.h"
+#include "FloatSize.h"
+#include "FontCascade.h"
+#include "RenderBlock.h"
+#include "RenderText.h"
+#include "TextRun.h"
+#include <unicode/ubrk.h>
+#include <wtf/Optional.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/text/TextBreakIterator.h>
+#include <wtf/unicode/CharacterNames.h>
+
+#if PLATFORM(IOS)
+#include <CoreText/CoreText.h>
+#endif
+
+#if PLATFORM(MAC)
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+namespace WebCore {
+
+class TextLayout {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ static bool isNeeded(RenderText& text, const FontCascade& font)
+ {
+ TextRun run = RenderBlock::constructTextRun(text, text.style());
+ return font.codePath(run) == FontCascade::Complex;
+ }
+
+ TextLayout(RenderText& text, const FontCascade& font, float xPos)
+ : m_font(font)
+ , m_run(constructTextRun(text, xPos))
+ , m_controller(std::make_unique<ComplexTextController>(m_font, m_run, true))
+ {
+ }
+
+ float width(unsigned from, unsigned len, HashSet<const Font*>* fallbackFonts)
+ {
+ m_controller->advance(from, 0, ByWholeGlyphs, fallbackFonts);
+ float beforeWidth = m_controller->runWidthSoFar();
+ if (m_font.wordSpacing() && from && FontCascade::treatAsSpace(m_run[from]))
+ beforeWidth += m_font.wordSpacing();
+ m_controller->advance(from + len, 0, ByWholeGlyphs, fallbackFonts);
+ float afterWidth = m_controller->runWidthSoFar();
+ return afterWidth - beforeWidth;
+ }
+
+private:
+ static TextRun constructTextRun(RenderText& text, float xPos)
+ {
+ TextRun run = RenderBlock::constructTextRun(text, text.style());
+ run.setCharactersLength(text.textLength());
+ ASSERT(run.charactersLength() >= run.length());
+ run.setXPos(xPos);
+ return run;
+ }
+
+ // ComplexTextController has only references to its FontCascade and TextRun so they must be kept alive here.
+ FontCascade m_font;
+ TextRun m_run;
+ std::unique_ptr<ComplexTextController> m_controller;
+};
+
+void TextLayoutDeleter::operator()(TextLayout* layout) const
+{
+ delete layout;
+}
+
+std::unique_ptr<TextLayout, TextLayoutDeleter> FontCascade::createLayout(RenderText& text, float xPos, bool collapseWhiteSpace) const
+{
+ if (!collapseWhiteSpace || !TextLayout::isNeeded(text, *this))
+ return nullptr;
+ return std::unique_ptr<TextLayout, TextLayoutDeleter>(new TextLayout(text, *this, xPos));
+}
+
+float FontCascade::width(TextLayout& layout, unsigned from, unsigned len, HashSet<const Font*>* fallbackFonts)
+{
+ return layout.width(from, len, fallbackFonts);
+}
+
+void ComplexTextController::computeExpansionOpportunity()
+{
+ if (!m_expansion)
+ m_expansionPerOpportunity = 0;
+ else {
+ unsigned expansionOpportunityCount = FontCascade::expansionOpportunityCount(m_run.text(), m_run.ltr() ? LTR : RTL, m_run.expansionBehavior()).first;
+
+ if (!expansionOpportunityCount)
+ m_expansionPerOpportunity = 0;
+ else
+ m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
+ }
+}
+
+ComplexTextController::ComplexTextController(const FontCascade& font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const Font*>* fallbackFonts, bool forTextEmphasis)
+ : m_fallbackFonts(fallbackFonts)
+ , m_font(font)
+ , m_run(run)
+ , m_end(run.length())
+ , m_expansion(run.expansion())
+ , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
+ , m_forTextEmphasis(forTextEmphasis)
+{
+ computeExpansionOpportunity();
+
+ collectComplexTextRuns();
+
+ finishConstruction();
+}
+
+ComplexTextController::ComplexTextController(const FontCascade& font, const TextRun& run, Vector<Ref<ComplexTextRun>>& runs)
+ : m_font(font)
+ , m_run(run)
+ , m_end(run.length())
+ , m_expansion(run.expansion())
+{
+ computeExpansionOpportunity();
+
+ for (auto& run : runs)
+ m_complexTextRuns.append(run.ptr());
+
+ finishConstruction();
+}
+
+void ComplexTextController::finishConstruction()
+{
+ adjustGlyphsAndAdvances();
+
+ if (!m_isLTROnly) {
+ m_runIndices.reserveInitialCapacity(m_complexTextRuns.size());
+
+ m_glyphCountFromStartToIndex.reserveInitialCapacity(m_complexTextRuns.size());
+ unsigned glyphCountSoFar = 0;
+ for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) {
+ m_glyphCountFromStartToIndex.uncheckedAppend(glyphCountSoFar);
+ glyphCountSoFar += m_complexTextRuns[i]->glyphCount();
+ }
+ }
+}
+
+unsigned ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
+{
+ if (h >= m_totalWidth)
+ return m_run.ltr() ? m_end : 0;
+
+ if (h < 0)
+ return m_run.ltr() ? 0 : m_end;
+
+ float x = h;
+
+ size_t runCount = m_complexTextRuns.size();
+ unsigned offsetIntoAdjustedGlyphs = 0;
+
+ for (size_t r = 0; r < runCount; ++r) {
+ const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
+ for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
+ unsigned index = offsetIntoAdjustedGlyphs + j;
+ float adjustedAdvance = m_adjustedBaseAdvances[index].width();
+ if (x < adjustedAdvance) {
+ unsigned hitGlyphStart = complexTextRun.indexAt(j);
+ unsigned hitGlyphEnd;
+ if (m_run.ltr())
+ hitGlyphEnd = std::max(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : complexTextRun.indexEnd());
+ else
+ hitGlyphEnd = std::max(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : complexTextRun.indexEnd());
+
+ // FIXME: Instead of dividing the glyph's advance equally between the characters, this
+ // could use the glyph's "ligature carets". This is available in CoreText via CTFontGetLigatureCaretPositions().
+ unsigned hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
+ unsigned stringLength = complexTextRun.stringLength();
+ UBreakIterator* cursorPositionIterator = cursorMovementIterator(StringView(complexTextRun.characters(), stringLength));
+ unsigned clusterStart;
+ if (ubrk_isBoundary(cursorPositionIterator, hitIndex))
+ clusterStart = hitIndex;
+ else {
+ int preceeding = ubrk_preceding(cursorPositionIterator, hitIndex);
+ clusterStart = preceeding == UBRK_DONE ? 0 : preceeding;
+ }
+
+ if (!includePartialGlyphs)
+ return complexTextRun.stringLocation() + clusterStart;
+
+ int following = ubrk_following(cursorPositionIterator, hitIndex);
+ unsigned clusterEnd = following == UBRK_DONE ? stringLength : following;
+
+ float clusterWidth;
+ // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
+ // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
+ // reordering and no font fallback should occur within a CTLine.
+ if (clusterEnd - clusterStart > 1) {
+ clusterWidth = adjustedAdvance;
+ if (j) {
+ unsigned firstGlyphBeforeCluster = j - 1;
+ while (complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
+ float width = m_adjustedBaseAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width();
+ clusterWidth += width;
+ x += width;
+ if (!firstGlyphBeforeCluster)
+ break;
+ firstGlyphBeforeCluster--;
+ }
+ }
+ unsigned firstGlyphAfterCluster = j + 1;
+ while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
+ clusterWidth += m_adjustedBaseAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width();
+ firstGlyphAfterCluster++;
+ }
+ } else {
+ clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
+ x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
+ }
+ if (x <= clusterWidth / 2)
+ return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
+ return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
+ }
+ x -= adjustedAdvance;
+ }
+ offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+// FIXME: We should consider reimplementing this function using ICU to advance by grapheme.
+// The current implementation only considers explicitly emoji sequences and emoji variations.
+static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount)
+{
+ ASSERT(iterator < end);
+
+ markCount = 0;
+
+ unsigned i = 0;
+ unsigned remainingCharacters = end - iterator;
+ U16_NEXT(iterator, i, remainingCharacters, baseCharacter);
+ iterator = iterator + i;
+ if (U_IS_SURROGATE(baseCharacter))
+ return false;
+
+ // Consume marks.
+ bool sawEmojiGroupCandidate = isEmojiGroupCandidate(baseCharacter);
+ bool sawJoiner = false;
+ while (iterator < end) {
+ UChar32 nextCharacter;
+ unsigned markLength = 0;
+ bool shouldContinue = false;
+ ASSERT(end >= iterator);
+ U16_NEXT(iterator, markLength, static_cast<unsigned>(end - iterator), nextCharacter);
+
+ if (isVariationSelector(nextCharacter) || isEmojiFitzpatrickModifier(nextCharacter))
+ shouldContinue = true;
+
+ if (sawJoiner && isEmojiGroupCandidate(nextCharacter))
+ shouldContinue = true;
+
+ sawJoiner = false;
+ if (sawEmojiGroupCandidate && nextCharacter == zeroWidthJoiner) {
+ sawJoiner = true;
+ shouldContinue = true;
+ }
+
+ if (!shouldContinue && !(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK))
+ break;
+
+ markCount += markLength;
+ iterator += markLength;
+ }
+
+ return true;
+}
+
+// FIXME: Capitalization is language-dependent and context-dependent and should operate on grapheme clusters instead of codepoints.
+static inline std::optional<UChar32> capitalized(UChar32 baseCharacter)
+{
+ if (U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK)
+ return std::nullopt;
+
+ UChar32 uppercaseCharacter = u_toupper(baseCharacter);
+ ASSERT(uppercaseCharacter == baseCharacter || (U_IS_BMP(baseCharacter) == U_IS_BMP(uppercaseCharacter)));
+ if (uppercaseCharacter != baseCharacter)
+ return uppercaseCharacter;
+ return std::nullopt;
+}
+
+static bool shouldSynthesize(bool dontSynthesizeSmallCaps, const Font* nextFont, UChar32 baseCharacter, std::optional<UChar32> capitalizedBase, FontVariantCaps fontVariantCaps, bool engageAllSmallCapsProcessing)
+{
+ if (dontSynthesizeSmallCaps)
+ return false;
+ if (!nextFont || nextFont == Font::systemFallback())
+ return false;
+ if (engageAllSmallCapsProcessing && isASCIISpace(baseCharacter))
+ return false;
+ if (!engageAllSmallCapsProcessing && !capitalizedBase)
+ return false;
+ return !nextFont->variantCapsSupportsCharacterForSynthesis(fontVariantCaps, baseCharacter);
+}
+
+void ComplexTextController::collectComplexTextRuns()
+{
+ if (!m_end)
+ return;
+
+ // We break up glyph run generation for the string by Font.
+ const UChar* cp;
+
+ if (m_run.is8Bit()) {
+ String stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(), m_run.length());
+ m_stringsFor8BitRuns.append(WTFMove(stringFor8BitRun));
+ cp = m_stringsFor8BitRuns.last().characters16();
+ } else
+ cp = m_run.characters16();
+
+ auto fontVariantCaps = m_font.fontDescription().variantCaps();
+ bool dontSynthesizeSmallCaps = !static_cast<bool>(m_font.fontDescription().fontSynthesis() & FontSynthesisSmallCaps);
+ bool engageAllSmallCapsProcessing = fontVariantCaps == FontVariantCaps::AllSmall || fontVariantCaps == FontVariantCaps::AllPetite;
+ bool engageSmallCapsProcessing = engageAllSmallCapsProcessing || fontVariantCaps == FontVariantCaps::Small || fontVariantCaps == FontVariantCaps::Petite;
+
+ if (engageAllSmallCapsProcessing || engageSmallCapsProcessing)
+ m_smallCapsBuffer.resize(m_end);
+
+ unsigned indexOfFontTransition = 0;
+ const UChar* curr = cp;
+ const UChar* end = cp + m_end;
+
+ const Font* font;
+ const Font* nextFont;
+ const Font* synthesizedFont = nullptr;
+ const Font* smallSynthesizedFont = nullptr;
+
+ unsigned markCount;
+ UChar32 baseCharacter;
+ if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
+ return;
+
+ nextFont = m_font.fontForCombiningCharacterSequence(cp, curr - cp);
+
+ bool isSmallCaps = false;
+ bool nextIsSmallCaps = false;
+
+ auto capitalizedBase = capitalized(baseCharacter);
+ if (shouldSynthesize(dontSynthesizeSmallCaps, nextFont, baseCharacter, capitalizedBase, fontVariantCaps, engageAllSmallCapsProcessing)) {
+ synthesizedFont = &nextFont->noSynthesizableFeaturesFont();
+ smallSynthesizedFont = synthesizedFont->smallCapsFont(m_font.fontDescription());
+ UChar32 characterToWrite = capitalizedBase ? capitalizedBase.value() : cp[0];
+ unsigned characterIndex = 0;
+ U16_APPEND_UNSAFE(m_smallCapsBuffer, characterIndex, characterToWrite);
+ for (unsigned i = characterIndex; cp + i < curr; ++i)
+ m_smallCapsBuffer[i] = cp[i];
+ nextIsSmallCaps = true;
+ }
+
+ while (curr < end) {
+ font = nextFont;
+ isSmallCaps = nextIsSmallCaps;
+ unsigned index = curr - cp;
+
+ if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
+ return;
+
+ if (synthesizedFont) {
+ if (auto capitalizedBase = capitalized(baseCharacter)) {
+ unsigned characterIndex = index;
+ U16_APPEND_UNSAFE(m_smallCapsBuffer, characterIndex, capitalizedBase.value());
+ for (unsigned i = 0; i < markCount; ++i)
+ m_smallCapsBuffer[i + characterIndex] = cp[i + characterIndex];
+ nextIsSmallCaps = true;
+ } else {
+ if (engageAllSmallCapsProcessing) {
+ for (unsigned i = 0; i < curr - cp - index; ++i)
+ m_smallCapsBuffer[index + i] = cp[index + i];
+ }
+ nextIsSmallCaps = engageAllSmallCapsProcessing;
+ }
+ }
+
+ if (baseCharacter == zeroWidthJoiner)
+ nextFont = font;
+ else
+ nextFont = m_font.fontForCombiningCharacterSequence(cp + index, curr - cp - index);
+
+ capitalizedBase = capitalized(baseCharacter);
+ if (!synthesizedFont && shouldSynthesize(dontSynthesizeSmallCaps, nextFont, baseCharacter, capitalizedBase, fontVariantCaps, engageAllSmallCapsProcessing)) {
+ // Rather than synthesize each character individually, we should synthesize the entire "run" if any character requires synthesis.
+ synthesizedFont = &nextFont->noSynthesizableFeaturesFont();
+ smallSynthesizedFont = synthesizedFont->smallCapsFont(m_font.fontDescription());
+ nextIsSmallCaps = true;
+ curr = cp + indexOfFontTransition;
+ continue;
+ }
+
+ if (nextFont != font || nextIsSmallCaps != isSmallCaps) {
+ unsigned itemLength = index - indexOfFontTransition;
+ if (itemLength) {
+ unsigned itemStart = indexOfFontTransition;
+ if (synthesizedFont) {
+ if (isSmallCaps)
+ collectComplexTextRunsForCharacters(m_smallCapsBuffer.data() + itemStart, itemLength, itemStart, smallSynthesizedFont);
+ else
+ collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, synthesizedFont);
+ } else
+ collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, font);
+ if (nextFont != font) {
+ synthesizedFont = nullptr;
+ smallSynthesizedFont = nullptr;
+ nextIsSmallCaps = false;
+ }
+ }
+ indexOfFontTransition = index;
+ }
+ }
+
+ ASSERT(m_end >= indexOfFontTransition);
+ unsigned itemLength = m_end - indexOfFontTransition;
+ if (itemLength) {
+ unsigned itemStart = indexOfFontTransition;
+ if (synthesizedFont) {
+ if (nextIsSmallCaps)
+ collectComplexTextRunsForCharacters(m_smallCapsBuffer.data() + itemStart, itemLength, itemStart, smallSynthesizedFont);
+ else
+ collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, synthesizedFont);
+ } else
+ collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, nextFont);
+ }
+
+ if (!m_run.ltr())
+ m_complexTextRuns.reverse();
+}
+
+unsigned ComplexTextController::ComplexTextRun::indexAt(unsigned i) const
+{
+ ASSERT(i < m_glyphCount);
+
+ return m_coreTextIndices[i];
+}
+
+void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
+{
+ ASSERT(m_isMonotonic);
+ m_isMonotonic = false;
+
+ Vector<bool, 64> mappedIndices(m_stringLength, false);
+ for (unsigned i = 0; i < m_glyphCount; ++i) {
+ ASSERT(indexAt(i) < m_stringLength);
+ mappedIndices[indexAt(i)] = true;
+ }
+
+ m_glyphEndOffsets.grow(m_glyphCount);
+ for (unsigned i = 0; i < m_glyphCount; ++i) {
+ unsigned nextMappedIndex = m_indexEnd;
+ for (unsigned j = indexAt(i) + 1; j < m_stringLength; ++j) {
+ if (mappedIndices[j]) {
+ nextMappedIndex = j;
+ break;
+ }
+ }
+ m_glyphEndOffsets[i] = nextMappedIndex;
+ }
+}
+
+unsigned ComplexTextController::indexOfCurrentRun(unsigned& leftmostGlyph)
+{
+ leftmostGlyph = 0;
+
+ size_t runCount = m_complexTextRuns.size();
+ if (m_currentRun >= runCount)
+ return runCount;
+
+ if (m_isLTROnly) {
+ for (unsigned i = 0; i < m_currentRun; ++i)
+ leftmostGlyph += m_complexTextRuns[i]->glyphCount();
+ return m_currentRun;
+ }
+
+ if (m_runIndices.isEmpty()) {
+ unsigned firstRun = 0;
+ unsigned firstRunOffset = stringBegin(*m_complexTextRuns[0]);
+ for (unsigned i = 1; i < runCount; ++i) {
+ unsigned offset = stringBegin(*m_complexTextRuns[i]);
+ if (offset < firstRunOffset) {
+ firstRun = i;
+ firstRunOffset = offset;
+ }
+ }
+ m_runIndices.uncheckedAppend(firstRun);
+ }
+
+ while (m_runIndices.size() <= m_currentRun) {
+ unsigned offset = stringEnd(*m_complexTextRuns[m_runIndices.last()]);
+
+ for (unsigned i = 0; i < runCount; ++i) {
+ if (offset == stringBegin(*m_complexTextRuns[i])) {
+ m_runIndices.uncheckedAppend(i);
+ break;
+ }
+ }
+ }
+
+ unsigned currentRunIndex = m_runIndices[m_currentRun];
+ leftmostGlyph = m_glyphCountFromStartToIndex[currentRunIndex];
+ return currentRunIndex;
+}
+
+unsigned ComplexTextController::incrementCurrentRun(unsigned& leftmostGlyph)
+{
+ if (m_isLTROnly) {
+ leftmostGlyph += m_complexTextRuns[m_currentRun++]->glyphCount();
+ return m_currentRun;
+ }
+
+ m_currentRun++;
+ leftmostGlyph = 0;
+ return indexOfCurrentRun(leftmostGlyph);
+}
+
+float ComplexTextController::runWidthSoFarFraction(unsigned glyphStartOffset, unsigned glyphEndOffset, unsigned oldCharacterInCurrentGlyph, GlyphIterationStyle iterationStyle) const
+{
+ // FIXME: Instead of dividing the glyph's advance equally between the characters, this
+ // could use the glyph's "ligature carets". This is available in CoreText via CTFontGetLigatureCaretPositions().
+ if (glyphStartOffset == glyphEndOffset) {
+ // When there are multiple glyphs per character we need to advance by the full width of the glyph.
+ ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
+ return 1;
+ }
+
+ if (iterationStyle == ByWholeGlyphs) {
+ if (!oldCharacterInCurrentGlyph)
+ return 1;
+ return 0;
+ }
+
+ return static_cast<float>(m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
+}
+
+void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, GlyphIterationStyle iterationStyle, HashSet<const Font*>* fallbackFonts)
+{
+ if (offset > m_end)
+ offset = m_end;
+
+ if (offset <= m_currentCharacter) {
+ m_runWidthSoFar = 0;
+ m_numGlyphsSoFar = 0;
+ m_currentRun = 0;
+ m_glyphInCurrentRun = 0;
+ m_characterInCurrentGlyph = 0;
+ }
+
+ m_currentCharacter = offset;
+
+ size_t runCount = m_complexTextRuns.size();
+
+ unsigned indexOfLeftmostGlyphInCurrentRun = 0; // Relative to the beginning of ComplexTextController.
+ unsigned currentRunIndex = indexOfCurrentRun(indexOfLeftmostGlyphInCurrentRun);
+ while (m_currentRun < runCount) {
+ const ComplexTextRun& complexTextRun = *m_complexTextRuns[currentRunIndex];
+ bool ltr = complexTextRun.isLTR();
+ unsigned glyphCount = complexTextRun.glyphCount();
+ unsigned glyphIndexIntoCurrentRun = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
+ unsigned glyphIndexIntoComplexTextController = indexOfLeftmostGlyphInCurrentRun + glyphIndexIntoCurrentRun;
+ if (fallbackFonts && &complexTextRun.font() != &m_font.primaryFont())
+ fallbackFonts->add(&complexTextRun.font());
+
+ // We must store the initial advance for the first glyph we are going to draw.
+ // When leftmostGlyph is 0, it represents the first glyph to draw, taking into
+ // account the text direction.
+ if (!indexOfLeftmostGlyphInCurrentRun && glyphBuffer)
+ glyphBuffer->setInitialAdvance(GlyphBufferAdvance(complexTextRun.initialAdvance().width(), complexTextRun.initialAdvance().height()));
+
+ while (m_glyphInCurrentRun < glyphCount) {
+ unsigned glyphStartOffset = complexTextRun.indexAt(glyphIndexIntoCurrentRun);
+ unsigned glyphEndOffset;
+ if (complexTextRun.isMonotonic()) {
+ if (ltr)
+ glyphEndOffset = std::max(glyphStartOffset, glyphIndexIntoCurrentRun + 1 < glyphCount ? complexTextRun.indexAt(glyphIndexIntoCurrentRun + 1) : complexTextRun.indexEnd());
+ else
+ glyphEndOffset = std::max(glyphStartOffset, glyphIndexIntoCurrentRun > 0 ? complexTextRun.indexAt(glyphIndexIntoCurrentRun - 1) : complexTextRun.indexEnd());
+ } else
+ glyphEndOffset = complexTextRun.endOffsetAt(glyphIndexIntoCurrentRun);
+
+ FloatSize adjustedBaseAdvance = m_adjustedBaseAdvances[glyphIndexIntoComplexTextController];
+
+ if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
+ return;
+
+ if (glyphBuffer && !m_characterInCurrentGlyph) {
+ auto currentGlyphOrigin = glyphOrigin(glyphIndexIntoComplexTextController);
+ GlyphBufferAdvance paintAdvance(adjustedBaseAdvance);
+ if (!glyphIndexIntoCurrentRun) {
+ // The first layout advance of every run includes the "initial layout advance." However, here, we need
+ // paint advances, so subtract it out before transforming the layout advance into a paint advance.
+ paintAdvance.setWidth(paintAdvance.width() - (complexTextRun.initialAdvance().width() - currentGlyphOrigin.x()));
+ paintAdvance.setHeight(paintAdvance.height() - (complexTextRun.initialAdvance().height() - currentGlyphOrigin.y()));
+ }
+ paintAdvance.setWidth(paintAdvance.width() + glyphOrigin(glyphIndexIntoComplexTextController + 1).x() - currentGlyphOrigin.x());
+ paintAdvance.setHeight(paintAdvance.height() + glyphOrigin(glyphIndexIntoComplexTextController + 1).y() - currentGlyphOrigin.y());
+ if (glyphIndexIntoCurrentRun == glyphCount - 1 && currentRunIndex + 1 < runCount) {
+ // Our paint advance points to the end of the run. However, the next run may have an
+ // initial advance, and our paint advance needs to point to the location of the next
+ // glyph. So, we need to add in the next run's initial advance.
+ paintAdvance.setWidth(paintAdvance.width() - glyphOrigin(glyphIndexIntoComplexTextController + 1).x() + m_complexTextRuns[currentRunIndex + 1]->initialAdvance().width());
+ paintAdvance.setHeight(paintAdvance.height() - glyphOrigin(glyphIndexIntoComplexTextController + 1).y() + m_complexTextRuns[currentRunIndex + 1]->initialAdvance().height());
+ }
+ paintAdvance.setHeight(-paintAdvance.height()); // Increasing y points down
+ glyphBuffer->add(m_adjustedGlyphs[glyphIndexIntoComplexTextController], &complexTextRun.font(), paintAdvance, complexTextRun.indexAt(m_glyphInCurrentRun));
+ }
+
+ unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
+ m_characterInCurrentGlyph = std::min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
+ m_runWidthSoFar += adjustedBaseAdvance.width() * runWidthSoFarFraction(glyphStartOffset, glyphEndOffset, oldCharacterInCurrentGlyph, iterationStyle);
+
+ if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
+ return;
+
+ m_numGlyphsSoFar++;
+ m_glyphInCurrentRun++;
+ m_characterInCurrentGlyph = 0;
+ if (ltr) {
+ glyphIndexIntoCurrentRun++;
+ glyphIndexIntoComplexTextController++;
+ } else {
+ glyphIndexIntoCurrentRun--;
+ glyphIndexIntoComplexTextController--;
+ }
+ }
+ currentRunIndex = incrementCurrentRun(indexOfLeftmostGlyphInCurrentRun);
+ m_glyphInCurrentRun = 0;
+ }
+}
+
+static inline std::pair<bool, bool> expansionLocation(bool ideograph, bool treatAsSpace, bool ltr, bool isAfterExpansion, bool forbidLeadingExpansion, bool forbidTrailingExpansion, bool forceLeadingExpansion, bool forceTrailingExpansion)
+{
+ bool expandLeft = ideograph;
+ bool expandRight = ideograph;
+ if (treatAsSpace) {
+ if (ltr)
+ expandRight = true;
+ else
+ expandLeft = true;
+ }
+ if (isAfterExpansion)
+ expandLeft = false;
+ ASSERT(!forbidLeadingExpansion || !forceLeadingExpansion);
+ ASSERT(!forbidTrailingExpansion || !forceTrailingExpansion);
+ if (forbidLeadingExpansion)
+ expandLeft = false;
+ if (forbidTrailingExpansion)
+ expandRight = false;
+ if (forceLeadingExpansion)
+ expandLeft = true;
+ if (forceTrailingExpansion)
+ expandRight = true;
+ return std::make_pair(expandLeft, expandRight);
+}
+
+void ComplexTextController::adjustGlyphsAndAdvances()
+{
+ bool afterExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
+ float widthSinceLastCommit = 0;
+ size_t runCount = m_complexTextRuns.size();
+ bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
+ bool runForcesLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForceLeadingExpansion;
+ bool runForcesTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForceTrailingExpansion;
+ bool runForbidsLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
+ bool runForbidsTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForbidTrailingExpansion;
+
+ // We are iterating in glyph order, not string order. Compare this to WidthIterator::advanceInternal()
+ for (size_t runIndex = 0; runIndex < runCount; ++runIndex) {
+ ComplexTextRun& complexTextRun = *m_complexTextRuns[runIndex];
+ unsigned glyphCount = complexTextRun.glyphCount();
+ const Font& font = complexTextRun.font();
+
+ if (!complexTextRun.isLTR())
+ m_isLTROnly = false;
+
+ const CGGlyph* glyphs = complexTextRun.glyphs();
+ const FloatSize* advances = complexTextRun.baseAdvances();
+
+ bool lastRun = runIndex + 1 == runCount;
+ float spaceWidth = font.spaceWidth() - font.syntheticBoldOffset();
+ const UChar* cp = complexTextRun.characters();
+ FloatPoint glyphOrigin;
+ unsigned lastCharacterIndex = m_run.ltr() ? std::numeric_limits<unsigned>::min() : std::numeric_limits<unsigned>::max();
+ bool isMonotonic = true;
+
+ for (unsigned i = 0; i < glyphCount; i++) {
+ unsigned characterIndex = complexTextRun.indexAt(i);
+ if (m_run.ltr()) {
+ if (characterIndex < lastCharacterIndex)
+ isMonotonic = false;
+ } else {
+ if (characterIndex > lastCharacterIndex)
+ isMonotonic = false;
+ }
+ UChar ch = *(cp + characterIndex);
+ bool lastGlyph = lastRun && i + 1 == glyphCount;
+ UChar nextCh;
+ if (lastGlyph)
+ nextCh = ' ';
+ else if (i + 1 < glyphCount)
+ nextCh = *(cp + complexTextRun.indexAt(i + 1));
+ else
+ nextCh = *(m_complexTextRuns[runIndex + 1]->characters() + m_complexTextRuns[runIndex + 1]->indexAt(0));
+
+ bool treatAsSpace = FontCascade::treatAsSpace(ch);
+ CGGlyph glyph = treatAsSpace ? font.spaceGlyph() : glyphs[i];
+ FloatSize advance = treatAsSpace ? FloatSize(spaceWidth, advances[i].height()) : advances[i];
+
+ if (ch == '\t' && m_run.allowTabs())
+ advance.setWidth(m_font.tabWidth(font, m_run.tabSize(), m_run.xPos() + m_totalWidth + widthSinceLastCommit));
+ else if (FontCascade::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
+ advance.setWidth(0);
+ glyph = font.spaceGlyph();
+ }
+
+ if (!i) {
+ advance.expand(complexTextRun.initialAdvance().width(), complexTextRun.initialAdvance().height());
+ if (auto* origins = complexTextRun.glyphOrigins())
+ advance.expand(-origins[0].x(), -origins[0].y());
+ }
+
+ advance.expand(font.syntheticBoldOffset(), 0);
+
+ if (hasExtraSpacing) {
+ // If we're a glyph with an advance, add in letter-spacing.
+ // That way we weed out zero width lurkers. This behavior matches the fast text code path.
+ if (advance.width())
+ advance.expand(m_font.letterSpacing(), 0);
+
+ unsigned characterIndexInRun = characterIndex + complexTextRun.stringLocation();
+ bool isFirstCharacter = !(characterIndex + complexTextRun.stringLocation());
+ bool isLastCharacter = characterIndexInRun + 1 == m_run.length() || (U16_IS_LEAD(ch) && characterIndexInRun + 2 == m_run.length() && U16_IS_TRAIL(*(cp + characterIndex + 1)));
+
+ bool forceLeadingExpansion = false; // On the left, regardless of m_run.ltr()
+ bool forceTrailingExpansion = false; // On the right, regardless of m_run.ltr()
+ bool forbidLeadingExpansion = false;
+ bool forbidTrailingExpansion = false;
+ if (runForcesLeadingExpansion)
+ forceLeadingExpansion = m_run.ltr() ? isFirstCharacter : isLastCharacter;
+ if (runForcesTrailingExpansion)
+ forceTrailingExpansion = m_run.ltr() ? isLastCharacter : isFirstCharacter;
+ if (runForbidsLeadingExpansion)
+ forbidLeadingExpansion = m_run.ltr() ? isFirstCharacter : isLastCharacter;
+ if (runForbidsTrailingExpansion)
+ forbidTrailingExpansion = m_run.ltr() ? isLastCharacter : isFirstCharacter;
+ // Handle justification and word-spacing.
+ bool ideograph = FontCascade::isCJKIdeographOrSymbol(ch);
+ if (treatAsSpace || ideograph || forceLeadingExpansion || forceTrailingExpansion) {
+ // Distribute the run's total expansion evenly over all expansion opportunities in the run.
+ if (m_expansion) {
+ bool expandLeft, expandRight;
+ std::tie(expandLeft, expandRight) = expansionLocation(ideograph, treatAsSpace, m_run.ltr(), afterExpansion, forbidLeadingExpansion, forbidTrailingExpansion, forceLeadingExpansion, forceTrailingExpansion);
+ m_expansion -= m_expansionPerOpportunity;
+ advance.expand(m_expansionPerOpportunity, 0);
+ if (expandLeft) {
+ // Increase previous width
+ if (m_adjustedBaseAdvances.isEmpty())
+ complexTextRun.growInitialAdvanceHorizontally(m_expansionPerOpportunity);
+ else
+ m_adjustedBaseAdvances.last().expand(m_expansionPerOpportunity, 0);
+ }
+ if (expandRight)
+ afterExpansion = true;
+ } else
+ afterExpansion = false;
+
+ // Account for word-spacing.
+ if (treatAsSpace && (ch != '\t' || !m_run.allowTabs()) && (characterIndex > 0 || runIndex > 0) && m_font.wordSpacing())
+ advance.expand(m_font.wordSpacing(), 0);
+ } else
+ afterExpansion = false;
+ }
+
+ widthSinceLastCommit += advance.width();
+
+ // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
+ if (m_forTextEmphasis && (!FontCascade::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
+ glyph = 0;
+
+ m_adjustedBaseAdvances.append(advance);
+ if (auto* origins = complexTextRun.glyphOrigins()) {
+ ASSERT(m_glyphOrigins.size() < m_adjustedBaseAdvances.size());
+ m_glyphOrigins.grow(m_adjustedBaseAdvances.size());
+ m_glyphOrigins[m_glyphOrigins.size() - 1] = origins[i];
+ ASSERT(m_glyphOrigins.size() == m_adjustedBaseAdvances.size());
+ }
+ m_adjustedGlyphs.append(glyph);
+
+ FloatRect glyphBounds = font.boundsForGlyph(glyph);
+ glyphBounds.move(glyphOrigin.x(), glyphOrigin.y());
+ m_minGlyphBoundingBoxX = std::min(m_minGlyphBoundingBoxX, glyphBounds.x());
+ m_maxGlyphBoundingBoxX = std::max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
+ m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, glyphBounds.y());
+ m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
+ glyphOrigin.move(advance);
+
+ lastCharacterIndex = characterIndex;
+ }
+ if (!isMonotonic)
+ complexTextRun.setIsNonMonotonic();
+ }
+
+ m_totalWidth += widthSinceLastCommit;
+}
+
+} // namespace WebCore
Copied: trunk/Source/WebCore/platform/graphics/ComplexTextController.h (from rev 211775, trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.h) (0 => 211776)
--- trunk/Source/WebCore/platform/graphics/ComplexTextController.h (rev 0)
+++ trunk/Source/WebCore/platform/graphics/ComplexTextController.h 2017-02-07 08:14:06 UTC (rev 211776)
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "FloatPoint.h"
+#include "GlyphBuffer.h"
+#include <wtf/HashSet.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RetainPtr.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+#define USE_LAYOUT_SPECIFIC_ADVANCES ((PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000))
+
+typedef unsigned short CGGlyph;
+
+typedef const struct __CTRun * CTRunRef;
+typedef const struct __CTLine * CTLineRef;
+
+namespace WebCore {
+
+class FontCascade;
+class Font;
+class TextRun;
+
+enum GlyphIterationStyle { IncludePartialGlyphs, ByWholeGlyphs };
+
+// See https://trac.webkit.org/wiki/ComplexTextController for more information about ComplexTextController.
+class ComplexTextController {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ ComplexTextController(const FontCascade&, const TextRun&, bool mayUseNaturalWritingDirection = false, HashSet<const Font*>* fallbackFonts = 0, bool forTextEmphasis = false);
+
+ class ComplexTextRun;
+ WEBCORE_EXPORT ComplexTextController(const FontCascade&, const TextRun&, Vector<Ref<ComplexTextRun>>&);
+
+ // Advance and emit glyphs up to the specified character.
+ WEBCORE_EXPORT void advance(unsigned to, GlyphBuffer* = nullptr, GlyphIterationStyle = IncludePartialGlyphs, HashSet<const Font*>* fallbackFonts = nullptr);
+
+ // Compute the character offset for a given x coordinate.
+ unsigned offsetForPosition(float x, bool includePartialGlyphs);
+
+ // Returns the width of everything we've consumed so far.
+ float runWidthSoFar() const { return m_runWidthSoFar; }
+
+ float totalWidth() const { return m_totalWidth; }
+
+ float minGlyphBoundingBoxX() const { return m_minGlyphBoundingBoxX; }
+ float maxGlyphBoundingBoxX() const { return m_maxGlyphBoundingBoxX; }
+ float minGlyphBoundingBoxY() const { return m_minGlyphBoundingBoxY; }
+ float maxGlyphBoundingBoxY() const { return m_maxGlyphBoundingBoxY; }
+
+ class ComplexTextRun : public RefCounted<ComplexTextRun> {
+ public:
+ static Ref<ComplexTextRun> create(CTRunRef ctRun, const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd)
+ {
+ return adoptRef(*new ComplexTextRun(ctRun, font, characters, stringLocation, stringLength, indexBegin, indexEnd));
+ }
+
+ static Ref<ComplexTextRun> create(const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr)
+ {
+ return adoptRef(*new ComplexTextRun(font, characters, stringLocation, stringLength, indexBegin, indexEnd, ltr));
+ }
+
+ static Ref<ComplexTextRun> create(const Vector<FloatSize>& advances, const Vector<FloatPoint>& origins, const Vector<Glyph>& glyphs, const Vector<unsigned>& stringIndices, FloatSize initialAdvance, const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr)
+ {
+ return adoptRef(*new ComplexTextRun(advances, origins, glyphs, stringIndices, initialAdvance, font, characters, stringLocation, stringLength, indexBegin, indexEnd, ltr));
+ }
+
+ unsigned glyphCount() const { return m_glyphCount; }
+ const Font& font() const { return m_font; }
+ const UChar* characters() const { return m_characters; }
+ unsigned stringLocation() const { return m_stringLocation; }
+ unsigned stringLength() const { return m_stringLength; }
+ ALWAYS_INLINE unsigned indexAt(unsigned) const;
+ unsigned indexBegin() const { return m_indexBegin; }
+ unsigned indexEnd() const { return m_indexEnd; }
+ unsigned endOffsetAt(unsigned i) const { ASSERT(!m_isMonotonic); return m_glyphEndOffsets[i]; }
+ const CGGlyph* glyphs() const { return m_glyphs.data(); }
+
+ /*
+ * This is the format of the information CoreText gives us about each run:
+ *
+ * ----->X (Paint glyph position) X (Paint glyph position) X (Paint glyph position)
+ * / 7 7 7
+ * / / / /
+ * (Initial advance) / / (Glyph origin) / (Glyph origin) / (Glyph origin)
+ * ------------------- / / /
+ * / / / /
+ * X X--------------------------X--------------------------X--------------------------X
+ * (text position ^) (base advance) (base advance) (base advance)
+ *
+ *
+ *
+ *
+ *
+ * And here is the output we transform this into (for each run):
+ *
+ * ----->X------------------------->X------------------------->X
+ * / (Paint advance) (Paint advance) \
+ * / \
+ * (Initial advance) / \ (Paint advance)
+ * ------------------- ----------------
+ * / \
+ * X--------------------------------------------------X--------------------------X--------------------------X
+ * (text position ^) (layout advance) (layout advance) (layout advance)
+ */
+ void growInitialAdvanceHorizontally(float delta) { m_initialAdvance.expand(delta, 0); }
+ FloatSize initialAdvance() const { return m_initialAdvance; }
+ const FloatSize* baseAdvances() const { return m_baseAdvances.data(); }
+ const FloatPoint* glyphOrigins() const { return m_glyphOrigins.size() == glyphCount() ? m_glyphOrigins.data() : nullptr; }
+ bool isLTR() const { return m_isLTR; }
+ bool isMonotonic() const { return m_isMonotonic; }
+ void setIsNonMonotonic();
+
+ private:
+ ComplexTextRun(CTRunRef, const Font&, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd);
+ ComplexTextRun(const Font&, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr);
+ WEBCORE_EXPORT ComplexTextRun(const Vector<FloatSize>& advances, const Vector<FloatPoint>& origins, const Vector<Glyph>& glyphs, const Vector<unsigned>& stringIndices, FloatSize initialAdvance, const Font&, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr);
+
+ Vector<FloatSize, 64> m_baseAdvances;
+ Vector<FloatPoint, 64> m_glyphOrigins;
+ Vector<CGGlyph, 64> m_glyphs;
+ Vector<unsigned, 64> m_glyphEndOffsets;
+ Vector<unsigned, 64> m_coreTextIndices;
+ FloatSize m_initialAdvance;
+ const Font& m_font;
+ const UChar* m_characters;
+ unsigned m_stringLength;
+ unsigned m_indexBegin;
+ unsigned m_indexEnd;
+ unsigned m_glyphCount;
+ unsigned m_stringLocation;
+ bool m_isLTR;
+ bool m_isMonotonic { true };
+ };
+private:
+ void computeExpansionOpportunity();
+ void finishConstruction();
+
+ static unsigned stringBegin(const ComplexTextRun& run) { return run.stringLocation() + run.indexBegin(); }
+ static unsigned stringEnd(const ComplexTextRun& run) { return run.stringLocation() + run.indexEnd(); }
+
+ void collectComplexTextRuns();
+
+ void collectComplexTextRunsForCharacters(const UChar*, unsigned length, unsigned stringLocation, const Font*);
+ void adjustGlyphsAndAdvances();
+
+ unsigned indexOfCurrentRun(unsigned& leftmostGlyph);
+ unsigned incrementCurrentRun(unsigned& leftmostGlyph);
+
+ float runWidthSoFarFraction(unsigned glyphStartOffset, unsigned glyphEndOffset, unsigned oldCharacterInCurrentGlyph, GlyphIterationStyle) const;
+
+ FloatPoint glyphOrigin(unsigned index) const { return index < m_glyphOrigins.size() ? m_glyphOrigins[index] : FloatPoint(); }
+
+ Vector<FloatSize, 256> m_adjustedBaseAdvances;
+ Vector<FloatPoint, 256> m_glyphOrigins;
+ Vector<CGGlyph, 256> m_adjustedGlyphs;
+
+ Vector<UChar, 256> m_smallCapsBuffer;
+
+ // There is a 3-level hierarchy here. At the top, we are interested in m_run.string(). We partition that string
+ // into Lines, each of which is a sequence of characters which should use the same Font. Core Text then partitions
+ // the Line into ComplexTextRuns.
+ // ComplexTextRun::stringLocation() and ComplexTextRun::stringLength() refer to the offset and length of the Line
+ // relative to m_run.string(). ComplexTextRun::indexAt() returns to the offset of a codepoint relative to
+ // its Line. ComplexTextRun::glyphs() and ComplexTextRun::advances() refer to glyphs relative to the ComplexTextRun.
+ // The length of the entire TextRun is m_run.length()
+ Vector<RefPtr<ComplexTextRun>, 16> m_complexTextRuns;
+
+ // The initial capacity of these vectors was selected as being the smallest power of two greater than
+ // the average (3.5) plus one standard deviation (7.5) of nonzero sizes used on Arabic Wikipedia.
+ Vector<unsigned, 16> m_runIndices;
+ Vector<unsigned, 16> m_glyphCountFromStartToIndex;
+
+ Vector<RetainPtr<CTLineRef>> m_coreTextLines;
+
+ Vector<String> m_stringsFor8BitRuns;
+
+ HashSet<const Font*>* m_fallbackFonts { nullptr };
+
+ const FontCascade& m_font;
+ const TextRun& m_run;
+
+ unsigned m_currentCharacter { 0 };
+ unsigned m_end { 0 };
+
+ float m_totalWidth { 0 };
+ float m_runWidthSoFar { 0 };
+ unsigned m_numGlyphsSoFar { 0 };
+ unsigned m_currentRun { 0 };
+ unsigned m_glyphInCurrentRun { 0 };
+ unsigned m_characterInCurrentGlyph { 0 };
+ float m_expansion { 0 };
+ float m_expansionPerOpportunity { 0 };
+
+ float m_minGlyphBoundingBoxX { std::numeric_limits<float>::max() };
+ float m_maxGlyphBoundingBoxX { std::numeric_limits<float>::min() };
+ float m_minGlyphBoundingBoxY { std::numeric_limits<float>::max() };
+ float m_maxGlyphBoundingBoxY { std::numeric_limits<float>::min() };
+
+ bool m_isLTROnly { true };
+ bool m_mayUseNaturalWritingDirection { false };
+ bool m_forTextEmphasis { false };
+};
+
+} // namespace WebCore
Deleted: trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp (211775 => 211776)
--- trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp 2017-02-07 06:45:43 UTC (rev 211775)
+++ trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp 2017-02-07 08:14:06 UTC (rev 211776)
@@ -1,833 +0,0 @@
-/*
- * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "ComplexTextController.h"
-
-#include "CharacterProperties.h"
-#include "FloatSize.h"
-#include "FontCascade.h"
-#include "RenderBlock.h"
-#include "RenderText.h"
-#include "TextRun.h"
-#include <unicode/ubrk.h>
-#include <wtf/Optional.h>
-#include <wtf/StdLibExtras.h>
-#include <wtf/text/TextBreakIterator.h>
-#include <wtf/unicode/CharacterNames.h>
-
-#if PLATFORM(IOS)
-#include <CoreText/CoreText.h>
-#endif
-
-#if PLATFORM(MAC)
-#include <ApplicationServices/ApplicationServices.h>
-#endif
-
-namespace WebCore {
-
-class TextLayout {
- WTF_MAKE_FAST_ALLOCATED;
-public:
- static bool isNeeded(RenderText& text, const FontCascade& font)
- {
- TextRun run = RenderBlock::constructTextRun(text, text.style());
- return font.codePath(run) == FontCascade::Complex;
- }
-
- TextLayout(RenderText& text, const FontCascade& font, float xPos)
- : m_font(font)
- , m_run(constructTextRun(text, xPos))
- , m_controller(std::make_unique<ComplexTextController>(m_font, m_run, true))
- {
- }
-
- float width(unsigned from, unsigned len, HashSet<const Font*>* fallbackFonts)
- {
- m_controller->advance(from, 0, ByWholeGlyphs, fallbackFonts);
- float beforeWidth = m_controller->runWidthSoFar();
- if (m_font.wordSpacing() && from && FontCascade::treatAsSpace(m_run[from]))
- beforeWidth += m_font.wordSpacing();
- m_controller->advance(from + len, 0, ByWholeGlyphs, fallbackFonts);
- float afterWidth = m_controller->runWidthSoFar();
- return afterWidth - beforeWidth;
- }
-
-private:
- static TextRun constructTextRun(RenderText& text, float xPos)
- {
- TextRun run = RenderBlock::constructTextRun(text, text.style());
- run.setCharactersLength(text.textLength());
- ASSERT(run.charactersLength() >= run.length());
- run.setXPos(xPos);
- return run;
- }
-
- // ComplexTextController has only references to its FontCascade and TextRun so they must be kept alive here.
- FontCascade m_font;
- TextRun m_run;
- std::unique_ptr<ComplexTextController> m_controller;
-};
-
-void TextLayoutDeleter::operator()(TextLayout* layout) const
-{
- delete layout;
-}
-
-std::unique_ptr<TextLayout, TextLayoutDeleter> FontCascade::createLayout(RenderText& text, float xPos, bool collapseWhiteSpace) const
-{
- if (!collapseWhiteSpace || !TextLayout::isNeeded(text, *this))
- return nullptr;
- return std::unique_ptr<TextLayout, TextLayoutDeleter>(new TextLayout(text, *this, xPos));
-}
-
-float FontCascade::width(TextLayout& layout, unsigned from, unsigned len, HashSet<const Font*>* fallbackFonts)
-{
- return layout.width(from, len, fallbackFonts);
-}
-
-void ComplexTextController::computeExpansionOpportunity()
-{
- if (!m_expansion)
- m_expansionPerOpportunity = 0;
- else {
- unsigned expansionOpportunityCount = FontCascade::expansionOpportunityCount(m_run.text(), m_run.ltr() ? LTR : RTL, m_run.expansionBehavior()).first;
-
- if (!expansionOpportunityCount)
- m_expansionPerOpportunity = 0;
- else
- m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
- }
-}
-
-ComplexTextController::ComplexTextController(const FontCascade& font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const Font*>* fallbackFonts, bool forTextEmphasis)
- : m_fallbackFonts(fallbackFonts)
- , m_font(font)
- , m_run(run)
- , m_end(run.length())
- , m_expansion(run.expansion())
- , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
- , m_forTextEmphasis(forTextEmphasis)
-{
- computeExpansionOpportunity();
-
- collectComplexTextRuns();
-
- finishConstruction();
-}
-
-ComplexTextController::ComplexTextController(const FontCascade& font, const TextRun& run, Vector<Ref<ComplexTextRun>>& runs)
- : m_font(font)
- , m_run(run)
- , m_end(run.length())
- , m_expansion(run.expansion())
-{
- computeExpansionOpportunity();
-
- for (auto& run : runs)
- m_complexTextRuns.append(run.ptr());
-
- finishConstruction();
-}
-
-void ComplexTextController::finishConstruction()
-{
- adjustGlyphsAndAdvances();
-
- if (!m_isLTROnly) {
- m_runIndices.reserveInitialCapacity(m_complexTextRuns.size());
-
- m_glyphCountFromStartToIndex.reserveInitialCapacity(m_complexTextRuns.size());
- unsigned glyphCountSoFar = 0;
- for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) {
- m_glyphCountFromStartToIndex.uncheckedAppend(glyphCountSoFar);
- glyphCountSoFar += m_complexTextRuns[i]->glyphCount();
- }
- }
-}
-
-unsigned ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
-{
- if (h >= m_totalWidth)
- return m_run.ltr() ? m_end : 0;
-
- if (h < 0)
- return m_run.ltr() ? 0 : m_end;
-
- float x = h;
-
- size_t runCount = m_complexTextRuns.size();
- unsigned offsetIntoAdjustedGlyphs = 0;
-
- for (size_t r = 0; r < runCount; ++r) {
- const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
- for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
- unsigned index = offsetIntoAdjustedGlyphs + j;
- float adjustedAdvance = m_adjustedBaseAdvances[index].width();
- if (x < adjustedAdvance) {
- unsigned hitGlyphStart = complexTextRun.indexAt(j);
- unsigned hitGlyphEnd;
- if (m_run.ltr())
- hitGlyphEnd = std::max(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : complexTextRun.indexEnd());
- else
- hitGlyphEnd = std::max(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : complexTextRun.indexEnd());
-
- // FIXME: Instead of dividing the glyph's advance equally between the characters, this
- // could use the glyph's "ligature carets". This is available in CoreText via CTFontGetLigatureCaretPositions().
- unsigned hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
- unsigned stringLength = complexTextRun.stringLength();
- UBreakIterator* cursorPositionIterator = cursorMovementIterator(StringView(complexTextRun.characters(), stringLength));
- unsigned clusterStart;
- if (ubrk_isBoundary(cursorPositionIterator, hitIndex))
- clusterStart = hitIndex;
- else {
- int preceeding = ubrk_preceding(cursorPositionIterator, hitIndex);
- clusterStart = preceeding == UBRK_DONE ? 0 : preceeding;
- }
-
- if (!includePartialGlyphs)
- return complexTextRun.stringLocation() + clusterStart;
-
- int following = ubrk_following(cursorPositionIterator, hitIndex);
- unsigned clusterEnd = following == UBRK_DONE ? stringLength : following;
-
- float clusterWidth;
- // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
- // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
- // reordering and no font fallback should occur within a CTLine.
- if (clusterEnd - clusterStart > 1) {
- clusterWidth = adjustedAdvance;
- if (j) {
- unsigned firstGlyphBeforeCluster = j - 1;
- while (complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
- float width = m_adjustedBaseAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width();
- clusterWidth += width;
- x += width;
- if (!firstGlyphBeforeCluster)
- break;
- firstGlyphBeforeCluster--;
- }
- }
- unsigned firstGlyphAfterCluster = j + 1;
- while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
- clusterWidth += m_adjustedBaseAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width();
- firstGlyphAfterCluster++;
- }
- } else {
- clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
- x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
- }
- if (x <= clusterWidth / 2)
- return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
- return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
- }
- x -= adjustedAdvance;
- }
- offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
- }
-
- ASSERT_NOT_REACHED();
- return 0;
-}
-
-// FIXME: We should consider reimplementing this function using ICU to advance by grapheme.
-// The current implementation only considers explicitly emoji sequences and emoji variations.
-static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount)
-{
- ASSERT(iterator < end);
-
- markCount = 0;
-
- unsigned i = 0;
- unsigned remainingCharacters = end - iterator;
- U16_NEXT(iterator, i, remainingCharacters, baseCharacter);
- iterator = iterator + i;
- if (U_IS_SURROGATE(baseCharacter))
- return false;
-
- // Consume marks.
- bool sawEmojiGroupCandidate = isEmojiGroupCandidate(baseCharacter);
- bool sawJoiner = false;
- while (iterator < end) {
- UChar32 nextCharacter;
- unsigned markLength = 0;
- bool shouldContinue = false;
- ASSERT(end >= iterator);
- U16_NEXT(iterator, markLength, static_cast<unsigned>(end - iterator), nextCharacter);
-
- if (isVariationSelector(nextCharacter) || isEmojiFitzpatrickModifier(nextCharacter))
- shouldContinue = true;
-
- if (sawJoiner && isEmojiGroupCandidate(nextCharacter))
- shouldContinue = true;
-
- sawJoiner = false;
- if (sawEmojiGroupCandidate && nextCharacter == zeroWidthJoiner) {
- sawJoiner = true;
- shouldContinue = true;
- }
-
- if (!shouldContinue && !(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK))
- break;
-
- markCount += markLength;
- iterator += markLength;
- }
-
- return true;
-}
-
-// FIXME: Capitalization is language-dependent and context-dependent and should operate on grapheme clusters instead of codepoints.
-static inline std::optional<UChar32> capitalized(UChar32 baseCharacter)
-{
- if (U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK)
- return std::nullopt;
-
- UChar32 uppercaseCharacter = u_toupper(baseCharacter);
- ASSERT(uppercaseCharacter == baseCharacter || (U_IS_BMP(baseCharacter) == U_IS_BMP(uppercaseCharacter)));
- if (uppercaseCharacter != baseCharacter)
- return uppercaseCharacter;
- return std::nullopt;
-}
-
-static bool shouldSynthesize(bool dontSynthesizeSmallCaps, const Font* nextFont, UChar32 baseCharacter, std::optional<UChar32> capitalizedBase, FontVariantCaps fontVariantCaps, bool engageAllSmallCapsProcessing)
-{
- if (dontSynthesizeSmallCaps)
- return false;
- if (!nextFont || nextFont == Font::systemFallback())
- return false;
- if (engageAllSmallCapsProcessing && isASCIISpace(baseCharacter))
- return false;
- if (!engageAllSmallCapsProcessing && !capitalizedBase)
- return false;
- return !nextFont->variantCapsSupportsCharacterForSynthesis(fontVariantCaps, baseCharacter);
-}
-
-void ComplexTextController::collectComplexTextRuns()
-{
- if (!m_end)
- return;
-
- // We break up glyph run generation for the string by Font.
- const UChar* cp;
-
- if (m_run.is8Bit()) {
- String stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(), m_run.length());
- m_stringsFor8BitRuns.append(WTFMove(stringFor8BitRun));
- cp = m_stringsFor8BitRuns.last().characters16();
- } else
- cp = m_run.characters16();
-
- auto fontVariantCaps = m_font.fontDescription().variantCaps();
- bool dontSynthesizeSmallCaps = !static_cast<bool>(m_font.fontDescription().fontSynthesis() & FontSynthesisSmallCaps);
- bool engageAllSmallCapsProcessing = fontVariantCaps == FontVariantCaps::AllSmall || fontVariantCaps == FontVariantCaps::AllPetite;
- bool engageSmallCapsProcessing = engageAllSmallCapsProcessing || fontVariantCaps == FontVariantCaps::Small || fontVariantCaps == FontVariantCaps::Petite;
-
- if (engageAllSmallCapsProcessing || engageSmallCapsProcessing)
- m_smallCapsBuffer.resize(m_end);
-
- unsigned indexOfFontTransition = 0;
- const UChar* curr = cp;
- const UChar* end = cp + m_end;
-
- const Font* font;
- const Font* nextFont;
- const Font* synthesizedFont = nullptr;
- const Font* smallSynthesizedFont = nullptr;
-
- unsigned markCount;
- UChar32 baseCharacter;
- if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
- return;
-
- nextFont = m_font.fontForCombiningCharacterSequence(cp, curr - cp);
-
- bool isSmallCaps = false;
- bool nextIsSmallCaps = false;
-
- auto capitalizedBase = capitalized(baseCharacter);
- if (shouldSynthesize(dontSynthesizeSmallCaps, nextFont, baseCharacter, capitalizedBase, fontVariantCaps, engageAllSmallCapsProcessing)) {
- synthesizedFont = &nextFont->noSynthesizableFeaturesFont();
- smallSynthesizedFont = synthesizedFont->smallCapsFont(m_font.fontDescription());
- UChar32 characterToWrite = capitalizedBase ? capitalizedBase.value() : cp[0];
- unsigned characterIndex = 0;
- U16_APPEND_UNSAFE(m_smallCapsBuffer, characterIndex, characterToWrite);
- for (unsigned i = characterIndex; cp + i < curr; ++i)
- m_smallCapsBuffer[i] = cp[i];
- nextIsSmallCaps = true;
- }
-
- while (curr < end) {
- font = nextFont;
- isSmallCaps = nextIsSmallCaps;
- unsigned index = curr - cp;
-
- if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
- return;
-
- if (synthesizedFont) {
- if (auto capitalizedBase = capitalized(baseCharacter)) {
- unsigned characterIndex = index;
- U16_APPEND_UNSAFE(m_smallCapsBuffer, characterIndex, capitalizedBase.value());
- for (unsigned i = 0; i < markCount; ++i)
- m_smallCapsBuffer[i + characterIndex] = cp[i + characterIndex];
- nextIsSmallCaps = true;
- } else {
- if (engageAllSmallCapsProcessing) {
- for (unsigned i = 0; i < curr - cp - index; ++i)
- m_smallCapsBuffer[index + i] = cp[index + i];
- }
- nextIsSmallCaps = engageAllSmallCapsProcessing;
- }
- }
-
- if (baseCharacter == zeroWidthJoiner)
- nextFont = font;
- else
- nextFont = m_font.fontForCombiningCharacterSequence(cp + index, curr - cp - index);
-
- capitalizedBase = capitalized(baseCharacter);
- if (!synthesizedFont && shouldSynthesize(dontSynthesizeSmallCaps, nextFont, baseCharacter, capitalizedBase, fontVariantCaps, engageAllSmallCapsProcessing)) {
- // Rather than synthesize each character individually, we should synthesize the entire "run" if any character requires synthesis.
- synthesizedFont = &nextFont->noSynthesizableFeaturesFont();
- smallSynthesizedFont = synthesizedFont->smallCapsFont(m_font.fontDescription());
- nextIsSmallCaps = true;
- curr = cp + indexOfFontTransition;
- continue;
- }
-
- if (nextFont != font || nextIsSmallCaps != isSmallCaps) {
- unsigned itemLength = index - indexOfFontTransition;
- if (itemLength) {
- unsigned itemStart = indexOfFontTransition;
- if (synthesizedFont) {
- if (isSmallCaps)
- collectComplexTextRunsForCharacters(m_smallCapsBuffer.data() + itemStart, itemLength, itemStart, smallSynthesizedFont);
- else
- collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, synthesizedFont);
- } else
- collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, font);
- if (nextFont != font) {
- synthesizedFont = nullptr;
- smallSynthesizedFont = nullptr;
- nextIsSmallCaps = false;
- }
- }
- indexOfFontTransition = index;
- }
- }
-
- ASSERT(m_end >= indexOfFontTransition);
- unsigned itemLength = m_end - indexOfFontTransition;
- if (itemLength) {
- unsigned itemStart = indexOfFontTransition;
- if (synthesizedFont) {
- if (nextIsSmallCaps)
- collectComplexTextRunsForCharacters(m_smallCapsBuffer.data() + itemStart, itemLength, itemStart, smallSynthesizedFont);
- else
- collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, synthesizedFont);
- } else
- collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, nextFont);
- }
-
- if (!m_run.ltr())
- m_complexTextRuns.reverse();
-}
-
-unsigned ComplexTextController::ComplexTextRun::indexAt(unsigned i) const
-{
- ASSERT(i < m_glyphCount);
-
- return m_coreTextIndices[i];
-}
-
-void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
-{
- ASSERT(m_isMonotonic);
- m_isMonotonic = false;
-
- Vector<bool, 64> mappedIndices(m_stringLength, false);
- for (unsigned i = 0; i < m_glyphCount; ++i) {
- ASSERT(indexAt(i) < m_stringLength);
- mappedIndices[indexAt(i)] = true;
- }
-
- m_glyphEndOffsets.grow(m_glyphCount);
- for (unsigned i = 0; i < m_glyphCount; ++i) {
- unsigned nextMappedIndex = m_indexEnd;
- for (unsigned j = indexAt(i) + 1; j < m_stringLength; ++j) {
- if (mappedIndices[j]) {
- nextMappedIndex = j;
- break;
- }
- }
- m_glyphEndOffsets[i] = nextMappedIndex;
- }
-}
-
-unsigned ComplexTextController::indexOfCurrentRun(unsigned& leftmostGlyph)
-{
- leftmostGlyph = 0;
-
- size_t runCount = m_complexTextRuns.size();
- if (m_currentRun >= runCount)
- return runCount;
-
- if (m_isLTROnly) {
- for (unsigned i = 0; i < m_currentRun; ++i)
- leftmostGlyph += m_complexTextRuns[i]->glyphCount();
- return m_currentRun;
- }
-
- if (m_runIndices.isEmpty()) {
- unsigned firstRun = 0;
- unsigned firstRunOffset = stringBegin(*m_complexTextRuns[0]);
- for (unsigned i = 1; i < runCount; ++i) {
- unsigned offset = stringBegin(*m_complexTextRuns[i]);
- if (offset < firstRunOffset) {
- firstRun = i;
- firstRunOffset = offset;
- }
- }
- m_runIndices.uncheckedAppend(firstRun);
- }
-
- while (m_runIndices.size() <= m_currentRun) {
- unsigned offset = stringEnd(*m_complexTextRuns[m_runIndices.last()]);
-
- for (unsigned i = 0; i < runCount; ++i) {
- if (offset == stringBegin(*m_complexTextRuns[i])) {
- m_runIndices.uncheckedAppend(i);
- break;
- }
- }
- }
-
- unsigned currentRunIndex = m_runIndices[m_currentRun];
- leftmostGlyph = m_glyphCountFromStartToIndex[currentRunIndex];
- return currentRunIndex;
-}
-
-unsigned ComplexTextController::incrementCurrentRun(unsigned& leftmostGlyph)
-{
- if (m_isLTROnly) {
- leftmostGlyph += m_complexTextRuns[m_currentRun++]->glyphCount();
- return m_currentRun;
- }
-
- m_currentRun++;
- leftmostGlyph = 0;
- return indexOfCurrentRun(leftmostGlyph);
-}
-
-float ComplexTextController::runWidthSoFarFraction(unsigned glyphStartOffset, unsigned glyphEndOffset, unsigned oldCharacterInCurrentGlyph, GlyphIterationStyle iterationStyle) const
-{
- // FIXME: Instead of dividing the glyph's advance equally between the characters, this
- // could use the glyph's "ligature carets". This is available in CoreText via CTFontGetLigatureCaretPositions().
- if (glyphStartOffset == glyphEndOffset) {
- // When there are multiple glyphs per character we need to advance by the full width of the glyph.
- ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
- return 1;
- }
-
- if (iterationStyle == ByWholeGlyphs) {
- if (!oldCharacterInCurrentGlyph)
- return 1;
- return 0;
- }
-
- return static_cast<float>(m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
-}
-
-void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, GlyphIterationStyle iterationStyle, HashSet<const Font*>* fallbackFonts)
-{
- if (offset > m_end)
- offset = m_end;
-
- if (offset <= m_currentCharacter) {
- m_runWidthSoFar = 0;
- m_numGlyphsSoFar = 0;
- m_currentRun = 0;
- m_glyphInCurrentRun = 0;
- m_characterInCurrentGlyph = 0;
- }
-
- m_currentCharacter = offset;
-
- size_t runCount = m_complexTextRuns.size();
-
- unsigned indexOfLeftmostGlyphInCurrentRun = 0; // Relative to the beginning of ComplexTextController.
- unsigned currentRunIndex = indexOfCurrentRun(indexOfLeftmostGlyphInCurrentRun);
- while (m_currentRun < runCount) {
- const ComplexTextRun& complexTextRun = *m_complexTextRuns[currentRunIndex];
- bool ltr = complexTextRun.isLTR();
- unsigned glyphCount = complexTextRun.glyphCount();
- unsigned glyphIndexIntoCurrentRun = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
- unsigned glyphIndexIntoComplexTextController = indexOfLeftmostGlyphInCurrentRun + glyphIndexIntoCurrentRun;
- if (fallbackFonts && &complexTextRun.font() != &m_font.primaryFont())
- fallbackFonts->add(&complexTextRun.font());
-
- // We must store the initial advance for the first glyph we are going to draw.
- // When leftmostGlyph is 0, it represents the first glyph to draw, taking into
- // account the text direction.
- if (!indexOfLeftmostGlyphInCurrentRun && glyphBuffer)
- glyphBuffer->setInitialAdvance(GlyphBufferAdvance(complexTextRun.initialAdvance().width(), complexTextRun.initialAdvance().height()));
-
- while (m_glyphInCurrentRun < glyphCount) {
- unsigned glyphStartOffset = complexTextRun.indexAt(glyphIndexIntoCurrentRun);
- unsigned glyphEndOffset;
- if (complexTextRun.isMonotonic()) {
- if (ltr)
- glyphEndOffset = std::max(glyphStartOffset, glyphIndexIntoCurrentRun + 1 < glyphCount ? complexTextRun.indexAt(glyphIndexIntoCurrentRun + 1) : complexTextRun.indexEnd());
- else
- glyphEndOffset = std::max(glyphStartOffset, glyphIndexIntoCurrentRun > 0 ? complexTextRun.indexAt(glyphIndexIntoCurrentRun - 1) : complexTextRun.indexEnd());
- } else
- glyphEndOffset = complexTextRun.endOffsetAt(glyphIndexIntoCurrentRun);
-
- FloatSize adjustedBaseAdvance = m_adjustedBaseAdvances[glyphIndexIntoComplexTextController];
-
- if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
- return;
-
- if (glyphBuffer && !m_characterInCurrentGlyph) {
- auto currentGlyphOrigin = glyphOrigin(glyphIndexIntoComplexTextController);
- GlyphBufferAdvance paintAdvance(adjustedBaseAdvance);
- if (!glyphIndexIntoCurrentRun) {
- // The first layout advance of every run includes the "initial layout advance." However, here, we need
- // paint advances, so subtract it out before transforming the layout advance into a paint advance.
- paintAdvance.setWidth(paintAdvance.width() - (complexTextRun.initialAdvance().width() - currentGlyphOrigin.x()));
- paintAdvance.setHeight(paintAdvance.height() - (complexTextRun.initialAdvance().height() - currentGlyphOrigin.y()));
- }
- paintAdvance.setWidth(paintAdvance.width() + glyphOrigin(glyphIndexIntoComplexTextController + 1).x() - currentGlyphOrigin.x());
- paintAdvance.setHeight(paintAdvance.height() + glyphOrigin(glyphIndexIntoComplexTextController + 1).y() - currentGlyphOrigin.y());
- if (glyphIndexIntoCurrentRun == glyphCount - 1 && currentRunIndex + 1 < runCount) {
- // Our paint advance points to the end of the run. However, the next run may have an
- // initial advance, and our paint advance needs to point to the location of the next
- // glyph. So, we need to add in the next run's initial advance.
- paintAdvance.setWidth(paintAdvance.width() - glyphOrigin(glyphIndexIntoComplexTextController + 1).x() + m_complexTextRuns[currentRunIndex + 1]->initialAdvance().width());
- paintAdvance.setHeight(paintAdvance.height() - glyphOrigin(glyphIndexIntoComplexTextController + 1).y() + m_complexTextRuns[currentRunIndex + 1]->initialAdvance().height());
- }
- paintAdvance.setHeight(-paintAdvance.height()); // Increasing y points down
- glyphBuffer->add(m_adjustedGlyphs[glyphIndexIntoComplexTextController], &complexTextRun.font(), paintAdvance, complexTextRun.indexAt(m_glyphInCurrentRun));
- }
-
- unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
- m_characterInCurrentGlyph = std::min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
- m_runWidthSoFar += adjustedBaseAdvance.width() * runWidthSoFarFraction(glyphStartOffset, glyphEndOffset, oldCharacterInCurrentGlyph, iterationStyle);
-
- if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
- return;
-
- m_numGlyphsSoFar++;
- m_glyphInCurrentRun++;
- m_characterInCurrentGlyph = 0;
- if (ltr) {
- glyphIndexIntoCurrentRun++;
- glyphIndexIntoComplexTextController++;
- } else {
- glyphIndexIntoCurrentRun--;
- glyphIndexIntoComplexTextController--;
- }
- }
- currentRunIndex = incrementCurrentRun(indexOfLeftmostGlyphInCurrentRun);
- m_glyphInCurrentRun = 0;
- }
-}
-
-static inline std::pair<bool, bool> expansionLocation(bool ideograph, bool treatAsSpace, bool ltr, bool isAfterExpansion, bool forbidLeadingExpansion, bool forbidTrailingExpansion, bool forceLeadingExpansion, bool forceTrailingExpansion)
-{
- bool expandLeft = ideograph;
- bool expandRight = ideograph;
- if (treatAsSpace) {
- if (ltr)
- expandRight = true;
- else
- expandLeft = true;
- }
- if (isAfterExpansion)
- expandLeft = false;
- ASSERT(!forbidLeadingExpansion || !forceLeadingExpansion);
- ASSERT(!forbidTrailingExpansion || !forceTrailingExpansion);
- if (forbidLeadingExpansion)
- expandLeft = false;
- if (forbidTrailingExpansion)
- expandRight = false;
- if (forceLeadingExpansion)
- expandLeft = true;
- if (forceTrailingExpansion)
- expandRight = true;
- return std::make_pair(expandLeft, expandRight);
-}
-
-void ComplexTextController::adjustGlyphsAndAdvances()
-{
- bool afterExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
- float widthSinceLastCommit = 0;
- size_t runCount = m_complexTextRuns.size();
- bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
- bool runForcesLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForceLeadingExpansion;
- bool runForcesTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForceTrailingExpansion;
- bool runForbidsLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
- bool runForbidsTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForbidTrailingExpansion;
-
- // We are iterating in glyph order, not string order. Compare this to WidthIterator::advanceInternal()
- for (size_t runIndex = 0; runIndex < runCount; ++runIndex) {
- ComplexTextRun& complexTextRun = *m_complexTextRuns[runIndex];
- unsigned glyphCount = complexTextRun.glyphCount();
- const Font& font = complexTextRun.font();
-
- if (!complexTextRun.isLTR())
- m_isLTROnly = false;
-
- const CGGlyph* glyphs = complexTextRun.glyphs();
- const FloatSize* advances = complexTextRun.baseAdvances();
-
- bool lastRun = runIndex + 1 == runCount;
- float spaceWidth = font.spaceWidth() - font.syntheticBoldOffset();
- const UChar* cp = complexTextRun.characters();
- FloatPoint glyphOrigin;
- unsigned lastCharacterIndex = m_run.ltr() ? std::numeric_limits<unsigned>::min() : std::numeric_limits<unsigned>::max();
- bool isMonotonic = true;
-
- for (unsigned i = 0; i < glyphCount; i++) {
- unsigned characterIndex = complexTextRun.indexAt(i);
- if (m_run.ltr()) {
- if (characterIndex < lastCharacterIndex)
- isMonotonic = false;
- } else {
- if (characterIndex > lastCharacterIndex)
- isMonotonic = false;
- }
- UChar ch = *(cp + characterIndex);
- bool lastGlyph = lastRun && i + 1 == glyphCount;
- UChar nextCh;
- if (lastGlyph)
- nextCh = ' ';
- else if (i + 1 < glyphCount)
- nextCh = *(cp + complexTextRun.indexAt(i + 1));
- else
- nextCh = *(m_complexTextRuns[runIndex + 1]->characters() + m_complexTextRuns[runIndex + 1]->indexAt(0));
-
- bool treatAsSpace = FontCascade::treatAsSpace(ch);
- CGGlyph glyph = treatAsSpace ? font.spaceGlyph() : glyphs[i];
- FloatSize advance = treatAsSpace ? FloatSize(spaceWidth, advances[i].height()) : advances[i];
-
- if (ch == '\t' && m_run.allowTabs())
- advance.setWidth(m_font.tabWidth(font, m_run.tabSize(), m_run.xPos() + m_totalWidth + widthSinceLastCommit));
- else if (FontCascade::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
- advance.setWidth(0);
- glyph = font.spaceGlyph();
- }
-
- if (!i) {
- advance.expand(complexTextRun.initialAdvance().width(), complexTextRun.initialAdvance().height());
- if (auto* origins = complexTextRun.glyphOrigins())
- advance.expand(-origins[0].x(), -origins[0].y());
- }
-
- advance.expand(font.syntheticBoldOffset(), 0);
-
- if (hasExtraSpacing) {
- // If we're a glyph with an advance, add in letter-spacing.
- // That way we weed out zero width lurkers. This behavior matches the fast text code path.
- if (advance.width())
- advance.expand(m_font.letterSpacing(), 0);
-
- unsigned characterIndexInRun = characterIndex + complexTextRun.stringLocation();
- bool isFirstCharacter = !(characterIndex + complexTextRun.stringLocation());
- bool isLastCharacter = characterIndexInRun + 1 == m_run.length() || (U16_IS_LEAD(ch) && characterIndexInRun + 2 == m_run.length() && U16_IS_TRAIL(*(cp + characterIndex + 1)));
-
- bool forceLeadingExpansion = false; // On the left, regardless of m_run.ltr()
- bool forceTrailingExpansion = false; // On the right, regardless of m_run.ltr()
- bool forbidLeadingExpansion = false;
- bool forbidTrailingExpansion = false;
- if (runForcesLeadingExpansion)
- forceLeadingExpansion = m_run.ltr() ? isFirstCharacter : isLastCharacter;
- if (runForcesTrailingExpansion)
- forceTrailingExpansion = m_run.ltr() ? isLastCharacter : isFirstCharacter;
- if (runForbidsLeadingExpansion)
- forbidLeadingExpansion = m_run.ltr() ? isFirstCharacter : isLastCharacter;
- if (runForbidsTrailingExpansion)
- forbidTrailingExpansion = m_run.ltr() ? isLastCharacter : isFirstCharacter;
- // Handle justification and word-spacing.
- bool ideograph = FontCascade::isCJKIdeographOrSymbol(ch);
- if (treatAsSpace || ideograph || forceLeadingExpansion || forceTrailingExpansion) {
- // Distribute the run's total expansion evenly over all expansion opportunities in the run.
- if (m_expansion) {
- bool expandLeft, expandRight;
- std::tie(expandLeft, expandRight) = expansionLocation(ideograph, treatAsSpace, m_run.ltr(), afterExpansion, forbidLeadingExpansion, forbidTrailingExpansion, forceLeadingExpansion, forceTrailingExpansion);
- m_expansion -= m_expansionPerOpportunity;
- advance.expand(m_expansionPerOpportunity, 0);
- if (expandLeft) {
- // Increase previous width
- if (m_adjustedBaseAdvances.isEmpty())
- complexTextRun.growInitialAdvanceHorizontally(m_expansionPerOpportunity);
- else
- m_adjustedBaseAdvances.last().expand(m_expansionPerOpportunity, 0);
- }
- if (expandRight)
- afterExpansion = true;
- } else
- afterExpansion = false;
-
- // Account for word-spacing.
- if (treatAsSpace && (ch != '\t' || !m_run.allowTabs()) && (characterIndex > 0 || runIndex > 0) && m_font.wordSpacing())
- advance.expand(m_font.wordSpacing(), 0);
- } else
- afterExpansion = false;
- }
-
- widthSinceLastCommit += advance.width();
-
- // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
- if (m_forTextEmphasis && (!FontCascade::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
- glyph = 0;
-
- m_adjustedBaseAdvances.append(advance);
- if (auto* origins = complexTextRun.glyphOrigins()) {
- ASSERT(m_glyphOrigins.size() < m_adjustedBaseAdvances.size());
- m_glyphOrigins.grow(m_adjustedBaseAdvances.size());
- m_glyphOrigins[m_glyphOrigins.size() - 1] = origins[i];
- ASSERT(m_glyphOrigins.size() == m_adjustedBaseAdvances.size());
- }
- m_adjustedGlyphs.append(glyph);
-
- FloatRect glyphBounds = font.boundsForGlyph(glyph);
- glyphBounds.move(glyphOrigin.x(), glyphOrigin.y());
- m_minGlyphBoundingBoxX = std::min(m_minGlyphBoundingBoxX, glyphBounds.x());
- m_maxGlyphBoundingBoxX = std::max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
- m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, glyphBounds.y());
- m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
- glyphOrigin.move(advance);
-
- lastCharacterIndex = characterIndex;
- }
- if (!isMonotonic)
- complexTextRun.setIsNonMonotonic();
- }
-
- m_totalWidth += widthSinceLastCommit;
-}
-
-} // namespace WebCore
Deleted: trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.h (211775 => 211776)
--- trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.h 2017-02-07 06:45:43 UTC (rev 211775)
+++ trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.h 2017-02-07 08:14:06 UTC (rev 211776)
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2007, 2008, 2009, 2011 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#pragma once
-
-#include "FloatPoint.h"
-#include "GlyphBuffer.h"
-#include <wtf/HashSet.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RetainPtr.h>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
-
-#define USE_LAYOUT_SPECIFIC_ADVANCES ((PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000))
-
-typedef unsigned short CGGlyph;
-
-typedef const struct __CTRun * CTRunRef;
-typedef const struct __CTLine * CTLineRef;
-
-namespace WebCore {
-
-class FontCascade;
-class Font;
-class TextRun;
-
-enum GlyphIterationStyle { IncludePartialGlyphs, ByWholeGlyphs };
-
-// See https://trac.webkit.org/wiki/ComplexTextController for more information about ComplexTextController.
-class ComplexTextController {
- WTF_MAKE_FAST_ALLOCATED;
-public:
- ComplexTextController(const FontCascade&, const TextRun&, bool mayUseNaturalWritingDirection = false, HashSet<const Font*>* fallbackFonts = 0, bool forTextEmphasis = false);
-
- class ComplexTextRun;
- WEBCORE_EXPORT ComplexTextController(const FontCascade&, const TextRun&, Vector<Ref<ComplexTextRun>>&);
-
- // Advance and emit glyphs up to the specified character.
- WEBCORE_EXPORT void advance(unsigned to, GlyphBuffer* = nullptr, GlyphIterationStyle = IncludePartialGlyphs, HashSet<const Font*>* fallbackFonts = nullptr);
-
- // Compute the character offset for a given x coordinate.
- unsigned offsetForPosition(float x, bool includePartialGlyphs);
-
- // Returns the width of everything we've consumed so far.
- float runWidthSoFar() const { return m_runWidthSoFar; }
-
- float totalWidth() const { return m_totalWidth; }
-
- float minGlyphBoundingBoxX() const { return m_minGlyphBoundingBoxX; }
- float maxGlyphBoundingBoxX() const { return m_maxGlyphBoundingBoxX; }
- float minGlyphBoundingBoxY() const { return m_minGlyphBoundingBoxY; }
- float maxGlyphBoundingBoxY() const { return m_maxGlyphBoundingBoxY; }
-
- class ComplexTextRun : public RefCounted<ComplexTextRun> {
- public:
- static Ref<ComplexTextRun> create(CTRunRef ctRun, const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd)
- {
- return adoptRef(*new ComplexTextRun(ctRun, font, characters, stringLocation, stringLength, indexBegin, indexEnd));
- }
-
- static Ref<ComplexTextRun> create(const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr)
- {
- return adoptRef(*new ComplexTextRun(font, characters, stringLocation, stringLength, indexBegin, indexEnd, ltr));
- }
-
- static Ref<ComplexTextRun> create(const Vector<FloatSize>& advances, const Vector<FloatPoint>& origins, const Vector<Glyph>& glyphs, const Vector<unsigned>& stringIndices, FloatSize initialAdvance, const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr)
- {
- return adoptRef(*new ComplexTextRun(advances, origins, glyphs, stringIndices, initialAdvance, font, characters, stringLocation, stringLength, indexBegin, indexEnd, ltr));
- }
-
- unsigned glyphCount() const { return m_glyphCount; }
- const Font& font() const { return m_font; }
- const UChar* characters() const { return m_characters; }
- unsigned stringLocation() const { return m_stringLocation; }
- unsigned stringLength() const { return m_stringLength; }
- ALWAYS_INLINE unsigned indexAt(unsigned) const;
- unsigned indexBegin() const { return m_indexBegin; }
- unsigned indexEnd() const { return m_indexEnd; }
- unsigned endOffsetAt(unsigned i) const { ASSERT(!m_isMonotonic); return m_glyphEndOffsets[i]; }
- const CGGlyph* glyphs() const { return m_glyphs.data(); }
-
- /*
- * This is the format of the information CoreText gives us about each run:
- *
- * ----->X (Paint glyph position) X (Paint glyph position) X (Paint glyph position)
- * / 7 7 7
- * / / / /
- * (Initial advance) / / (Glyph origin) / (Glyph origin) / (Glyph origin)
- * ------------------- / / /
- * / / / /
- * X X--------------------------X--------------------------X--------------------------X
- * (text position ^) (base advance) (base advance) (base advance)
- *
- *
- *
- *
- *
- * And here is the output we transform this into (for each run):
- *
- * ----->X------------------------->X------------------------->X
- * / (Paint advance) (Paint advance) \
- * / \
- * (Initial advance) / \ (Paint advance)
- * ------------------- ----------------
- * / \
- * X--------------------------------------------------X--------------------------X--------------------------X
- * (text position ^) (layout advance) (layout advance) (layout advance)
- */
- void growInitialAdvanceHorizontally(float delta) { m_initialAdvance.expand(delta, 0); }
- FloatSize initialAdvance() const { return m_initialAdvance; }
- const FloatSize* baseAdvances() const { return m_baseAdvances.data(); }
- const FloatPoint* glyphOrigins() const { return m_glyphOrigins.size() == glyphCount() ? m_glyphOrigins.data() : nullptr; }
- bool isLTR() const { return m_isLTR; }
- bool isMonotonic() const { return m_isMonotonic; }
- void setIsNonMonotonic();
-
- private:
- ComplexTextRun(CTRunRef, const Font&, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd);
- ComplexTextRun(const Font&, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr);
- WEBCORE_EXPORT ComplexTextRun(const Vector<FloatSize>& advances, const Vector<FloatPoint>& origins, const Vector<Glyph>& glyphs, const Vector<unsigned>& stringIndices, FloatSize initialAdvance, const Font&, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr);
-
- Vector<FloatSize, 64> m_baseAdvances;
- Vector<FloatPoint, 64> m_glyphOrigins;
- Vector<CGGlyph, 64> m_glyphs;
- Vector<unsigned, 64> m_glyphEndOffsets;
- Vector<unsigned, 64> m_coreTextIndices;
- FloatSize m_initialAdvance;
- const Font& m_font;
- const UChar* m_characters;
- unsigned m_stringLength;
- unsigned m_indexBegin;
- unsigned m_indexEnd;
- unsigned m_glyphCount;
- unsigned m_stringLocation;
- bool m_isLTR;
- bool m_isMonotonic { true };
- };
-private:
- void computeExpansionOpportunity();
- void finishConstruction();
-
- static unsigned stringBegin(const ComplexTextRun& run) { return run.stringLocation() + run.indexBegin(); }
- static unsigned stringEnd(const ComplexTextRun& run) { return run.stringLocation() + run.indexEnd(); }
-
- void collectComplexTextRuns();
-
- void collectComplexTextRunsForCharacters(const UChar*, unsigned length, unsigned stringLocation, const Font*);
- void adjustGlyphsAndAdvances();
-
- unsigned indexOfCurrentRun(unsigned& leftmostGlyph);
- unsigned incrementCurrentRun(unsigned& leftmostGlyph);
-
- float runWidthSoFarFraction(unsigned glyphStartOffset, unsigned glyphEndOffset, unsigned oldCharacterInCurrentGlyph, GlyphIterationStyle) const;
-
- FloatPoint glyphOrigin(unsigned index) const { return index < m_glyphOrigins.size() ? m_glyphOrigins[index] : FloatPoint(); }
-
- Vector<FloatSize, 256> m_adjustedBaseAdvances;
- Vector<FloatPoint, 256> m_glyphOrigins;
- Vector<CGGlyph, 256> m_adjustedGlyphs;
-
- Vector<UChar, 256> m_smallCapsBuffer;
-
- // There is a 3-level hierarchy here. At the top, we are interested in m_run.string(). We partition that string
- // into Lines, each of which is a sequence of characters which should use the same Font. Core Text then partitions
- // the Line into ComplexTextRuns.
- // ComplexTextRun::stringLocation() and ComplexTextRun::stringLength() refer to the offset and length of the Line
- // relative to m_run.string(). ComplexTextRun::indexAt() returns to the offset of a codepoint relative to
- // its Line. ComplexTextRun::glyphs() and ComplexTextRun::advances() refer to glyphs relative to the ComplexTextRun.
- // The length of the entire TextRun is m_run.length()
- Vector<RefPtr<ComplexTextRun>, 16> m_complexTextRuns;
-
- // The initial capacity of these vectors was selected as being the smallest power of two greater than
- // the average (3.5) plus one standard deviation (7.5) of nonzero sizes used on Arabic Wikipedia.
- Vector<unsigned, 16> m_runIndices;
- Vector<unsigned, 16> m_glyphCountFromStartToIndex;
-
- Vector<RetainPtr<CTLineRef>> m_coreTextLines;
-
- Vector<String> m_stringsFor8BitRuns;
-
- HashSet<const Font*>* m_fallbackFonts { nullptr };
-
- const FontCascade& m_font;
- const TextRun& m_run;
-
- unsigned m_currentCharacter { 0 };
- unsigned m_end { 0 };
-
- float m_totalWidth { 0 };
- float m_runWidthSoFar { 0 };
- unsigned m_numGlyphsSoFar { 0 };
- unsigned m_currentRun { 0 };
- unsigned m_glyphInCurrentRun { 0 };
- unsigned m_characterInCurrentGlyph { 0 };
- float m_expansion { 0 };
- float m_expansionPerOpportunity { 0 };
-
- float m_minGlyphBoundingBoxX { std::numeric_limits<float>::max() };
- float m_maxGlyphBoundingBoxX { std::numeric_limits<float>::min() };
- float m_minGlyphBoundingBoxY { std::numeric_limits<float>::max() };
- float m_maxGlyphBoundingBoxY { std::numeric_limits<float>::min() };
-
- bool m_isLTROnly { true };
- bool m_mayUseNaturalWritingDirection { false };
- bool m_forTextEmphasis { false };
-};
-
-} // namespace WebCore