Chromium Code Reviews| 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): The surrogate pair concept should be refactored to a single | |
|
fs
2016/03/09 11:31:44
I think we should rather strive to move in the dir
pdr.
2016/03/10 04:55:15
Nice! I didn't know the name for this. I've rewrit
| |
| 49 // place to hide this complexity. | |
| 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): Our font APIs currently only have per-glyph data so we |
| 118 ASSERT(startPosition <= textPosition); | 141 // need to synthesize per-grapheme data. For example, if 'fi' is shaped |
| 119 SVGTextMetrics complexStartToCurrentMetrics = SVGTextMetrics::measureCharact erRange(m_text, startPosition, textPosition - startPosition + metricsLength, m_t extDirection); | 142 // into a single glyph, we do not have access to the 'i' position. The |
| 120 // Frequent case for Arabic text: when measuring a single character the arab ic isolated form is taken | 143 // code below synthesizes a position when two characters share the same |
|
fs
2016/03/09 11:31:44
Well technically "two or more" (i.e ligatures like
pdr.
2016/03/10 04:55:15
+1, good catch. I've ffixed this loop and added a
pdr.
2016/03/10 07:21:48
I looked at this some more and we're violating the
fs
2016/03/10 09:55:57
Yes, it more common for us weird people with diacr
| |
| 121 // when laying out the glyph "in context" (with it's surrounding characters) it changes due to shaping. | 144 // position. See: https://crbug.com/473476. |
| 122 // So whenever currentWidth != currentMetrics.width(), we are processing a t ext run whose length is | 145 for (unsigned rangeIndex = m_subrunRanges.size() - 1; rangeIndex > 0; -- rangeIndex) { |
| 123 // not equal to the sum of the individual lengths of the glyphs, when measur ing them isolated. | 146 CharacterRange& currentRange = m_subrunRanges[rangeIndex]; |
| 124 float currentWidth = complexStartToCurrentMetrics.width() - m_totalWidth; | 147 CharacterRange& previousRange = m_subrunRanges[rangeIndex - 1]; |
| 125 if (currentWidth != metrics.width()) | 148 if (currentRange.width() == 0 && previousRange.end == currentRange.s tart) { |
| 126 metrics.setWidth(currentWidth); | 149 float midpoint = (previousRange.end - previousRange.start) / 2; |
| 150 previousRange.end = previousRange.start + midpoint; | |
|
fs
2016/03/09 11:31:44
(previousRange.start + previousRange.end) / 2
for
pdr.
2016/03/10 04:55:15
The new loop now requires a divide 😭
| |
| 151 currentRange.start = previousRange.end; | |
| 152 } | |
| 153 } | |
| 154 } | |
| 127 | 155 |
| 128 m_totalWidth = complexStartToCurrentMetrics.width(); | 156 return positionInRun; |
| 129 return metrics; | 157 } |
| 158 | |
| 159 SVGTextMetrics SVGTextMetricsCalculator::currentCharacterMetrics() | |
| 160 { | |
| 161 unsigned currentSubrunPosition = updateSubrunRangesForCurrentPosition(); | |
| 162 unsigned length = currentCharacterStartsSurrogatePair() ? 2 : 1; | |
| 163 float width = m_subrunRanges[currentSubrunPosition].width(); | |
| 164 return SVGTextMetrics(m_text, length, width); | |
| 130 } | 165 } |
| 131 | 166 |
| 132 struct MeasureTextData { | 167 struct MeasureTextData { |
| 133 MeasureTextData(SVGCharacterDataMap* characterDataMap) | 168 MeasureTextData(SVGCharacterDataMap* characterDataMap) |
| 134 : allCharactersMap(characterDataMap) | 169 : allCharactersMap(characterDataMap) |
| 135 , lastCharacterWasWhiteSpace(true) | 170 , lastCharacterWasWhiteSpace(true) |
| 136 , valueListPosition(0) | 171 , valueListPosition(0) |
| 137 { | 172 { |
| 138 } | 173 } |
| 139 | 174 |
| 140 SVGCharacterDataMap* allCharactersMap; | 175 SVGCharacterDataMap* allCharactersMap; |
| 141 bool lastCharacterWasWhiteSpace; | 176 bool lastCharacterWasWhiteSpace; |
| 142 unsigned valueListPosition; | 177 unsigned valueListPosition; |
| 143 }; | 178 }; |
| 144 | 179 |
| 145 static void measureTextLayoutObject(LayoutSVGInlineText* text, MeasureTextData* data, bool processLayoutObject) | 180 static void measureTextLayoutObject(LayoutSVGInlineText* text, MeasureTextData* data, bool processLayoutObject) |
| 146 { | 181 { |
| 147 ASSERT(text); | 182 ASSERT(text); |
| 148 | 183 |
| 149 SVGTextLayoutAttributes* attributes = text->layoutAttributes(); | 184 SVGTextLayoutAttributes* attributes = text->layoutAttributes(); |
| 150 Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues() ; | 185 Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues() ; |
| 151 if (processLayoutObject) { | 186 if (processLayoutObject) { |
| 152 if (data->allCharactersMap) | 187 if (data->allCharactersMap) |
| 153 attributes->clear(); | 188 attributes->clear(); |
| 154 else | 189 else |
| 155 textMetricsValues->clear(); | 190 textMetricsValues->clear(); |
| 156 } | 191 } |
| 157 | 192 |
| 193 // TODO(pdr): This loop is too tightly coupled to SVGTextMetricsCalculator. | |
|
fs
2016/03/09 11:31:44
Should we file a (separate) bug for this, or will
pdr.
2016/03/10 04:55:15
Yeah, I plan to do this one in a followup but want
fs
2016/03/10 09:55:57
Roger that.
| |
| 194 // We should refactor SVGTextMetricsCalculator to be a simple bidi run | |
| 195 // iterator and move all subrun logic to a single function. | |
| 158 SVGTextMetricsCalculator calculator(text); | 196 SVGTextMetricsCalculator calculator(text); |
| 159 bool preserveWhiteSpace = text->style()->whiteSpace() == PRE; | 197 bool preserveWhiteSpace = text->style()->whiteSpace() == PRE; |
| 160 unsigned surrogatePairCharacters = 0; | 198 unsigned surrogatePairCharacters = 0; |
| 161 unsigned skippedCharacters = 0; | 199 unsigned skippedCharacters = 0; |
| 162 unsigned textPosition = 0; | 200 do { |
| 163 unsigned textLength = calculator.textLength(); | 201 SVGTextMetrics metrics = calculator.currentCharacterMetrics(); |
| 164 | 202 if (!metrics.length()) |
| 165 SVGTextMetrics currentMetrics; | |
| 166 for (; textPosition < textLength; textPosition += currentMetrics.length()) { | |
| 167 currentMetrics = calculator.computeMetricsForCharacter(textPosition); | |
| 168 if (!currentMetrics.length()) | |
| 169 break; | 203 break; |
| 170 | 204 |
| 171 bool characterIsWhiteSpace = calculator.characterIsWhiteSpace(textPositi on); | 205 bool characterIsWhiteSpace = calculator.currentCharacterIsWhiteSpace(); |
| 172 if (characterIsWhiteSpace && !preserveWhiteSpace && data->lastCharacterW asWhiteSpace) { | 206 if (characterIsWhiteSpace && !preserveWhiteSpace && data->lastCharacterW asWhiteSpace) { |
| 173 if (processLayoutObject) | 207 if (processLayoutObject) |
| 174 textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::Skipped SpaceMetrics)); | 208 textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::Skipped SpaceMetrics)); |
| 175 if (data->allCharactersMap) | 209 if (data->allCharactersMap) |
| 176 skippedCharacters += currentMetrics.length(); | 210 skippedCharacters += metrics.length(); |
| 177 continue; | 211 continue; |
| 178 } | 212 } |
| 179 | 213 |
| 180 if (processLayoutObject) { | 214 if (processLayoutObject) { |
| 181 if (data->allCharactersMap) { | 215 if (data->allCharactersMap) { |
| 182 const SVGCharacterDataMap::const_iterator it = data->allCharacte rsMap->find(data->valueListPosition + textPosition - skippedCharacters - surroga tePairCharacters + 1); | 216 const SVGCharacterDataMap::const_iterator it = data->allCharacte rsMap->find(data->valueListPosition + calculator.currentPosition() - skippedChar acters - surrogatePairCharacters + 1); |
| 183 if (it != data->allCharactersMap->end()) | 217 if (it != data->allCharactersMap->end()) |
| 184 attributes->characterDataMap().set(textPosition + 1, it->val ue); | 218 attributes->characterDataMap().set(calculator.currentPositio n() + 1, it->value); |
| 185 } | 219 } |
| 186 textMetricsValues->append(currentMetrics); | 220 textMetricsValues->append(metrics); |
| 187 } | 221 } |
| 188 | 222 |
| 189 if (data->allCharactersMap && calculator.characterStartsSurrogatePair(te xtPosition)) | 223 if (data->allCharactersMap && calculator.currentCharacterStartsSurrogate Pair()) |
| 190 surrogatePairCharacters++; | 224 surrogatePairCharacters++; |
| 191 | 225 |
| 192 data->lastCharacterWasWhiteSpace = characterIsWhiteSpace; | 226 data->lastCharacterWasWhiteSpace = characterIsWhiteSpace; |
| 193 } | 227 } while (calculator.advancePosition()); |
| 194 | 228 |
| 195 if (!data->allCharactersMap) | 229 if (!data->allCharactersMap) |
| 196 return; | 230 return; |
| 197 | 231 |
| 198 data->valueListPosition += textPosition - skippedCharacters; | 232 data->valueListPosition += calculator.currentPosition() - skippedCharacters; |
| 199 } | 233 } |
| 200 | 234 |
| 201 static void walkTree(LayoutSVGText* start, LayoutSVGInlineText* stopAtLeaf, Meas ureTextData* data) | 235 static void walkTree(LayoutSVGText* start, LayoutSVGInlineText* stopAtLeaf, Meas ureTextData* data) |
| 202 { | 236 { |
| 203 LayoutObject* child = start->firstChild(); | 237 LayoutObject* child = start->firstChild(); |
| 204 while (child) { | 238 while (child) { |
| 205 if (child->isSVGInlineText()) { | 239 if (child->isSVGInlineText()) { |
| 206 LayoutSVGInlineText* text = toLayoutSVGInlineText(child); | 240 LayoutSVGInlineText* text = toLayoutSVGInlineText(child); |
| 207 measureTextLayoutObject(text, data, !stopAtLeaf || stopAtLeaf == tex t); | 241 measureTextLayoutObject(text, data, !stopAtLeaf || stopAtLeaf == tex t); |
| 208 if (stopAtLeaf && stopAtLeaf == text) | 242 if (stopAtLeaf && stopAtLeaf == text) |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 231 } | 265 } |
| 232 | 266 |
| 233 void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(LayoutSVGText* textR oot, LayoutSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap) | 267 void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(LayoutSVGText* textR oot, LayoutSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap) |
| 234 { | 268 { |
| 235 ASSERT(textRoot); | 269 ASSERT(textRoot); |
| 236 MeasureTextData data(&allCharactersMap); | 270 MeasureTextData data(&allCharactersMap); |
| 237 walkTree(textRoot, stopAtLeaf, &data); | 271 walkTree(textRoot, stopAtLeaf, &data); |
| 238 } | 272 } |
| 239 | 273 |
| 240 } // namespace blink | 274 } // namespace blink |
| OLD | NEW |