OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved. | 2 * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved. |
3 * | 3 * |
4 * This library is free software; you can redistribute it and/or | 4 * This library is free software; you can redistribute it and/or |
5 * modify it under the terms of the GNU Library General Public | 5 * modify it under the terms of the GNU Library General Public |
6 * License as published by the Free Software Foundation; either | 6 * License as published by the Free Software Foundation; either |
7 * version 2 of the License, or (at your option) any later version. | 7 * version 2 of the License, or (at your option) any later version. |
8 * | 8 * |
9 * This library is distributed in the hope that it will be useful, | 9 * This library is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 * Library General Public License for more details. | 12 * Library General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU Library General Public License | 14 * You should have received a copy of the GNU Library General Public License |
15 * along with this library; see the file COPYING.LIB. If not, write to | 15 * along with this library; see the file COPYING.LIB. If not, write to |
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 * Boston, MA 02110-1301, USA. | 17 * Boston, MA 02110-1301, USA. |
18 */ | 18 */ |
19 | 19 |
20 #include "core/layout/svg/SVGTextMetricsBuilder.h" | 20 #include "core/layout/svg/SVGTextMetricsBuilder.h" |
21 | 21 |
22 #include "core/layout/api/LineLayoutSVGInlineText.h" | 22 #include "core/layout/api/LineLayoutSVGInlineText.h" |
23 #include "core/layout/svg/LayoutSVGInline.h" | 23 #include "core/layout/svg/LayoutSVGInline.h" |
24 #include "core/layout/svg/LayoutSVGInlineText.h" | 24 #include "core/layout/svg/LayoutSVGInlineText.h" |
25 #include "core/layout/svg/LayoutSVGText.h" | 25 #include "core/layout/svg/LayoutSVGText.h" |
26 #include "core/layout/svg/SVGTextMetrics.h" | 26 #include "core/layout/svg/SVGTextMetrics.h" |
27 #include "platform/fonts/GlyphBuffer.h" | 27 #include "platform/fonts/CharacterRange.h" |
28 #include "platform/fonts/shaping/SimpleShaper.h" | |
29 #include "platform/text/BidiCharacterRun.h" | 28 #include "platform/text/BidiCharacterRun.h" |
30 #include "platform/text/BidiResolver.h" | 29 #include "platform/text/BidiResolver.h" |
31 #include "platform/text/TextDirection.h" | 30 #include "platform/text/TextDirection.h" |
32 #include "platform/text/TextPath.h" | 31 #include "platform/text/TextPath.h" |
33 #include "platform/text/TextRun.h" | 32 #include "platform/text/TextRun.h" |
34 #include "platform/text/TextRunIterator.h" | 33 #include "platform/text/TextRunIterator.h" |
35 #include "wtf/Vector.h" | 34 #include "wtf/Vector.h" |
36 | 35 |
37 namespace blink { | 36 namespace blink { |
38 | 37 |
39 class SVGTextMetricsCalculator { | 38 class SVGTextMetricsCalculator { |
40 public: | 39 public: |
41 SVGTextMetricsCalculator(LayoutSVGInlineText*); | 40 SVGTextMetricsCalculator(LayoutSVGInlineText*); |
42 ~SVGTextMetricsCalculator(); | 41 ~SVGTextMetricsCalculator(); |
43 | 42 |
44 SVGTextMetrics computeMetricsForCharacter(unsigned textPosition); | 43 bool advancePosition(); |
45 unsigned textLength() const { return static_cast<unsigned>(m_run.charactersL ength()); } | 44 unsigned currentPosition() const { return m_currentPosition; } |
46 | 45 |
47 bool characterStartsSurrogatePair(unsigned textPosition) const | 46 SVGTextMetrics currentCharacterMetrics(); |
47 | |
48 // TODO(pdr): Character-based iteration is ambiguous and error-prone. It | |
49 // should be unified under a single concept. See: https://crbug.com/593570 | |
50 bool currentCharacterStartsSurrogatePair() const | |
48 { | 51 { |
49 return U16_IS_LEAD(m_run[textPosition]) && textPosition + 1 < textLength () && U16_IS_TRAIL(m_run[textPosition + 1]); | 52 return U16_IS_LEAD(m_run[m_currentPosition]) && m_currentPosition + 1 < characterCount() && U16_IS_TRAIL(m_run[m_currentPosition + 1]); |
50 } | 53 } |
51 bool characterIsWhiteSpace(unsigned textPosition) const | 54 bool currentCharacterIsWhiteSpace() const |
52 { | 55 { |
53 return m_run[textPosition] == ' '; | 56 return m_run[m_currentPosition] == ' '; |
57 } | |
58 unsigned characterCount() const | |
59 { | |
60 return static_cast<unsigned>(m_run.charactersLength()); | |
54 } | 61 } |
55 | 62 |
56 private: | 63 private: |
57 void setupBidiRuns(); | 64 void setupBidiRuns(); |
58 | 65 |
66 // Ensure |m_subrunRanges| is updated for the current bidi run, or the | |
67 // complete m_run if no bidi runs are present. Returns the current position | |
68 // in the subrun which can be used to index into |m_subrunRanges|. | |
69 unsigned updateSubrunRangesForCurrentPosition(); | |
70 | |
71 // Current character position in m_text. | |
72 unsigned m_currentPosition; | |
73 | |
59 LineLayoutSVGInlineText m_text; | 74 LineLayoutSVGInlineText m_text; |
75 TextRun m_run; | |
76 | |
60 BidiCharacterRun* m_bidiRun; | 77 BidiCharacterRun* m_bidiRun; |
61 TextRun m_run; | |
62 BidiResolver<TextRunIterator, BidiCharacterRun> m_bidiResolver; | 78 BidiResolver<TextRunIterator, BidiCharacterRun> m_bidiResolver; |
63 float m_totalWidth; | 79 |
64 TextDirection m_textDirection; | 80 // Ranges for the current bidi run if present, or the entire run otherwise. |
81 Vector<CharacterRange> m_subrunRanges; | |
65 }; | 82 }; |
66 | 83 |
67 SVGTextMetricsCalculator::SVGTextMetricsCalculator(LayoutSVGInlineText* text) | 84 SVGTextMetricsCalculator::SVGTextMetricsCalculator(LayoutSVGInlineText* text) |
68 : m_text(LineLayoutSVGInlineText(text)) | 85 : m_currentPosition(0) |
86 , m_text(LineLayoutSVGInlineText(text)) | |
87 , m_run(SVGTextMetrics::constructTextRun(m_text, 0, m_text.textLength(), m_t ext.styleRef().direction())) | |
69 , m_bidiRun(nullptr) | 88 , m_bidiRun(nullptr) |
70 , m_run(SVGTextMetrics::constructTextRun(m_text, 0, m_text.textLength(), m_t ext.styleRef().direction())) | |
71 , m_totalWidth(0) | |
72 { | 89 { |
73 setupBidiRuns(); | 90 setupBidiRuns(); |
74 } | 91 } |
75 | 92 |
76 SVGTextMetricsCalculator::~SVGTextMetricsCalculator() | 93 SVGTextMetricsCalculator::~SVGTextMetricsCalculator() |
77 { | 94 { |
78 if (m_bidiRun) | 95 if (m_bidiRun) |
79 m_bidiResolver.runs().deleteRuns(); | 96 m_bidiResolver.runs().deleteRuns(); |
80 } | 97 } |
81 | 98 |
82 void SVGTextMetricsCalculator::setupBidiRuns() | 99 void SVGTextMetricsCalculator::setupBidiRuns() |
83 { | 100 { |
84 const ComputedStyle& style = m_text.styleRef(); | 101 if (isOverride(m_text.styleRef().unicodeBidi())) |
85 m_textDirection = style.direction(); | |
86 if (isOverride(style.unicodeBidi())) | |
87 return; | 102 return; |
88 | |
89 BidiStatus status(LTR, false); | 103 BidiStatus status(LTR, false); |
90 status.last = status.lastStrong = WTF::Unicode::OtherNeutral; | 104 status.last = status.lastStrong = WTF::Unicode::OtherNeutral; |
91 m_bidiResolver.setStatus(status); | 105 m_bidiResolver.setStatus(status); |
92 m_bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&m_run, 0)) ; | 106 m_bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&m_run, 0)) ; |
93 const bool hardLineBreak = false; | 107 const bool hardLineBreak = false; |
94 const bool reorderRuns = false; | 108 const bool reorderRuns = false; |
95 m_bidiResolver.createBidiRunsForLine(TextRunIterator(&m_run, m_run.length()) , NoVisualOverride, hardLineBreak, reorderRuns); | 109 m_bidiResolver.createBidiRunsForLine(TextRunIterator(&m_run, m_run.length()) , NoVisualOverride, hardLineBreak, reorderRuns); |
96 BidiRunList<BidiCharacterRun>& bidiRuns = m_bidiResolver.runs(); | 110 BidiRunList<BidiCharacterRun>& bidiRuns = m_bidiResolver.runs(); |
97 m_bidiRun = bidiRuns.firstRun(); | 111 m_bidiRun = bidiRuns.firstRun(); |
98 } | 112 } |
99 | 113 |
100 SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacter(unsigned tex tPosition) | 114 bool SVGTextMetricsCalculator::advancePosition() |
115 { | |
116 m_currentPosition += currentCharacterStartsSurrogatePair() ? 2 : 1; | |
117 return m_currentPosition < characterCount(); | |
118 } | |
119 | |
120 unsigned SVGTextMetricsCalculator::updateSubrunRangesForCurrentPosition() | |
101 { | 121 { |
102 if (m_bidiRun) { | 122 if (m_bidiRun) { |
103 if (textPosition >= static_cast<unsigned>(m_bidiRun->stop())) { | 123 if (m_currentPosition >= static_cast<unsigned>(m_bidiRun->stop())) { |
104 m_bidiRun = m_bidiRun->next(); | 124 m_bidiRun = m_bidiRun->next(); |
105 // New BiDi run means new reference position for measurements, so re set |m_totalWidth|. | 125 // Ensure new subrange ranges are computed below. |
106 m_totalWidth = 0; | 126 m_subrunRanges.clear(); |
107 } | 127 } |
108 ASSERT(m_bidiRun); | 128 ASSERT(m_bidiRun); |
109 ASSERT(static_cast<int>(textPosition) < m_bidiRun->stop()); | 129 ASSERT(static_cast<int>(m_currentPosition) < m_bidiRun->stop()); |
110 m_textDirection = m_bidiRun->direction(); | |
111 } | 130 } |
112 | 131 |
113 unsigned metricsLength = characterStartsSurrogatePair(textPosition) ? 2 : 1; | 132 unsigned positionInRun = m_bidiRun ? m_currentPosition - m_bidiRun->start() : m_currentPosition; |
114 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(m_text, textP osition, metricsLength, m_textDirection); | 133 if (positionInRun >= m_subrunRanges.size()) { |
115 ASSERT(metrics.length() == metricsLength); | 134 unsigned subrunStart = m_bidiRun ? m_bidiRun->start() : 0; |
135 unsigned subrunEnd = m_bidiRun ? m_bidiRun->stop() : m_run.charactersLen gth(); | |
136 TextDirection subrunDirection = m_bidiRun ? m_bidiRun->direction() : m_t ext.styleRef().direction(); | |
137 TextRun subRun = SVGTextMetrics::constructTextRun(m_text, subrunStart, s ubrunEnd - subrunStart, subrunDirection); | |
138 m_subrunRanges = m_text.scaledFont().individualCharacterRanges(subRun, 0 , subRun.length() - 1); | |
116 | 139 |
117 unsigned startPosition = m_bidiRun ? m_bidiRun->start() : 0; | 140 // TODO(pdr): We only have per-glyph data so we need to synthesize per- |
118 ASSERT(startPosition <= textPosition); | 141 // grapheme data. E.g., if 'fi' is shaped into a single glyph, we do not |
119 SVGTextMetrics complexStartToCurrentMetrics = SVGTextMetrics::measureCharact erRange(m_text, startPosition, textPosition - startPosition + metricsLength, m_t extDirection); | 142 // know the 'i' position. The code below synthesizes an average glyph |
120 // Frequent case for Arabic text: when measuring a single character the arab ic isolated form is taken | 143 // width when characters share a position. This will incorrectly split |
121 // when laying out the glyph "in context" (with it's surrounding characters) it changes due to shaping. | 144 // combining diacritics. See: https://crbug.com/473476. |
122 // So whenever currentWidth != currentMetrics.width(), we are processing a t ext run whose length is | 145 unsigned distributeCount = 0; |
123 // not equal to the sum of the individual lengths of the glyphs, when measur ing them isolated. | 146 for (int rangeIndex = m_subrunRanges.size() - 1; rangeIndex >= 0; --rang eIndex) { |
fs
2016/03/10 09:55:57
Nit: People like to yell about "implementation-def
| |
124 float currentWidth = complexStartToCurrentMetrics.width() - m_totalWidth; | 147 CharacterRange& currentRange = m_subrunRanges[rangeIndex]; |
125 if (currentWidth != metrics.width()) | 148 if (currentRange.width() == 0) { |
126 metrics.setWidth(currentWidth); | 149 distributeCount++; |
150 } else if (distributeCount != 0) { | |
151 distributeCount++; | |
152 float newWidth = currentRange.width() / distributeCount; | |
153 currentRange.end = currentRange.start + newWidth; | |
154 for (unsigned distribute = 1; distribute < distributeCount; dist ribute++) { | |
155 unsigned distributeIndex = rangeIndex + distribute; | |
156 float newStartPosition = m_subrunRanges[distributeIndex - 1] .end; | |
157 m_subrunRanges[distributeIndex].start = newStartPosition; | |
158 m_subrunRanges[distributeIndex].end = newStartPosition + new Width; | |
159 } | |
160 distributeCount = 0; | |
161 } | |
162 } | |
163 } | |
127 | 164 |
128 m_totalWidth = complexStartToCurrentMetrics.width(); | 165 return positionInRun; |
129 return metrics; | 166 } |
167 | |
168 SVGTextMetrics SVGTextMetricsCalculator::currentCharacterMetrics() | |
169 { | |
170 unsigned currentSubrunPosition = updateSubrunRangesForCurrentPosition(); | |
171 unsigned length = currentCharacterStartsSurrogatePair() ? 2 : 1; | |
172 float width = m_subrunRanges[currentSubrunPosition].width(); | |
173 return SVGTextMetrics(m_text, length, width); | |
130 } | 174 } |
131 | 175 |
132 struct MeasureTextData { | 176 struct MeasureTextData { |
133 MeasureTextData(SVGCharacterDataMap* characterDataMap) | 177 MeasureTextData(SVGCharacterDataMap* characterDataMap) |
134 : allCharactersMap(characterDataMap) | 178 : allCharactersMap(characterDataMap) |
135 , lastCharacterWasWhiteSpace(true) | 179 , lastCharacterWasWhiteSpace(true) |
136 , valueListPosition(0) | 180 , valueListPosition(0) |
137 { | 181 { |
138 } | 182 } |
139 | 183 |
140 SVGCharacterDataMap* allCharactersMap; | 184 SVGCharacterDataMap* allCharactersMap; |
141 bool lastCharacterWasWhiteSpace; | 185 bool lastCharacterWasWhiteSpace; |
142 unsigned valueListPosition; | 186 unsigned valueListPosition; |
143 }; | 187 }; |
144 | 188 |
145 static void measureTextLayoutObject(LayoutSVGInlineText* text, MeasureTextData* data, bool processLayoutObject) | 189 static void measureTextLayoutObject(LayoutSVGInlineText* text, MeasureTextData* data, bool processLayoutObject) |
146 { | 190 { |
147 ASSERT(text); | 191 ASSERT(text); |
148 | 192 |
149 SVGTextLayoutAttributes* attributes = text->layoutAttributes(); | 193 SVGTextLayoutAttributes* attributes = text->layoutAttributes(); |
150 Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues() ; | 194 Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues() ; |
151 if (processLayoutObject) { | 195 if (processLayoutObject) { |
152 if (data->allCharactersMap) | 196 if (data->allCharactersMap) |
153 attributes->clear(); | 197 attributes->clear(); |
154 else | 198 else |
155 textMetricsValues->clear(); | 199 textMetricsValues->clear(); |
156 } | 200 } |
157 | 201 |
202 // TODO(pdr): This loop is too tightly coupled to SVGTextMetricsCalculator. | |
203 // We should refactor SVGTextMetricsCalculator to be a simple bidi run | |
204 // iterator and move all subrun logic to a single function. | |
158 SVGTextMetricsCalculator calculator(text); | 205 SVGTextMetricsCalculator calculator(text); |
159 bool preserveWhiteSpace = text->style()->whiteSpace() == PRE; | 206 bool preserveWhiteSpace = text->style()->whiteSpace() == PRE; |
160 unsigned surrogatePairCharacters = 0; | 207 unsigned surrogatePairCharacters = 0; |
161 unsigned skippedCharacters = 0; | 208 unsigned skippedCharacters = 0; |
162 unsigned textPosition = 0; | 209 do { |
163 unsigned textLength = calculator.textLength(); | 210 SVGTextMetrics metrics = calculator.currentCharacterMetrics(); |
164 | 211 if (!metrics.length()) |
165 SVGTextMetrics currentMetrics; | |
166 for (; textPosition < textLength; textPosition += currentMetrics.length()) { | |
167 currentMetrics = calculator.computeMetricsForCharacter(textPosition); | |
168 if (!currentMetrics.length()) | |
169 break; | 212 break; |
170 | 213 |
171 bool characterIsWhiteSpace = calculator.characterIsWhiteSpace(textPositi on); | 214 bool characterIsWhiteSpace = calculator.currentCharacterIsWhiteSpace(); |
172 if (characterIsWhiteSpace && !preserveWhiteSpace && data->lastCharacterW asWhiteSpace) { | 215 if (characterIsWhiteSpace && !preserveWhiteSpace && data->lastCharacterW asWhiteSpace) { |
173 if (processLayoutObject) | 216 if (processLayoutObject) |
174 textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::Skipped SpaceMetrics)); | 217 textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::Skipped SpaceMetrics)); |
175 if (data->allCharactersMap) | 218 if (data->allCharactersMap) |
176 skippedCharacters += currentMetrics.length(); | 219 skippedCharacters += metrics.length(); |
177 continue; | 220 continue; |
178 } | 221 } |
179 | 222 |
180 if (processLayoutObject) { | 223 if (processLayoutObject) { |
181 if (data->allCharactersMap) { | 224 if (data->allCharactersMap) { |
182 const SVGCharacterDataMap::const_iterator it = data->allCharacte rsMap->find(data->valueListPosition + textPosition - skippedCharacters - surroga tePairCharacters + 1); | 225 const SVGCharacterDataMap::const_iterator it = data->allCharacte rsMap->find(data->valueListPosition + calculator.currentPosition() - skippedChar acters - surrogatePairCharacters + 1); |
183 if (it != data->allCharactersMap->end()) | 226 if (it != data->allCharactersMap->end()) |
184 attributes->characterDataMap().set(textPosition + 1, it->val ue); | 227 attributes->characterDataMap().set(calculator.currentPositio n() + 1, it->value); |
185 } | 228 } |
186 textMetricsValues->append(currentMetrics); | 229 textMetricsValues->append(metrics); |
187 } | 230 } |
188 | 231 |
189 if (data->allCharactersMap && calculator.characterStartsSurrogatePair(te xtPosition)) | 232 if (data->allCharactersMap && calculator.currentCharacterStartsSurrogate Pair()) |
190 surrogatePairCharacters++; | 233 surrogatePairCharacters++; |
191 | 234 |
192 data->lastCharacterWasWhiteSpace = characterIsWhiteSpace; | 235 data->lastCharacterWasWhiteSpace = characterIsWhiteSpace; |
193 } | 236 } while (calculator.advancePosition()); |
194 | 237 |
195 if (!data->allCharactersMap) | 238 if (!data->allCharactersMap) |
196 return; | 239 return; |
197 | 240 |
198 data->valueListPosition += textPosition - skippedCharacters; | 241 data->valueListPosition += calculator.currentPosition() - skippedCharacters; |
199 } | 242 } |
200 | 243 |
201 static void walkTree(LayoutSVGText* start, LayoutSVGInlineText* stopAtLeaf, Meas ureTextData* data) | 244 static void walkTree(LayoutSVGText* start, LayoutSVGInlineText* stopAtLeaf, Meas ureTextData* data) |
202 { | 245 { |
203 LayoutObject* child = start->firstChild(); | 246 LayoutObject* child = start->firstChild(); |
204 while (child) { | 247 while (child) { |
205 if (child->isSVGInlineText()) { | 248 if (child->isSVGInlineText()) { |
206 LayoutSVGInlineText* text = toLayoutSVGInlineText(child); | 249 LayoutSVGInlineText* text = toLayoutSVGInlineText(child); |
207 measureTextLayoutObject(text, data, !stopAtLeaf || stopAtLeaf == tex t); | 250 measureTextLayoutObject(text, data, !stopAtLeaf || stopAtLeaf == tex t); |
208 if (stopAtLeaf && stopAtLeaf == text) | 251 if (stopAtLeaf && stopAtLeaf == text) |
(...skipping 22 matching lines...) Expand all Loading... | |
231 } | 274 } |
232 | 275 |
233 void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(LayoutSVGText* textR oot, LayoutSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap) | 276 void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(LayoutSVGText* textR oot, LayoutSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap) |
234 { | 277 { |
235 ASSERT(textRoot); | 278 ASSERT(textRoot); |
236 MeasureTextData data(&allCharactersMap); | 279 MeasureTextData data(&allCharactersMap); |
237 walkTree(textRoot, stopAtLeaf, &data); | 280 walkTree(textRoot, stopAtLeaf, &data); |
238 } | 281 } |
239 | 282 |
240 } // namespace blink | 283 } // namespace blink |
OLD | NEW |