1/*
2 * Copyright (C) 2015-2016 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#pragma once
27
28#include "BreakLines.h"
29#include "RenderLineBreak.h"
30#include "SimpleLineLayoutFlowContents.h"
31
32namespace WebCore {
33
34class RenderBlockFlow;
35class RenderStyle;
36
37namespace SimpleLineLayout {
38
39class TextFragmentIterator {
40public:
41 TextFragmentIterator(const RenderBlockFlow&);
42 class TextFragment {
43 public:
44 enum Type { Invalid, ContentEnd, SoftLineBreak, HardLineBreak, Whitespace, NonWhitespace };
45 TextFragment() = default;
46 TextFragment(unsigned start, unsigned end, float width, Type type, bool isLastInRenderer = false, bool overlapsToNextRenderer = false, bool isCollapsed = false, bool isCollapsible = false)
47 : m_start(start)
48 , m_end(end)
49 , m_width(width)
50 , m_type(type)
51 , m_isLastInRenderer(isLastInRenderer)
52 , m_overlapsToNextRenderer(overlapsToNextRenderer)
53 , m_isCollapsed(isCollapsed)
54 , m_isCollapsible(isCollapsible)
55 {
56 }
57
58 bool isValid() const { return m_type != Invalid; }
59 unsigned start() const { return m_start; }
60 unsigned end() const { return m_end; }
61 unsigned length() const { ASSERT(m_end >= m_start); return m_end - m_start; }
62 float width() const { return m_width; }
63 Type type() const { return m_type; }
64 bool overlapsToNextRenderer() const { return m_overlapsToNextRenderer; }
65 bool isLastInRenderer() const { return m_isLastInRenderer; }
66 bool isLineBreak() const { return m_type == SoftLineBreak || m_type == HardLineBreak; }
67 bool isCollapsed() const { return m_isCollapsed; }
68 bool isCollapsible() const { return m_isCollapsible; }
69 bool hasHyphen() const { return m_hasHyphen; }
70
71 bool isEmpty() const { return start() == end() && !isLineBreak(); }
72 TextFragment split(unsigned splitPosition, float leftSideWidth, float rightSideWidth);
73 TextFragment splitWithHyphen(unsigned hyphenPosition, float hyphenStringWidth, float leftSideWidth, float rightSideWidth);
74 bool operator==(const TextFragment& other) const
75 {
76 return m_start == other.m_start
77 && m_end == other.m_end
78 && m_width == other.m_width
79 && m_type == other.m_type
80 && m_isLastInRenderer == other.m_isLastInRenderer
81 && m_overlapsToNextRenderer == other.m_overlapsToNextRenderer
82 && m_isCollapsed == other.m_isCollapsed
83 && m_isCollapsible == other.m_isCollapsible
84 && m_hasHyphen == other.m_hasHyphen;
85 }
86
87 private:
88 unsigned m_start { 0 };
89 unsigned m_end { 0 };
90 float m_width { 0 };
91 Type m_type { Invalid };
92 bool m_isLastInRenderer { false };
93 bool m_overlapsToNextRenderer { false };
94 bool m_isCollapsed { false };
95 bool m_isCollapsible { false };
96 bool m_hasHyphen { false };
97 };
98 TextFragment nextTextFragment(float xPosition = 0);
99 void revertToEndOfFragment(const TextFragment&);
100
101 // FIXME: These functions below should be decoupled from the text iterator.
102 float textWidth(unsigned startPosition, unsigned endPosition, float xPosition) const;
103 Optional<unsigned> lastHyphenPosition(const TextFragmentIterator::TextFragment& run, unsigned beforeIndex) const;
104
105 struct Style {
106 explicit Style(const RenderStyle&);
107
108 const FontCascade& font;
109 TextAlignMode textAlign;
110 bool hasKerningOrLigatures;
111 bool collapseWhitespace;
112 bool preserveNewline;
113 bool wrapLines;
114 bool breakSpaces;
115 bool breakAnyWordOnOverflow;
116 bool breakWordOnOverflow;
117 bool breakFirstWordOnOverflow;
118 bool breakNBSP;
119 bool keepAllWordsForCJK;
120 float wordSpacing;
121 unsigned tabWidth;
122 bool shouldHyphenate;
123 float hyphenStringWidth;
124 unsigned hyphenLimitBefore;
125 unsigned hyphenLimitAfter;
126 AtomicString locale;
127 Optional<unsigned> hyphenLimitLines;
128 };
129 const Style& style() const { return m_style; }
130
131private:
132 TextFragment findNextTextFragment(float xPosition);
133 enum PositionType { Breakable, NonWhitespace };
134 unsigned skipToNextPosition(PositionType, unsigned startPosition, float& width, float xPosition, bool& overlappingFragment);
135 bool isSoftLineBreak(unsigned position) const;
136 bool isHardLineBreak(const FlowContents::Iterator& segment) const;
137 unsigned nextBreakablePosition(const FlowContents::Segment&, unsigned startPosition);
138 unsigned nextNonWhitespacePosition(const FlowContents::Segment&, unsigned startPosition);
139
140 FlowContents m_flowContents;
141 FlowContents::Iterator m_currentSegment;
142 LazyLineBreakIterator m_lineBreakIterator;
143 const Style m_style;
144 unsigned m_position { 0 };
145 bool m_atEndOfSegment { false };
146};
147
148inline TextFragmentIterator::TextFragment TextFragmentIterator::TextFragment::split(unsigned splitPosition, float leftSideWidth, float rightSideWidth)
149{
150 auto updateFragmentProperties = [] (TextFragment& fragment, unsigned start, unsigned end, float width)
151 {
152 fragment.m_start = start;
153 fragment.m_end = end;
154 fragment.m_width = width;
155 if (fragment.start() + 1 > fragment.end())
156 return;
157 fragment.m_isCollapsed = false;
158 };
159
160 TextFragment rightSide(*this);
161 updateFragmentProperties(*this, start(), splitPosition, leftSideWidth);
162 updateFragmentProperties(rightSide, splitPosition, rightSide.end(), rightSideWidth);
163 return rightSide;
164}
165
166inline TextFragmentIterator::TextFragment TextFragmentIterator::TextFragment::splitWithHyphen(unsigned hyphenPosition, float hyphenStringWidth,
167 float leftSideWidth, float rightSideWidth)
168{
169 auto rightSide = split(hyphenPosition, leftSideWidth, rightSideWidth);
170 m_hasHyphen = true;
171 m_width += hyphenStringWidth;
172 return rightSide;
173}
174
175inline bool TextFragmentIterator::isSoftLineBreak(unsigned position) const
176{
177 const auto& segment = *m_currentSegment;
178 ASSERT(segment.start <= position && position < segment.end);
179 return m_style.preserveNewline && segment.text[position - segment.start] == '\n';
180}
181
182inline bool TextFragmentIterator::isHardLineBreak(const FlowContents::Iterator& segment) const
183{
184 ASSERT(segment->start != segment->end || (segment->start == segment->end && is<RenderLineBreak>(segment->renderer)));
185 return segment->start == segment->end;
186}
187
188}
189}
190