e22bbbcdeea72905955e7929b30dd7c5a177c49c
[WebKit-https.git] / Source / WebCore / layout / inlineformatting / textlayout / TextContentProvider.cpp
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "TextContentProvider.h"
28
29 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
30
31 #include "FontCascade.h"
32 #include "RenderStyle.h"
33 #include "SimpleTextRunGenerator.h"
34 #include <wtf/IsoMallocInlines.h>
35
36 namespace WebCore {
37 namespace Layout {
38
39 WTF_MAKE_ISO_ALLOCATED_IMPL(TextContentProvider);
40
41 TextContentProvider::TextItem::Style::Style(const RenderStyle& style)
42     : font(style.fontCascade())
43     , collapseWhitespace(style.collapseWhiteSpace())
44     , hasKerningOrLigatures(font.enableKerning() || font.requiresShaping())
45     , wordSpacing(font.wordSpacing())
46     , tabWidth(collapseWhitespace ? 0 : style.tabSize())
47     , preserveNewline(style.preserveNewline())
48     , breakNBSP(style.autoWrap() && style.nbspMode() == NBSPMode::Space)
49     , keepAllWordsForCJK(style.wordBreak() == WordBreak::KeepAll)
50     , locale(style.locale())
51 {
52 }
53
54 TextContentProvider::TextContentProvider()
55     : m_textContentIterator(m_textContent)
56 {
57 }
58
59 TextContentProvider::~TextContentProvider()
60 {
61 }
62
63 void TextContentProvider::appendText(String text, const RenderStyle& style, bool canUseSimplifiedMeasure)
64 {
65     ASSERT(text.length());
66
67     auto start = length();
68     auto end = start + text.length();
69     m_textContent.append({ text, start, end, TextItem::Style(style), canUseSimplifiedMeasure });
70 }
71
72 void TextContentProvider::appendLineBreak()
73 {
74     m_hardLineBreaks.append(length());
75 }
76
77 static bool contains(ContentPosition position, const TextContentProvider::TextItem& textItem)
78 {
79     return textItem.start <= position && position < textItem.end;
80 }
81
82 float TextContentProvider::width(ContentPosition from, ContentPosition to, float xPosition) const
83 {
84     auto textItemPositionSlow = [&]() -> unsigned {
85         // Since this is an iterator like class, check the next item first (instead of starting the search from the beginning).
86         if (auto* textItem = (++m_textContentIterator).current()) {
87             if (contains(from, *textItem))
88                 return textItem->start;
89         }
90
91         m_textContentIterator.reset();
92         while (auto* textItem = m_textContentIterator.current()) {
93             if (contains(from, *textItem))
94                 return textItem->start;
95             ++m_textContentIterator;
96         }
97
98         ASSERT_NOT_REACHED();
99         return 0;
100     };
101
102     if (from >= to) {
103         ASSERT_NOT_REACHED();
104         return 0;
105     }
106
107     auto contentLength = length();
108     if (from >= contentLength || to > contentLength) {
109         ASSERT_NOT_REACHED();
110         return 0;
111     }
112
113     float width = 0;
114     auto length = to - from;
115     auto* textItem = m_textContentIterator.current();
116     auto startPosition = from - (textItem && contains(from, *textItem) ? textItem->start : textItemPositionSlow());
117
118     while (length) {
119         textItem = m_textContentIterator.current();
120         if (!textItem) {
121             ASSERT_NOT_REACHED();
122             break;
123         }
124
125         auto endPosition = std::min<ItemPosition>(startPosition + length, textItem->text.length());
126         ASSERT(endPosition >= startPosition);
127         auto textWidth = this->textWidth(*textItem, startPosition, endPosition, xPosition);
128
129         xPosition += textWidth;
130         width += textWidth;
131         length -= (endPosition - startPosition);
132
133         startPosition = 0;
134         if (length)
135             ++m_textContentIterator;
136     }
137
138     return width;
139 }
140
141 float TextContentProvider::textWidth(const TextItem& textItem, ItemPosition from, ItemPosition to, float xPosition) const
142 {
143     if (from > to || to > textItem.end - textItem.start) {
144         ASSERT_NOT_REACHED();
145         return 0;
146     }
147
148     auto& style = textItem.style;
149     auto& font = style.font;
150     if (!font.size() || from == to)
151         return 0;
152
153     if (font.isFixedPitch())
154         return fixedPitchWidth(textItem.text, style, from, to, xPosition);
155
156     auto measureWithEndSpace = style.hasKerningOrLigatures && to < textItem.text.length() && textItem.text[to] == ' ';
157     if (measureWithEndSpace)
158         ++to;
159     float width = 0;
160     if (textItem.canUseSimplifiedMeasure)
161         width = font.widthForSimpleText(StringView(textItem.text).substring(from, to - from));
162     else  {
163         WebCore::TextRun run(StringView(textItem.text).substring(from, to - from), xPosition);
164         if (style.tabWidth)
165             run.setTabSize(true, style.tabWidth);
166         width = font.width(run);
167     }
168
169     if (measureWithEndSpace)
170         width -= (font.spaceWidth() + style.wordSpacing);
171
172     return std::max<float>(0, width);
173 }
174
175 float TextContentProvider::fixedPitchWidth(String text, const TextItem::Style& style, ItemPosition from, ItemPosition to, float xPosition) const
176 {
177     auto monospaceCharacterWidth = style.font.spaceWidth();
178     float width = 0;
179     for (auto i = from; i < to; ++i) {
180         auto character = text[i];
181         if (character >= ' ' || character == '\n')
182             width += monospaceCharacterWidth;
183         else if (character == '\t')
184             width += style.collapseWhitespace ? monospaceCharacterWidth : style.font.tabWidth(style.tabWidth, xPosition + width);
185
186         if (i > from && (character == ' ' || character == '\t' || character == '\n'))
187             width += style.font.wordSpacing();
188     }
189
190     return width;
191 }
192
193 unsigned TextContentProvider::length() const
194 {
195     if (!m_textContent.size())
196         return 0;
197     return m_textContent.last().end;
198 }
199
200 TextContentProvider::Iterator TextContentProvider::iterator()
201 {
202     // TODO: This is where we decide whether we need a simple or a more complex provider.
203     if (!m_simpleTextRunGenerator)
204         m_simpleTextRunGenerator = std::make_unique<SimpleTextRunGenerator>(*this);
205     else
206         m_simpleTextRunGenerator->reset();
207
208     m_simpleTextRunGenerator->findNextRun();
209     return Iterator(*this);
210 }
211
212 TextContentProvider::TextRunList TextContentProvider::textRuns()
213 {
214     TextRunList textRunList;
215
216     auto textRunIterator = iterator();
217     while (auto textRum = textRunIterator.current()) {
218         textRunList.append(*textRum);
219         ++textRunIterator;
220     }
221     return textRunList;
222 }
223
224 void TextContentProvider::findNextRun()
225 {
226     m_simpleTextRunGenerator->findNextRun();
227 }
228
229 std::optional<TextRun> TextContentProvider::current() const
230 {
231     return m_simpleTextRunGenerator->current();
232 }
233
234 }
235 }
236 #endif